我可以在没有SearchSession和Class的情况下在Hibernate Search中构建SearchPredi



我正在尝试将一个使用ElasticSearch的项目迁移到Hibernate Search。目前,我们通过组合由以下不同方法创建的多个QueryBuilder对象来构建复杂的ElasticSearch查询:

protected QueryBuilder createSetQuery(String key, Set<?> values, boolean contains) {
if (contains && !values.isEmpty()) {
return QueryBuilders.termsQuery(key, values);
} else if (!contains && Objects.nonNull(values)) {
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
return boolQuery.mustNot(QueryBuilders.termsQuery(key, values));
} else {
return QueryBuilders.matchQuery(key, 0);
}
}

然后,返回的QueryBuilder对象通过BooleanQueryBuilder对象组合到调用方法中,如下所示:

...
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
boolQuery.must(createSetQuery(...);
...

现在,我尝试将这些方法迁移到类似的Hibernate Search方法中,返回SearchPredicates而不是QueryBuilders(阅读Hibernate Search文档https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single我得到的印象是最接近的匹配(:

protected SearchPredicate createSetSearchPredicate(Class<?> clazz, SearchSession searchSession, String fieldName,
Set<?> fieldValues, boolean contains) {
if (contains) {
SearchScope<?> scope = searchSession.scope(clazz);
SearchPredicateFactory factory = scope.predicate();
return factory.terms().field( fieldName ).matchingAny( fieldValues ).toPredicate();
} else {
SearchScope<?> scope = searchSession.scope(clazz);
SearchPredicateFactory factory = scope.predicate();
return factory.bool().mustNot(factory.terms().field( fieldName ).matchingAny( fieldValues )).toPredicate();
}
}

我遇到的问题是创建一个SearchPredicate似乎总是需要一个给定的类和一个Hibernate SearchSearchSession。有没有办法在没有这两个对象的情况下创建这样的SearchPredicate实例?在类似的";阶级不可知论者;就像构建ElasticSearchQueryBuilder一样?

目前,我必须选择

  • 在最外层的调用方法中创建SearchSession,并通过所有中间方法将其与Class对象一起传递,直到我到达SearchPredicate创建方法或
  • 在每个SearchPredicate创建方法中创建SearchSession对象

我的问题:

  • 以后在每个方法中创建SearchSession对象是个问题吗?我是否必须确保重用现有会话,或者SearchSession已经是一个单例,我不必担心这一点
  • 有没有一种方法可以在没有ClassSearchSession对象的情况下创建SearchPredicate,类似于我们创建ElasticSearchQueryBuilder对象的方法
  • 有没有更合适的方法来做我正在尝试的事情?其他一些Hibernate Search类比SearchPredicate更适合作为可组合的ElasticSearchQueryBuilder对象的替代品

非常感谢您的帮助!

我建议看一下参考文档的这一部分,了解如何使用Hibernate Search API。

你可以这样做:

protected List<MyEntity> myOuterMostMethod() {
List<MyEntity> hits = searchSession.search( MyEntity.class )
.where( f -> f.bool( b -> { 
b.must( createSetSearchPredicate( f, "myField1",
Set.of( "val1", "val2" ), true ) );
b.must( createSetSearchPredicate( f, "myField2",
Set.of( "val3", "val4" ), false ) );
} ) )
.fetchHits( 20 ); 
}

protected SearchPredicate createSetSearchPredicate(SearchPredicateFactory factory,
String fieldName, Set<?> fieldValues, boolean contains) {
if (contains) {
return factory.terms()
.field( fieldName ).matchingAny( fieldValues )
.toPredicate();
} else {
return factory.bool().mustNot(factory.terms()
.field( fieldName ).matchingAny( fieldValues ))
.toPredicate();
}
}

你也可以这样做,在我看来更清楚:


protected List<MyEntity> myOuterMostMethod() {
List<MyEntity> hits = searchSession.search( MyEntity.class )
.where( f -> f.bool( b -> { 
b.must( containAny( f, "myField1", Set.of( "val1", "val2" ) ) );
b.mustNot( containAny( f, "myField2", Set.of( "val3", "val4" ) ) );
} ) )
.fetchHits( 20 ); 
}

protected SearchPredicate containAny(SearchPredicateFactory factory,
String fieldName, Set<?> fieldValues) {
return factory.terms().field( fieldName ).matchingAny( fieldValues ).toPredicate();
}

关于您的问题:

以后在每个方法中创建SearchSession对象是个问题吗?我是否必须确保重用现有会话,或者SearchSession已经是一个单例,我不必担心这一点?

后台有一定程度的缓存,所以从性能角度来看,应该不会太差。但实际上,没有必要每次都重新创建SearchSession。只需获取SearchPredicateFactory,如我的示例所示,并将其传递给您的方法。

有没有一种方法可以在没有Class和SearchSession对象的情况下创建SearchPredicates,类似于我们创建ElasticSearch QueryBuilder对象的方法?

不是。Hibernate Search检查您引用的字段是否确实存在,并使用元数据将您传递的值转换为JSON。它需要上下文来访问元数据,所以它不能仅仅从静态方法调用中创建谓词。

但是,您可以在没有会话的情况下创建搜索谓词。这消除了从Hibernate ORM获得一个打开的EntityManager/Session的要求,但您仍然需要访问EntityManagerFactory/SessionFactory(其中包含Hibernate Search需要的元数据(,并且您仍然需要告诉Hibernate Search您将针对哪个类。类似这样的东西:

EntityManagerFactory entityManagerFactory = ...;
SearchScope<?> scope = Search.mapping(entityManagerFactory).scope(clazz);
SearchPredicateFactory factory = scope.predicate()

此外,如果您真的想绕过Hibernate Search元数据,可以直接将JSON查询传递给Hibernate Search。

有没有更合适的方法来做我正在尝试的事情?其他一些Hibernate Search类比SearchPredicates更适合作为可组合的ElasticSearch QueryBuilder对象的替代品?

您使用的类是正确的。我认为您只需要接受这样一个事实:Hibernate Search需要一些上下文(SearchPredicateFactory实例(来创建谓词,并且不提供创建谓词的静态方法。

最新更新