Skip to content

Commit

Permalink
Use approximation to advance matched queries (#120133) (#120146)
Browse files Browse the repository at this point in the history
This PR resolves a regression introduced in #94564 by ensuring that the approximation is used when advancing matched query clauses.
Utilizing the two-phase iterator to validate matches guarantees that we do not attempt to find the next document fulfilling the two-phase criteria beyond the current document.
This fix prevents scenarios where matching a document in the second phase significantly increases query complexity, especially in cases involving restrictive second-pass filters.

Closes #120130
  • Loading branch information
jimczi authored Jan 14, 2025
1 parent a047477 commit 85f8a64
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 8 deletions.
6 changes: 6 additions & 0 deletions docs/changelog/120133.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pr: 120133
summary: Use approximation to advance matched queries
area: Search
type: bug
issues:
- 120130
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
package org.elasticsearch.search.fetch.subphase;

import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreMode;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.ScorerSupplier;
import org.apache.lucene.search.TwoPhaseIterator;
import org.apache.lucene.search.Weight;
import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.search.fetch.FetchContext;
Expand Down Expand Up @@ -52,8 +54,9 @@ public FetchSubPhaseProcessor getProcessor(FetchContext context) throws IOExcept
);
}
return new FetchSubPhaseProcessor() {
record ScorerAndIterator(Scorer scorer, DocIdSetIterator approximation, TwoPhaseIterator twoPhase) {}

final Map<String, Scorer> matchingIterators = new HashMap<>();
final Map<String, ScorerAndIterator> matchingIterators = new HashMap<>();

@Override
public void setNextReader(LeafReaderContext readerContext) throws IOException {
Expand All @@ -63,7 +66,14 @@ public void setNextReader(LeafReaderContext readerContext) throws IOException {
if (ss != null) {
Scorer scorer = ss.get(0L);
if (scorer != null) {
matchingIterators.put(entry.getKey(), scorer);
final TwoPhaseIterator twoPhase = scorer.twoPhaseIterator();
final DocIdSetIterator iterator;
if (twoPhase == null) {
iterator = scorer.iterator();
} else {
iterator = twoPhase.approximation();
}
matchingIterators.put(entry.getKey(), new ScorerAndIterator(scorer, iterator, twoPhase));
}
}
}
Expand All @@ -73,13 +83,13 @@ public void setNextReader(LeafReaderContext readerContext) throws IOException {
public void process(HitContext hitContext) throws IOException {
Map<String, Float> matches = new LinkedHashMap<>();
int doc = hitContext.docId();
for (Map.Entry<String, Scorer> entry : matchingIterators.entrySet()) {
Scorer scorer = entry.getValue();
if (scorer.iterator().docID() < doc) {
scorer.iterator().advance(doc);
for (Map.Entry<String, ScorerAndIterator> entry : matchingIterators.entrySet()) {
ScorerAndIterator query = entry.getValue();
if (query.approximation.docID() < doc) {
query.approximation.advance(doc);
}
if (scorer.iterator().docID() == doc) {
matches.put(entry.getKey(), scorer.score());
if (query.approximation.docID() == doc && (query.twoPhase == null || query.twoPhase.matches())) {
matches.put(entry.getKey(), query.scorer.score());
}
}
hitContext.hit().matchedQueries(matches);
Expand Down

0 comments on commit 85f8a64

Please sign in to comment.