Hibernate Search(Lucene)过滤集合

时间:2015-01-29 15:49:53

标签: lucene hibernate-search

我在使用Hibernate Search Filter实现布尔逻辑时遇到问题。 有些人可以成为团体的一部分。每个组都具有状态目录中的状态。

我需要过滤组1中的所有用户并具有状态2.为此我对两个子句使用Occur.MUST的布尔查询,但是在过滤结果中包含具有grop列表的人员其中一个是1,该组的一个状态是2,例如:

person | group | status
105      (1)       3
105       2        3
105       3       (2)

188      (1)       3
188       7       (2)

197      (1)       4
197       8        5
197       9       (2)

用户105,188和197不包括在过滤结果中。完成这项工作的正确方法是什么?

过滤器:

BooleanQuery bq = new BooleanQuery();
TermQuery tqGroup = new TermQuery(new Term("groupPersons.id.groupId", "1"));
TermQuery tqStatus = new TermQuery(new Term("groupPersons.status.id", "2"));
bq.add(tqGroup, BooleanClause.Occur.MUST);
bq.add(tqStatus, BooleanClause.Occur.MUST);
filter = new QueryWrapperFilter(bq);

人物实体:

...
private List<GroupPerson> groupPersons = new ArrayList<GroupPerson>(0);

@IndexedEmbedded
@OneToMany(fetch = FetchType.LAZY, mappedBy = "person")
public List<GroupPerson> getGroupPersons() {
    return this.groupPersons;
}

GroupPerson实体:

...

@EmbeddedId
@AttributeOverrides({
        @AttributeOverride(name = "groupId", column = @Column(name = "group_id", nullable = false)),
        @AttributeOverride(name = "personId", column = @Column(name = "person_id", nullable = false)) })
@NotNull
@DocumentId
@FieldBridge(impl = GroupPersonIdBridge.class) 
public GroupPersonId getId() {
    return this.id;
}

...

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "status_id",nullable = false)
@IndexedEmbedded
@NotNull
public Status getStatus() {
     return this.Status;
}

OrganizationPersonIdBridge:

public Object get(String name, Document document) {
    GroupPersonId id = new GroupPersonId();
    Field field = document.getField( name + ".groupId" );
    id.setGroupId(Long.parseLong(field.stringValue()));
    field = document.getField( name + ".personId" );
    id.setPersonId(Long.parseLong(field.stringValue()));
    return id;
  }

  public String objectToString(Object object) {
     GroupPersonId id = (GroupPersonId) object;
    StringBuilder sb = new StringBuilder();
    sb.append( id.getGroupId() )
    .append(" ")
    .append(id.getPersonId());
    return sb.toString();
  }


  public void set(String name,Object value,Document document,LuceneOptions luceneOptions) {
    GroupPersonId id = (GroupPersonId)value;
    Store store = luceneOptions.getStore();
    Index index = luceneOptions.getIndex();
    TermVector termVector = luceneOptions.getTermVector();
    Float boost = luceneOptions.getBoost();
    //store each property in a unique field
    Field field = new Field(name + ".groupId", id.getGroupId() + "", store, index, termVector);
    field.setBoost( boost );
    document.add( field );

    field = new Field(name + ".personId", id.getPersonId() + "", store, index, termVector);
    field.setBoost( boost );
    document.add( field );
    //store the unique string representation in the named field
    field = new Field( name,
    objectToString( id ),
    store, index, termVector );
    field.setBoost( boost );
    document.add( field );
  } 

Hibernate搜索的版本是4.5.1.Final

2 个答案:

答案 0 :(得分:2)

问题是Lucene Document没有关联。当您使用@IndexedEmbedded时,您实际上将所有关联扁平化为单个Lucene Document(这是将其添加到Lucene索引并在搜索时检索的)。 Document可以多次添加具有相同名称的字段。举个例子,Document的{​​{1}}代码为105将包含字段值对的以下字段名称:

Person

如果您现在查看您的查询,您就会明白为什么人105是匹配的。两个布尔查询都匹配。

你怎么解决这个问题?您需要确保有一些独特的搜索功能。这样做的一种方法是使用自定义网桥将组和状态索引到单个字段中。然后,您可以编写一个仅针对该字段的查询。

答案 1 :(得分:0)

对于具有相同用例的人,以下是使用classBridge的解决方案:

public class CustomClassBridge implements FieldBridge, Serializable {
public final static String SEPARATOR = "-";

@Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
    GroupPerson gp = (GroupPerson)value;
    String fieldValue = gp.getId().getGroupId() + SEPARATOR + gp.getStatus().getId();
    Field field = new Field(name, fieldValue, luceneOptions.getStore(), luceneOptions.getIndex(), luceneOptions.getTermVector());
    field.setBoost(luceneOptions.getBoost());
    document.add(field);
}
}

在类级别向GroupPerson实体添加注释:

@ClassBridge(name="groupStatus",index=Index.YES, analyze=Analyze.NO, store=Store.YES, impl = CustomClassBridge.class)

最后在过滤器中:

TermQuery tq = new TermQuery(new Term("groupPersons.groupStatus", 1 + CustomClassBridge.SEPARATOR + 2));