diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java b/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java index c8698e8223d..fd7c8f6c49d 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java @@ -37,6 +37,8 @@ import org.elasticsearch.common.lucene.Lucene; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -224,17 +226,23 @@ public class FiltersFunctionScoreQuery extends Query { filterExplanations.add(filterExplanation); } } + FiltersFunctionFactorScorer scorer = functionScorer(context); + int actualDoc = scorer.iterator().advance(doc); + assert (actualDoc == doc); + double score = scorer.computeScore(doc, expl.getValue()); + Explanation factorExplanation; if (filterExplanations.size() > 0) { - FiltersFunctionFactorScorer scorer = functionScorer(context); - int actualDoc = scorer.iterator().advance(doc); - assert (actualDoc == doc); - double score = scorer.computeScore(doc, expl.getValue()); - Explanation factorExplanation = Explanation.match( + factorExplanation = Explanation.match( CombineFunction.toFloat(score), "function score, score mode [" + scoreMode.toString().toLowerCase(Locale.ROOT) + "]", 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()) { expl = Explanation.noMatch("Score value is too low, expected at least " + minScore + " but got " + expl.getValue(), expl); } diff --git a/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreTests.java b/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreTests.java index ba80aca31e9..652c4b4aef3 100644 --- a/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreTests.java @@ -599,7 +599,7 @@ public class FunctionScoreTests extends ESTestCase { Explanation ffsqExpl = searcher.explain(ffsq, 0); assertTrue(ffsqExpl.isMatch()); 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, 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 { protected DummyScoreFunction(CombineFunction scoreCombiner) { super(scoreCombiner);