Skip to content

Commit e711269

Browse files
committed
Refactor SourceProvider creation to consistently use MappingLookup
This change updates the code to always create SourceProvider instances via MappingLookup, avoiding direct exposure to the underlying source format (synthetic or stored). It also aligns source filtering behavior between SourceProvider and SourceLoader, ensuring consistent application of filters. This change is needed to enable source filtering to occur earlier in the fetch phase, for example, when constructing a synthetic source.
1 parent d10ef76 commit e711269

File tree

38 files changed

+196
-78
lines changed

38 files changed

+196
-78
lines changed

benchmarks/src/main/java/org/elasticsearch/benchmark/script/ScriptScoreBenchmark.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
import org.elasticsearch.index.fielddata.IndexFieldDataCache;
3131
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
3232
import org.elasticsearch.index.mapper.MappedFieldType;
33+
import org.elasticsearch.index.mapper.MappingLookup;
3334
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType;
3435
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
36+
import org.elasticsearch.index.mapper.SourceFieldMetrics;
3537
import org.elasticsearch.indices.breaker.CircuitBreakerService;
3638
import org.elasticsearch.indices.breaker.NoneCircuitBreakerService;
3739
import org.elasticsearch.plugins.PluginsLoader;
@@ -90,7 +92,7 @@ public class ScriptScoreBenchmark {
9092
private final SearchLookup lookup = new SearchLookup(
9193
fieldTypes::get,
9294
(mft, lookup, fdo) -> mft.fielddataBuilder(FieldDataContext.noRuntimeFields("benchmark")).build(fieldDataCache, breakerService),
93-
SourceProvider.fromStoredFields()
95+
SourceProvider.fromLookup(MappingLookup.EMPTY, null, SourceFieldMetrics.NOOP)
9496
);
9597

9698
@Param({ "expression", "metal", "painless_cast", "painless_def" })

plugins/mapper-murmur3/src/test/java/org/elasticsearch/index/mapper/murmur3/Murmur3FieldMapperTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ protected void assertFetch(MapperService mapperService, String field, Object val
107107
ValueFetcher nativeFetcher = ft.valueFetcher(searchExecutionContext, format);
108108
ParsedDocument doc = mapperService.documentMapper().parse(source);
109109
withLuceneIndex(mapperService, iw -> iw.addDocuments(doc.docs()), ir -> {
110-
Source s = SourceProvider.fromStoredFields().getSource(ir.leaves().get(0), 0);
110+
Source s = SourceProvider.fromLookup(mapperService.mappingLookup(), null, mapperService.getMapperMetrics().sourceFieldMetrics())
111+
.getSource(ir.leaves().get(0), 0);
111112
docValueFetcher.setNextReader(ir.leaves().get(0));
112113
nativeFetcher.setNextReader(ir.leaves().get(0));
113114
List<Object> fromDocValues = docValueFetcher.fetchValues(s, 0, new ArrayList<>());

server/src/main/java/org/elasticsearch/index/query/FilteredSearchExecutionContext.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.elasticsearch.action.ActionListener;
2020
import org.elasticsearch.client.internal.Client;
2121
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
22+
import org.elasticsearch.core.Nullable;
2223
import org.elasticsearch.index.Index;
2324
import org.elasticsearch.index.IndexSettings;
2425
import org.elasticsearch.index.IndexVersion;
@@ -39,6 +40,7 @@
3940
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
4041
import org.elasticsearch.search.lookup.LeafFieldLookupProvider;
4142
import org.elasticsearch.search.lookup.SearchLookup;
43+
import org.elasticsearch.search.lookup.SourceFilter;
4244
import org.elasticsearch.search.lookup.SourceProvider;
4345
import org.elasticsearch.xcontent.XContentParserConfiguration;
4446

@@ -162,8 +164,8 @@ public boolean isSourceSynthetic() {
162164
}
163165

164166
@Override
165-
public SourceLoader newSourceLoader(boolean forceSyntheticSource) {
166-
return in.newSourceLoader(forceSyntheticSource);
167+
public SourceLoader newSourceLoader(@Nullable SourceFilter filter, boolean forceSyntheticSource) {
168+
return in.newSourceLoader(filter, forceSyntheticSource);
167169
}
168170

169171
@Override

server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.common.ParsingException;
2626
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
2727
import org.elasticsearch.common.lucene.search.Queries;
28+
import org.elasticsearch.core.Nullable;
2829
import org.elasticsearch.index.Index;
2930
import org.elasticsearch.index.IndexSettings;
3031
import org.elasticsearch.index.IndexSortConfig;
@@ -57,6 +58,7 @@
5758
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
5859
import org.elasticsearch.search.lookup.LeafFieldLookupProvider;
5960
import org.elasticsearch.search.lookup.SearchLookup;
61+
import org.elasticsearch.search.lookup.SourceFilter;
6062
import org.elasticsearch.search.lookup.SourceProvider;
6163
import org.elasticsearch.transport.RemoteClusterAware;
6264
import org.elasticsearch.xcontent.XContentParserConfiguration;
@@ -439,15 +441,15 @@ public boolean isSourceSynthetic() {
439441
/**
440442
* Build something to load source {@code _source}.
441443
*/
442-
public SourceLoader newSourceLoader(boolean forceSyntheticSource) {
444+
public SourceLoader newSourceLoader(@Nullable SourceFilter filter, boolean forceSyntheticSource) {
443445
if (forceSyntheticSource) {
444446
return new SourceLoader.Synthetic(
445-
null,
447+
filter,
446448
() -> mappingLookup.getMapping().syntheticFieldLoader(null),
447449
mapperMetrics.sourceFieldMetrics()
448450
);
449451
}
450-
return mappingLookup.newSourceLoader(null, mapperMetrics.sourceFieldMetrics());
452+
return mappingLookup.newSourceLoader(filter, mapperMetrics.sourceFieldMetrics());
451453
}
452454

453455
/**
@@ -506,9 +508,7 @@ public SearchLookup lookup() {
506508
}
507509

508510
public SourceProvider createSourceProvider() {
509-
return isSourceSynthetic()
510-
? SourceProvider.fromSyntheticSource(mappingLookup.getMapping(), null, mapperMetrics.sourceFieldMetrics())
511-
: SourceProvider.fromStoredFields();
511+
return SourceProvider.fromLookup(mappingLookup, null, mapperMetrics.sourceFieldMetrics());
512512
}
513513

514514
/**

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import org.elasticsearch.search.internal.SearchContext;
7171
import org.elasticsearch.search.internal.ShardSearchContextId;
7272
import org.elasticsearch.search.internal.ShardSearchRequest;
73+
import org.elasticsearch.search.lookup.SourceFilter;
7374
import org.elasticsearch.search.profile.Profilers;
7475
import org.elasticsearch.search.query.QuerySearchResult;
7576
import org.elasticsearch.search.rank.context.QueryPhaseRankShardContext;
@@ -943,8 +944,8 @@ public ReaderContext readerContext() {
943944
}
944945

945946
@Override
946-
public SourceLoader newSourceLoader() {
947-
return searchExecutionContext.newSourceLoader(request.isForceSyntheticSource());
947+
public SourceLoader newSourceLoader(SourceFilter filter) {
948+
return searchExecutionContext.newSourceLoader(filter, request.isForceSyntheticSource());
948949
}
949950

950951
@Override

server/src/main/java/org/elasticsearch/search/fetch/FetchContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ public class FetchContext {
4545
/**
4646
* Create a FetchContext based on a SearchContext
4747
*/
48-
public FetchContext(SearchContext searchContext) {
48+
public FetchContext(SearchContext searchContext, SourceLoader sourceLoader) {
4949
this.searchContext = searchContext;
50-
this.sourceLoader = searchContext.newSourceLoader();
50+
this.sourceLoader = sourceLoader;
5151
this.storedFieldsContext = buildStoredFieldsContext(searchContext);
5252
this.fetchSourceContext = buildFetchSourceContext(searchContext);
5353
}

server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,8 @@ public Source getSource(LeafReaderContext ctx, int doc) {
111111
}
112112

113113
private SearchHits buildSearchHits(SearchContext context, int[] docIdsToLoad, Profiler profiler, RankDocShardInfo rankDocs) {
114-
115-
FetchContext fetchContext = new FetchContext(context);
116-
SourceLoader sourceLoader = context.newSourceLoader();
114+
SourceLoader sourceLoader = context.newSourceLoader(null);
115+
FetchContext fetchContext = new FetchContext(context, sourceLoader);
117116

118117
PreloadedSourceProvider sourceProvider = new PreloadedSourceProvider();
119118
PreloadedFieldLookupProvider fieldLookupProvider = new PreloadedFieldLookupProvider();

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.apache.lucene.search.TotalHits;
1515
import org.elasticsearch.action.search.SearchType;
1616
import org.elasticsearch.common.breaker.CircuitBreaker;
17+
import org.elasticsearch.core.Nullable;
1718
import org.elasticsearch.core.TimeValue;
1819
import org.elasticsearch.index.cache.bitset.BitsetFilterCache;
1920
import org.elasticsearch.index.mapper.IdLoader;
@@ -33,6 +34,7 @@
3334
import org.elasticsearch.search.fetch.subphase.InnerHitsContext;
3435
import org.elasticsearch.search.fetch.subphase.ScriptFieldsContext;
3536
import org.elasticsearch.search.fetch.subphase.highlight.SearchHighlightContext;
37+
import org.elasticsearch.search.lookup.SourceFilter;
3638
import org.elasticsearch.search.profile.Profilers;
3739
import org.elasticsearch.search.query.QuerySearchResult;
3840
import org.elasticsearch.search.rank.context.QueryPhaseRankShardContext;
@@ -453,8 +455,8 @@ public ReaderContext readerContext() {
453455
}
454456

455457
@Override
456-
public SourceLoader newSourceLoader() {
457-
return in.newSourceLoader();
458+
public SourceLoader newSourceLoader(@Nullable SourceFilter filter) {
459+
return in.newSourceLoader(filter);
458460
}
459461

460462
@Override

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.elasticsearch.search.fetch.subphase.InnerHitsContext;
4242
import org.elasticsearch.search.fetch.subphase.ScriptFieldsContext;
4343
import org.elasticsearch.search.fetch.subphase.highlight.SearchHighlightContext;
44+
import org.elasticsearch.search.lookup.SourceFilter;
4445
import org.elasticsearch.search.profile.Profilers;
4546
import org.elasticsearch.search.query.QueryPhase;
4647
import org.elasticsearch.search.query.QuerySearchResult;
@@ -441,7 +442,7 @@ public String toString() {
441442
/**
442443
* Build something to load source {@code _source}.
443444
*/
444-
public abstract SourceLoader newSourceLoader();
445+
public abstract SourceLoader newSourceLoader(@Nullable SourceFilter sourceFilter);
445446

446447
public abstract IdLoader newIdLoader();
447448
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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.search.lookup;
11+
12+
import org.apache.lucene.index.LeafReaderContext;
13+
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
14+
import org.elasticsearch.index.fieldvisitor.LeafStoredFieldLoader;
15+
import org.elasticsearch.index.fieldvisitor.StoredFieldLoader;
16+
import org.elasticsearch.index.mapper.SourceLoader;
17+
18+
import java.io.IOException;
19+
import java.util.Map;
20+
21+
/**
22+
* A {@link SourceProvider} that loads _source from a concurrent search.
23+
*
24+
* NOTE: This is written under the assumption that individual segments are accessed by a single
25+
* thread, even if separate segments may be searched concurrently. If we ever implement
26+
* within-segment concurrency this will have to work entirely differently.
27+
* **/
28+
class ConcurrentSegmentSourceProvider implements SourceProvider {
29+
private final SourceLoader sourceLoader;
30+
private final StoredFieldLoader storedFieldLoader;
31+
private final Map<Object, Leaf> leaves = ConcurrentCollections.newConcurrentMap();
32+
33+
ConcurrentSegmentSourceProvider(SourceLoader loader, boolean loadSource) {
34+
this.sourceLoader = loader;
35+
this.storedFieldLoader = StoredFieldLoader.create(loadSource, sourceLoader.requiredStoredFields());
36+
}
37+
38+
@Override
39+
public Source getSource(LeafReaderContext ctx, int doc) throws IOException {
40+
final Object id = ctx.id();
41+
var leaf = leaves.get(id);
42+
if (leaf == null) {
43+
leaf = new Leaf(sourceLoader.leaf(ctx.reader(), null), storedFieldLoader.getLoader(ctx, null));
44+
var existing = leaves.put(id, leaf);
45+
assert existing == null : "unexpected source provider [" + existing + "]";
46+
}
47+
return leaf.getSource(ctx, doc);
48+
}
49+
50+
private static class Leaf implements SourceProvider {
51+
private final SourceLoader.Leaf sourceLoader;
52+
private final LeafStoredFieldLoader storedFieldLoader;
53+
int doc = -1;
54+
Source source = null;
55+
56+
private Leaf(SourceLoader.Leaf sourceLoader, LeafStoredFieldLoader storedFieldLoader) {
57+
this.sourceLoader = sourceLoader;
58+
this.storedFieldLoader = storedFieldLoader;
59+
}
60+
61+
@Override
62+
public Source getSource(LeafReaderContext ctx, int doc) throws IOException {
63+
if (this.doc == doc) {
64+
return source;
65+
}
66+
this.doc = doc;
67+
storedFieldLoader.advanceTo(doc);
68+
return source = sourceLoader.source(storedFieldLoader, doc);
69+
}
70+
}
71+
}

0 commit comments

Comments
 (0)