Skip to content

Commit

Permalink
HSEARCH-4542 .within() and .withinAny() for the range predicate
Browse files Browse the repository at this point in the history
  • Loading branch information
marko-bekhta authored and yrodiere committed May 17, 2024
1 parent d99fe16 commit d7e88c8
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ private Builder(ElasticsearchFieldCodec<F> codec, ElasticsearchSearchIndexScope<
}

@Override
public void range(Range<?> range, ValueConvert convertLowerBound, ValueConvert convertUpperBound) {
public void within(Range<?> range, ValueConvert convertLowerBound, ValueConvert convertUpperBound) {
this.range = Range.between(
convertToFieldValue( range.lowerBoundValue(), convertLowerBound ),
range.lowerBoundInclusion(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ protected Query newRangeQuery(String field, String part1, String part2, boolean

if ( !state.field().type().valueClass().isAssignableFrom( String.class ) ) {
var builder = state.field().queryElement( PredicateTypeKeys.RANGE, scope );
builder.range(
builder.within(
org.hibernate.search.util.common.data.Range.between(
part1, startInclusive ? RangeBoundInclusion.INCLUDED : RangeBoundInclusion.EXCLUDED,
part2, endInclusive ? RangeBoundInclusion.INCLUDED : RangeBoundInclusion.EXCLUDED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private static class Builder<F, E extends Number> extends AbstractBuilder<F>
}

@Override
public void range(Range<?> range, ValueConvert convertLowerBound, ValueConvert convertUpperBound) {
public void within(Range<?> range, ValueConvert convertLowerBound, ValueConvert convertUpperBound) {
this.range = convertAndEncode( codec, range, convertLowerBound, convertUpperBound );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private Builder(LuceneStandardFieldCodec<F, String> codec, LuceneSearchIndexScop
}

@Override
public void range(Range<?> range, ValueConvert convertLowerBound, ValueConvert convertUpperBound) {
public void within(Range<?> range, ValueConvert convertLowerBound, ValueConvert convertUpperBound) {
this.range = convertAndEncode( codec, range, convertLowerBound, convertUpperBound );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,18 @@ include::{sourcedir}/org/hibernate/search/documentation/search/predicate/Predica
----
====

Sometimes it may be needed to match the value that is in one of the ranges. While it is possible to create an
<<search-dsl-predicate-or,`or` predicate>> and add a <<search-dsl-predicate-range,`range` predicate>> for each of the ranges,
there is a simpler way to do that:

.Matching values that are within any of the provided ranges
====
[source, JAVA, indent=0, subs="+callouts"]
----
include::{sourcedir}/org/hibernate/search/documentation/search/predicate/PredicateDslIT.java[tags=range-within-any]
----
====

[[search-dsl-predicate-range-argument-type]]
=== Expected type of arguments

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.hibernate.search.mapper.orm.scope.SearchScope;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.hibernate.search.mapper.pojo.mapping.definition.programmatic.TypeMappingStep;
import org.hibernate.search.util.common.data.Range;
import org.hibernate.search.util.common.data.RangeBoundInclusion;
import org.hibernate.search.util.impl.integrationtest.backend.elasticsearch.dialect.ElasticsearchTestDialect;
import org.hibernate.search.util.impl.integrationtest.common.extension.BackendConfiguration;
Expand Down Expand Up @@ -748,6 +749,21 @@ void range() {
.extracting( Book::getId )
.containsExactlyInAnyOrder( BOOK2_ID, BOOK4_ID );
} );

withinSearchSession( searchSession -> {
// tag::range-within-any[]
List<Book> hits = searchSession.search( Book.class )
.where( f -> f.range().field( "pageCount" )
.withinAny(
Range.between( 200, 250 ),
Range.between( 500, 800 )
) )
.fetchHits( 20 );
// end::range-within-any[]
assertThat( hits )
.extracting( Book::getId )
.containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID, BOOK4_ID );
} );
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
*/
package org.hibernate.search.engine.search.predicate.dsl;

import java.util.Arrays;
import java.util.Collection;

import org.hibernate.search.engine.search.common.ValueConvert;
import org.hibernate.search.util.common.data.Range;
import org.hibernate.search.util.common.data.RangeBoundInclusion;
Expand Down Expand Up @@ -53,7 +56,7 @@ default N between(Object lowerBound, Object upperBound) {
* @return The next step.
*/
default N between(Object lowerBound, Object upperBound, ValueConvert convert) {
return range( Range.between( lowerBound, upperBound ), convert );
return within( Range.between( lowerBound, upperBound ), convert );
}

/**
Expand All @@ -74,7 +77,7 @@ default N between(Object lowerBound, Object upperBound, ValueConvert convert) {
*/
default N between(Object lowerBound, RangeBoundInclusion lowerBoundInclusion,
Object upperBound, RangeBoundInclusion upperBoundInclusion) {
return range( Range.between( lowerBound, lowerBoundInclusion, upperBound, upperBoundInclusion ) );
return within( Range.between( lowerBound, lowerBoundInclusion, upperBound, upperBoundInclusion ) );
}

/**
Expand Down Expand Up @@ -105,7 +108,7 @@ default N atLeast(Object lowerBoundValue) {
* @return The next step.
*/
default N atLeast(Object lowerBoundValue, ValueConvert convert) {
return range( Range.atLeast( lowerBoundValue ), convert );
return within( Range.atLeast( lowerBoundValue ), convert );
}

/**
Expand Down Expand Up @@ -136,7 +139,7 @@ default N greaterThan(Object lowerBoundValue) {
* @return The next step.
*/
default N greaterThan(Object lowerBoundValue, ValueConvert convert) {
return range( Range.greaterThan( lowerBoundValue ), convert );
return within( Range.greaterThan( lowerBoundValue ), convert );
}

/**
Expand Down Expand Up @@ -167,7 +170,7 @@ default N atMost(Object upperBoundValue) {
* @return The next step.
*/
default N atMost(Object upperBoundValue, ValueConvert convert) {
return range( Range.atMost( upperBoundValue ), convert );
return within( Range.atMost( upperBoundValue ), convert );
}

/**
Expand Down Expand Up @@ -198,7 +201,7 @@ default N lessThan(Object upperBoundValue) {
* @return The next step.
*/
default N lessThan(Object upperBoundValue, ValueConvert convert) {
return range( Range.lessThan( upperBoundValue ), convert );
return within( Range.lessThan( upperBoundValue ), convert );
}

/**
Expand All @@ -209,9 +212,11 @@ default N lessThan(Object upperBoundValue, ValueConvert convert) {
* but a specific type is expected depending on the targeted field.
* See {@link ValueConvert#YES} for more information.
* @return The next step.
* @deprecated Use {@link #within(Range)} instead.
*/
@Deprecated
default N range(Range<?> range) {
return range( range, ValueConvert.YES );
return within( range, ValueConvert.YES );
}

/**
Expand All @@ -225,7 +230,77 @@ default N range(Range<?> range) {
* before Hibernate Search attempts to interpret them as a field value.
* See {@link ValueConvert} for more information.
* @return The next step.
* @deprecated Use {@link #within(Range, ValueConvert)} instead.
*/
@Deprecated
default N range(Range<?> range, ValueConvert convert) {
return within( range, convert );
}

/**
* Require at least one of the targeted fields to be in the given range.
*
* @param range The range to match.
* The signature of this method defines this parameter as a range with bounds of any type,
* but a specific type is expected depending on the targeted field.
* See {@link ValueConvert#YES} for more information.
* @return The next step.
*/
default N within(Range<?> range) {
return within( range, ValueConvert.YES );
}

/**
* Require at least one of the targeted fields to be in the given range.
*
* @param range The range to match.
* The signature of this method defines this parameter as a range with bounds of any type,
* but a specific type is expected depending on the targeted field and on the {@code convert} parameter.
* See {@link ValueConvert} for more information.
* @param convert Controls how the range bounds should be converted
* before Hibernate Search attempts to interpret them as a field value.
* See {@link ValueConvert} for more information.
* @return The next step.
*/
N within(Range<?> range, ValueConvert convert);

/**
* Require at least one of the targeted fields to be in any of the given ranges.
*
* @param ranges The ranges to match.
* The signature of this method defines this parameter as a range with bounds of any type,
* but a specific type is expected depending on the targeted field.
* See {@link ValueConvert#YES} for more information.
* @return The next step.
*/
N range(Range<?> range, ValueConvert convert);
default N withinAny(Range<?>... ranges) {
return withinAny( Arrays.asList( ranges ) );
}

/**
* Require at least one of the targeted fields to be in any of the given ranges.
*
* @param ranges The ranges to match.
* The signature of this method defines this parameter as a range with bounds of any type,
* but a specific type is expected depending on the targeted field.
* See {@link ValueConvert#YES} for more information.
* @return The next step.
*/
default N withinAny(Collection<? extends Range<?>> ranges) {
return withinAny( ranges, ValueConvert.YES );
}

/**
* Require at least one of the targeted fields to be in any of the given ranges.
*
* @param ranges The ranges to match.
* The signature of this method defines this parameter as a range with bounds of any type,
* but a specific type is expected depending on the targeted field and on the {@code convert} parameter.
* See {@link ValueConvert} for more information.
* @param convert Controls how the range bounds should be converted
* before Hibernate Search attempts to interpret them as a field value.
* See {@link ValueConvert} for more information.
* @return The next step.
*/
N withinAny(Collection<? extends Range<?>> ranges, ValueConvert convert);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.hibernate.search.engine.logging.impl.Log;
import org.hibernate.search.engine.reporting.spi.EventContexts;
import org.hibernate.search.engine.search.common.ValueConvert;
import org.hibernate.search.engine.search.common.spi.SearchIndexScope;
import org.hibernate.search.engine.search.predicate.SearchPredicate;
import org.hibernate.search.engine.search.predicate.dsl.RangePredicateFieldMoreStep;
import org.hibernate.search.engine.search.predicate.dsl.RangePredicateOptionsStep;
Expand Down Expand Up @@ -45,9 +45,9 @@ class RangePredicateFieldMoreStepImpl
this.commonState = commonState;
this.commonState.add( this );
this.fieldPaths = fieldPaths;
SearchIndexScope<?> scope = commonState.scope();
for ( String fieldPath : fieldPaths ) {
predicateBuilders.add( scope.fieldQueryElement( fieldPath, PredicateTypeKeys.RANGE ) );
for ( String path : fieldPaths ) {
// only check that the range predicate can be applied to the requested field:
commonState.scope().fieldQueryElement( path, PredicateTypeKeys.RANGE );
}
}

Expand All @@ -63,8 +63,13 @@ public RangePredicateFieldMoreStepImpl boost(float boost) {
}

@Override
public RangePredicateOptionsStep<?> range(Range<?> range, ValueConvert convert) {
return commonState.range( range, convert, convert );
public RangePredicateOptionsStep<?> within(Range<?> range, ValueConvert convert) {
return commonState.within( range, convert, convert );
}

@Override
public RangePredicateOptionsStep<?> withinAny(Collection<? extends Range<?>> ranges, ValueConvert convert) {
return commonState.withinAny( ranges, convert );
}

@Override
Expand All @@ -85,18 +90,39 @@ static class CommonState
super( dslContext );
}

CommonState range(Range<?> range, ValueConvert lowerBoundConvert, ValueConvert upperBoundConvert) {
CommonState within(Range<?> range, ValueConvert lowerBoundConvert, ValueConvert upperBoundConvert) {
Contracts.assertNotNull( range, "range" );
Contracts.assertNotNull( lowerBoundConvert, "lowerBoundConvert" );
Contracts.assertNotNull( upperBoundConvert, "upperBoundConvert" );
if ( !range.lowerBoundValue().isPresent() && !range.upperBoundValue().isPresent() ) {
if ( range.lowerBoundValue().isEmpty() && range.upperBoundValue().isEmpty() ) {
throw log.rangePredicateCannotMatchNullValue( getEventContext() );
}
for ( RangePredicateFieldMoreStepImpl fieldSetState : getFieldSetStates() ) {
for ( RangePredicateBuilder predicateBuilder : fieldSetState.predicateBuilders ) {
predicateBuilder.range( range, lowerBoundConvert, upperBoundConvert );
for ( String path : fieldSetState.fieldPaths ) {
RangePredicateBuilder builder = scope().fieldQueryElement( path, PredicateTypeKeys.RANGE );
builder.within( range, lowerBoundConvert, upperBoundConvert );
fieldSetState.predicateBuilders.add( builder );
}
}
return this;
}

public CommonState withinAny(Collection<? extends Range<?>> ranges, ValueConvert valueConvert) {
Contracts.assertNotNull( valueConvert, "valueConvert" );
for ( RangePredicateFieldMoreStepImpl fieldSetState : getFieldSetStates() ) {
for ( String path : fieldSetState.fieldPaths ) {
for ( var range : ranges ) {
Contracts.assertNotNull( range, "range" );
if ( range.lowerBoundValue().isEmpty() && range.upperBoundValue().isEmpty() ) {
throw log.rangePredicateCannotMatchNullValue( getEventContext() );
}
RangePredicateBuilder builder = scope().fieldQueryElement( path, PredicateTypeKeys.RANGE );
builder.within( range, valueConvert, valueConvert );
fieldSetState.predicateBuilders.add( builder );
}
}
}

return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@

public interface RangePredicateBuilder extends SearchPredicateBuilder {

void range(Range<?> range, ValueConvert convertLowerBound, ValueConvert convertUpperBound);
void within(Range<?> range, ValueConvert convertLowerBound, ValueConvert convertUpperBound);

}

0 comments on commit d7e88c8

Please sign in to comment.