【问题标题】:AND-searching child objects in Lucene / Hibernate-search在 Lucene / Hibernate-search 中搜索子对象
【发布时间】:2013-02-20 19:47:30
【问题描述】:

在我的 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

}

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
}

当我尝试构造一个强制某个能力和最低级别的查询时,它似乎找到了所有具有该能力和 any 具有指定级别的能力的人。如何使其仅返回具有正确 UserCompetence 子级的用户?我怀疑我需要重新映射一些索引才能完成这项工作。

【问题讨论】:

    标签: lucene hibernate-search


    【解决方案1】:

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

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

    【讨论】:

    • 我尝试为 UserCompetence 编制索引,但重建索引所需的时间从 30 秒缩短到将近一个小时。如果我采用该解决方案,将如何进行搜索?我仍然希望搜索用户对象而不是用户能力对象。我还发现了在单个索引中组合字段的可能性,这本身似乎有些问题。
    • 您可以为 UserUserCompetence 创建索引,并根据查询定位正确的索引。你如何重建索引?质量索引器可以提高性能。或者根据您的索引,仅依赖自动索引作为数据库更改的一部分。您可能想查看@IndexedEmbedded#depth。根据您的对象图,您可能会索引太多。关于搜索结果。对于您提到的特定查询,您将搜索 UserCompetence 实例,然后从那里导航到用户。
    • 我计划在夜间工作中使用海量索引器,同时依靠休眠搜索在白天更新更改。我不太喜欢查询 UserCompetence 而不是 User 的解决方案,这听起来可能会导致以后的麻烦,并且听起来它会随着能力的数量线性增加搜索时间,并且还会使用我的 boost/norm 值来做事。我找到了一个似乎工作得很好的解决方案:使用桥将能力和级别组合在一个字段中,然后使用短语查询来搜索能力级别的确切实例。
    • 一个问题,从网桥获取的相关实体的更改会更新索引吗?
    【解决方案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());
    }
    

    这提升了高水平的人,看起来很快。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多