集合上的 Hibernate Search (Lucene) 过滤器
Hibernate Search (Lucene) filter on collections
我在使用 Hibernate 搜索过滤器实现布尔逻辑时遇到问题。
有些人可以成为团体的一部分。每个组都有来自状态目录的状态。
我需要过滤组 1 中状态为 2 的所有用户。为此,我对两个子句都使用了带有 Occur.MUST 的布尔查询,但在过滤结果中包括了那些有 grops 列表,其中一个是 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
问题是 Lucene Document
没有关联。当您使用 @IndexedEmbedded
时,您实际上是将所有关联扁平化为单个 Lucene Document
(这是添加到 Lucene 索引并在搜索时检索的内容)。 Document
可以多次添加同名字段。以您的示例为例,ID 为 105 的 Person
的 Document
将包含以下字段名称到字段值对:
+-------------------------+-------------+
| field name | field value |
+-------------------------+-------------+
| groupPersons.id.groupId | 1 |
| groupPersons.id.groupId | 2 |
| groupPersons.id.groupId | 3 |
| groupPersons.status.id | 3 |
| groupPersons.status.id | 3 |
| groupPersons.status.id | 2 |
+-------------------------+-------------+
如果您现在查看您的查询,就会明白为什么人 105 是匹配项。两个布尔查询匹配。
你是怎么解决这个问题的?您需要确保有一些独特的东西可以搜索。这样做的一种方法是使用自定义桥将组和状态索引到单个字段中。然后您可以编写一个只针对该字段的查询。
对于有相同用例的人,这里是使用 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);
}
}
在 class 级别向 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));
我在使用 Hibernate 搜索过滤器实现布尔逻辑时遇到问题。 有些人可以成为团体的一部分。每个组都有来自状态目录的状态。
我需要过滤组 1 中状态为 2 的所有用户。为此,我对两个子句都使用了带有 Occur.MUST 的布尔查询,但在过滤结果中包括了那些有 grops 列表,其中一个是 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
问题是 Lucene Document
没有关联。当您使用 @IndexedEmbedded
时,您实际上是将所有关联扁平化为单个 Lucene Document
(这是添加到 Lucene 索引并在搜索时检索的内容)。 Document
可以多次添加同名字段。以您的示例为例,ID 为 105 的 Person
的 Document
将包含以下字段名称到字段值对:
+-------------------------+-------------+
| field name | field value |
+-------------------------+-------------+
| groupPersons.id.groupId | 1 |
| groupPersons.id.groupId | 2 |
| groupPersons.id.groupId | 3 |
| groupPersons.status.id | 3 |
| groupPersons.status.id | 3 |
| groupPersons.status.id | 2 |
+-------------------------+-------------+
如果您现在查看您的查询,就会明白为什么人 105 是匹配项。两个布尔查询匹配。
你是怎么解决这个问题的?您需要确保有一些独特的东西可以搜索。这样做的一种方法是使用自定义桥将组和状态索引到单个字段中。然后您可以编写一个只针对该字段的查询。
对于有相同用例的人,这里是使用 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);
}
}
在 class 级别向 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));