LUCENE-9762: FunctionScoreQuery must guard score() called twice (#2358)

The score() may be called multiple times. It should take care to call DoubleValues.advanceExact only the first time, or risk faulty behavior including exceptions.
This commit is contained in:
David Smiley 2021-02-14 00:13:23 -05:00 committed by GitHub
parent 23755ddfdd
commit 6c140b6dcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 1 deletions

View File

@ -234,9 +234,13 @@ public final class FunctionScoreQuery extends Query {
}
DoubleValues scores = valueSource.getValues(context, DoubleValuesSource.fromScorer(in));
return new FilterScorer(in) {
int scoresDocId = -1; // remember the last docId we called score() on
@Override
public float score() throws IOException {
if (scores.advanceExact(docID())) {
int docId = docID();
if (scoresDocId == docId || scores.advanceExact(docId)) {
scoresDocId = docId;
double factor = scores.doubleValue();
if (factor >= 0) {
return (float) (factor * boost);

View File

@ -20,13 +20,16 @@ package org.apache.lucene.queries.function;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.expressions.Expression;
import org.apache.lucene.expressions.SimpleBindings;
import org.apache.lucene.expressions.js.JavascriptCompiler;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
@ -35,6 +38,7 @@ import org.apache.lucene.search.DoubleValuesSource;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryUtils;
import org.apache.lucene.search.ScoreMode;
@ -332,4 +336,29 @@ public class TestFunctionScoreQuery extends FunctionTestSetup {
fq.createWeight(searcher, inputScoreMode, 1f);
assertEquals(expectedScoreMode, scoreModeInWeight.get());
}
/** The FunctionScoreQuery's Scorer score() is going to be called twice for the same doc. */
public void testScoreCalledTwice() throws Exception {
try (Directory dir = newDirectory()) {
IndexWriterConfig conf = newIndexWriterConfig();
IndexWriter indexWriter = new IndexWriter(dir, conf);
Document doc = new Document();
doc.add(new TextField("ExampleText", "periodic function", Field.Store.NO));
doc.add(new TextField("ExampleText", "plot of the original function", Field.Store.NO));
indexWriter.addDocument(doc);
indexWriter.commit();
indexWriter.close();
try (DirectoryReader reader = DirectoryReader.open(dir)) {
Query q = new TermQuery(new Term("ExampleText", "function"));
q =
FunctionScoreQuery.boostByQuery(
q, new PhraseQuery(1, "ExampleText", "function", "plot"), 2);
q = FunctionScoreQuery.boostByValue(q, DoubleValuesSource.SCORES);
assertEquals(1, new IndexSearcher(reader).search(q, 10).totalHits.value);
}
}
}
}