Skip to content

Commit acae7f1

Browse files
committed
Optimize innerhits query performance
Signed-off-by: kkewwei <kewei.11@bytedance.com> Signed-off-by: kkewwei <kkewwei@163.com>
1 parent 34ef146 commit acae7f1

File tree

4 files changed

+40
-33
lines changed

4 files changed

+40
-33
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
7272
- Allow extended plugins to be optional ([#16909](https://github.com/opensearch-project/OpenSearch/pull/16909))
7373
- Use the correct type to widen the sort fields when merging top docs ([#16881](https://github.com/opensearch-project/OpenSearch/pull/16881))
7474
- Limit reader writer separation to remote store enabled clusters [#16760](https://github.com/opensearch-project/OpenSearch/pull/16760)
75+
- Optimize innerhits query performance [#16937](https://github.com/opensearch-project/OpenSearch/pull/16937)
7576

7677
### Deprecated
7778
- Performing update operation with default pipeline or final pipeline is deprecated ([#16712](https://github.com/opensearch-project/OpenSearch/pull/16712))

server/src/main/java/org/opensearch/index/mapper/DocumentMapper.java

+28-20
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@
3535
import org.apache.lucene.document.StoredField;
3636
import org.apache.lucene.index.LeafReaderContext;
3737
import org.apache.lucene.search.Query;
38-
import org.apache.lucene.search.ScoreMode;
39-
import org.apache.lucene.search.Scorer;
40-
import org.apache.lucene.search.Weight;
38+
import org.apache.lucene.util.BitSet;
4139
import org.apache.lucene.util.BytesRef;
4240
import org.opensearch.OpenSearchGenerationException;
4341
import org.opensearch.common.annotation.PublicApi;
@@ -53,6 +51,7 @@
5351
import org.opensearch.index.analysis.IndexAnalyzers;
5452
import org.opensearch.index.mapper.MapperService.MergeReason;
5553
import org.opensearch.index.mapper.MetadataFieldMapper.TypeParser;
54+
import org.opensearch.index.query.NestedQueryBuilder;
5655
import org.opensearch.search.internal.SearchContext;
5756

5857
import java.io.IOException;
@@ -270,25 +269,15 @@ public ParsedDocument createNoopTombstoneDoc(String index, String reason) throws
270269
* Returns the best nested {@link ObjectMapper} instances that is in the scope of the specified nested docId.
271270
*/
272271
public ObjectMapper findNestedObjectMapper(int nestedDocId, SearchContext sc, LeafReaderContext context) throws IOException {
272+
if (sc instanceof NestedQueryBuilder.NestedInnerHitSubContext) {
273+
ObjectMapper objectMapper = ((NestedQueryBuilder.NestedInnerHitSubContext) sc).getChildObjectMapper();
274+
assert objectMappers().containsKey(objectMapper.fullPath());
275+
assert containSubDocIdWithObjectMapper(nestedDocId, objectMapper, sc, context);
276+
return objectMapper;
277+
}
273278
ObjectMapper nestedObjectMapper = null;
274279
for (ObjectMapper objectMapper : objectMappers().values()) {
275-
if (!objectMapper.nested().isNested()) {
276-
continue;
277-
}
278-
279-
Query filter = objectMapper.nestedTypeFilter();
280-
if (filter == null) {
281-
continue;
282-
}
283-
// We can pass down 'null' as acceptedDocs, because nestedDocId is a doc to be fetched and
284-
// therefore is guaranteed to be a live doc.
285-
final Weight nestedWeight = filter.createWeight(sc.searcher(), ScoreMode.COMPLETE_NO_SCORES, 1f);
286-
Scorer scorer = nestedWeight.scorer(context);
287-
if (scorer == null) {
288-
continue;
289-
}
290-
291-
if (scorer.iterator().advance(nestedDocId) == nestedDocId) {
280+
if (containSubDocIdWithObjectMapper(nestedDocId, objectMapper, sc, context)) {
292281
if (nestedObjectMapper == null) {
293282
nestedObjectMapper = objectMapper;
294283
} else {
@@ -301,6 +290,25 @@ public ObjectMapper findNestedObjectMapper(int nestedDocId, SearchContext sc, Le
301290
return nestedObjectMapper;
302291
}
303292

293+
private boolean containSubDocIdWithObjectMapper(int nestedDocId, ObjectMapper objectMapper, SearchContext sc, LeafReaderContext context)
294+
throws IOException {
295+
if (!objectMapper.nested().isNested()) {
296+
return false;
297+
}
298+
Query filter = objectMapper.nestedTypeFilter();
299+
if (filter == null) {
300+
return false;
301+
}
302+
// We can pass down 'null' as acceptedDocs, because nestedDocId is a doc to be fetched and
303+
// therefore is guaranteed to be a live doc.
304+
BitSet nestedDocIds = sc.bitsetFilterCache().getBitSetProducer(filter).getBitSet(context);
305+
if (nestedDocIds != null && nestedDocIds.get(nestedDocId)) {
306+
return true;
307+
} else {
308+
return false;
309+
}
310+
}
311+
304312
public DocumentMapper merge(Mapping mapping, MergeReason reason) {
305313
Mapping merged = this.mapping.merge(mapping, reason);
306314
return new DocumentMapper(mapperService, merged);

server/src/main/java/org/opensearch/index/query/NestedQueryBuilder.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ protected void doBuild(SearchContext parentSearchContext, InnerHitsContext inner
438438
*
439439
* @opensearch.internal
440440
*/
441-
static final class NestedInnerHitSubContext extends InnerHitsContext.InnerHitSubContext {
441+
public static final class NestedInnerHitSubContext extends InnerHitsContext.InnerHitSubContext {
442442

443443
private final ObjectMapper parentObjectMapper;
444444
private final ObjectMapper childObjectMapper;
@@ -507,6 +507,10 @@ public TopDocsAndMaxScore topDocs(SearchHit hit) throws IOException {
507507
return new TopDocsAndMaxScore(td, maxScore);
508508
}
509509
}
510+
511+
public ObjectMapper getChildObjectMapper() {
512+
return childObjectMapper;
513+
}
510514
}
511515

512516
@Override

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

+6-12
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,7 @@
3838
import org.apache.lucene.index.ReaderUtil;
3939
import org.apache.lucene.search.DocIdSetIterator;
4040
import org.apache.lucene.search.Query;
41-
import org.apache.lucene.search.ScoreMode;
42-
import org.apache.lucene.search.Scorer;
4341
import org.apache.lucene.search.TotalHits;
44-
import org.apache.lucene.search.Weight;
4542
import org.apache.lucene.util.BitSet;
4643
import org.opensearch.common.CheckedBiConsumer;
4744
import org.opensearch.common.annotation.PublicApi;
@@ -55,7 +52,6 @@
5552
import org.opensearch.core.common.text.Text;
5653
import org.opensearch.core.tasks.TaskCancelledException;
5754
import org.opensearch.core.xcontent.MediaType;
58-
import org.opensearch.index.IndexSettings;
5955
import org.opensearch.index.fieldvisitor.CustomFieldsVisitor;
6056
import org.opensearch.index.fieldvisitor.FieldsVisitor;
6157
import org.opensearch.index.mapper.DocumentMapper;
@@ -501,7 +497,6 @@ private SearchHit.NestedIdentity getInternalNestedIdentity(
501497
ObjectMapper current = nestedObjectMapper;
502498
String originalName = nestedObjectMapper.name();
503499
SearchHit.NestedIdentity nestedIdentity = null;
504-
final IndexSettings indexSettings = context.getQueryShardContext().getIndexSettings();
505500
do {
506501
Query parentFilter;
507502
nestedParentObjectMapper = current.getParentObjectMapper(mapperService);
@@ -520,14 +515,13 @@ private SearchHit.NestedIdentity getInternalNestedIdentity(
520515
current = nestedParentObjectMapper;
521516
continue;
522517
}
523-
final Weight childWeight = context.searcher()
524-
.createWeight(context.searcher().rewrite(childFilter), ScoreMode.COMPLETE_NO_SCORES, 1f);
525-
Scorer childScorer = childWeight.scorer(subReaderContext);
526-
if (childScorer == null) {
518+
BitSet childIter = context.bitsetFilterCache()
519+
.getBitSetProducer(context.searcher().rewrite(childFilter))
520+
.getBitSet(subReaderContext);
521+
if (childIter == null) {
527522
current = nestedParentObjectMapper;
528523
continue;
529524
}
530-
DocIdSetIterator childIter = childScorer.iterator();
531525

532526
BitSet parentBits = context.bitsetFilterCache().getBitSetProducer(parentFilter).getBitSet(subReaderContext);
533527

@@ -541,8 +535,8 @@ private SearchHit.NestedIdentity getInternalNestedIdentity(
541535
* that appear before him.
542536
*/
543537
int previousParent = parentBits.prevSetBit(currentParent);
544-
for (int docId = childIter.advance(previousParent + 1); docId < nestedSubDocId
545-
&& docId != DocIdSetIterator.NO_MORE_DOCS; docId = childIter.nextDoc()) {
538+
for (int docId = childIter.nextSetBit(previousParent + 1); docId < nestedSubDocId
539+
&& docId != DocIdSetIterator.NO_MORE_DOCS; docId = childIter.nextSetBit(docId + 1)) {
546540
offset++;
547541
}
548542
currentParent = nestedSubDocId;

0 commit comments

Comments
 (0)