Skip to content

Commit 149406b

Browse files
committed
HSEARCH-5384 Allow to "rescope" predicate/projection factories
HSEARCH-5384 Check typed predicate definition compatibility HSEARCH-**** WIP HSEARCH-**** Use typed predicate definition
1 parent edfbab4 commit 149406b

File tree

222 files changed

+1616
-707
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

222 files changed

+1616
-707
lines changed

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/ElasticsearchExtension.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,12 @@ private ElasticsearchExtension() {
106106
@Override
107107
public Optional<ElasticsearchSearchQuerySelectStep<SR, R, E, LOS>> extendOptional(
108108
SearchQuerySelectStep<SR, ?, R, E, LOS, ?, ?> original,
109-
SearchQueryIndexScope<?> scope,
109+
SearchQueryIndexScope<SR, ?> scope,
110110
BackendSessionContext sessionContext,
111111
SearchLoadingContextBuilder<E, LOS> loadingContextBuilder) {
112112
if ( scope instanceof ElasticsearchSearchQueryIndexScope ) {
113113
return Optional.of( new ElasticsearchSearchQuerySelectStepImpl<>(
114-
(ElasticsearchSearchQueryIndexScope<?>) scope, sessionContext, loadingContextBuilder
114+
(ElasticsearchSearchQueryIndexScope<SR, ?>) scope, sessionContext, loadingContextBuilder
115115
) );
116116
}
117117
else {

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/document/model/dsl/impl/AbstractElasticsearchIndexCompositeNodeBuilder.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
package org.hibernate.search.backend.elasticsearch.document.model.dsl.impl;
66

77
import java.util.LinkedHashMap;
8+
import java.util.LinkedHashSet;
89
import java.util.Map;
10+
import java.util.Set;
911

1012
import org.hibernate.search.backend.elasticsearch.document.model.impl.ElasticsearchIndexCompositeNode;
1113
import org.hibernate.search.backend.elasticsearch.document.model.impl.ElasticsearchIndexField;
@@ -26,6 +28,7 @@
2628
import org.hibernate.search.engine.backend.types.ObjectStructure;
2729
import org.hibernate.search.engine.common.tree.spi.TreeNodeInclusion;
2830
import org.hibernate.search.engine.search.predicate.definition.PredicateDefinition;
31+
import org.hibernate.search.engine.search.predicate.definition.TypedPredicateDefinition;
2932
import org.hibernate.search.engine.search.predicate.spi.PredicateTypeKeys;
3033

3134
public abstract class AbstractElasticsearchIndexCompositeNodeBuilder implements IndexCompositeNodeBuilder {
@@ -35,7 +38,7 @@ public abstract class AbstractElasticsearchIndexCompositeNodeBuilder implements
3538
// Use a LinkedHashMap for deterministic iteration
3639
private final Map<String, ElasticsearchIndexNodeContributor> fields = new LinkedHashMap<>();
3740
private final Map<String, ElasticsearchIndexNodeContributor> templates = new LinkedHashMap<>();
38-
private final Map<String, ElasticsearchIndexNamedPredicateOptions> namedPredicates = new LinkedHashMap<>();
41+
private final Set<String> namedPredicates = new LinkedHashSet<>();
3942

4043
protected AbstractElasticsearchIndexCompositeNodeBuilder(
4144
ElasticsearchIndexCompositeNodeType.Builder typeBuilder) {
@@ -74,14 +77,23 @@ public IndexObjectFieldBuilder addObjectField(String relativeFieldName, TreeNode
7477
@Override
7578
public IndexSchemaNamedPredicateOptionsStep addNamedPredicate(String name, TreeNodeInclusion inclusion,
7679
PredicateDefinition definition) {
77-
ElasticsearchIndexNamedPredicateOptions options = new ElasticsearchIndexNamedPredicateOptions(
78-
inclusion, definition );
79-
putNamedPredicate( name, options );
80+
putNamedPredicate( name );
8081
if ( TreeNodeInclusion.INCLUDED.equals( inclusion ) ) {
8182
typeBuilder.queryElementFactory( PredicateTypeKeys.named( name ),
82-
new ElasticsearchNamedPredicate.Factory( options.definition, name ) );
83+
new ElasticsearchNamedPredicate.Factory( definition, name ) );
8384
}
84-
return options;
85+
return new ElasticsearchIndexNamedPredicateOptions<>( inclusion, definition );
86+
}
87+
88+
@Override
89+
public IndexSchemaNamedPredicateOptionsStep addNamedPredicate(String name,
90+
TreeNodeInclusion inclusion, TypedPredicateDefinition<?> definition) {
91+
putNamedPredicate( name );
92+
if ( TreeNodeInclusion.INCLUDED.equals( inclusion ) ) {
93+
typeBuilder.queryElementFactory( PredicateTypeKeys.named( name ),
94+
new ElasticsearchNamedPredicate.TypedFactory<>( definition, name ) );
95+
}
96+
return new ElasticsearchIndexNamedPredicateOptions<>( inclusion, definition );
8597
}
8698

8799
@Override
@@ -147,9 +159,8 @@ private void putTemplate(String name, ElasticsearchIndexNodeContributor contribu
147159
}
148160
}
149161

150-
private void putNamedPredicate(String name, ElasticsearchIndexNamedPredicateOptions options) {
151-
Object previous = namedPredicates.putIfAbsent( name, options );
152-
if ( previous != null ) {
162+
private void putNamedPredicate(String name) {
163+
if ( !namedPredicates.add( name ) ) {
153164
throw MappingLog.INSTANCE.indexSchemaNamedPredicateNameConflict( name, eventContext() );
154165
}
155166
}

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/document/model/dsl/impl/ElasticsearchIndexNamedPredicateOptions.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,8 @@
66

77
import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaNamedPredicateOptionsStep;
88
import org.hibernate.search.engine.common.tree.spi.TreeNodeInclusion;
9-
import org.hibernate.search.engine.search.predicate.definition.PredicateDefinition;
109

11-
public class ElasticsearchIndexNamedPredicateOptions implements IndexSchemaNamedPredicateOptionsStep {
12-
13-
public final TreeNodeInclusion inclusion;
14-
public final PredicateDefinition definition;
15-
16-
ElasticsearchIndexNamedPredicateOptions(TreeNodeInclusion inclusion, PredicateDefinition definition) {
17-
this.inclusion = inclusion;
18-
this.definition = definition;
19-
}
10+
public record ElasticsearchIndexNamedPredicateOptions<T>(TreeNodeInclusion inclusion, T definition)
11+
implements IndexSchemaNamedPredicateOptionsStep {
2012

2113
}

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/index/impl/ElasticsearchIndexManagerImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,9 @@ public IndexWorkspace createWorkspace(BackendMappingContext mappingContext, Set<
195195
}
196196

197197
@Override
198-
public IndexScopeBuilder createScopeBuilder(BackendMappingContext mappingContext) {
199-
return new ElasticsearchIndexScopeBuilder(
200-
backendContext, mappingContext, this
198+
public <SR> IndexScopeBuilder<SR> createScopeBuilder(BackendMappingContext mappingContext, Class<SR> rootScopeType) {
199+
return new ElasticsearchIndexScopeBuilder<>(
200+
backendContext, mappingContext, rootScopeType, this
201201
);
202202
}
203203

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/index/impl/ElasticsearchIndexScopeBuilder.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,20 @@
1515
import org.hibernate.search.engine.backend.scope.spi.IndexScope;
1616
import org.hibernate.search.engine.backend.scope.spi.IndexScopeBuilder;
1717

18-
class ElasticsearchIndexScopeBuilder implements IndexScopeBuilder {
18+
class ElasticsearchIndexScopeBuilder<SR> implements IndexScopeBuilder<SR> {
1919

2020
private final IndexManagerBackendContext backendContext;
2121
private final BackendMappingContext mappingContext;
22+
private final Class<SR> rootScopeType;
2223

2324
// Use LinkedHashSet to ensure stable order when generating requests
2425
private final Set<ElasticsearchIndexManagerImpl> indexManagers = new LinkedHashSet<>();
2526

2627
ElasticsearchIndexScopeBuilder(IndexManagerBackendContext backendContext,
27-
BackendMappingContext mappingContext, ElasticsearchIndexManagerImpl indexManager) {
28+
BackendMappingContext mappingContext, Class<SR> rootScopeType, ElasticsearchIndexManagerImpl indexManager) {
2829
this.backendContext = backendContext;
2930
this.mappingContext = mappingContext;
31+
this.rootScopeType = rootScopeType;
3032
this.indexManagers.add( indexManager );
3133
}
3234

@@ -40,11 +42,11 @@ void add(IndexManagerBackendContext backendContext, ElasticsearchIndexManagerImp
4042
}
4143

4244
@Override
43-
public IndexScope build() {
45+
public IndexScope<SR> build() {
4446
// Use LinkedHashSet to ensure stable order when generating requests
4547
Set<ElasticsearchIndexModel> indexModels = indexManagers.stream().map( ElasticsearchIndexManagerImpl::model )
4648
.collect( Collectors.toCollection( LinkedHashSet::new ) );
47-
return new ElasticsearchIndexScope( mappingContext, backendContext, indexModels );
49+
return new ElasticsearchIndexScope<>( mappingContext, backendContext, rootScopeType, indexModels );
4850
}
4951

5052
@Override

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/index/impl/IndexManagerBackendContext.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,12 @@ public SearchProjectionBackendContext getSearchProjectionBackendContext() {
146146
}
147147

148148
@Override
149-
public ElasticsearchSearchQueryIndexScope<?> createSearchContext(BackendMappingContext mappingContext,
149+
public <SR> ElasticsearchSearchQueryIndexScope<SR, ?> createSearchContext(BackendMappingContext mappingContext,
150+
Class<SR> rootScopeType,
150151
Set<ElasticsearchIndexModel> indexModels) {
151-
return new ElasticsearchSearchIndexScopeImpl(
152+
return new ElasticsearchSearchIndexScopeImpl<>(
152153
mappingContext,
154+
rootScopeType,
153155
this,
154156
userFacingGson, link.getSearchSyntax(),
155157
multiTenancyStrategy,

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/logging/impl/ElasticsearchLog.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ public interface ElasticsearchLog
4040
* here to the next value.
4141
*/
4242
@LogMessage(level = TRACE)
43-
@Message(id = ID_OFFSET + 193, value = "")
43+
@Message(id = ID_OFFSET + 194, value = "")
4444
void nextLoggerIdForConvenience();
4545
}

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/logging/impl/QueryLog.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,8 @@ SearchException vectorKnnMatchVectorTypeDiffersFromField(String absoluteFieldPat
333333
@Message(id = ID_OFFSET + 190, value = "A single-valued highlight projection requested, "
334334
+ "but the corresponding highlighter does not set number of fragments to 1.")
335335
SearchException highlighterIncompatibleCardinality();
336+
337+
@Message(id = ID_OFFSET + 193, value = "Current factory cannot be resocped to '%1$s' as it is scoped to '%2$s'.")
338+
SearchException incompatibleScopeRootType(@FormatWith(ClassFormatter.class) Class<?> requested,
339+
@FormatWith(ClassFormatter.class) Class<?> actual);
336340
}

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/scope/impl/ElasticsearchIndexScope.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
import org.hibernate.search.engine.backend.mapping.spi.BackendMappingContext;
1313
import org.hibernate.search.engine.backend.scope.spi.IndexScope;
1414

15-
public class ElasticsearchIndexScope
16-
implements IndexScope {
15+
public class ElasticsearchIndexScope<SR>
16+
implements IndexScope<SR> {
1717

18-
private final ElasticsearchSearchQueryIndexScope<?> searchScope;
18+
private final ElasticsearchSearchQueryIndexScope<SR, ?> searchScope;
1919

2020
public ElasticsearchIndexScope(BackendMappingContext mappingContext, SearchBackendContext backendContext,
21+
Class<SR> rootScopeType,
2122
Set<ElasticsearchIndexModel> indexModels) {
22-
this.searchScope = backendContext.createSearchContext( mappingContext, indexModels );
23+
this.searchScope = backendContext.createSearchContext( mappingContext, rootScopeType, indexModels );
2324
}
2425

2526
@Override
@@ -28,7 +29,7 @@ public String toString() {
2829
}
2930

3031
@Override
31-
public ElasticsearchSearchQueryIndexScope<?> searchScope() {
32+
public ElasticsearchSearchQueryIndexScope<SR, ?> searchScope() {
3233
return searchScope;
3334
}
3435

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/scope/model/impl/ElasticsearchSearchIndexScopeImpl.java

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,15 @@
5454
import com.google.gson.Gson;
5555
import com.google.gson.JsonObject;
5656

57-
public final class ElasticsearchSearchIndexScopeImpl
57+
public final class ElasticsearchSearchIndexScopeImpl<SR>
5858
extends AbstractSearchIndexScope<
59-
ElasticsearchSearchIndexScopeImpl,
59+
SR,
60+
ElasticsearchSearchIndexScopeImpl<SR>,
6061
ElasticsearchIndexModel,
6162
ElasticsearchSearchIndexNodeContext,
6263
ElasticsearchSearchIndexCompositeNodeContext>
63-
implements ElasticsearchSearchIndexScope<ElasticsearchSearchIndexScopeImpl>,
64-
ElasticsearchSearchQueryIndexScope<ElasticsearchSearchIndexScopeImpl> {
64+
implements ElasticsearchSearchIndexScope<ElasticsearchSearchIndexScopeImpl<SR>>,
65+
ElasticsearchSearchQueryIndexScope<SR, ElasticsearchSearchIndexScopeImpl<SR>> {
6566

6667
// Backend context
6768
private final SearchBackendContext backendContext;
@@ -81,12 +82,13 @@ public final class ElasticsearchSearchIndexScopeImpl
8182
private final ElasticsearchSearchAggregationBuilderFactory aggregationFactory;
8283

8384
public ElasticsearchSearchIndexScopeImpl(BackendMappingContext mappingContext,
85+
Class<SR> rootScopeType,
8486
SearchBackendContext backendContext,
8587
Gson userFacingGson, ElasticsearchSearchSyntax searchSyntax,
8688
MultiTenancyStrategy multiTenancyStrategy,
8789
TimingSource timingSource,
8890
Set<ElasticsearchIndexModel> indexModels) {
89-
super( mappingContext, indexModels );
91+
super( mappingContext, rootScopeType, indexModels );
9092
this.backendContext = backendContext;
9193
this.userFacingGson = userFacingGson;
9294
this.searchSyntax = searchSyntax;
@@ -115,7 +117,7 @@ public ElasticsearchSearchIndexScopeImpl(BackendMappingContext mappingContext,
115117
this.aggregationFactory = new ElasticsearchSearchAggregationBuilderFactory( this );
116118
}
117119

118-
private ElasticsearchSearchIndexScopeImpl(ElasticsearchSearchIndexScopeImpl parentScope,
120+
private ElasticsearchSearchIndexScopeImpl(ElasticsearchSearchIndexScopeImpl<SR> parentScope,
119121
ElasticsearchSearchIndexCompositeNodeContext overriddenRoot) {
120122
super( parentScope, overriddenRoot );
121123
this.backendContext = parentScope.backendContext;
@@ -134,13 +136,13 @@ private ElasticsearchSearchIndexScopeImpl(ElasticsearchSearchIndexScopeImpl pare
134136
}
135137

136138
@Override
137-
protected ElasticsearchSearchIndexScopeImpl self() {
139+
protected ElasticsearchSearchIndexScopeImpl<SR> self() {
138140
return this;
139141
}
140142

141143
@Override
142-
public ElasticsearchSearchIndexScopeImpl withRoot(String objectFieldPath) {
143-
return new ElasticsearchSearchIndexScopeImpl( this, field( objectFieldPath ).toComposite() );
144+
public ElasticsearchSearchIndexScopeImpl<SR> withRoot(String objectFieldPath) {
145+
return new ElasticsearchSearchIndexScopeImpl<>( this, field( objectFieldPath ).toComposite() );
144146
}
145147

146148
@Override
@@ -171,23 +173,23 @@ public <P> ElasticsearchSearchQueryBuilder<P> select(BackendSessionContext sessi
171173
}
172174

173175
@Override
174-
public <SR> ElasticsearchSearchPredicateFactory<SR> predicateFactory() {
175-
return new ElasticsearchSearchPredicateFactoryImpl<>( SearchPredicateDslContext.root( this ) );
176+
public ElasticsearchSearchPredicateFactory<SR> predicateFactory() {
177+
return new ElasticsearchSearchPredicateFactoryImpl<>( rootScopeType, SearchPredicateDslContext.root( this ) );
176178
}
177179

178180
@Override
179-
public <SR> ElasticsearchSearchSortFactory<SR> sortFactory() {
181+
public ElasticsearchSearchSortFactory<SR> sortFactory() {
180182
return new ElasticsearchSearchSortFactoryImpl<>( SearchSortDslContext
181183
.root( this, ElasticsearchSearchSortFactoryImpl::new, predicateFactory() ) );
182184
}
183185

184186
@Override
185-
public <SR, R, E> ElasticsearchSearchProjectionFactory<SR, R, E> projectionFactory() {
187+
public <R, E> ElasticsearchSearchProjectionFactory<SR, R, E> projectionFactory() {
186188
return new ElasticsearchSearchProjectionFactoryImpl<>( SearchProjectionDslContext.root( this ) );
187189
}
188190

189191
@Override
190-
public <SR> ElasticsearchSearchAggregationFactory<SR> aggregationFactory() {
192+
public ElasticsearchSearchAggregationFactory<SR> aggregationFactory() {
191193
return new ElasticsearchSearchAggregationFactoryImpl<>(
192194
SearchAggregationDslContext.root( this, predicateFactory() ) );
193195
}

backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/search/predicate/dsl/impl/ElasticsearchSearchPredicateFactoryImpl.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
*/
55
package org.hibernate.search.backend.elasticsearch.search.predicate.dsl.impl;
66

7+
import org.hibernate.search.backend.elasticsearch.logging.impl.QueryLog;
78
import org.hibernate.search.backend.elasticsearch.search.predicate.dsl.ElasticsearchSearchPredicateFactory;
89
import org.hibernate.search.backend.elasticsearch.search.predicate.impl.ElasticsearchSearchPredicateIndexScope;
10+
import org.hibernate.search.engine.search.common.NonStaticMetamodelScope;
911
import org.hibernate.search.engine.search.predicate.dsl.PredicateFinalStep;
1012
import org.hibernate.search.engine.search.predicate.dsl.spi.AbstractSearchPredicateFactory;
1113
import org.hibernate.search.engine.search.predicate.dsl.spi.SearchPredicateDslContext;
@@ -21,16 +23,36 @@ public class ElasticsearchSearchPredicateFactoryImpl<SR>
2123
implements ElasticsearchSearchPredicateFactory<SR> {
2224

2325
public ElasticsearchSearchPredicateFactoryImpl(
26+
Class<SR> scopeRootType,
2427
SearchPredicateDslContext<ElasticsearchSearchPredicateIndexScope<?>> dslContext) {
25-
super( dslContext );
28+
super( scopeRootType, dslContext );
2629
}
2730

2831
@Override
2932
public ElasticsearchSearchPredicateFactory<SR> withRoot(String objectFieldPath) {
30-
return new ElasticsearchSearchPredicateFactoryImpl<>( dslContext.rescope(
33+
return new ElasticsearchSearchPredicateFactoryImpl<>( scopeRootType, dslContext.rescope(
3134
dslContext.scope().withRoot( objectFieldPath ) ) );
3235
}
3336

37+
@SuppressWarnings("unchecked") // well because we check ;)
38+
@Override
39+
public <SR2> ElasticsearchSearchPredicateFactory<SR2> withScopeRoot(Class<SR2> scopeRootType) {
40+
if ( this.scopeRootType.equals( scopeRootType ) ) {
41+
return (ElasticsearchSearchPredicateFactory<SR2>) this;
42+
}
43+
if (
44+
// if we want the "untyped" version of the factory we can get it from any other factory
45+
// e.g. we have one tied to a Book__ and we want to use some "raw" string paths in a named predicate.
46+
scopeRootType.equals( NonStaticMetamodelScope.class )
47+
// scope type is in the same hierarchy:
48+
|| this.scopeRootType.isAssignableFrom( scopeRootType )
49+
|| scopeRootType.isAssignableFrom( this.scopeRootType )
50+
) {
51+
return new ElasticsearchSearchPredicateFactoryImpl<>( scopeRootType, dslContext );
52+
}
53+
throw QueryLog.INSTANCE.incompatibleScopeRootType( scopeRootType, this.scopeRootType );
54+
}
55+
3456
@Override
3557
public PredicateFinalStep fromJson(String jsonString) {
3658
return new StaticPredicateFinalStep( dslContext.scope().predicateBuilders().fromJson( jsonString ) );

0 commit comments

Comments
 (0)