1

私の Web アプリケーションでは、ユーザーの能力データを 0 ~ 5 のランキングで保存できます。Hibernate 検索 (Lucene の上に構築) を使用して、ランキング 3 の Java など、コンピテンス X が最小ランク A のすべてのユーザーを検索するクエリを作成したいと考えています。 、ランキング4のユーザーをランキング3のユーザーの前に配置します。これは可能だと思いますが、子オブジェクトのフィールドを組み合わせる方法がわかりません。私のデータ構造は次のようになります(簡略化):

ユーザークラス

@Entity
@Table(schema = "COMPETENCE", name = "users")
@Indexed
public class User{    
    @Id
    @Field(store = Store.YES, index = Index.UN_TOKENIZED)
    private Long id;

    @OneToMany(mappedBy = "user")
    @IndexedEmbedded
    private List<UserCompetence> competenceList= new ArrayList<UserCompetence>();

    // Snip: Other irrelevant fields and get/setters

}

UserCompetence.class

@Entity
@Table(name = "user_competence")
public class Competence {

    @ManyToOne
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    @NotNull
    @ContainedIn
    private User user;

    @ManyToOne
    @JoinColumn(name = "competence_id", referencedColumnName = "id")
    @NotNull
    @IndexedEmbedded
    private Competence competence;

    @Basic
    @Field(index = Index.UN_TOKENIZED, store = Store.YES)
    @NumericField
    private Integer level;

     // Snip: Other irrelevant fields and get/setters
}

@Entity()
@Table(name = "competence")
public class Competence{

    @Column(name = "name")
    @Field(index = Index.TOKENIZED, store = Store.YES)
    private String name;

    // Snip: Other irrelevant fields and get/setters
}

特定のコンピテンスと最低レベルを強制するクエリを作成しようとすると、そのコンピテンスと指定されたレベルのコンピテンスを持つすべての人が検索されるようです。正しい UserCompetence の子を持つユーザーのみを返すようにするにはどうすればよいですか? これを機能させるには、インデックスの一部を再マップする必要があると思います。

4

2 に答える 2

2

最終的に、2つのフィールドを1つに結合するカスタムブリッジを作成し、フレーズ検索を使用して結合されたフィールドを検索しました。

public class UserCompetenceBridge implements FieldBridge {
    @Override
    public void set(
            String name, Object value, Document document, LuceneOptions luceneOptions) {
        UserCompetence pc = (UserCompetence ) value;

        // Add competence level + competence id combined field for specific competence querying
        String lvl = pc.getLevel() == null ? "0" : pc.getLevel().toString();
        String comp = pc.getCompetence().getId().toString();

        String fieldValue = comp + SearchFields.FIELD_SEPERATOR + lvl;
        Field compLvl = new Field(SearchFields.COMPETENCE_LEVEL, fieldValue, Field.Store.NO, Field.Index.NOT_ANALYZED);
        compLvl.setBoost(luceneOptions.getBoost());
        document.add(compLvl);

        // Add competence names for free text search
        Field compName = new Field(SearchFields.COMPETENCE_NAME, pc.getCompetence().getName(), Field.Store.NO, Field.Index.ANALYZED);
        document.add(compName);

    }
}

@Entity
@Table(name = "user_competence")
@ClassBridge(impl = UserCompetenceBridge.class)
public class UserCompetence {

    @ManyToOne
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    @ContainedIn
    private User user;

    @ManyToOne
    @JoinColumn(name = "competence_id", referencedColumnName = "id")
    private Competence competence;

    @Basic
    @Column(name = "competence_level")
    private Integer level;
}

次のようにレベル>xを検索します。

for (CompetenceParam cp : param.getCompetences()) {
    BooleanJunction or = qb.bool();
    for(int i = cp.getMinLevel(); i <= 5 ; i++){
        or = or.should(qb.phrase()
                .onField(SearchFields.COMPETENCE_LEVEL)
                .boostedTo(1 + i/5f)
                .sentence(cp.getCompetenceId() + " " + i)
                .createQuery());
    }
    queries.add(or.createQuery());
}

これは高レベルの人々を後押しし、非常に速いようです。

于 2013-02-20T11:47:30.040 に答える
2

あなたがやろうとしていることは、現在のマッピングでは不可能です。ユーザーのすべてのUserCompetenceインスタンスは、同じユーザーDocumentの一部として索引付けされます。この場合、データはフラットエンドになります。AND クエリを作成すると、1 つのUserCompetenceインスタンスで 1 つのコンピテンス ヒットが得られ、別のインスタンスで最小レベルの 1 つのヒットが得られる可能性があります。それらは同じユーザーに属していますが、同じUserCompetenceインスタンスの一部として索引付けされるユーザーの同じインスタンスの一部ではありません。

1 つの方法は、UserCompetenceのインデックスを作成することです。次に、AND ロジックを使用して検索し、単一のUserCompetenceインスタンスに一致させることができます。

于 2013-02-16T16:53:03.323 に答える