用于减少 HibernateSearch SearchPredicate 流的累加器函数
Accumulator function to reduce stream of HibernateSearch SearchPredicates
以下摘自 https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#query-predicate 的示例(示例 183)解释了如何将多个过滤器应用于 HibernateSearch 查询:
List<Book> hits = searchSession.search( Book.class )
.where( f -> f.bool()
.should( f.bool()
.filter( f.match().field( "genre" )
.matching( Genre.SCIENCE_FICTION ) )
.must( f.match().fields( "description" )
.matching( "crime" ) )
)
.should( f.bool()
.filter( f.match().field( "genre" )
.matching( Genre.CRIME_FICTION ) )
.must( f.match().fields( "description" )
.matching( "robot" ) )
)
)
.fetchHits( 20 );
但是,如果我对文档的理解正确,我总是必须单独应用过滤器(此处:类型 =“科幻小说”或类型 = 犯罪小说)。
相反,我需要将任意过滤器列表应用于 HibernateSearch 搜索查询,例如作为 Map<String, String>
提供,其中每个条目键代表一个过滤器字段名称,条目值是过滤器字段值。
我尝试首先通过以下方式创建搜索谓词列表:
List<SearchPredicate> searchPredicates = new ArrayList<>();
for (Map.Entry<String, String> filterEntry : filterMap.entrySet()) {
searchPredicates.add(scope.predicate().match().field(filterEntry.getKey())
.matching(filterEntry.getValue()).toPredicate());
}
然后通过创建流在查询的 filter
子句中应用此谓词列表,然后按照 https://www.baeldung.com/java-predicate-chain.
中的说明减少流
但是我无法为流的“reduce”函数识别合适的 'identity' SearchPredicate 和 'accumulator' 函数参数(我使用 must()
而不是 should()
因为我希望 所有 我的过滤器应用而不是只应用一个,所以逻辑合取而不是上面示例中的析取):
List<MyClass> hits = searchSession.search(MyClass.class)
.where( f -> f.bool()
.must( f.bool()
.filter(searchPredicates.stream()
.reduce(SearchPredicateFactory::matchAll, (partialResult, nextFilter) -> f.bool().must(nextFilter))))
.fetchHits(10));
不过,“reduce”函数的这两个参数都不好:
- 我选择
SearchPredicateFactory::matchAll
是因为我想在没有应用过滤器的情况下获取 所有 文档,但在这里我得到错误 SearchPredicate is not a functional interface
.
(partialResult, nextFilter) -> f.bool().must(nextFilter))
应该将下一个 SearchPredicate
过滤器添加到现有过滤器链中,但在这里我收到错误消息 Bad return type in lambda expression: capture of ? cannot be converted to SearchPredicate
.
是否有适用于 HibernateSearch SearchPredicates 的累加器函数(如果有:对应的 'identity' 是什么?)还是我需要自己写一个?
(抱歉,如果这是一个天真的问题,我阅读了 HibernateSearch 和 Java 谓词的文档,但我想我仍然是这些主题的相对初学者,并且找不到与我相同的问题试图解决 - 将搜索谓词列表应用于 HibernateSearch 查询 - 在其他地方介绍)
编辑:我在阅读 https://www.baeldung.com/java-stream-reduce 后更新了我的尝试,这至少让我对“reduce”及其参数应该如何工作有了更多的了解!
我会说 reduce
不是这项工作的正确工具。
Hibernate Search DSL 提供了 builder-like 个可以聚合多个谓词的实例(如下面的 b
):只需使用它们即可。你甚至不需要这里的流。
使用 f.bool(b -> { ... })
还不够吗,其中 b
就像一个累加器,像这样?
Map<String, String> searchParameters = getSearchParameters();
List<MyClass> hits = searchSession.search( MyClass.class )
.where( f -> f.bool( b -> {
b.must( f.matchAll() ); // By default, return all documents
// ... unless field/value pairs are provided,
// in which case return documents that match every field/value pair.
for ( Map.Entry<String, String> filterEntry : filterMap.entrySet() ) {
b.must( f.match().field( filterEntry.getKey() )
.matching( filterEntry.getValue() ) );
}
} ) )
.fetchHits( 10 );
这与 this section of the documentation 中的建议差不多。
如果你真的出于某种原因需要使用流,我想这样的事情可能会起作用:
List<SearchPredicate> searchPredicates = getSearchPredicates();
List<MyClass> hits = searchSession.search( MyClass.class )
.where( f -> f.bool( b -> {
b.must( f.matchAll() ); // By default, return all documents
// ... unless predicates are provided,
// in which case return documents that match every predicate.
searchPredicates.stream().forEach( b::must );
} ) )
.fetchHits( 10 );
如果你是一个函数式编程的极端主义者,使用 collect
而不是 reduce
可以工作,但坦率地说,这是非常丑陋的代码,比上面的两个建议更糟糕:
List<SearchPredicate> searchPredicates = getSearchPredicates();
List<MyClass> hits = searchSession.search( MyClass.class )
.where( f -> f.bool()
.must( f.matchAll() ) // By default, return all documents
// PLEASE DO YOUR COWORKERS A FAVOR: DON'T DO THIS
.must(searchPredicates.stream().collect(
() -> f.bool(),
BooleanPredicateClausesStep::must,
(left, right) -> f.bool().must( left ).must( right ) ) )
)
.fetchHits( 10 );
以下摘自 https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#query-predicate 的示例(示例 183)解释了如何将多个过滤器应用于 HibernateSearch 查询:
List<Book> hits = searchSession.search( Book.class )
.where( f -> f.bool()
.should( f.bool()
.filter( f.match().field( "genre" )
.matching( Genre.SCIENCE_FICTION ) )
.must( f.match().fields( "description" )
.matching( "crime" ) )
)
.should( f.bool()
.filter( f.match().field( "genre" )
.matching( Genre.CRIME_FICTION ) )
.must( f.match().fields( "description" )
.matching( "robot" ) )
)
)
.fetchHits( 20 );
但是,如果我对文档的理解正确,我总是必须单独应用过滤器(此处:类型 =“科幻小说”或类型 = 犯罪小说)。
相反,我需要将任意过滤器列表应用于 HibernateSearch 搜索查询,例如作为 Map<String, String>
提供,其中每个条目键代表一个过滤器字段名称,条目值是过滤器字段值。
我尝试首先通过以下方式创建搜索谓词列表:
List<SearchPredicate> searchPredicates = new ArrayList<>();
for (Map.Entry<String, String> filterEntry : filterMap.entrySet()) {
searchPredicates.add(scope.predicate().match().field(filterEntry.getKey())
.matching(filterEntry.getValue()).toPredicate());
}
然后通过创建流在查询的 filter
子句中应用此谓词列表,然后按照 https://www.baeldung.com/java-predicate-chain.
但是我无法为流的“reduce”函数识别合适的 'identity' SearchPredicate 和 'accumulator' 函数参数(我使用 must()
而不是 should()
因为我希望 所有 我的过滤器应用而不是只应用一个,所以逻辑合取而不是上面示例中的析取):
List<MyClass> hits = searchSession.search(MyClass.class)
.where( f -> f.bool()
.must( f.bool()
.filter(searchPredicates.stream()
.reduce(SearchPredicateFactory::matchAll, (partialResult, nextFilter) -> f.bool().must(nextFilter))))
.fetchHits(10));
不过,“reduce”函数的这两个参数都不好:
- 我选择
SearchPredicateFactory::matchAll
是因为我想在没有应用过滤器的情况下获取 所有 文档,但在这里我得到错误SearchPredicate is not a functional interface
. (partialResult, nextFilter) -> f.bool().must(nextFilter))
应该将下一个SearchPredicate
过滤器添加到现有过滤器链中,但在这里我收到错误消息Bad return type in lambda expression: capture of ? cannot be converted to SearchPredicate
.
是否有适用于 HibernateSearch SearchPredicates 的累加器函数(如果有:对应的 'identity' 是什么?)还是我需要自己写一个?
(抱歉,如果这是一个天真的问题,我阅读了 HibernateSearch 和 Java 谓词的文档,但我想我仍然是这些主题的相对初学者,并且找不到与我相同的问题试图解决 - 将搜索谓词列表应用于 HibernateSearch 查询 - 在其他地方介绍)
编辑:我在阅读 https://www.baeldung.com/java-stream-reduce 后更新了我的尝试,这至少让我对“reduce”及其参数应该如何工作有了更多的了解!
我会说 reduce
不是这项工作的正确工具。
Hibernate Search DSL 提供了 builder-like 个可以聚合多个谓词的实例(如下面的 b
):只需使用它们即可。你甚至不需要这里的流。
使用 f.bool(b -> { ... })
还不够吗,其中 b
就像一个累加器,像这样?
Map<String, String> searchParameters = getSearchParameters();
List<MyClass> hits = searchSession.search( MyClass.class )
.where( f -> f.bool( b -> {
b.must( f.matchAll() ); // By default, return all documents
// ... unless field/value pairs are provided,
// in which case return documents that match every field/value pair.
for ( Map.Entry<String, String> filterEntry : filterMap.entrySet() ) {
b.must( f.match().field( filterEntry.getKey() )
.matching( filterEntry.getValue() ) );
}
} ) )
.fetchHits( 10 );
这与 this section of the documentation 中的建议差不多。
如果你真的出于某种原因需要使用流,我想这样的事情可能会起作用:
List<SearchPredicate> searchPredicates = getSearchPredicates();
List<MyClass> hits = searchSession.search( MyClass.class )
.where( f -> f.bool( b -> {
b.must( f.matchAll() ); // By default, return all documents
// ... unless predicates are provided,
// in which case return documents that match every predicate.
searchPredicates.stream().forEach( b::must );
} ) )
.fetchHits( 10 );
如果你是一个函数式编程的极端主义者,使用 collect
而不是 reduce
可以工作,但坦率地说,这是非常丑陋的代码,比上面的两个建议更糟糕:
List<SearchPredicate> searchPredicates = getSearchPredicates();
List<MyClass> hits = searchSession.search( MyClass.class )
.where( f -> f.bool()
.must( f.matchAll() ) // By default, return all documents
// PLEASE DO YOUR COWORKERS A FAVOR: DON'T DO THIS
.must(searchPredicates.stream().collect(
() -> f.bool(),
BooleanPredicateClausesStep::must,
(left, right) -> f.bool().must( left ).must( right ) ) )
)
.fetchHits( 10 );