Skip to content

Commit 82c5431

Browse files
committed
Add a simple query rewriter to support rewriting known match queries to semantic queries
1 parent 61b3e0b commit 82c5431

File tree

18 files changed

+234
-13
lines changed

18 files changed

+234
-13
lines changed

server/src/main/java/org/elasticsearch/index/IndexModule.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import org.elasticsearch.indices.recovery.RecoveryState;
6464
import org.elasticsearch.plugins.IndexStorePlugin;
6565
import org.elasticsearch.plugins.internal.rewriter.QueryRewriteInterceptor;
66+
import org.elasticsearch.plugins.internal.rewriter.SimpleQueryRewriter;
6667
import org.elasticsearch.script.ScriptService;
6768
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
6869
import org.elasticsearch.threadpool.ThreadPool;
@@ -496,7 +497,8 @@ public IndexService newIndexService(
496497
ValuesSourceRegistry valuesSourceRegistry,
497498
IndexStorePlugin.IndexFoldersDeletionListener indexFoldersDeletionListener,
498499
Map<String, IndexStorePlugin.SnapshotCommitSupplier> snapshotCommitSuppliers,
499-
QueryRewriteInterceptor queryRewriteInterceptor
500+
QueryRewriteInterceptor queryRewriteInterceptor,
501+
SimpleQueryRewriter simpleQueryRewriter
500502
) throws IOException {
501503
final IndexEventListener eventListener = freeze();
502504
Function<IndexService, CheckedFunction<DirectoryReader, DirectoryReader, IOException>> readerWrapperFactory = indexReaderWrapper
@@ -561,6 +563,7 @@ public IndexService newIndexService(
561563
indexCommitListener.get(),
562564
mapperMetrics,
563565
queryRewriteInterceptor,
566+
simpleQueryRewriter,
564567
indexingStatsSettings,
565568
searchStatsSettings,
566569
mergeMetrics

server/src/main/java/org/elasticsearch/index/IndexService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
import org.elasticsearch.indices.recovery.RecoveryState;
9292
import org.elasticsearch.plugins.IndexStorePlugin;
9393
import org.elasticsearch.plugins.internal.rewriter.QueryRewriteInterceptor;
94+
import org.elasticsearch.plugins.internal.rewriter.SimpleQueryRewriter;
9495
import org.elasticsearch.script.ScriptService;
9596
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
9697
import org.elasticsearch.threadpool.ThreadPool;
@@ -171,6 +172,7 @@ public class IndexService extends AbstractIndexComponent implements IndicesClust
171172
private final ValuesSourceRegistry valuesSourceRegistry;
172173
private final MapperMetrics mapperMetrics;
173174
private final QueryRewriteInterceptor queryRewriteInterceptor;
175+
private final SimpleQueryRewriter simpleQueryRewriter;
174176
private final IndexingStatsSettings indexingStatsSettings;
175177
private final SearchStatsSettings searchStatsSettings;
176178
private final MergeMetrics mergeMetrics;
@@ -211,6 +213,7 @@ public IndexService(
211213
Engine.IndexCommitListener indexCommitListener,
212214
MapperMetrics mapperMetrics,
213215
QueryRewriteInterceptor queryRewriteInterceptor,
216+
SimpleQueryRewriter simpleQueryRewriter,
214217
IndexingStatsSettings indexingStatsSettings,
215218
SearchStatsSettings searchStatsSettings,
216219
MergeMetrics mergeMetrics
@@ -291,6 +294,7 @@ public IndexService(
291294
this.indexCommitListener = indexCommitListener;
292295
this.mapperMetrics = mapperMetrics;
293296
this.queryRewriteInterceptor = queryRewriteInterceptor;
297+
this.simpleQueryRewriter = simpleQueryRewriter;
294298
try (var ignored = threadPool.getThreadContext().clearTraceContext()) {
295299
// kick off async ops for the first shard in this index
296300
this.refreshTask = new AsyncRefreshTask(this);

server/src/main/java/org/elasticsearch/indices/IndicesService.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
import org.elasticsearch.plugins.IndexStorePlugin;
147147
import org.elasticsearch.plugins.PluginsService;
148148
import org.elasticsearch.plugins.internal.rewriter.QueryRewriteInterceptor;
149+
import org.elasticsearch.plugins.internal.rewriter.SimpleQueryRewriter;
149150
import org.elasticsearch.repositories.RepositoriesService;
150151
import org.elasticsearch.script.ScriptService;
151152
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
@@ -281,7 +282,8 @@ public class IndicesService extends AbstractLifecycleComponent
281282
private final PostRecoveryMerger postRecoveryMerger;
282283
private final List<SearchOperationListener> searchOperationListeners;
283284
private final QueryRewriteInterceptor queryRewriteInterceptor;
284-
final SlowLogFieldProvider slowLogFieldProvider; // pkg-private for testingå
285+
private final SimpleQueryRewriter simpleQueryRewriter;
286+
final SlowLogFieldProvider slowLogFieldProvider; // pkg-private for testing
285287
private final IndexingStatsSettings indexStatsSettings;
286288
private final SearchStatsSettings searchStatsSettings;
287289
private final MergeMetrics mergeMetrics;
@@ -359,6 +361,7 @@ public void onRemoval(ShardId shardId, String fieldName, boolean wasEvicted, lon
359361
this.snapshotCommitSuppliers = builder.snapshotCommitSuppliers;
360362
this.requestCacheKeyDifferentiator = builder.requestCacheKeyDifferentiator;
361363
this.queryRewriteInterceptor = builder.queryRewriteInterceptor;
364+
this.simpleQueryRewriter = builder.simpleQueryRewriter;
362365
this.mapperMetrics = builder.mapperMetrics;
363366
this.mergeMetrics = builder.mergeMetrics;
364367
// doClose() is called when shutting down a node, yet there might still be ongoing requests
@@ -834,7 +837,8 @@ private synchronized IndexService createIndexService(
834837
valuesSourceRegistry,
835838
indexFoldersDeletionListeners,
836839
snapshotCommitSuppliers,
837-
queryRewriteInterceptor
840+
queryRewriteInterceptor,
841+
simpleQueryRewriter
838842
);
839843
}
840844

@@ -1865,6 +1869,10 @@ public CoordinatorRewriteContextProvider getCoordinatorRewriteContextProvider(Lo
18651869
);
18661870
}
18671871

1872+
public SimpleQueryRewriter getSimpleQueryRewriter() {
1873+
return simpleQueryRewriter;
1874+
}
1875+
18681876
/**
18691877
* Clears the caches for the given shard id if the shard is still allocated on this node
18701878
*/

server/src/main/java/org/elasticsearch/indices/IndicesServiceBuilder.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.elasticsearch.plugins.PluginsService;
3838
import org.elasticsearch.plugins.internal.InternalSearchPlugin;
3939
import org.elasticsearch.plugins.internal.rewriter.QueryRewriteInterceptor;
40+
import org.elasticsearch.plugins.internal.rewriter.SimpleQueryRewriter;
4041
import org.elasticsearch.script.ScriptService;
4142
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
4243
import org.elasticsearch.search.internal.ShardSearchRequest;
@@ -83,6 +84,7 @@ public class IndicesServiceBuilder {
8384
MergeMetrics mergeMetrics;
8485
List<SearchOperationListener> searchOperationListener = List.of();
8586
QueryRewriteInterceptor queryRewriteInterceptor = null;
87+
SimpleQueryRewriter simpleQueryRewriter = null;
8688
SlowLogFieldProvider slowLogFieldProvider = new SlowLogFieldProvider() {
8789
@Override
8890
public SlowLogFields create() {
@@ -301,6 +303,27 @@ public IndicesService build() {
301303
}));
302304
queryRewriteInterceptor = QueryRewriteInterceptor.multi(queryRewriteInterceptors);
303305

306+
var simpleQueryRewriters = pluginsService.filterPlugins(InternalSearchPlugin.class)
307+
.map(InternalSearchPlugin::getSimpleQueryRewriters)
308+
.flatMap(List::stream)
309+
.collect(Collectors.toMap(SimpleQueryRewriter::getName, interceptor -> {
310+
if (interceptor.getName() == null) {
311+
throw new IllegalArgumentException("SimpleQueryRewriter [" + interceptor.getClass().getName() + "] requires name");
312+
}
313+
return interceptor;
314+
}, (a, b) -> {
315+
throw new IllegalStateException(
316+
"Conflicting simple rewriters ["
317+
+ a.getName()
318+
+ "] found in ["
319+
+ a.getClass().getName()
320+
+ "] and ["
321+
+ b.getClass().getName()
322+
+ "]"
323+
);
324+
}));
325+
simpleQueryRewriter = SimpleQueryRewriter.multi(simpleQueryRewriters);
326+
304327
return new IndicesService(this);
305328
}
306329
}

server/src/main/java/org/elasticsearch/plugins/internal/InternalSearchPlugin.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package org.elasticsearch.plugins.internal;
1111

1212
import org.elasticsearch.plugins.internal.rewriter.QueryRewriteInterceptor;
13+
import org.elasticsearch.plugins.internal.rewriter.SimpleQueryRewriter;
1314

1415
import java.util.List;
1516

@@ -24,4 +25,8 @@ public interface InternalSearchPlugin {
2425
default List<QueryRewriteInterceptor> getQueryRewriteInterceptors() {
2526
return emptyList();
2627
}
28+
29+
default List<SimpleQueryRewriter> getSimpleQueryRewriters() {
30+
return emptyList();
31+
}
2732
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.plugins.internal.rewriter;
11+
12+
import org.elasticsearch.index.query.QueryBuilder;
13+
14+
import java.util.Map;
15+
16+
public interface SimpleQueryRewriter {
17+
18+
String getName();
19+
20+
QueryBuilder rewrite(QueryBuilder queryBuilder);
21+
22+
static SimpleQueryRewriter multi(Map<String, SimpleQueryRewriter> rewriters) {
23+
return rewriters.isEmpty() ? new NoOpSimpleQueryRewriter() : new CompositeSimpleQueryRewriter(rewriters);
24+
}
25+
26+
class CompositeSimpleQueryRewriter implements SimpleQueryRewriter {
27+
final String NAME = "composite";
28+
private final Map<String, SimpleQueryRewriter> simpleQueryRewriters;
29+
30+
private CompositeSimpleQueryRewriter(Map<String, SimpleQueryRewriter> simpleQueryRewriters) {
31+
this.simpleQueryRewriters = simpleQueryRewriters;
32+
}
33+
34+
@Override
35+
public String getName() {
36+
return NAME;
37+
}
38+
39+
@Override
40+
public QueryBuilder rewrite(QueryBuilder queryBuilder) {
41+
SimpleQueryRewriter rewriter = simpleQueryRewriters.get(queryBuilder.getName());
42+
if (rewriter != null) {
43+
return rewriter.rewrite(queryBuilder);
44+
}
45+
return queryBuilder;
46+
}
47+
}
48+
49+
class NoOpSimpleQueryRewriter implements SimpleQueryRewriter {
50+
@Override
51+
public QueryBuilder rewrite(QueryBuilder queryBuilder) {
52+
return queryBuilder;
53+
}
54+
55+
@Override
56+
public String getName() {
57+
return null;
58+
}
59+
}
60+
}

server/src/main/java/org/elasticsearch/search/DefaultSearchContext.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.elasticsearch.index.query.SearchExecutionContext;
5252
import org.elasticsearch.index.search.NestedHelper;
5353
import org.elasticsearch.index.shard.IndexShard;
54+
import org.elasticsearch.plugins.internal.rewriter.SimpleQueryRewriter;
5455
import org.elasticsearch.search.aggregations.SearchContextAggregations;
5556
import org.elasticsearch.search.aggregations.support.AggregationContext;
5657
import org.elasticsearch.search.builder.SearchSourceBuilder;
@@ -161,6 +162,7 @@ final class DefaultSearchContext extends SearchContext {
161162
private final Map<String, SearchExtBuilder> searchExtBuilders = new HashMap<>();
162163
private final SearchExecutionContext searchExecutionContext;
163164
private final FetchPhase fetchPhase;
165+
private final SimpleQueryRewriter simpleQueryRewriter;
164166

165167
DefaultSearchContext(
166168
ReaderContext readerContext,
@@ -174,7 +176,8 @@ final class DefaultSearchContext extends SearchContext {
174176
SearchService.ResultsType resultsType,
175177
boolean enableQueryPhaseParallelCollection,
176178
int minimumDocsPerSlice,
177-
long memoryAccountingBufferSize
179+
long memoryAccountingBufferSize,
180+
SimpleQueryRewriter simpleQueryRewriter
178181
) throws IOException {
179182
this.readerContext = readerContext;
180183
this.request = request;
@@ -186,6 +189,7 @@ final class DefaultSearchContext extends SearchContext {
186189
this.indexService = readerContext.indexService();
187190
this.indexShard = readerContext.indexShard();
188191
this.memoryAccountingBufferSize = memoryAccountingBufferSize;
192+
this.simpleQueryRewriter = simpleQueryRewriter;
189193

190194
Engine.Searcher engineSearcher = readerContext.acquireSearcher("search");
191195
int maximumNumberOfSlices = determineMaximumNumberOfSlices(
@@ -976,4 +980,9 @@ public IdLoader newIdLoader() {
976980
return IdLoader.fromLeafStoredFieldLoader();
977981
}
978982
}
983+
984+
@Override
985+
public SimpleQueryRewriter simpleQueryRewriter() {
986+
return simpleQueryRewriter;
987+
}
979988
}

server/src/main/java/org/elasticsearch/search/SearchService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1402,7 +1402,8 @@ private DefaultSearchContext createSearchContext(
14021402
resultsType,
14031403
enableQueryPhaseParallelCollection,
14041404
minimumDocsPerSlice,
1405-
memoryAccountingBufferSize
1405+
memoryAccountingBufferSize,
1406+
indicesService.getSimpleQueryRewriter()
14061407
);
14071408
// we clone the query shard context here just for rewriting otherwise we
14081409
// might end up with incorrect state since we are using now() or script services

server/src/main/java/org/elasticsearch/search/internal/SearchContext.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.elasticsearch.index.query.QueryShardException;
2727
import org.elasticsearch.index.query.SearchExecutionContext;
2828
import org.elasticsearch.index.shard.IndexShard;
29+
import org.elasticsearch.plugins.internal.rewriter.SimpleQueryRewriter;
2930
import org.elasticsearch.search.RescoreDocIds;
3031
import org.elasticsearch.search.SearchExtBuilder;
3132
import org.elasticsearch.search.SearchShardTarget;
@@ -449,4 +450,6 @@ public String toString() {
449450
public abstract SourceLoader newSourceLoader(@Nullable SourceFilter sourceFilter);
450451

451452
public abstract IdLoader newIdLoader();
453+
454+
public abstract SimpleQueryRewriter simpleQueryRewriter();
452455
}

server/src/main/java/org/elasticsearch/search/internal/SubSearchContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.apache.lucene.search.Query;
1212
import org.apache.lucene.search.TotalHits;
1313
import org.elasticsearch.index.query.ParsedQuery;
14+
import org.elasticsearch.plugins.internal.rewriter.SimpleQueryRewriter;
1415
import org.elasticsearch.search.aggregations.SearchContextAggregations;
1516
import org.elasticsearch.search.collapse.CollapseContext;
1617
import org.elasticsearch.search.fetch.FetchSearchResult;
@@ -305,4 +306,9 @@ public TotalHits getTotalHits() {
305306
public float getMaxScore() {
306307
return querySearchResult.getMaxScore();
307308
}
309+
310+
@Override
311+
public SimpleQueryRewriter simpleQueryRewriter() {
312+
throw new UnsupportedOperationException("Not supported");
313+
}
308314
}

0 commit comments

Comments
 (0)