Skip to content

HSEARCH-4577 @ProjectionConstructor: aggregating multi-valued field/object projections in Set, SortedSet, etc. instead of List #4344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ SearchException invalidFieldValueType(@FormatWith(ClassFormatter.class) Class<?>
@Message(id = ID_OFFSET + 113,
value = "Invalid cardinality for projection on field '%1$s': the projection is single-valued,"
+ " but this field is multi-valued."
+ " Make sure to call '.multi()' when you create the projection.")
+ " Make sure to call '.collector(...)' when you create the projection.")
SearchException invalidSingleValuedProjectionOnMultiValuedField(String absolutePath, @Param EventContext context);

@Message(id = ID_OFFSET + 117,
Expand Down Expand Up @@ -732,9 +732,9 @@ SearchException invalidContextForProjectionOnField(String absolutePath,
value = "Invalid cardinality for projection on field '%1$s': the projection is single-valued,"
+ " but this field is effectively multi-valued in this context,"
+ " because parent object field '%2$s' is multi-valued."
+ " Either call '.multi()' when you create the projection on field '%1$s',"
+ " Either call '.collector(...)' when you create the projection on field '%1$s',"
+ " or wrap that projection in an object projection like this:"
+ " 'f.object(\"%2$s\").from(<the projection on field %1$s>).as(...).multi()'.")
+ " 'f.object(\"%2$s\").from(<the projection on field %1$s>).as(...).collector(...)'.")
SearchException invalidSingleValuedProjectionOnValueFieldInMultiValuedObjectField(String absolutePath,
String objectFieldAbsolutePath);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.search.highlighter.SearchHighlighter;
import org.hibernate.search.engine.search.highlighter.spi.SearchHighlighterType;
import org.hibernate.search.engine.search.projection.spi.ProjectionAccumulator;
import org.hibernate.search.engine.search.projection.ProjectionCollector;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;

import com.google.gson.JsonObject;
Expand All @@ -31,7 +31,7 @@ public interface ElasticsearchSearchHighlighter extends SearchHighlighter {

SearchHighlighterType type();

boolean isCompatible(ProjectionAccumulator.Provider<?, ?> accumulatorProvider);
boolean isCompatible(ProjectionCollector.Provider<?, ?> collectorProvider);

static ElasticsearchSearchHighlighter from(ElasticsearchSearchIndexScope<?> scope, SearchHighlighter highlighter) {
if ( !( highlighter instanceof ElasticsearchSearchHighlighter ) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import org.hibernate.search.engine.search.highlighter.spi.BoundaryScannerType;
import org.hibernate.search.engine.search.highlighter.spi.SearchHighlighterBuilder;
import org.hibernate.search.engine.search.highlighter.spi.SearchHighlighterType;
import org.hibernate.search.engine.search.projection.spi.ProjectionAccumulator;
import org.hibernate.search.engine.search.projection.ProjectionCollector;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
Expand Down Expand Up @@ -143,9 +143,9 @@ public SearchHighlighterType type() {
}

@Override
public boolean isCompatible(ProjectionAccumulator.Provider<?, ?> provider) {
return !provider.isSingleValued()
|| ( provider.isSingleValued() && ( numberOfFragments != null && numberOfFragments.equals( 1 ) ) );
public boolean isCompatible(ProjectionCollector.Provider<?, ?> collectorProvider) {
return !collectorProvider.isSingleValued()
|| ( collectorProvider.isSingleValued() && ( numberOfFragments != null && numberOfFragments.equals( 1 ) ) );
}

private JsonObject toJson(JsonObject result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import org.hibernate.search.backend.elasticsearch.gson.impl.JsonElementTypes;
import org.hibernate.search.backend.elasticsearch.gson.impl.UnexpectedJsonElementTypeException;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
import org.hibernate.search.engine.search.projection.spi.ProjectionAccumulator;
import org.hibernate.search.engine.search.projection.ProjectionCollector;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
Expand All @@ -24,18 +24,18 @@ abstract class AccumulatingSourceExtractor<E, V, A, P>
JsonAccessor.root().property( "_source" ).asArray();

private final String[] fieldPathComponents;
final ProjectionAccumulator<E, V, A, P> accumulator;
final ProjectionCollector<E, V, A, P> collector;

public AccumulatingSourceExtractor(String[] fieldPathComponents,
ProjectionAccumulator<E, V, A, P> accumulator) {
ProjectionCollector<E, V, A, P> collector) {
this.fieldPathComponents = fieldPathComponents;
this.accumulator = accumulator;
this.collector = collector;
}

@Override
public final A extract(ProjectionHitMapper<?> projectionHitMapper, JsonObject hit,
JsonObject source, ProjectionExtractContext context) {
A accumulated = accumulator.createInitial();
A accumulated = collector.createInitial();
accumulated = collect( projectionHitMapper, hit, source, context, accumulated, 0 );
return accumulated;
}
Expand Down Expand Up @@ -83,17 +83,17 @@ private A collectTargetField(ProjectionHitMapper<?> projectionHitMapper, JsonObj
}
else if ( fieldValue.isJsonNull() ) {
// Present, but null
return accumulator.accumulate( accumulated, extract( projectionHitMapper, hit, fieldValue, context ) );
return collector.accumulate( accumulated, extract( projectionHitMapper, hit, fieldValue, context ) );
}
else if ( !canDecodeArrays() && fieldValue.isJsonArray() ) {
for ( JsonElement childElement : fieldValue.getAsJsonArray() ) {
accumulated = accumulator.accumulate( accumulated,
accumulated = collector.accumulate( accumulated,
extract( projectionHitMapper, hit, childElement, context ) );
}
return accumulated;
}
else {
return accumulator.accumulate( accumulated, extract( projectionHitMapper, hit, fieldValue, context ) );
return collector.accumulate( accumulated, extract( projectionHitMapper, hit, fieldValue, context ) );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.search.loading.spi.LoadingResult;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
import org.hibernate.search.engine.search.projection.ProjectionCollector;
import org.hibernate.search.engine.search.projection.SearchProjection;
import org.hibernate.search.engine.search.projection.spi.CompositeProjectionBuilder;
import org.hibernate.search.engine.search.projection.spi.ProjectionAccumulator;
import org.hibernate.search.engine.search.projection.spi.ProjectionCompositor;

import com.google.gson.JsonObject;
Expand All @@ -31,22 +31,22 @@ class ElasticsearchCompositeProjection<E, V, A, P>

private final ElasticsearchSearchProjection<?>[] inners;
private final ProjectionCompositor<E, V> compositor;
private final ProjectionAccumulator<E, V, A, P> accumulator;
private final ProjectionCollector<E, V, A, P> collector;

public ElasticsearchCompositeProjection(Builder builder, ElasticsearchSearchProjection<?>[] inners,
ProjectionCompositor<E, V> compositor, ProjectionAccumulator<E, V, A, P> accumulator) {
ProjectionCompositor<E, V> compositor, ProjectionCollector<E, V, A, P> collector) {
super( builder.scope );
this.inners = inners;
this.compositor = compositor;
this.accumulator = accumulator;
this.collector = collector;
}

@Override
public String toString() {
return getClass().getSimpleName() + "["
+ "inners=" + Arrays.toString( inners )
+ ", compositor=" + compositor
+ ", accumulator=" + accumulator
+ ", collector=" + collector
+ "]";
}

Expand All @@ -71,30 +71,30 @@ public String toString() {
return getClass().getSimpleName() + "["
+ "inners=" + Arrays.toString( inners )
+ ", compositor=" + compositor
+ ", accumulator=" + accumulator
+ ", collector=" + collector
+ "]";
}

@Override
public A extract(ProjectionHitMapper<?> projectionHitMapper, JsonObject hit,
JsonObject source, ProjectionExtractContext context) {
A accumulated = accumulator.createInitial();
A accumulated = collector.createInitial();

E components = compositor.createInitial();
for ( int i = 0; i < inners.length; i++ ) {
Object extractedDataForInner = inners[i].extract( projectionHitMapper, hit, source, context );
components = compositor.set( components, i, extractedDataForInner );
}
accumulated = accumulator.accumulate( accumulated, components );
accumulated = collector.accumulate( accumulated, components );

return accumulated;
}

@Override
public final P transform(LoadingResult<?> loadingResult, A accumulated,
ProjectionTransformContext context) {
for ( int i = 0; i < accumulator.size( accumulated ); i++ ) {
E transformedData = accumulator.get( accumulated, i );
for ( int i = 0; i < collector.size( accumulated ); i++ ) {
E transformedData = collector.get( accumulated, i );
// Transform in-place
for ( int j = 0; j < inners.length; j++ ) {
Object extractedDataForInner = compositor.get( transformedData, j );
Expand All @@ -103,9 +103,9 @@ public final P transform(LoadingResult<?> loadingResult, A accumulated,
transformedData = compositor.set( transformedData, j, transformedDataForInner );
}

accumulated = accumulator.transform( accumulated, i, compositor.finish( transformedData ) );
accumulated = collector.transform( accumulated, i, compositor.finish( transformedData ) );
}
return accumulator.finish( accumulated );
return collector.finish( accumulated );
}
}

Expand All @@ -119,14 +119,14 @@ static class Builder implements CompositeProjectionBuilder {

@Override
public <E, V, P> SearchProjection<P> build(SearchProjection<?>[] inners, ProjectionCompositor<E, V> compositor,
ProjectionAccumulator.Provider<V, P> accumulatorProvider) {
ProjectionCollector.Provider<V, P> collectorProvider) {
ElasticsearchSearchProjection<?>[] typedInners =
new ElasticsearchSearchProjection<?>[inners.length];
for ( int i = 0; i < inners.length; i++ ) {
typedInners[i] = ElasticsearchSearchProjection.from( scope, inners[i] );
}
return new ElasticsearchCompositeProjection<>( this, typedInners,
compositor, accumulatorProvider.get() );
compositor, collectorProvider.get() );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
import org.hibernate.search.engine.backend.types.converter.spi.ProjectionConverter;
import org.hibernate.search.engine.search.loading.spi.LoadingResult;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
import org.hibernate.search.engine.search.projection.ProjectionCollector;
import org.hibernate.search.engine.search.projection.SearchProjection;
import org.hibernate.search.engine.search.projection.spi.DistanceToFieldProjectionBuilder;
import org.hibernate.search.engine.search.projection.spi.ProjectionAccumulator;
import org.hibernate.search.engine.spatial.DistanceUnit;
import org.hibernate.search.engine.spatial.GeoPoint;

Expand Down Expand Up @@ -62,22 +62,22 @@ public class ElasticsearchDistanceToFieldProjection<A, P> extends AbstractElasti
private final GeoPoint center;
private final DistanceUnit unit;

private final ProjectionAccumulator<Double, Double, A, P> accumulator;
private final ProjectionCollector<Double, Double, A, P> collector;

private final String scriptFieldName;
private final ElasticsearchFieldProjection<?, Double, P, ?> sourceProjection;

private ElasticsearchDistanceToFieldProjection(Builder builder,
ProjectionAccumulator.Provider<Double, P> accumulatorProvider,
ProjectionAccumulator<Double, Double, A, P> accumulator) {
ProjectionCollector.Provider<Double, P> collectorProvider,
ProjectionCollector<Double, Double, A, P> collector) {
super( builder );
this.codec = builder.field.type().codec();

this.absoluteFieldPath = builder.field.absolutePath();
this.singleValuedInRoot = !builder.field.multiValuedInRoot();
this.center = builder.center;
this.unit = builder.unit;
this.accumulator = accumulator;
this.collector = collector;
if ( singleValuedInRoot && builder.field.nestedPathHierarchy().isEmpty() ) {
// Rely on docValues when there is no sort to extract the distance from.
scriptFieldName = createScriptFieldName( absoluteFieldPath, center );
Expand All @@ -88,7 +88,7 @@ private ElasticsearchDistanceToFieldProjection(Builder builder,
scriptFieldName = null;
this.sourceProjection = new ElasticsearchFieldProjection<>(
builder.scope, builder.field,
this::computeDistanceWithUnit, false, NO_OP_DOUBLE_CONVERTER, accumulatorProvider
this::computeDistanceWithUnit, false, NO_OP_DOUBLE_CONVERTER, collectorProvider
);
}
}
Expand All @@ -99,7 +99,7 @@ public String toString() {
+ "absoluteFieldPath=" + absoluteFieldPath
+ ", center=" + center
+ ", unit=" + unit
+ ", accumulator=" + accumulator
+ ", collector=" + collector
+ "]";
}

Expand Down Expand Up @@ -130,13 +130,13 @@ public A extract(ProjectionHitMapper<?> projectionHitMapper, JsonObject hit,
Integer distanceSortIndex = singleValuedInRoot ? context.getDistanceSortIndex( absoluteFieldPath, center ) : null;

if ( distanceSortIndex != null ) {
A accumulated = accumulator.createInitial();
accumulated = accumulator.accumulate( accumulated, extractDistanceFromSortKey( hit, distanceSortIndex ) );
A accumulated = collector.createInitial();
accumulated = collector.accumulate( accumulated, extractDistanceFromSortKey( hit, distanceSortIndex ) );
return accumulated;
}
else {
A accumulated = accumulator.createInitial();
accumulated = accumulator.accumulate( accumulated, extractDistanceFromScriptField( hit ) );
A accumulated = collector.createInitial();
accumulated = collector.accumulate( accumulated, extractDistanceFromScriptField( hit ) );
return accumulated;
}
}
Expand All @@ -145,7 +145,7 @@ public A extract(ProjectionHitMapper<?> projectionHitMapper, JsonObject hit,
public P transform(LoadingResult<?> loadingResult, A extractedData,
ProjectionTransformContext context) {
// Nothing to transform: we take the values as they are.
return accumulator.finish( extractedData );
return collector.finish( extractedData );
}

private Double extractDistanceFromScriptField(JsonObject hit) {
Expand Down Expand Up @@ -255,9 +255,9 @@ public void unit(DistanceUnit unit) {
}

@Override
public <P> SearchProjection<P> build(ProjectionAccumulator.Provider<Double, P> accumulatorProvider) {
return new ElasticsearchDistanceToFieldProjection<>( this, accumulatorProvider,
accumulatorProvider.get() );
public <P> SearchProjection<P> build(ProjectionCollector.Provider<Double, P> collectorProvider) {
return new ElasticsearchDistanceToFieldProjection<>( this, collectorProvider,
collectorProvider.get() );
}
}
}
Loading