Skip to content

Commit

Permalink
HSEARCH-4676 Use .and() syntax by default with .where(BiFunction)
Browse files Browse the repository at this point in the history
  • Loading branch information
yrodiere committed Aug 25, 2022
1 parent 962e04a commit 24cb2ed
Show file tree
Hide file tree
Showing 14 changed files with 121 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import org.hibernate.search.engine.backend.session.spi.BackendSessionContext;
import org.hibernate.search.engine.search.loading.spi.SearchLoadingContextBuilder;
import org.hibernate.search.engine.search.predicate.SearchPredicate;
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateOptionsCollector;
import org.hibernate.search.engine.search.predicate.dsl.PredicateFinalStep;
import org.hibernate.search.engine.search.predicate.dsl.SimpleBooleanOperatorPredicateClausesCollector;
import org.hibernate.search.engine.search.projection.SearchProjection;
import org.hibernate.search.engine.search.projection.dsl.ProjectionFinalStep;
import org.hibernate.search.engine.search.projection.spi.ProjectionAccumulator;
Expand Down Expand Up @@ -101,7 +101,7 @@ public ElasticsearchSearchQueryOptionsStep<E, LOS> where(

@Override
public ElasticsearchSearchQueryOptionsStep<E, LOS> where(
BiConsumer<? super ElasticsearchSearchPredicateFactory, ? super BooleanPredicateOptionsCollector<?>> predicateContributor) {
BiConsumer<? super ElasticsearchSearchPredicateFactory, ? super SimpleBooleanOperatorPredicateClausesCollector<?>> predicateContributor) {
return selectEntity().where( predicateContributor );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import org.hibernate.search.engine.backend.session.spi.BackendSessionContext;
import org.hibernate.search.engine.search.loading.spi.SearchLoadingContextBuilder;
import org.hibernate.search.engine.search.predicate.SearchPredicate;
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateOptionsCollector;
import org.hibernate.search.engine.search.predicate.dsl.PredicateFinalStep;
import org.hibernate.search.engine.search.predicate.dsl.SimpleBooleanOperatorPredicateClausesCollector;
import org.hibernate.search.engine.search.projection.SearchProjection;
import org.hibernate.search.engine.search.projection.dsl.ProjectionFinalStep;
import org.hibernate.search.engine.search.projection.spi.ProjectionAccumulator;
Expand Down Expand Up @@ -100,7 +100,7 @@ public LuceneSearchQueryOptionsStep<E, LOS> where(

@Override
public LuceneSearchQueryOptionsStep<E, LOS> where(
BiConsumer<? super LuceneSearchPredicateFactory, ? super BooleanPredicateOptionsCollector<?>> predicateContributor) {
BiConsumer<? super LuceneSearchPredicateFactory, ? super SimpleBooleanOperatorPredicateClausesCollector<?>> predicateContributor) {
return selectEntity().where( predicateContributor );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -813,13 +813,32 @@ It is possible to define the `and` predicate inside a lambda expression.
This is especially useful when clauses need to be added dynamically to the `and` predicate,
for example based on user input.

.Easily adding clauses dynamically using `with(...)` and the lambda syntax
.Easily adding clauses dynamically using `.where(...)` and the lambda syntax
====
[source, JAVA, indent=0, subs="+callouts"]
----
include::{sourcedir}/org/hibernate/search/documentation/search/predicate/PredicateDslIT.java[tags=and-dynamicParameters-root]
----
<1> Get a custom object holding the search parameters provided by the user through a web form, for example.
<2> Call `.where(BiConsumer)`.
The consumer, implemented by a lambda expression, will receive a predicate factory as well as clause collector as an argument,
and will add clauses to that collector as necessary.
<3> By default, a boolean predicate will match nothing if there is no clause.
To match every document when there is no clause, add a `and` clause that matches everything.
<4> Inside the lambda, the code is free to use any Java language constructs, such as `if` or `for`,
to control the addition of clauses.
In this case, we only add clauses if the relevant parameter was filled in by the user.
====

Another syntax relying on the method `with(...)` can be useful when the `and` predicate is not the root predicate:

.Easily adding clauses dynamically using `with(...)` and the lambda syntax
====
[source, JAVA, indent=0, subs="+callouts"]
----
include::{sourcedir}/org/hibernate/search/documentation/search/predicate/PredicateDslIT.java[tags=and-dynamicParameters-with]
----
<1> Get a custom object holding the search parameters provided by the user through a web form, for example.
<2> Call `.where(Function)`.
The function, implemented by a lambda expression, will receive a predicate factory, use it to build an `and` predicate,
invoke the `with(Consumer)` method and return this predicate. The consumer, implemented by a lambda expression,
Expand Down Expand Up @@ -1056,24 +1075,8 @@ It is possible to define the `bool` predicate inside a lambda expression.
This is especially useful when clauses need to be added dynamically to the `bool` predicate,
for example based on user input.

.Easily adding clauses dynamically using `.where(...)` and the lambda syntax
====
[source, JAVA, indent=0, subs="+callouts"]
----
include::{sourcedir}/org/hibernate/search/documentation/search/predicate/PredicateDslIT.java[tags=bool-dynamicParameters-root]
----
<1> Get a custom object holding the search parameters provided by the user through a web form, for example.
<2> Call `.where(BiConsumer)`.
The consumer, implemented by a lambda expression, will receive a predicate factory as well as clause collector as an argument,
and will add clauses to that collector as necessary.
<3> By default, a boolean predicate will match nothing if there is no clause.
To match every document when there is no clause, add a `must` clause that matches everything.
<4> Inside the lambda, the code is free to use any Java language constructs, such as `if` or `for`,
to control the addition of clauses.
In this case, we only add clauses if the relevant parameter was filled in by the user.
====

Another syntax relying on the method `with(...)` can be useful when the boolean predicate is not the root predicate:
TIP: If you simply want to build a root predicate matching multiple, dynamically generated clauses,
consider using the <<search-dsl-predicate-and-lambda,`.where( (f, root) -> ... )` syntax>> instead.

.Easily adding clauses dynamically using `with(...)` and the lambda syntax
====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,32 @@ public void and() {
withinSearchSession( searchSession -> {
// tag::and-dynamicParameters-root[]
MySearchParameters searchParameters = getSearchParameters(); // <1>
List<Book> hits = searchSession.search( Book.class )
.where( (f, root) -> { // <2>
root.add( f.matchAll() ); // <3>
if ( searchParameters.getGenreFilter() != null ) { // <4>
root.add( f.match().field( "genre" )
.matching( searchParameters.getGenreFilter() ) );
}
if ( searchParameters.getFullTextFilter() != null ) {
root.add( f.match().fields( "title", "description" )
.matching( searchParameters.getFullTextFilter() ) );
}
if ( searchParameters.getPageCountMaxFilter() != null ) {
root.add( f.range().field( "pageCount" )
.atMost( searchParameters.getPageCountMaxFilter() ) );
}
} )
.fetchHits( 20 );
// end::and-dynamicParameters-root[]
assertThat( hits )
.extracting( Book::getId )
.containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID );
} );

withinSearchSession( searchSession -> {
// tag::and-dynamicParameters-with[]
MySearchParameters searchParameters = getSearchParameters(); // <1>
List<Book> hits = searchSession.search( Book.class )
.where( f -> f.and().with( and -> { // <2>
and.add( f.matchAll() ); // <3>
Expand All @@ -206,7 +232,7 @@ public void and() {
}
} ) )
.fetchHits( 20 );
// end::and-dynamicParameters-root[]
// end::and-dynamicParameters-with[]
assertThat( hits )
.extracting( Book::getId )
.containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID );
Expand Down Expand Up @@ -332,46 +358,20 @@ public void bool() {
.containsExactlyInAnyOrder( BOOK2_ID, BOOK4_ID );
} );

withinSearchSession( searchSession -> {
// tag::bool-dynamicParameters-root[]
MySearchParameters searchParameters = getSearchParameters(); // <1>
List<Book> hits = searchSession.search( Book.class )
.where( (f, b) -> { // <2>
b.must( f.matchAll() ); // <3>
if ( searchParameters.getGenreFilter() != null ) { // <4>
b.must( f.match().field( "genre" )
.matching( searchParameters.getGenreFilter() ) );
}
if ( searchParameters.getFullTextFilter() != null ) {
b.must( f.match().fields( "title", "description" )
.matching( searchParameters.getFullTextFilter() ) );
}
if ( searchParameters.getPageCountMaxFilter() != null ) {
b.must( f.range().field( "pageCount" )
.atMost( searchParameters.getPageCountMaxFilter() ) );
}
} )
.fetchHits( 20 );
// end::bool-dynamicParameters-root[]
assertThat( hits )
.extracting( Book::getId )
.containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID );
} );

withinSearchSession( searchSession -> {
// tag::bool-dynamicParameters-with[]
MySearchParameters searchParameters = getSearchParameters(); // <1>
List<Book> hits = searchSession.search( Book.class )
.where( (f, b) -> { // <2>
b.must( f.matchAll() );
.where( (f, root) -> { // <2>
root.add( f.matchAll() );
if ( searchParameters.getGenreFilter() != null ) {
b.must( f.match().field( "genre" )
root.add( f.match().field( "genre" )
.matching( searchParameters.getGenreFilter() ) );
}
if ( !searchParameters.getAuthorFilters().isEmpty() ) {
b.must( f.bool().with( b2 -> { // <3>
root.add( f.bool().with( b -> { // <3>
for ( String authorFilter : searchParameters.getAuthorFilters() ) { // <4>
b2.should( f.match().fields( "authors.firstName", "authors.lastName" )
b.should( f.match().fields( "authors.firstName", "authors.lastName" )
.matching( authorFilter ) );
}
} ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.search.engine.search.predicate.dsl;

import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

Expand Down Expand Up @@ -34,6 +35,11 @@
* For the {@link SearchPredicateFactory#nested(String) nested} predicate,
* documents will have to match <em>all</em> clauses.
* </li>
* <li>
* For the root predicate defined through the second parameter of the lambda passed
* to {@link org.hibernate.search.engine.search.query.dsl.SearchQueryWhereStep#where(BiConsumer)},
* documents will have to match <em>all</em> clauses.
* </li>
* </ul>
*
* @param <S> The "self" type (the actual exposed type of this collector).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateOptionsCollector;
import org.hibernate.search.engine.search.predicate.dsl.PredicateFinalStep;
import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory;
import org.hibernate.search.engine.search.predicate.dsl.SimpleBooleanOperatorPredicateClausesCollector;

/**
* The step in a query definition where the predicate, i.e. the "WHERE" clause, can be set.
Expand Down Expand Up @@ -49,10 +50,12 @@ public interface SearchQueryWhereStep<
/**
* Set the predicate for this query.
* @param predicateContributor A consumer that will use the factory passed in parameter to create predicates
* and add them to the collector passed in parameter.
* and add them as clauses to the collector passed in parameter.
* Should generally be a lambda expression.
* The resulting root predicate will have to match <em>all</em> clauses.
* @return The next step.
* @see SimpleBooleanPredicateClausesCollector
*/
N where(BiConsumer<? super PDF, ? super BooleanPredicateOptionsCollector<?>> predicateContributor);
N where(BiConsumer<? super PDF, ? super SimpleBooleanOperatorPredicateClausesCollector<?>> predicateContributor);

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
import org.hibernate.search.engine.backend.session.spi.BackendSessionContext;
import org.hibernate.search.engine.search.loading.spi.SearchLoadingContextBuilder;
import org.hibernate.search.engine.search.predicate.SearchPredicate;
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateOptionsCollector;
import org.hibernate.search.engine.search.predicate.dsl.PredicateFinalStep;
import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory;
import org.hibernate.search.engine.search.predicate.dsl.SimpleBooleanOperatorPredicateClausesCollector;
import org.hibernate.search.engine.search.projection.SearchProjection;
import org.hibernate.search.engine.search.projection.dsl.ProjectionFinalStep;
import org.hibernate.search.engine.search.projection.dsl.SearchProjectionFactory;
Expand Down Expand Up @@ -96,7 +96,7 @@ public DefaultSearchQueryOptionsStep<List<?>, LOS> select(SearchProjection<?>...

@Override
public SearchQueryOptionsStep<?, E, LOS, ?, ?> where(
BiConsumer<? super SearchPredicateFactory, ? super BooleanPredicateOptionsCollector<?>> predicateContributor) {
BiConsumer<? super SearchPredicateFactory, ? super SimpleBooleanOperatorPredicateClausesCollector<?>> predicateContributor) {
return selectEntity().where( predicateContributor );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import java.util.function.Function;

import org.hibernate.search.engine.search.predicate.SearchPredicate;
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateOptionsCollector;
import org.hibernate.search.engine.search.predicate.dsl.SimpleBooleanOperatorPredicateClausesCollector;
import org.hibernate.search.engine.search.projection.SearchProjection;
import org.hibernate.search.engine.search.predicate.dsl.PredicateFinalStep;
import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory;
Expand Down Expand Up @@ -78,7 +78,7 @@ public AbstractDelegatingSearchQuerySelectStep(SearchQuerySelectStep<?, R, E, LO

@Override
public SearchQueryOptionsStep<?, E, LOS, ?, ?> where(
BiConsumer<? super SearchPredicateFactory, ? super BooleanPredicateOptionsCollector<?>> predicateContributor) {
BiConsumer<? super SearchPredicateFactory, ? super SimpleBooleanOperatorPredicateClausesCollector<?>> predicateContributor) {
return delegate.where( predicateContributor );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
import org.hibernate.search.engine.search.aggregation.dsl.SearchAggregationFactory;
import org.hibernate.search.engine.search.loading.spi.SearchLoadingContextBuilder;
import org.hibernate.search.engine.search.predicate.SearchPredicate;
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateOptionsCollector;
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateClausesStep;
import org.hibernate.search.engine.search.predicate.dsl.PredicateFinalStep;
import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory;
import org.hibernate.search.engine.search.predicate.dsl.SimpleBooleanOperatorPredicateClausesCollector;
import org.hibernate.search.engine.search.predicate.dsl.SimpleBooleanOperatorPredicateClausesStep;
import org.hibernate.search.engine.search.query.SearchQuery;
import org.hibernate.search.engine.search.query.SearchResult;
import org.hibernate.search.engine.search.query.SearchScroll;
Expand Down Expand Up @@ -72,11 +72,11 @@ public S where(Function<? super PDF, ? extends PredicateFinalStep> predicateCont
}

@Override
public S where(BiConsumer<? super PDF, ? super BooleanPredicateOptionsCollector<?>> predicateContributor) {
public S where(BiConsumer<? super PDF, ? super SimpleBooleanOperatorPredicateClausesCollector<?>> predicateContributor) {
PDF factory = predicateFactory();
BooleanPredicateClausesStep<?> boolStep = factory.bool();
predicateContributor.accept( factory, boolStep );
searchQueryBuilder.predicate( boolStep.toPredicate() );
SimpleBooleanOperatorPredicateClausesStep<?> andStep = factory.and();
predicateContributor.accept( factory, andStep );
searchQueryBuilder.predicate( andStep.toPredicate() );
return thisAsS();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,29 @@ public void add_function() {
.hasDocRefHitsAnyOrder( index.typeName(), DOCUMENT_1 );
}

@Test
public void where() {
assertThatQuery( index.query()
.where( (f, root) -> {
root.add( f.match().field( "field1" ).matching( FIELD1_VALUE1 ) );
} ) )
.hasDocRefHitsAnyOrder( index.typeName(), DOCUMENT_1 );

assertThatQuery( index.query()
.where( (f, root) -> {
root.add( f.match().field( "field1" ).matching( FIELD1_VALUE1 ) );
root.add( f.match().field( "field2" ).matching( FIELD2_VALUE2 ) );
} ) )
.hasNoHits();

assertThatQuery( index.query()
.where( (f, root) -> {
root.add( f.match().field( "field1" ).matching( FIELD1_VALUE1 ) );
root.add( f.match().field( "field2" ).matching( FIELD2_VALUE1 ) );
} ) )
.hasDocRefHitsAnyOrder( index.typeName(), DOCUMENT_1 );
}

@Test
public void with() {
assertThatQuery( index.query()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,16 +363,6 @@ public void mustNot_should() {
.hasDocRefHitsAnyOrder( index.typeName(), DOCUMENT_2 );
}

@Test
public void where() {
assertThatQuery( index.query()
.where( (f, b) -> {
b.should( f.match().field( "field1" ).matching( FIELD1_VALUE1 ) );
b.should( f.match().field( "field1" ).matching( FIELD1_VALUE2 ) );
} ) )
.hasDocRefHitsAnyOrder( index.typeName(), DOCUMENT_1, DOCUMENT_2 );
}

@Test
public void with() {
assertThatQuery( index.query()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ public void search_nestedOnTwoLevels() {
@Test
public void search_nestedOnTwoLevels_onlySecondLevel() {
assertThatQuery( mainIndex.query()
.where( (f, b) -> b
.where( (f, root) -> root
// This is referred to as "condition 1" in the data initialization method
.must( f.nested( "nestedObject.nestedObject" )
.add( f.nested( "nestedObject.nestedObject" )
.add( f.match()
.field( "nestedObject.nestedObject.field1" )
.matching( MATCHING_SECOND_LEVEL_CONDITION1_FIELD1 )
Expand All @@ -108,7 +108,7 @@ public void search_nestedOnTwoLevels_onlySecondLevel() {
)
)
// This is referred to as "condition 2" in the data initialization method
.must( f.nested( "nestedObject.nestedObject" )
.add( f.nested( "nestedObject.nestedObject" )
.add( f.match()
.field( "nestedObject.nestedObject.field1" )
.matching( MATCHING_SECOND_LEVEL_CONDITION2_FIELD1 )
Expand All @@ -126,9 +126,9 @@ public void search_nestedOnTwoLevels_onlySecondLevel() {
@Test
public void with() {
assertThatQuery( mainIndex.query()
.where( (f, b) -> b
.where( (f, root) -> root
// This is referred to as "condition 1" in the data initialization method
.must( f.nested( "nestedObject.nestedObject" )
.add( f.nested( "nestedObject.nestedObject" )
.with( n -> {
n.add( f.match()
.field( "nestedObject.nestedObject.field1" )
Expand All @@ -141,7 +141,7 @@ public void with() {
} )
)
// This is referred to as "condition 2" in the data initialization method
.must( f.nested( "nestedObject.nestedObject" )
.add( f.nested( "nestedObject.nestedObject" )
.with( n -> {
n.add( f.match()
.field( "nestedObject.nestedObject.field1" )
Expand Down

0 comments on commit 24cb2ed

Please sign in to comment.