Skip to content

Commit

Permalink
HSEARCH-4577 More cleanups and addressing remaining TODOs
Browse files Browse the repository at this point in the history
  • Loading branch information
marko-bekhta committed Sep 27, 2024
1 parent 341b610 commit 7e7e889
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;

import org.hibernate.search.engine.environment.bean.BeanHolder;
import org.hibernate.search.engine.search.projection.SearchProjection;
import org.hibernate.search.engine.search.projection.definition.ProjectionDefinitionContext;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReference;
import org.hibernate.search.engine.search.projection.dsl.SearchProjectionFactory;
import org.hibernate.search.engine.search.projection.dsl.impl.SortedSetMultiProjectionTypeReference;
import org.hibernate.search.util.common.annotation.Incubating;
import org.hibernate.search.util.common.spi.ToStringTreeAppender;

Expand All @@ -23,20 +26,39 @@ public final class ConstantProjectionDefinition<T> extends AbstractProjectionDef
@SuppressWarnings("rawtypes")
private static final BeanHolder<? extends ConstantProjectionDefinition> EMPTY_LIST_INSTANCE =
BeanHolder.of( new ConstantProjectionDefinition<List>( Collections.emptyList() ) );
@SuppressWarnings("rawtypes")
private static final BeanHolder<? extends ConstantProjectionDefinition> EMPTY_SET_INSTANCE =
BeanHolder.of( new ConstantProjectionDefinition<Set>( Collections.emptySet() ) );
@SuppressWarnings("rawtypes")
private static final BeanHolder<? extends ConstantProjectionDefinition> EMPTY_SORTED_SET_INSTANCE =
BeanHolder.of( new ConstantProjectionDefinition<SortedSet>( Collections.emptySortedSet() ) );

@SuppressWarnings("unchecked") // NULL_VALUE_INSTANCE works for any T
public static <T> BeanHolder<ConstantProjectionDefinition<T>> nullValue() {
return (BeanHolder<ConstantProjectionDefinition<T>>) NULL_VALUE_INSTANCE;
}

/**
* @deprecated Use {@link #empty(MultiProjectionTypeReference)} instead.
*/
@Deprecated(since = "8.0")
@SuppressWarnings("unchecked") // EMPTY_LIST_INSTANCE works for any T
public static <T> BeanHolder<ConstantProjectionDefinition<List<T>>> emptyList() {
return (BeanHolder<ConstantProjectionDefinition<List<T>>>) EMPTY_LIST_INSTANCE;
}

@SuppressWarnings("unchecked") // empty collections works for any T
public static <T> BeanHolder<ConstantProjectionDefinition<T>> empty(
MultiProjectionTypeReference<T, ?> multiProjectionTypeReference) {
// TODO: add predefined bean holders for the types we can safely cache
if ( MultiProjectionTypeReference.list().equals( multiProjectionTypeReference ) ) {
return (BeanHolder<ConstantProjectionDefinition<T>>) EMPTY_LIST_INSTANCE;
}
if ( MultiProjectionTypeReference.set().equals( multiProjectionTypeReference ) ) {
return (BeanHolder<ConstantProjectionDefinition<T>>) EMPTY_SET_INSTANCE;
}
if ( multiProjectionTypeReference instanceof SortedSetMultiProjectionTypeReference<?> ) {
return (BeanHolder<ConstantProjectionDefinition<T>>) EMPTY_SORTED_SET_INSTANCE;
}
return BeanHolder.of( new ConstantProjectionDefinition<>( multiProjectionTypeReference.empty() ) );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
*/
package org.hibernate.search.engine.search.projection.definition.spi;

import java.util.List;

import org.hibernate.search.engine.search.projection.SearchProjection;
import org.hibernate.search.engine.search.projection.definition.ProjectionDefinitionContext;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReference;
Expand Down Expand Up @@ -83,7 +81,7 @@ public SearchProjection<C> create(SearchProjectionFactory<?, ?> factory,
ProjectionDefinitionContext context) {
return factory.withParameters( params -> factory
.distance( fieldPath, params.get( parameterName, GeoPoint.class ) )
.multi(collectionTypeReference)
.multi( collectionTypeReference )
.unit( unit )
).toProjection();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.search.engine.search.projection.dsl;

import org.hibernate.search.util.common.annotation.Incubating;

/**
* Defines the provider that can create {@link MultiProjectionTypeReference projection type references} based
* on a container type {@code C} and container element type {@code V}.
*/
@Incubating
public interface MultiProjectionTypeReferenceProvider {
/**
*
* @param containerType The type of the expected container.
* @param containerElementType The type of the container elements
* @return The projection type reference for a requested container/element types.
* @param <C> The type of the container.
* @param <V> The type of the container elements.
*/
<C, V> MultiProjectionTypeReference<C, V> multiProjectionTypeReference(Class<C> containerType,
Class<V> containerElementType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReference;

public class ListMultiProjectionTypeReference<V> implements MultiProjectionTypeReference<List<V>, V> {
public final class ListMultiProjectionTypeReference<V> implements MultiProjectionTypeReference<List<V>, V> {

@SuppressWarnings("rawtypes")
private static final ListMultiProjectionTypeReference INSTANCE = new ListMultiProjectionTypeReference();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReference;

public class SetMultiProjectionTypeReference<V> implements MultiProjectionTypeReference<Set<V>, V> {
public final class SetMultiProjectionTypeReference<V> implements MultiProjectionTypeReference<Set<V>, V> {

@SuppressWarnings("rawtypes")
private static final SetMultiProjectionTypeReference INSTANCE = new SetMultiProjectionTypeReference();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReference;

public class SortedSetMultiProjectionTypeReference<V> implements MultiProjectionTypeReference<SortedSet<V>, V> {
public final class SortedSetMultiProjectionTypeReference<V> implements MultiProjectionTypeReference<SortedSet<V>, V> {

@SuppressWarnings("rawtypes")
private static final SortedSetMultiProjectionTypeReference INSTANCE = new SortedSetMultiProjectionTypeReference();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.hibernate.search.engine.common.EntityReference;
import org.hibernate.search.engine.logging.spi.MappableTypeModelFormatter;
import org.hibernate.search.engine.mapper.model.spi.MappingElement;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReference;
import org.hibernate.search.mapper.pojo.automaticindexing.building.impl.DerivedDependencyWalkingInfo;
import org.hibernate.search.mapper.pojo.common.annotation.impl.SearchProcessingWithContextException;
import org.hibernate.search.mapper.pojo.extractor.ContainerExtractor;
Expand Down Expand Up @@ -706,8 +705,9 @@ SearchException invalidAbstractTypeForProjectionConstructor(
@Message(id = ID_OFFSET + 115,
value = "Invalid parameter type for projection constructor: %1$s."
+ " When inferring the cardinality of inner projections from constructor parameters,"
+ " multi-valued constructor parameters must be lists (java.util.List<...>)"
+ " or list supertypes (java.lang.Iterable<...>, java.util.Collection<...>)")
+ " multi-valued constructor parameters must be lists/sets (java.util.List<...>/java.util.Set<...>/java.util.SortedSet<...>)"
+ ", their supertypes (java.lang.Iterable<...>, java.util.Collection<...>)"
+ " or arrays")
SearchException invalidMultiValuedParameterTypeForProjectionConstructor(
@FormatWith(PojoTypeModelFormatter.class) PojoTypeModel<?> parentTypeModel);

Expand Down Expand Up @@ -1043,7 +1043,10 @@ void indexingProgressWithRemainingTime(float estimatePercentileComplete, long do
@Message(id = ID_OFFSET + 168, value = "Mass indexing complete in %3$s. Indexed %1$d/%2$d entities.")
void indexingEntitiesCompleted(long indexed, long total, Duration indexingTime);

@Message(id = ID_OFFSET + 169, value = "Projection type reference %1$s does not accept the elements of type %2$s.")
SearchException projectionTypeReferenceDoesNotAcceptType(MultiProjectionTypeReference<?, ?> multiProjectionTypeReference,
@FormatWith(ClassFormatter.class) Class<?> containerElementType, @Param EventContext eventContext);
@Message(id = ID_OFFSET + 169,
value = "Implicit binding of a java.util.SortedSet<%1$s> constructor parameter is not possible since %1$s is not implementing java.lang.Comparable."
+ " Either make %1$s implement java.lang.Comparable or use a programmatic mapping and provide"
+ " a custom MultiProjectionTypeReferenceProvider that, for example, utilizes a MultiProjectionTypeReference.sortedSet(comparator) reference.")
SearchException cannotBindSortedSetWithNonComparableElements(@FormatWith(ClassFormatter.class) Class<?> elementType,
@Param EventContext eventContext);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import org.hibernate.search.engine.environment.bean.BeanHolder;
import org.hibernate.search.engine.search.projection.definition.ProjectionDefinition;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReference;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReferenceProvider;
import org.hibernate.search.mapper.pojo.model.PojoModelValue;
import org.hibernate.search.util.common.annotation.Incubating;

Expand Down Expand Up @@ -61,6 +61,6 @@ <C, P> void definition(Class<P> expectedValueType,
PojoModelValue<?> container();

@Incubating
<T> MultiProjectionTypeReference<?, T> multiProjectionTypeReference(Class<T> containerElementType);
MultiProjectionTypeReferenceProvider builtInMultiProjectionTypeReferenceProvider();

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
import org.hibernate.search.engine.search.projection.definition.spi.ConstantProjectionDefinition;
import org.hibernate.search.engine.search.projection.definition.spi.DistanceProjectionDefinition;
import org.hibernate.search.engine.search.projection.dsl.DistanceToFieldProjectionOptionsStep;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReference;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReferenceProvider;
import org.hibernate.search.engine.search.projection.dsl.SearchProjectionFactory;
import org.hibernate.search.engine.spatial.DistanceUnit;
import org.hibernate.search.engine.spatial.GeoPoint;
import org.hibernate.search.mapper.pojo.logging.impl.Log;
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBinder;
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingContext;
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingMultiContext;
import org.hibernate.search.util.common.annotation.Incubating;
import org.hibernate.search.util.common.impl.Contracts;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;

Expand Down Expand Up @@ -75,6 +76,7 @@ public static DistanceProjectionBinder create(String fieldPath, String parameter
private final String fieldPathOrNull;
private final String parameterName;
private DistanceUnit unit = DistanceUnit.METERS;
private MultiProjectionTypeReferenceProvider multiProjectionTypeReferenceProvider;

private DistanceProjectionBinder(String fieldPathOrNull, String parameterName) {
this.fieldPathOrNull = fieldPathOrNull;
Expand All @@ -92,6 +94,14 @@ public DistanceProjectionBinder unit(DistanceUnit unit) {
return this;
}

@Incubating
public DistanceProjectionBinder multiProjectionTypeReferenceProvider(
MultiProjectionTypeReferenceProvider multiProjectionTypeReferenceProvider) {
Contracts.assertNotNull( multiProjectionTypeReferenceProvider, "multiProjectionTypeReferenceProvider" );
this.multiProjectionTypeReferenceProvider = multiProjectionTypeReferenceProvider;
return this;
}

@Override
public void bind(ProjectionBindingContext context) {
Contracts.assertNotNullNorEmpty( parameterName, "parameterName" );
Expand Down Expand Up @@ -121,7 +131,11 @@ private void bind(ProjectionBindingContext context, String fieldPath) {
}

private void bind(ProjectionBindingContext context, ProjectionBindingMultiContext multi, String fieldPath) {
var reference = multi.multiProjectionTypeReference( Double.class );
var typeReferenceProvider = this.multiProjectionTypeReferenceProvider == null
? multi.builtInMultiProjectionTypeReferenceProvider()
: this.multiProjectionTypeReferenceProvider;
var reference = typeReferenceProvider.multiProjectionTypeReference( multi.container().rawType(), Double.class );

multi.definition( Double.class, context.isIncluded( fieldPath )
? BeanHolder.of( new DistanceProjectionDefinition.MultiValued<>( fieldPath, parameterName, unit, reference ) )
: ConstantProjectionDefinition.empty( reference ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
import org.hibernate.search.engine.search.projection.definition.spi.ConstantProjectionDefinition;
import org.hibernate.search.engine.search.projection.definition.spi.FieldProjectionDefinition;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReference;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReferenceProvider;
import org.hibernate.search.engine.search.projection.dsl.SearchProjectionFactory;
import org.hibernate.search.mapper.pojo.logging.impl.Log;
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBinder;
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingContext;
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingMultiContext;
import org.hibernate.search.util.common.annotation.Incubating;
import org.hibernate.search.util.common.impl.Contracts;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;

/**
Expand Down Expand Up @@ -60,6 +63,7 @@ public static FieldProjectionBinder create(String fieldPath) {

private final String fieldPathOrNull;
private ValueModel valueModel = ValueModel.MAPPING;
private MultiProjectionTypeReferenceProvider multiProjectionTypeReferenceProvider;

private FieldProjectionBinder(String fieldPathOrNull) {
this.fieldPathOrNull = fieldPathOrNull;
Expand Down Expand Up @@ -88,6 +92,14 @@ public FieldProjectionBinder valueModel(ValueModel valueModel) {
return this;
}

@Incubating
public FieldProjectionBinder multiProjectionTypeReferenceProvider(
MultiProjectionTypeReferenceProvider multiProjectionTypeReferenceProvider) {
Contracts.assertNotNull( multiProjectionTypeReferenceProvider, "multiProjectionTypeReferenceProvider" );
this.multiProjectionTypeReferenceProvider = multiProjectionTypeReferenceProvider;
return this;
}

@Override
public void bind(ProjectionBindingContext context) {
Optional<? extends ProjectionBindingMultiContext> multiOptional = context.multi();
Expand All @@ -108,11 +120,15 @@ private <T> void bind(ProjectionBindingContext context, String fieldPath, Class<
: ConstantProjectionDefinition.nullValue() );
}

@SuppressWarnings("unchecked") // we know that containerElementType should match the multiProjectionTypeReference as they both come from the same context
private <T> void bind(ProjectionBindingContext context, ProjectionBindingMultiContext multi, String fieldPath,
Class<T> containerElementType) {
MultiProjectionTypeReference<?, T> multiProjectionTypeReference =
multi.multiProjectionTypeReference( containerElementType );
var typeReferenceProvider = this.multiProjectionTypeReferenceProvider == null
? multi.builtInMultiProjectionTypeReferenceProvider()
: this.multiProjectionTypeReferenceProvider;

MultiProjectionTypeReference<?, T> multiProjectionTypeReference = typeReferenceProvider
.multiProjectionTypeReference( multi.container().rawType(), containerElementType );

multi.definition( containerElementType, context.isIncluded( fieldPath )
? BeanHolder.of( new FieldProjectionDefinition.MultiValued<>( fieldPath, containerElementType,
multiProjectionTypeReference, valueModel ) )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
import org.hibernate.search.engine.search.projection.definition.ProjectionDefinitionContext;
import org.hibernate.search.engine.search.projection.definition.spi.AbstractProjectionDefinition;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReference;
import org.hibernate.search.engine.search.projection.dsl.MultiProjectionTypeReferenceProvider;
import org.hibernate.search.engine.search.projection.dsl.SearchProjectionFactory;
import org.hibernate.search.mapper.pojo.logging.impl.Log;
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBinder;
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingContext;
import org.hibernate.search.mapper.pojo.search.definition.binding.ProjectionBindingMultiContext;
import org.hibernate.search.util.common.annotation.Incubating;
import org.hibernate.search.util.common.impl.Contracts;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;
import org.hibernate.search.util.common.spi.ToStringTreeAppender;

Expand Down Expand Up @@ -63,6 +66,7 @@ public static HighlightProjectionBinder create(String fieldPath) {

private final String fieldPathOrNull;
private String highlighterName;
private MultiProjectionTypeReferenceProvider multiProjectionTypeReferenceProvider;

private HighlightProjectionBinder(String fieldPathOrNull) {
this.fieldPathOrNull = fieldPathOrNull;
Expand All @@ -80,6 +84,14 @@ public HighlightProjectionBinder highlighter(String highlighterName) {
return this;
}

@Incubating
public HighlightProjectionBinder multiProjectionTypeReferenceProvider(
MultiProjectionTypeReferenceProvider multiProjectionTypeReferenceProvider) {
Contracts.assertNotNull( multiProjectionTypeReferenceProvider, "multiProjectionTypeReferenceProvider" );
this.multiProjectionTypeReferenceProvider = multiProjectionTypeReferenceProvider;
return this;
}

@Override
public void bind(ProjectionBindingContext context) {
String fieldPath = fieldPathOrFail( context );
Expand All @@ -88,13 +100,14 @@ public void bind(ProjectionBindingContext context) {

if ( multiOptional.isPresent() ) {
ProjectionBindingMultiContext multiContext = multiOptional.get();
// TODO: do the check here as we are doing a cast just after it and better to be sure what types we have ...
// if ( !multiContext.containerElement().rawType().isAssignableFrom( String.class ) ) {
// throw log.invalidParameterTypeForHighlightProjectionInProjectionConstructor(
// multiContext.containerElement().rawType(), "String" );
// }
MultiProjectionTypeReference<?, String> multiProjectionTypeReference =
multiContext.multiProjectionTypeReference( String.class );
var typeReferenceProvider = this.multiProjectionTypeReferenceProvider == null
? multiContext.builtInMultiProjectionTypeReferenceProvider()
: this.multiProjectionTypeReferenceProvider;

MultiProjectionTypeReference<?, String> multiProjectionTypeReference = typeReferenceProvider
.multiProjectionTypeReference( multiContext.container().rawType(), String.class );
// If the container element type is not string the check in `multiContext.definition`
// will fail with a message explaining what went wrong:
multiContext.definition( String.class,
new Multi<>( fieldPath, multiProjectionTypeReference, highlighterName ) );
}
Expand Down
Loading

0 comments on commit 7e7e889

Please sign in to comment.