在Lucene / Hibernate-search中搜索子对象

时间:2013-02-15 14:39:38

标签: lucene hibernate-search

在我的网络应用程序中,用户的能力数据可以按0-5排名存储。使用Hibernate搜索(建立在Lucene之上),我希望构建一个查询,查找具有最低排名A的权限X的所有用户,例如排名为3的Java。此外,我希望在结果中提高排名在排名为3的用户之前放置排名为4的用户。我觉得这应该是可能的,但我无法弄清楚如何组合子对象中的字段。我的数据结构看起来像这样(简化):

User.class

@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儿童的用户?我怀疑我需要重新映射一些索引才能使其正常工作。

2 个答案:

答案 0 :(得分:2)

您当前的映射无法尝试执行的操作。用户的所有 UserCompetence 实例将被编入索引,作为同一用户文档的一部分。这种情况下的数据是flatend。如果你编写AND查询,你可能会在一个 UserCompetence 实例中获得一个权限,在另一个实例中获得一个最低级别。它们属于同一个用户,但不属于相同的用户实例,将被编入索引作为同一 UserCompetence 实例的一部分。

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

答案 1 :(得分: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;
}

搜索级别&gt; 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());
}

这可以提升高水平的人,而且看起来非常快。