Fix npe when using source confirmed text query against missing field (#127414)

We should check for the field and statistics actually existing when
checking matches and explanation with `match_only_text` fields

closes: https://github.com/elastic/elasticsearch/issues/125635
This commit is contained in:
Benjamin Trent 2025-04-29 13:05:01 -04:00 committed by GitHub
parent 83300ea1f1
commit 3d67e0e7ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 37 additions and 2 deletions

View File

@ -0,0 +1,5 @@
pr: 127414
summary: Fix npe when using source confirmed text query against missing field
area: Search
type: bug
issues: []

View File

@ -267,7 +267,11 @@ public final class SourceConfirmedTextQuery extends Query {
@Override
public Explanation explain(LeafReaderContext context, int doc) throws IOException {
NumericDocValues norms = context.reader().getNormValues(field);
RuntimePhraseScorer scorer = (RuntimePhraseScorer) scorerSupplier(context).get(0);
ScorerSupplier scorerSupplier = scorerSupplier(context);
if (scorerSupplier == null) {
return Explanation.noMatch("No matching phrase");
}
RuntimePhraseScorer scorer = (RuntimePhraseScorer) scorerSupplier.get(0);
if (scorer == null) {
return Explanation.noMatch("No matching phrase");
}
@ -277,6 +281,7 @@ public final class SourceConfirmedTextQuery extends Query {
}
float phraseFreq = scorer.freq();
Explanation freqExplanation = Explanation.match(phraseFreq, "phraseFreq=" + phraseFreq);
assert simScorer != null;
Explanation scoreExplanation = simScorer.explain(freqExplanation, getNormValue(norms, doc));
return Explanation.match(
scoreExplanation.getValue(),
@ -321,7 +326,11 @@ public final class SourceConfirmedTextQuery extends Query {
Weight innerWeight = in.createWeight(searcher, ScoreMode.COMPLETE_NO_SCORES, 1);
return innerWeight.matches(context, doc);
}
RuntimePhraseScorer scorer = (RuntimePhraseScorer) scorerSupplier(context).get(0L);
ScorerSupplier scorerSupplier = scorerSupplier(context);
if (scorerSupplier == null) {
return null;
}
RuntimePhraseScorer scorer = (RuntimePhraseScorer) scorerSupplier.get(0L);
if (scorer == null) {
return null;
}

View File

@ -24,6 +24,7 @@ import org.apache.lucene.queries.spans.SpanQuery;
import org.apache.lucene.queries.spans.SpanTermQuery;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Matches;
@ -101,6 +102,26 @@ public class SourceConfirmedTextQueryTests extends ESTestCase {
}
}
public void testMissingPhrase() throws Exception {
try (Directory dir = newDirectory(); IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(Lucene.STANDARD_ANALYZER))) {
Document doc = new Document();
doc.add(new TextField("body", "a b c b a b c", Store.YES));
w.addDocument(doc);
try (IndexReader reader = DirectoryReader.open(w)) {
IndexSearcher searcher = newSearcher(reader);
PhraseQuery query = new PhraseQuery("missing_field", "b", "c");
Query sourceConfirmedPhraseQuery = new SourceConfirmedTextQuery(query, SOURCE_FETCHER_PROVIDER, Lucene.STANDARD_ANALYZER);
Explanation explanation = searcher.explain(sourceConfirmedPhraseQuery, 0);
assertFalse(explanation.isMatch());
Weight weight = searcher.createWeight(query, ScoreMode.COMPLETE, 1);
assertNull(weight.matches(getOnlyLeafReader(reader).getContext(), 0));
}
}
}
public void testPhrase() throws Exception {
try (Directory dir = newDirectory(); IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(Lucene.STANDARD_ANALYZER))) {