浏览代码

function_score: undo "Remove explanation of query score from functions"

This adds the Explanation to the explain score again. It is needed
because the explanation of script functions will otherwise not contain
an explanation of _score if boost mode is set to replace.

closes #9826
Britta Weber 10 年之前
父节点
当前提交
3add12a970

+ 1 - 1
src/main/java/org/elasticsearch/common/lucene/search/function/BoostScoreFunction.java

@@ -53,7 +53,7 @@ public class BoostScoreFunction extends ScoreFunction {
     }
     }
 
 
     @Override
     @Override
-    public Explanation explainScore(int docId, float subQueryScore) {
+    public Explanation explainScore(int docId, Explanation subQueryScore) {
         Explanation exp = new Explanation(boost, "static boost factor");
         Explanation exp = new Explanation(boost, "static boost factor");
         exp.addDetail(new Explanation(boost, "boostFactor"));
         exp.addDetail(new Explanation(boost, "boostFactor"));
         return exp;
         return exp;

+ 2 - 2
src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java

@@ -70,10 +70,10 @@ public class FieldValueFactorFunction extends ScoreFunction {
     }
     }
 
 
     @Override
     @Override
-    public Explanation explainScore(int docId, float subQueryScore) {
+    public Explanation explainScore(int docId, Explanation subQueryScore) {
         Explanation exp = new Explanation();
         Explanation exp = new Explanation();
         String modifierStr = modifier != null ? modifier.toString() : "";
         String modifierStr = modifier != null ? modifier.toString() : "";
-        double score = score(docId, subQueryScore);
+        double score = score(docId, subQueryScore.getValue());
         exp.setValue(CombineFunction.toFloat(score));
         exp.setValue(CombineFunction.toFloat(score));
         exp.setDescription("field value function: " +
         exp.setDescription("field value function: " +
                 modifierStr + "(" + "doc['" + field + "'].value * factor=" + boostFactor + ")");
                 modifierStr + "(" + "doc['" + field + "'].value * factor=" + boostFactor + ")");

+ 1 - 1
src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java

@@ -189,7 +189,7 @@ public class FiltersFunctionScoreQuery extends Query {
                         filterFunction.filter.getDocIdSet(context, context.reader().getLiveDocs()));
                         filterFunction.filter.getDocIdSet(context, context.reader().getLiveDocs()));
                 if (docSet.get(doc)) {
                 if (docSet.get(doc)) {
                     filterFunction.function.setNextReader(context);
                     filterFunction.function.setNextReader(context);
-                    Explanation functionExplanation = filterFunction.function.explainScore(doc, subQueryExpl.getValue());
+                    Explanation functionExplanation = filterFunction.function.explainScore(doc, subQueryExpl);
                     double factor = functionExplanation.getValue();
                     double factor = functionExplanation.getValue();
                     float sc = CombineFunction.toFloat(factor);
                     float sc = CombineFunction.toFloat(factor);
                     ComplexExplanation filterExplanation = new ComplexExplanation(true, sc, "function score, product of:");
                     ComplexExplanation filterExplanation = new ComplexExplanation(true, sc, "function score, product of:");

+ 1 - 1
src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java

@@ -135,7 +135,7 @@ public class FunctionScoreQuery extends Query {
                 return subQueryExpl;
                 return subQueryExpl;
             }
             }
             function.setNextReader(context);
             function.setNextReader(context);
-            Explanation functionExplanation = function.explainScore(doc, subQueryExpl.getValue());
+            Explanation functionExplanation = function.explainScore(doc, subQueryExpl);
             return combineFunction.explain(getBoost(), subQueryExpl, functionExplanation, maxBoost);
             return combineFunction.explain(getBoost(), subQueryExpl, functionExplanation, maxBoost);
         }
         }
     }
     }

+ 1 - 1
src/main/java/org/elasticsearch/common/lucene/search/function/RandomScoreFunction.java

@@ -73,7 +73,7 @@ public class RandomScoreFunction extends ScoreFunction {
     }
     }
 
 
     @Override
     @Override
-    public Explanation explainScore(int docId, float subQueryScore) {
+    public Explanation explainScore(int docId, Explanation subQueryScore) {
         Explanation exp = new Explanation();
         Explanation exp = new Explanation();
         exp.setDescription("random score function (seed: " + originalSeed + ")");
         exp.setDescription("random score function (seed: " + originalSeed + ")");
         return exp;
         return exp;

+ 1 - 1
src/main/java/org/elasticsearch/common/lucene/search/function/ScoreFunction.java

@@ -33,7 +33,7 @@ public abstract class ScoreFunction implements ReaderContextAware {
 
 
     public abstract double score(int docId, float subQueryScore);
     public abstract double score(int docId, float subQueryScore);
 
 
-    public abstract Explanation explainScore(int docId, float subQueryScore) throws IOException;
+    public abstract Explanation explainScore(int docId, Explanation subQueryScore) throws IOException;
 
 
     public CombineFunction getDefaultScoreCombiner() {
     public CombineFunction getDefaultScoreCombiner() {
         return scoreCombiner;
         return scoreCombiner;

+ 6 - 3
src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java

@@ -107,20 +107,23 @@ public class ScriptScoreFunction extends ScoreFunction {
     }
     }
 
 
     @Override
     @Override
-    public Explanation explainScore(int docId, float subQueryScore) throws IOException {
+    public Explanation explainScore(int docId, Explanation subQueryScore) throws IOException {
         Explanation exp;
         Explanation exp;
         if (script instanceof ExplainableSearchScript) {
         if (script instanceof ExplainableSearchScript) {
             script.setNextDocId(docId);
             script.setNextDocId(docId);
             scorer.docid = docId;
             scorer.docid = docId;
-            scorer.score = subQueryScore;
+            scorer.score = subQueryScore.getValue();
             exp = ((ExplainableSearchScript) script).explain(subQueryScore);
             exp = ((ExplainableSearchScript) script).explain(subQueryScore);
         } else {
         } else {
-            double score = score(docId, subQueryScore);
+            double score = score(docId, subQueryScore.getValue());
             String explanation = "script score function, computed with script:\"" + sScript;
             String explanation = "script score function, computed with script:\"" + sScript;
             if (params != null) {
             if (params != null) {
                 explanation += "\" and parameters: \n" + params.toString();
                 explanation += "\" and parameters: \n" + params.toString();
             }
             }
             exp = new Explanation(CombineFunction.toFloat(score), explanation);
             exp = new Explanation(CombineFunction.toFloat(score), explanation);
+            Explanation scoreExp = new Explanation(subQueryScore.getValue(), "_score: ");
+            scoreExp.addDetail(subQueryScore);
+            exp.addDetail(scoreExp);
         }
         }
         return exp;
         return exp;
     }
     }

+ 3 - 3
src/main/java/org/elasticsearch/common/lucene/search/function/WeightFactorFunction.java

@@ -65,9 +65,9 @@ public class WeightFactorFunction extends ScoreFunction {
     }
     }
 
 
     @Override
     @Override
-    public Explanation explainScore(int docId, float score) throws IOException {
+    public Explanation explainScore(int docId, Explanation subQueryScore) throws IOException {
         Explanation functionScoreExplanation;
         Explanation functionScoreExplanation;
-        Explanation functionExplanation = scoreFunction.explainScore(docId, score);
+        Explanation functionExplanation = scoreFunction.explainScore(docId, subQueryScore);
         functionScoreExplanation = new ComplexExplanation(true, functionExplanation.getValue() * (float) getWeight(), "product of:");
         functionScoreExplanation = new ComplexExplanation(true, functionExplanation.getValue() * (float) getWeight(), "product of:");
         functionScoreExplanation.addDetail(functionExplanation);
         functionScoreExplanation.addDetail(functionExplanation);
         functionScoreExplanation.addDetail(explainWeight());
         functionScoreExplanation.addDetail(explainWeight());
@@ -99,7 +99,7 @@ public class WeightFactorFunction extends ScoreFunction {
         }
         }
 
 
         @Override
         @Override
-        public Explanation explainScore(int docId, float subQueryScore) {
+        public Explanation explainScore(int docId, Explanation subQueryScore) {
             return new Explanation(1.0f, "constant score 1.0 - no function provided");
             return new Explanation(1.0f, "constant score 1.0 - no function provided");
         }
         }
     }
     }

+ 2 - 2
src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionParser.java

@@ -456,9 +456,9 @@ public abstract class DecayFunctionParser implements ScoreFunctionParser {
         protected abstract String getFieldName();
         protected abstract String getFieldName();
 
 
         @Override
         @Override
-        public Explanation explainScore(int docId, float subQueryScore) {
+        public Explanation explainScore(int docId, Explanation subQueryScore) {
             ComplexExplanation ce = new ComplexExplanation();
             ComplexExplanation ce = new ComplexExplanation();
-            ce.setValue(CombineFunction.toFloat(score(docId, subQueryScore)));
+            ce.setValue(CombineFunction.toFloat(score(docId, subQueryScore.getValue())));
             ce.setMatch(true);
             ce.setMatch(true);
             ce.setDescription("Function for field " + getFieldName() + ":");
             ce.setDescription("Function for field " + getFieldName() + ":");
             ce.addDetail(func.explainFunction(getDistanceString(docId), distance(docId), scale));
             ce.addDetail(func.explainFunction(getDistanceString(docId), distance(docId), scale));

+ 4 - 2
src/main/java/org/elasticsearch/script/ExplainableSearchScript.java

@@ -51,9 +51,11 @@ public interface ExplainableSearchScript extends SearchScript {
 
 
     /**
     /**
      * Build the explanation of the current document being scored
      * Build the explanation of the current document being scored
+     * The script score needs the Explanation of the sub query score because it might use _score and
+     * want to explain how that was computed.
      *
      *
-     * @param score the score
+     * @param subQueryScore the Explanation for _score
      */
      */
-    Explanation explain(float score) throws IOException;
+    Explanation explain(Explanation subQueryScore) throws IOException;
 
 
 }
 }

+ 11 - 3
src/test/java/org/elasticsearch/search/functionscore/ExplainableScriptTests.java

@@ -24,6 +24,7 @@ import org.elasticsearch.action.index.IndexRequestBuilder;
 import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.action.search.SearchType;
 import org.elasticsearch.action.search.SearchType;
 import org.elasticsearch.common.Nullable;
 import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.lucene.search.function.CombineFunction;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 import org.elasticsearch.script.AbstractDoubleSearchScript;
 import org.elasticsearch.script.AbstractDoubleSearchScript;
@@ -47,6 +48,7 @@ import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilde
 import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
 import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
 import static org.elasticsearch.index.query.FilterBuilders.termFilter;
 import static org.elasticsearch.index.query.FilterBuilders.termFilter;
 import static org.elasticsearch.index.query.QueryBuilders.functionScoreQuery;
 import static org.elasticsearch.index.query.QueryBuilders.functionScoreQuery;
+import static org.elasticsearch.index.query.QueryBuilders.termQuery;
 import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.scriptFunction;
 import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.scriptFunction;
 import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
 import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
 import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
 import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
@@ -77,7 +79,7 @@ public class ExplainableScriptTests extends ElasticsearchIntegrationTest {
         client().admin().indices().prepareRefresh().execute().actionGet();
         client().admin().indices().prepareRefresh().execute().actionGet();
         ensureYellow();
         ensureYellow();
         SearchResponse response = client().search(searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
         SearchResponse response = client().search(searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
-                searchSource().explain(true).query(functionScoreQuery(termFilter("text", "text")).add(scriptFunction("native_explainable_script", "native")).boostMode("sum")))).actionGet();
+                searchSource().explain(true).query(functionScoreQuery(termQuery("text", "text")).add(scriptFunction("native_explainable_script", "native")).boostMode("replace")))).actionGet();
 
 
         ElasticsearchAssertions.assertNoFailures(response);
         ElasticsearchAssertions.assertNoFailures(response);
         SearchHits hits = response.getHits();
         SearchHits hits = response.getHits();
@@ -86,6 +88,8 @@ public class ExplainableScriptTests extends ElasticsearchIntegrationTest {
         for (SearchHit hit : hits.getHits()) {
         for (SearchHit hit : hits.getHits()) {
             assertThat(hit.getId(), equalTo(Integer.toString(idCounter)));
             assertThat(hit.getId(), equalTo(Integer.toString(idCounter)));
             assertThat(hit.explanation().toString(), containsString(Double.toString(idCounter) + " = This script returned " + Double.toString(idCounter)));
             assertThat(hit.explanation().toString(), containsString(Double.toString(idCounter) + " = This script returned " + Double.toString(idCounter)));
+            assertThat(hit.explanation().toString(), containsString("1.0 = tf(freq=1.0), with freq of"));
+            assertThat(hit.explanation().getDetails().length, equalTo(2));
             idCounter--;
             idCounter--;
         }
         }
     }
     }
@@ -105,8 +109,12 @@ public class ExplainableScriptTests extends ElasticsearchIntegrationTest {
         }
         }
 
 
         @Override
         @Override
-        public Explanation explain(float score) throws IOException {
-            return new Explanation((float) (runAsDouble()), "This script returned " + runAsDouble());
+        public Explanation explain(Explanation subQueryScore) throws IOException {
+            Explanation exp = new Explanation((float) (runAsDouble()), "This script returned " + runAsDouble());
+            Explanation scoreExp = new Explanation(subQueryScore.getValue(), "_score: ");
+            scoreExp.addDetail(subQueryScore);
+            exp.addDetail(scoreExp);
+            return exp;
         }
         }
     }
     }
 }
 }

+ 1 - 1
src/test/java/org/elasticsearch/search/functionscore/FunctionScoreTests.java

@@ -177,7 +177,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
                         ).explain(true))).actionGet();
                         ).explain(true))).actionGet();
 
 
         assertThat(responseWithWeights.getHits().getAt(0).getExplanation().toString(),
         assertThat(responseWithWeights.getHits().getAt(0).getExplanation().toString(),
-                equalTo("6.0 = (MATCH) function score, product of:\n  1.0 = (MATCH) ConstantScore(text_field:value), product of:\n    1.0 = boost\n    1.0 = queryNorm\n  6.0 = (MATCH) Math.min of\n    6.0 = (MATCH) function score, score mode [multiply]\n      1.0 = (MATCH) function score, product of:\n        1.0 = match filter: *:*\n        1.0 = (MATCH) Function for field geo_point_field:\n          1.0 = exp(-0.5*pow(MIN of: [Math.max(arcDistance([10.0, 20.0](=doc value),[10.0, 20.0](=origin)) - 0.0(=offset), 0)],2.0)/7.213475204444817E11)\n      2.0 = (MATCH) function score, product of:\n        1.0 = match filter: *:*\n        2.0 = (MATCH) product of:\n          1.0 = field value function: ln(doc['double_field'].value * factor=1.0)\n          2.0 = weight\n      3.0 = (MATCH) function score, product of:\n        1.0 = match filter: *:*\n        3.0 = (MATCH) product of:\n          1.0 = script score function, computed with script:\"_index['text_field']['value'].tf()\n          3.0 = weight\n    3.4028235E38 = maxBoost\n  1.0 = queryBoost\n")
+                equalTo("6.0 = (MATCH) function score, product of:\n  1.0 = (MATCH) ConstantScore(text_field:value), product of:\n    1.0 = boost\n    1.0 = queryNorm\n  6.0 = (MATCH) Math.min of\n    6.0 = (MATCH) function score, score mode [multiply]\n      1.0 = (MATCH) function score, product of:\n        1.0 = match filter: *:*\n        1.0 = (MATCH) Function for field geo_point_field:\n          1.0 = exp(-0.5*pow(MIN of: [Math.max(arcDistance([10.0, 20.0](=doc value),[10.0, 20.0](=origin)) - 0.0(=offset), 0)],2.0)/7.213475204444817E11)\n      2.0 = (MATCH) function score, product of:\n        1.0 = match filter: *:*\n        2.0 = (MATCH) product of:\n          1.0 = field value function: ln(doc['double_field'].value * factor=1.0)\n          2.0 = weight\n      3.0 = (MATCH) function score, product of:\n        1.0 = match filter: *:*\n        3.0 = (MATCH) product of:\n          1.0 = script score function, computed with script:\"_index['text_field']['value'].tf()\n            1.0 = _score: \n              1.0 = (MATCH) ConstantScore(text_field:value), product of:\n                1.0 = boost\n                1.0 = queryNorm\n          3.0 = weight\n    3.4028235E38 = maxBoost\n  1.0 = queryBoost\n")
         );
         );
         responseWithWeights = client().search(
         responseWithWeights = client().search(
                 searchRequest().source(
                 searchRequest().source(