fix explain in function_score if no function filter matches (#19185)
* fix explain in function_score if no function filter matches When each function in function_score has a filter but none of them matches we always assume 1 for the combined functions and then combine that with the sub query score. But the explanation did not reflect that because in case no function matched we did not even use the actual score that was computed in the explanation.
This commit is contained in:
parent
ebf96bbc35
commit
105dce0e07
|
@ -37,6 +37,8 @@ import org.elasticsearch.common.lucene.Lucene;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -224,17 +226,23 @@ public class FiltersFunctionScoreQuery extends Query {
|
||||||
filterExplanations.add(filterExplanation);
|
filterExplanations.add(filterExplanation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filterExplanations.size() > 0) {
|
|
||||||
FiltersFunctionFactorScorer scorer = functionScorer(context);
|
FiltersFunctionFactorScorer scorer = functionScorer(context);
|
||||||
int actualDoc = scorer.iterator().advance(doc);
|
int actualDoc = scorer.iterator().advance(doc);
|
||||||
assert (actualDoc == doc);
|
assert (actualDoc == doc);
|
||||||
double score = scorer.computeScore(doc, expl.getValue());
|
double score = scorer.computeScore(doc, expl.getValue());
|
||||||
Explanation factorExplanation = Explanation.match(
|
Explanation factorExplanation;
|
||||||
|
if (filterExplanations.size() > 0) {
|
||||||
|
factorExplanation = Explanation.match(
|
||||||
CombineFunction.toFloat(score),
|
CombineFunction.toFloat(score),
|
||||||
"function score, score mode [" + scoreMode.toString().toLowerCase(Locale.ROOT) + "]",
|
"function score, score mode [" + scoreMode.toString().toLowerCase(Locale.ROOT) + "]",
|
||||||
filterExplanations);
|
filterExplanations);
|
||||||
expl = combineFunction.explain(expl, factorExplanation, maxBoost);
|
|
||||||
|
} else {
|
||||||
|
// it is a little weird to add a match although no function matches but that is the way function_score behaves right now
|
||||||
|
factorExplanation = Explanation.match(1.0f,
|
||||||
|
"No function matched", Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
expl = combineFunction.explain(expl, factorExplanation, maxBoost);
|
||||||
if (minScore != null && minScore > expl.getValue()) {
|
if (minScore != null && minScore > expl.getValue()) {
|
||||||
expl = Explanation.noMatch("Score value is too low, expected at least " + minScore + " but got " + expl.getValue(), expl);
|
expl = Explanation.noMatch("Score value is too low, expected at least " + minScore + " but got " + expl.getValue(), expl);
|
||||||
}
|
}
|
||||||
|
|
|
@ -599,7 +599,7 @@ public class FunctionScoreTests extends ESTestCase {
|
||||||
Explanation ffsqExpl = searcher.explain(ffsq, 0);
|
Explanation ffsqExpl = searcher.explain(ffsq, 0);
|
||||||
assertTrue(ffsqExpl.isMatch());
|
assertTrue(ffsqExpl.isMatch());
|
||||||
assertEquals(queryExpl.getValue(), ffsqExpl.getValue(), 0f);
|
assertEquals(queryExpl.getValue(), ffsqExpl.getValue(), 0f);
|
||||||
assertEquals(queryExpl.getDescription(), ffsqExpl.getDescription());
|
assertEquals(queryExpl.getDescription(), ffsqExpl.getDetails()[0].getDescription());
|
||||||
|
|
||||||
ffsq = new FiltersFunctionScoreQuery(query, ScoreMode.SUM, new FilterFunction[0], Float.POSITIVE_INFINITY, 10f,
|
ffsq = new FiltersFunctionScoreQuery(query, ScoreMode.SUM, new FilterFunction[0], Float.POSITIVE_INFINITY, 10f,
|
||||||
CombineFunction.MULTIPLY);
|
CombineFunction.MULTIPLY);
|
||||||
|
@ -726,6 +726,31 @@ public class FunctionScoreTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testExplanationAndScoreEqualsEvenIfNoFunctionMatches() throws IOException {
|
||||||
|
IndexSearcher localSearcher = newSearcher(reader);
|
||||||
|
ScoreMode scoreMode = randomFrom(new
|
||||||
|
ScoreMode[]{ScoreMode.SUM, ScoreMode.AVG, ScoreMode.FIRST, ScoreMode.MIN, ScoreMode.MAX, ScoreMode.MULTIPLY});
|
||||||
|
CombineFunction combineFunction = randomFrom(new
|
||||||
|
CombineFunction[]{CombineFunction.SUM, CombineFunction.AVG, CombineFunction.MIN, CombineFunction.MAX,
|
||||||
|
CombineFunction.MULTIPLY, CombineFunction.REPLACE});
|
||||||
|
|
||||||
|
// check for document that has no macthing function
|
||||||
|
FiltersFunctionScoreQuery query = new FiltersFunctionScoreQuery(new TermQuery(new Term(FIELD, "out")), scoreMode,
|
||||||
|
new FilterFunction[]{new FilterFunction(new TermQuery(new Term("_uid", "2")), new WeightFactorFunction(10))},
|
||||||
|
Float.MAX_VALUE, Float.NEGATIVE_INFINITY, combineFunction);
|
||||||
|
TopDocs searchResult = localSearcher.search(query, 1);
|
||||||
|
Explanation explanation = localSearcher.explain(query, searchResult.scoreDocs[0].doc);
|
||||||
|
assertThat(searchResult.scoreDocs[0].score, equalTo(explanation.getValue()));
|
||||||
|
|
||||||
|
// check for document that has a matching function
|
||||||
|
query = new FiltersFunctionScoreQuery(new TermQuery(new Term(FIELD, "out")), scoreMode,
|
||||||
|
new FilterFunction[]{new FilterFunction(new TermQuery(new Term("_uid", "1")), new WeightFactorFunction(10))},
|
||||||
|
Float.MAX_VALUE, Float.NEGATIVE_INFINITY, combineFunction);
|
||||||
|
searchResult = localSearcher.search(query, 1);
|
||||||
|
explanation = localSearcher.explain(query, searchResult.scoreDocs[0].doc);
|
||||||
|
assertThat(searchResult.scoreDocs[0].score, equalTo(explanation.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
private static class DummyScoreFunction extends ScoreFunction {
|
private static class DummyScoreFunction extends ScoreFunction {
|
||||||
protected DummyScoreFunction(CombineFunction scoreCombiner) {
|
protected DummyScoreFunction(CombineFunction scoreCombiner) {
|
||||||
super(scoreCombiner);
|
super(scoreCombiner);
|
||||||
|
|
Loading…
Reference in New Issue