lucene - 在对象列表中搜索
lucene - search in list of objects
我有实体 ExerciseEntity
,其中包含一个对象列表 identifiers
@ElementCollection
@IndexedEmbedded(targetElement = IdentifierEntity.class)
private Set<IdentifierEntity> identifiers;
列表示例:
"identifiers": [
{
"identifierType": "AM",
"identifierValue": "333333333"
},
{
"identifierType": "FINESS",
"identifierValue": "888888888"
}
]
属性 identifierType
是枚举类型,所以我实现了一个像这样的自定义桥 。
@Enumerated
@Field(name = "identifierType", bridge = @FieldBridge(impl = EnumAsIntegerBridge.class))
private IdentifierTypeEnum identifierType;
我想通过 identifierValue
搜索所有 ExerciseEntity
,其中 identifierType
等于 FINESS。
我尝试了以下查询,但它无法正常工作,当我使用 identifierValue=333333333 进行测试时,我得到了一个结果,但我应该什么也得不到,因为它的类型不是 FINESS。
List<Query> listOfQuery = new ArrayList<>();
listOfQuery.add(getQueryBuilder().keyword().onField("identifiers.identifierType").matching(IdentifierTypeEnum.FINESS.ordinal()).createQuery());
listOfQuery.add(getQueryBuilder().keyword().onField("identifiers.identifierValue").matching(identifierValue).createQuery());
Builder finalLuceneQuery = new BooleanQuery.Builder();
listOfQuery.stream().forEach(query -> finalLuceneQuery.add(query, BooleanClause.Occur.MUST));
FullTextQuery fullTextQuery = getFullTextEntityManager().createFullTextQuery(finalLuceneQuery.build(), ExerciseEntity.class);
有两种解决方法。 Upgrading to Hibernate Search 6.0 或更高版本仅对第一个解决方案是强制性的,但我还是建议升级,因为 Hibernate Search 5.x 不再获得新功能。
解决方案一:嵌套文档
我将展示如何使用 Hibernate Search 6.x 执行此操作,因为 Hibernate Search 5.x.
中没有等效项
在 Hibernate Search 6+ 中,您可以 mark your @IndexedEmbedded
as NESTED
。
@ElementCollection
@IndexedEmbedded(structure = ObjectStructure.NESTED)
private Set<IdentifierEntity> identifiers;
然后 IdentifierEntity
对象的结构将保留在索引中,并且您可以在搜索期间指定您希望两个谓词应用于同一对象,方法是将它们包装在 nested
谓词:
List<ExerciseEntity> hits = searchSession.search( ExerciseEntity.class )
.where( f -> f.nested().objectField( "identifiers" )
.nest( f.bool()
.must( f.match().field( "identifiers.identifierType" )
.matching( IdentifierTypeEnum.FINESS ) )
.must( f.match().field( "identifiers.identifierValue" )
.matching( identifierValue ) )
) )
.fetchHits( 20 );
解决方案 2:两个值的单个字段
有一种技巧可以在不使用嵌套文档的情况下复制上述行为:只需将两个字段合并为一个。
我将展示如何使用 Hibernate Search 5.x 执行此操作,因为您似乎正在使用它,但是使用 Hibernate Search 6.x 可以很好地实现相同的解决方案(使用value bridge).
实施完全符合此要求的自定义网桥:
public class IdentifierAsStringBridge implements MetadataProvidingFieldBridge, StringBridge {
public static String toString(IdentifierTypeEnum type, String value) {
return type.name() + ":" + value;
}
@Override
public void configureFieldMetadata(String name, FieldMetadataBuilder builder) {
builder.field( name, FieldType.String );
}
@Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
if ( value == null ) {
return;
}
Collection<IdentifierEntity> idCollection = (Collection<IdentifierEntity>) value;
for ( IdentifierEntity id : idCollection ) {
luceneOptions.addFieldToDocument( name, toString( id.getIdentifierType(), id.getIdentifierValue() ), document );
}
}
@Override
public String objectToString(Object value) {
if ( value instanceof String ) {
return (String) value;
}
else if ( value instanceof IdentifierEntity ) {
IdentifierEntity id = (IdentifierEntity) value;
return toString( id.getIdentifierType(), id.getIdentifierValue() );
else {
throw new IllegalArgumentException( "This bridge only supports passing arguments of type String or IdentifierEntity" );
}
}
}
在您的“标识符”集合中使用该新桥:
@ElementCollection
@Field(name = "identifiers_strings", bridge = @FieldBridge(impl = IdentifierAsStringBridge.class))
private Set<IdentifierEntity> identifiers;
最后,更新您的查询以定位您刚刚添加的单个字段:
List<Query> listOfQuery = new ArrayList<>();
listOfQuery.add(getQueryBuilder().keyword()
.onField("identifiers_strings")
.matching(IdentifierAsStringBridge.toString(
IdentifierTypeEnum.FINESS, identifierValue
))
.createQuery());
Builder finalLuceneQuery = new BooleanQuery.Builder();
listOfQuery.stream().forEach(query -> finalLuceneQuery.add(query, BooleanClause.Occur.MUST));
FullTextQuery fullTextQuery = getFullTextEntityManager()
.createFullTextQuery(finalLuceneQuery.build(), ExerciseEntity.class);
我有实体 ExerciseEntity
,其中包含一个对象列表 identifiers
@ElementCollection
@IndexedEmbedded(targetElement = IdentifierEntity.class)
private Set<IdentifierEntity> identifiers;
列表示例:
"identifiers": [
{
"identifierType": "AM",
"identifierValue": "333333333"
},
{
"identifierType": "FINESS",
"identifierValue": "888888888"
}
]
属性 identifierType
是枚举类型,所以我实现了一个像这样的自定义桥
@Enumerated
@Field(name = "identifierType", bridge = @FieldBridge(impl = EnumAsIntegerBridge.class))
private IdentifierTypeEnum identifierType;
我想通过 identifierValue
搜索所有 ExerciseEntity
,其中 identifierType
等于 FINESS。
我尝试了以下查询,但它无法正常工作,当我使用 identifierValue=333333333 进行测试时,我得到了一个结果,但我应该什么也得不到,因为它的类型不是 FINESS。
List<Query> listOfQuery = new ArrayList<>();
listOfQuery.add(getQueryBuilder().keyword().onField("identifiers.identifierType").matching(IdentifierTypeEnum.FINESS.ordinal()).createQuery());
listOfQuery.add(getQueryBuilder().keyword().onField("identifiers.identifierValue").matching(identifierValue).createQuery());
Builder finalLuceneQuery = new BooleanQuery.Builder();
listOfQuery.stream().forEach(query -> finalLuceneQuery.add(query, BooleanClause.Occur.MUST));
FullTextQuery fullTextQuery = getFullTextEntityManager().createFullTextQuery(finalLuceneQuery.build(), ExerciseEntity.class);
有两种解决方法。 Upgrading to Hibernate Search 6.0 或更高版本仅对第一个解决方案是强制性的,但我还是建议升级,因为 Hibernate Search 5.x 不再获得新功能。
解决方案一:嵌套文档
我将展示如何使用 Hibernate Search 6.x 执行此操作,因为 Hibernate Search 5.x.
中没有等效项在 Hibernate Search 6+ 中,您可以 mark your @IndexedEmbedded
as NESTED
。
@ElementCollection
@IndexedEmbedded(structure = ObjectStructure.NESTED)
private Set<IdentifierEntity> identifiers;
然后 IdentifierEntity
对象的结构将保留在索引中,并且您可以在搜索期间指定您希望两个谓词应用于同一对象,方法是将它们包装在 nested
谓词:
List<ExerciseEntity> hits = searchSession.search( ExerciseEntity.class )
.where( f -> f.nested().objectField( "identifiers" )
.nest( f.bool()
.must( f.match().field( "identifiers.identifierType" )
.matching( IdentifierTypeEnum.FINESS ) )
.must( f.match().field( "identifiers.identifierValue" )
.matching( identifierValue ) )
) )
.fetchHits( 20 );
解决方案 2:两个值的单个字段
有一种技巧可以在不使用嵌套文档的情况下复制上述行为:只需将两个字段合并为一个。
我将展示如何使用 Hibernate Search 5.x 执行此操作,因为您似乎正在使用它,但是使用 Hibernate Search 6.x 可以很好地实现相同的解决方案(使用value bridge).
实施完全符合此要求的自定义网桥:
public class IdentifierAsStringBridge implements MetadataProvidingFieldBridge, StringBridge {
public static String toString(IdentifierTypeEnum type, String value) {
return type.name() + ":" + value;
}
@Override
public void configureFieldMetadata(String name, FieldMetadataBuilder builder) {
builder.field( name, FieldType.String );
}
@Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
if ( value == null ) {
return;
}
Collection<IdentifierEntity> idCollection = (Collection<IdentifierEntity>) value;
for ( IdentifierEntity id : idCollection ) {
luceneOptions.addFieldToDocument( name, toString( id.getIdentifierType(), id.getIdentifierValue() ), document );
}
}
@Override
public String objectToString(Object value) {
if ( value instanceof String ) {
return (String) value;
}
else if ( value instanceof IdentifierEntity ) {
IdentifierEntity id = (IdentifierEntity) value;
return toString( id.getIdentifierType(), id.getIdentifierValue() );
else {
throw new IllegalArgumentException( "This bridge only supports passing arguments of type String or IdentifierEntity" );
}
}
}
在您的“标识符”集合中使用该新桥:
@ElementCollection
@Field(name = "identifiers_strings", bridge = @FieldBridge(impl = IdentifierAsStringBridge.class))
private Set<IdentifierEntity> identifiers;
最后,更新您的查询以定位您刚刚添加的单个字段:
List<Query> listOfQuery = new ArrayList<>();
listOfQuery.add(getQueryBuilder().keyword()
.onField("identifiers_strings")
.matching(IdentifierAsStringBridge.toString(
IdentifierTypeEnum.FINESS, identifierValue
))
.createQuery());
Builder finalLuceneQuery = new BooleanQuery.Builder();
listOfQuery.stream().forEach(query -> finalLuceneQuery.add(query, BooleanClause.Occur.MUST));
FullTextQuery fullTextQuery = getFullTextEntityManager()
.createFullTextQuery(finalLuceneQuery.build(), ExerciseEntity.class);