Skip to content

Commit ece4aef

Browse files
committed
HSEARCH-4577 Use single binding context and simplify projection binders
1 parent 0ed9939 commit ece4aef

File tree

12 files changed

+160
-283
lines changed

12 files changed

+160
-283
lines changed

documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/binding/projectionbinder/multi/MyFieldProjectionBinder.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@
1212
import org.hibernate.search.engine.search.projection.definition.ProjectionDefinition;
1313
import org.hibernate.search.engine.search.projection.definition.ProjectionDefinitionContext;
1414
import org.hibernate.search.engine.search.projection.dsl.SearchProjectionFactory;
15+
import org.hibernate.search.mapper.pojo.model.PojoModelValue;
1516
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBinder;
16-
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingContainerContext;
1717
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingContext;
1818

1919
//tag::include[]
2020
public class MyFieldProjectionBinder implements ProjectionBinder {
2121
@Override
2222
public void bind(ProjectionBindingContext context) {
23-
Optional<? extends ProjectionBindingContainerContext> container = context.container(); // <1>
24-
if ( container.isPresent() ) {
25-
container.get().definition( String.class, new MyProjectionDefinition() ); // <2>
23+
Optional<PojoModelValue<?>> containerElement = context.containerElement(); // <1>
24+
if ( containerElement.isPresent() ) {
25+
context.definition( String.class, new MyProjectionDefinition() ); // <2>
2626
}
2727
else {
2828
throw new RuntimeException( "This binder only supports container-wrapped constructor parameters" ); // <3>

engine/src/main/java/org/hibernate/search/engine/search/projection/dsl/CompositeProjectionValueStep.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
/**
1212
* The step in a composite projection definition
13-
* where the projection (optionally) can be marked as multi-valued (returning Lists),
13+
* where (optionally) the projection accumulator can be provided, e.g. to mark a projection as multi-valued (returning {@code List}/{@code Set} etc.)
14+
* or wrapped in some other container (e.g. {@code Optional<..>}),
1415
* and where optional parameters can be set.
1516
* <p>
1617
* By default (if {@link #accumulator(ProjectionAccumulator.Provider)} is not called), the projection is single-valued.

engine/src/main/java/org/hibernate/search/engine/search/projection/dsl/DistanceToFieldProjectionValueStep.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
/**
1212
* The initial step in a "distance to field" projection definition,
13-
* where the projection (optionally) can be marked as multi-valued (returning Lists),
13+
* where (optionally) the projection accumulator can be provided, e.g. to mark a projection as multi-valued (returning {@code List}/{@code Set} etc.)
14+
* or wrapped in some other container (e.g. {@code Optional<..>}),
1415
* and where optional parameters can be set.
1516
* <p>
1617
* By default (if {@link #accumulator(ProjectionAccumulator.Provider)} is not called), the projection is considered single-valued,

engine/src/main/java/org/hibernate/search/engine/search/projection/dsl/FieldProjectionValueStep.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
/**
1212
* The initial step in a "field" projection definition,
13-
* where the projection (optionally) can be marked as multi-valued (returning Lists),
13+
* where (optionally) the projection accumulator can be provided, e.g. to mark a projection as multi-valued (returning {@code List}/{@code Set} etc.)
14+
* or wrapped in some other container (e.g. {@code Optional<..>}),
1415
* and where optional parameters can be set.
1516
* <p>
1617
* By default (if {@link #accumulator(ProjectionAccumulator.Provider)} is not called), the projection is considered single-valued,

mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/search/definition/binding/ProjectionBindingContainerContext.java

Lines changed: 0 additions & 66 deletions
This file was deleted.

mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/search/definition/binding/ProjectionBindingContext.java

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
import org.hibernate.search.engine.environment.bean.BeanResolver;
1515
import org.hibernate.search.engine.search.projection.ProjectionAccumulator;
1616
import org.hibernate.search.engine.search.projection.definition.ProjectionDefinition;
17+
import org.hibernate.search.engine.search.projection.dsl.ProjectionAccumulatorProviderFactory;
1718
import org.hibernate.search.engine.search.projection.dsl.SearchProjectionFactory;
1819
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.PropertyBinderRef;
1920
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ObjectProjection;
2021
import org.hibernate.search.mapper.pojo.model.PojoModelConstructorParameter;
22+
import org.hibernate.search.mapper.pojo.model.PojoModelValue;
2123
import org.hibernate.search.util.common.SearchException;
2224
import org.hibernate.search.util.common.annotation.Incubating;
2325

@@ -36,9 +38,12 @@ public interface ProjectionBindingContext {
3638
* Hibernate Search will check that these expectations are met, and throw an exception if they are not.
3739
* @param definition A definition of the projection
3840
* to bind to the {@link #constructorParameter()}.
39-
* @param <P> The type of values returned by the projection.
41+
* @param <P> The type of single projected value.
42+
* @param <C> The type of values returned by the projection.
43+
* It may be the same as {@code P}, if it is a simple single-valued projection,
44+
* or a type of the container if the {@code P} values are wrapped in some type of container (e.g. {@code Optional<..>}, {@code Collection<..>}..).
4045
*/
41-
<P> void definition(Class<P> expectedValueType, ProjectionDefinition<? extends P> definition);
46+
<P, C> void definition(Class<P> expectedValueType, ProjectionDefinition<? extends C> definition);
4247

4348
/**
4449
* Binds the {@link #constructorParameter()} to the given projection definition.
@@ -48,9 +53,13 @@ public interface ProjectionBindingContext {
4853
* Hibernate Search will check that these expectations are met, and throw an exception if they are not.
4954
* @param definitionHolder A {@link BeanHolder} containing the definition of the projection
5055
* to bind to the {@link #constructorParameter()}.
51-
* @param <P> The type of values returned by the projection.
56+
* @param <P> The type of single projected value.
57+
* @param <C> The type of values returned by the projection.
58+
* It may be the same as {@code P}, if it is a simple single-valued projection,
59+
* or a type of the container if the {@code P} values are wrapped in some type of container (e.g. {@code Optional<..>}, {@code Collection<..>}..).
5260
*/
53-
<P> void definition(Class<P> expectedValueType, BeanHolder<? extends ProjectionDefinition<? extends P>> definitionHolder);
61+
<P, C> void definition(Class<P> expectedValueType,
62+
BeanHolder<? extends ProjectionDefinition<? extends C>> definitionHolder);
5463

5564
/**
5665
* Inspects the type of the {@link #constructorParameter()}
@@ -59,23 +68,12 @@ public interface ProjectionBindingContext {
5968
* @return An optional containing a context that can be used to bind a projection
6069
* if the type of the {@link #constructorParameter()} can be bound to a multi-valued projection;
6170
* an empty optional otherwise.
62-
* @deprecated Use {@link #container()} instead.
71+
* @deprecated Use {@link #containerElement()} and various bind methods of this context instead.
6372
*/
6473
@Deprecated(since = "8.0")
6574
@Incubating
6675
Optional<? extends ProjectionBindingMultiContext> multi();
6776

68-
/**
69-
* Inspects the type of the {@link #constructorParameter()}
70-
* to determine if it may be bound to a multi-valued projection.
71-
*
72-
* @return An optional containing a context that can be used to bind a projection
73-
* if the type of the {@link #constructorParameter()} can be bound to a multi-valued projection;
74-
* an empty optional otherwise.
75-
*/
76-
@Incubating
77-
Optional<? extends ProjectionBindingContainerContext> container();
78-
7977
/**
8078
* @return A bean provider, allowing the retrieval of beans,
8179
* including CDI/Spring DI beans when in the appropriate environment.
@@ -88,6 +86,14 @@ public interface ProjectionBindingContext {
8886
@Incubating
8987
PojoModelConstructorParameter constructorParameter();
9088

89+
/**
90+
* @return An entry point allowing to inspect the constructor parameter container element being bound to a projection.
91+
* Returns non-empty optional only if a {@link #constructorParameter()} can be bound to a container-wrapped projection
92+
* (be it a single-valued optional, or some multi-valued collection).
93+
*/
94+
@Incubating
95+
Optional<PojoModelValue<?>> containerElement();
96+
9197
/**
9298
* @param name The name of the parameter.
9399
* @return The value provided for this parameter.
@@ -213,4 +219,7 @@ <C, T> BeanHolder<? extends ProjectionDefinition<C>> createObjectDefinition(Stri
213219
*/
214220
boolean isIncluded(String fieldPath);
215221

222+
@Incubating
223+
ProjectionAccumulatorProviderFactory projectionAccumulatorProviderFactory();
224+
216225
}

mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/search/definition/binding/ProjectionBindingMultiContext.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
/**
1515
* The context returned by {@link ProjectionBindingContext#multi()}.
1616
* @see ProjectionBindingContext#multi()
17+
* @deprecated Use {@link ProjectionBindingContext}/{@link ProjectionBindingContext#containerElement()} instead.
1718
*/
19+
@Deprecated(since = "8.0")
1820
@Incubating
1921
public interface ProjectionBindingMultiContext {
2022

mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/search/definition/binding/builtin/DistanceProjectionBinder.java

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
import org.hibernate.search.engine.spatial.DistanceUnit;
1818
import org.hibernate.search.engine.spatial.GeoPoint;
1919
import org.hibernate.search.mapper.pojo.logging.impl.Log;
20+
import org.hibernate.search.mapper.pojo.model.PojoModelValue;
2021
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBinder;
21-
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingContainerContext;
2222
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingContext;
2323
import org.hibernate.search.util.common.impl.Contracts;
2424
import org.hibernate.search.util.common.logging.impl.LoggerFactory;
@@ -95,38 +95,33 @@ public DistanceProjectionBinder unit(DistanceUnit unit) {
9595
@Override
9696
public void bind(ProjectionBindingContext context) {
9797
Contracts.assertNotNullNorEmpty( parameterName, "parameterName" );
98-
Optional<? extends ProjectionBindingContainerContext> containerOptional = context.container();
98+
Optional<PojoModelValue<?>> containerElementOptional = context.containerElement();
9999
String fieldPath = fieldPathOrFail( context );
100-
if ( containerOptional.isPresent() ) {
101-
ProjectionBindingContainerContext container = containerOptional.get();
102-
if ( !container.containerElement().rawType().isAssignableFrom( Double.class ) ) {
100+
Class<?> containerClass;
101+
if ( containerElementOptional.isPresent() ) {
102+
PojoModelValue<?> containerElement = containerElementOptional.get();
103+
if ( !containerElement.rawType().isAssignableFrom( Double.class ) ) {
103104
throw log.invalidParameterTypeForDistanceProjectionInProjectionConstructor(
104-
container.containerElement().rawType(),
105+
containerElement.rawType(),
105106
"SomeContainer<Double>" );
106107
}
107-
bind( context, container, fieldPath );
108+
containerClass = context.constructorParameter().rawType();
108109
}
109110
else {
110111
if ( !context.constructorParameter().rawType().isAssignableFrom( Double.class ) ) {
111112
throw log.invalidParameterTypeForDistanceProjectionInProjectionConstructor(
112113
context.constructorParameter().rawType(), "Double" );
113114
}
114-
bind( context, fieldPath );
115+
containerClass = null;
115116
}
117+
ProjectionAccumulator.Provider<Double, ?> accumulator = context.projectionAccumulatorProviderFactory()
118+
.projectionAccumulatorProvider( containerClass, Double.class );
119+
bind( context, fieldPath, accumulator );
116120
}
117121

118-
private void bind(ProjectionBindingContext context, String fieldPath) {
122+
private void bind(ProjectionBindingContext context, String fieldPath,
123+
ProjectionAccumulator.Provider<Double, ?> accumulator) {
119124
context.definition( Double.class, context.isIncluded( fieldPath )
120-
? BeanHolder.of( new DistanceProjectionDefinition.WrappedValued<>( fieldPath, parameterName, unit,
121-
ProjectionAccumulator.nullable() ) )
122-
: ConstantProjectionDefinition.nullValue() );
123-
}
124-
125-
private void bind(ProjectionBindingContext context, ProjectionBindingContainerContext container, String fieldPath) {
126-
var accumulator = container.projectionAccumulatorProviderFactory()
127-
.projectionAccumulatorProvider( container.container().rawType(), Double.class );
128-
129-
container.definition( Double.class, context.isIncluded( fieldPath )
130125
? BeanHolder
131126
.of( new DistanceProjectionDefinition.WrappedValued<>( fieldPath, parameterName, unit, accumulator ) )
132127
: ConstantProjectionDefinition.empty( accumulator ) );

mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/search/definition/binding/builtin/FieldProjectionBinder.java

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99

1010
import org.hibernate.search.engine.environment.bean.BeanHolder;
1111
import org.hibernate.search.engine.search.common.ValueModel;
12-
import org.hibernate.search.engine.search.projection.ProjectionAccumulator;
1312
import org.hibernate.search.engine.search.projection.definition.spi.ConstantProjectionDefinition;
1413
import org.hibernate.search.engine.search.projection.definition.spi.FieldProjectionDefinition;
1514
import org.hibernate.search.engine.search.projection.dsl.SearchProjectionFactory;
1615
import org.hibernate.search.mapper.pojo.logging.impl.Log;
16+
import org.hibernate.search.mapper.pojo.model.PojoModelValue;
1717
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBinder;
18-
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingContainerContext;
1918
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingContext;
2019
import org.hibernate.search.util.common.logging.impl.LoggerFactory;
2120

@@ -90,30 +89,28 @@ public FieldProjectionBinder valueModel(ValueModel valueModel) {
9089

9190
@Override
9291
public void bind(ProjectionBindingContext context) {
93-
Optional<? extends ProjectionBindingContainerContext> containerOptional = context.container();
92+
Optional<PojoModelValue<?>> containerElementOptional = context.containerElement();
9493
String fieldPath = fieldPathOrFail( context );
95-
if ( containerOptional.isPresent() ) {
96-
ProjectionBindingContainerContext container = containerOptional.get();
97-
bind( context, container, fieldPath, container.containerElement().rawType() );
94+
Class<?> containerClass;
95+
Class<?> containerElementClass;
96+
if ( containerElementOptional.isPresent() ) {
97+
PojoModelValue<?> containerElement = containerElementOptional.get();
98+
containerElementClass = containerElement.rawType();
99+
containerClass = context.constructorParameter().rawType();
98100
}
99101
else {
100-
bind( context, fieldPath, context.constructorParameter().rawType() );
102+
containerElementClass = context.constructorParameter().rawType();
103+
containerClass = null;
101104
}
105+
bind( context, fieldPath, containerClass, containerElementClass );
102106
}
103107

104-
private <T> void bind(ProjectionBindingContext context, String fieldPath, Class<T> constructorParameterType) {
105-
context.definition( constructorParameterType, context.isIncluded( fieldPath )
106-
? BeanHolder.of( new FieldProjectionDefinition.AccumulatedValued<>( fieldPath, constructorParameterType,
107-
ProjectionAccumulator.nullable(), valueModel ) )
108-
: ConstantProjectionDefinition.nullValue() );
109-
}
110-
111-
private <T> void bind(ProjectionBindingContext context, ProjectionBindingContainerContext container, String fieldPath,
112-
Class<T> containerElementType) {
113-
var accumulator = container.projectionAccumulatorProviderFactory()
114-
.projectionAccumulatorProvider( container.container().rawType(), containerElementType );
108+
private <T, C> void bind(ProjectionBindingContext context, String fieldPath,
109+
Class<C> containerType, Class<T> containerElementType) {
110+
var accumulator = context.projectionAccumulatorProviderFactory()
111+
.projectionAccumulatorProvider( containerType, containerElementType );
115112

116-
container.definition( containerElementType, context.isIncluded( fieldPath )
113+
context.definition( containerElementType, context.isIncluded( fieldPath )
117114
? BeanHolder.of( new FieldProjectionDefinition.AccumulatedValued<>( fieldPath, containerElementType,
118115
accumulator, valueModel ) )
119116
: ConstantProjectionDefinition.empty( accumulator ) );

0 commit comments

Comments
 (0)