-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Acquire searcher in field caps only when needed #126137
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
pr: 126137 | ||
summary: Acquire searcher in field caps only when needed | ||
area: Search | ||
type: bug | ||
issues: [] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,14 +11,12 @@ | |
|
||
import org.elasticsearch.cluster.metadata.MappingMetadata; | ||
import org.elasticsearch.core.Booleans; | ||
import org.elasticsearch.core.Nullable; | ||
import org.elasticsearch.index.IndexService; | ||
import org.elasticsearch.index.engine.Engine; | ||
import org.elasticsearch.index.mapper.MappedFieldType; | ||
import org.elasticsearch.index.mapper.RuntimeField; | ||
import org.elasticsearch.index.query.MatchAllQueryBuilder; | ||
import org.elasticsearch.index.query.QueryBuilder; | ||
import org.elasticsearch.index.query.SearchExecutionContext; | ||
import org.elasticsearch.index.query.QueryRewriteContext; | ||
import org.elasticsearch.index.shard.IndexShard; | ||
import org.elasticsearch.index.shard.ShardId; | ||
import org.elasticsearch.indices.IndicesService; | ||
|
@@ -65,28 +63,11 @@ FieldCapabilitiesIndexResponse fetch( | |
) throws IOException { | ||
final IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex()); | ||
final IndexShard indexShard = indexService.getShard(shardId.getId()); | ||
final Engine.Searcher searcher; | ||
if (alwaysMatches(indexFilter)) { | ||
// no need to open a searcher if we aren't filtering, but make sure we are reading from an up-to-dated shard | ||
indexShard.readAllowed(); | ||
searcher = null; | ||
} else { | ||
searcher = indexShard.acquireSearcher(Engine.CAN_MATCH_SEARCH_SOURCE); | ||
} | ||
try (searcher) { | ||
return doFetch( | ||
task, | ||
shardId, | ||
fieldNameFilter, | ||
filters, | ||
fieldTypes, | ||
indexFilter, | ||
nowInMillis, | ||
runtimeFields, | ||
indexService, | ||
searcher | ||
); | ||
} | ||
return doFetch(task, shardId, fieldNameFilter, filters, fieldTypes, indexFilter, nowInMillis, runtimeFields, indexService); | ||
} | ||
|
||
private FieldCapabilitiesIndexResponse doFetch( | ||
|
@@ -98,22 +79,13 @@ private FieldCapabilitiesIndexResponse doFetch( | |
QueryBuilder indexFilter, | ||
long nowInMillis, | ||
Map<String, Object> runtimeFields, | ||
IndexService indexService, | ||
@Nullable Engine.Searcher searcher | ||
IndexService indexService | ||
) throws IOException { | ||
final SearchExecutionContext searchExecutionContext = indexService.newSearchExecutionContext( | ||
shardId.id(), | ||
0, | ||
searcher, | ||
() -> nowInMillis, | ||
null, | ||
runtimeFields | ||
); | ||
var indexMode = searchExecutionContext.getIndexSettings().getMode(); | ||
if (searcher != null && canMatchShard(shardId, indexFilter, nowInMillis, searchExecutionContext) == false) { | ||
QueryRewriteContext queryRewriteContext = indexService.newQueryRewriteContext(() -> nowInMillis, runtimeFields, null); | ||
var indexMode = indexService.getIndexSettings().getMode(); | ||
if (canMatchShard(shardId, indexFilter, nowInMillis, queryRewriteContext) == false) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here is what I missed: while this method takes a query rewrite context, it can indirectly call convertToSearchExecutionContext which performs additional actions, and only does something when the original argument was a SearchExecutionContext. That means a searcher may be needed, but not necessarily. |
||
return new FieldCapabilitiesIndexResponse(shardId.getIndexName(), null, Collections.emptyMap(), false, indexMode); | ||
} | ||
|
||
final MappingMetadata mapping = indexService.getMetadata().mapping(); | ||
String indexMappingHash; | ||
if (includeEmptyFields || enableFieldHasValue == false) { | ||
|
@@ -135,7 +107,7 @@ private FieldCapabilitiesIndexResponse doFetch( | |
} | ||
task.ensureNotCancelled(); | ||
final Map<String, IndexFieldCapabilities> responseMap = retrieveFieldCaps( | ||
searchExecutionContext, | ||
queryRewriteContext, | ||
fieldNameFilter, | ||
filters, | ||
fieldTypes, | ||
|
@@ -150,7 +122,7 @@ private FieldCapabilitiesIndexResponse doFetch( | |
} | ||
|
||
static Map<String, IndexFieldCapabilities> retrieveFieldCaps( | ||
SearchExecutionContext context, | ||
QueryRewriteContext context, | ||
Predicate<String> fieldNameFilter, | ||
String[] filters, | ||
String[] types, | ||
|
@@ -236,20 +208,20 @@ private static boolean canMatchShard( | |
ShardId shardId, | ||
QueryBuilder indexFilter, | ||
long nowInMillis, | ||
SearchExecutionContext searchExecutionContext | ||
QueryRewriteContext queryRewriteContext | ||
) throws IOException { | ||
assert alwaysMatches(indexFilter) == false : "should not be called for always matching [" + indexFilter + "]"; | ||
assert nowInMillis != 0L; | ||
ShardSearchRequest searchRequest = new ShardSearchRequest(shardId, nowInMillis, AliasFilter.EMPTY); | ||
searchRequest.source(new SearchSourceBuilder().query(indexFilter)); | ||
return SearchService.queryStillMatchesAfterRewrite(searchRequest, searchExecutionContext); | ||
return SearchService.queryStillMatchesAfterRewrite(searchRequest, queryRewriteContext); | ||
} | ||
|
||
private static boolean alwaysMatches(QueryBuilder indexFilter) { | ||
return indexFilter == null || indexFilter instanceof MatchAllQueryBuilder; | ||
} | ||
|
||
private static Predicate<MappedFieldType> buildFilter(String[] filters, String[] fieldTypes, SearchExecutionContext context) { | ||
private static Predicate<MappedFieldType> buildFilter(String[] filters, String[] fieldTypes, QueryRewriteContext context) { | ||
// security filters don't exclude metadata fields | ||
Predicate<MappedFieldType> fcf = null; | ||
if (fieldTypes.length > 0) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ | |
import org.elasticsearch.index.mapper.MapperBuilderContext; | ||
import org.elasticsearch.index.mapper.MapperService; | ||
import org.elasticsearch.index.mapper.MappingLookup; | ||
import org.elasticsearch.index.mapper.NestedLookup; | ||
import org.elasticsearch.index.mapper.SourceFieldMapper; | ||
import org.elasticsearch.index.mapper.TextFieldMapper; | ||
import org.elasticsearch.plugins.internal.rewriter.QueryRewriteInterceptor; | ||
|
@@ -465,4 +466,22 @@ public void setQueryRewriteInterceptor(QueryRewriteInterceptor queryRewriteInter | |
this.queryRewriteInterceptor = queryRewriteInterceptor; | ||
} | ||
|
||
public boolean isMetadataField(String field) { | ||
return mapperService.isMetadataField(field); | ||
} | ||
|
||
public boolean isMultiField(String field) { | ||
if (runtimeMappings.containsKey(field)) { | ||
return false; | ||
} | ||
return mapperService.isMultiField(field); | ||
} | ||
|
||
public NestedLookup nestedLookup() { | ||
return mappingLookup.nestedLookup(); | ||
} | ||
|
||
public boolean hasMappings() { | ||
return mappingLookup.hasMappings(); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these methods rely on state that's already available in query rewrite context, there's no good reason to expose them only in SearchExecutionContext, that's more of a leftover from before we were able to rewrite based on mappings only. |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what the purpose of this is, I think we can drop it as well, can't we?