1

在我的 Web 应用程序中,用户的能力数据可以按 0-5 的排名存储。使用 Hibernate 搜索(建立在 Lucene 之上),我希望构建一个查询来查找所有具有能力 X 且具有最低排名 A 的用户,例如排名 3 的 Java。此外,我希望在结果中提高排名,将排名 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

}

用户能力类

@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

您当前的映射无法执行您尝试执行的操作。用户的所有UserCompetence实例都将作为同一用户Document的一部分进行索引。在这种情况下,数据会变平。如果您编写 AND 查询,您可能会在一个UserCompetence实例中获得一次能力命中,而在另一个实例中获得最低级别的一次命中。它们属于同一个用户,但不属于同一个用户实例的部分将被索引为同一个UserCompetence实例的一部分。

一种解决方法是索引UserCompetence。然后,您可以使用 AND 逻辑进行搜索以匹配单个UserCompetence实例。

于 2013-02-16T16:53:03.323 回答
2

最后,我最终得到了一个自定义桥,它将两个字段合二为一,然后使用短语搜索在组合字段中进行搜索。

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;
}

像这样搜索 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 回答