diff --git a/src/main/java/org/elasticsearch/common/lucene/search/function/BoostScoreFunction.java b/src/main/java/org/elasticsearch/common/lucene/search/function/BoostScoreFunction.java index b22f50c1e11..d2a2f3210b4 100644 --- a/src/main/java/org/elasticsearch/common/lucene/search/function/BoostScoreFunction.java +++ b/src/main/java/org/elasticsearch/common/lucene/search/function/BoostScoreFunction.java @@ -53,7 +53,7 @@ public class BoostScoreFunction extends ScoreFunction { } @Override - public Explanation explainScore(int docId, float subQueryScore) { + public Explanation explainScore(int docId, Explanation subQueryScore) { Explanation exp = new Explanation(boost, "static boost factor"); exp.addDetail(new Explanation(boost, "boostFactor")); return exp; diff --git a/src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java b/src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java index 87a75c241a5..567fec7b7c3 100644 --- a/src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java +++ b/src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java @@ -70,10 +70,10 @@ public class FieldValueFactorFunction extends ScoreFunction { } @Override - public Explanation explainScore(int docId, float subQueryScore) { + public Explanation explainScore(int docId, Explanation subQueryScore) { Explanation exp = new Explanation(); String modifierStr = modifier != null ? modifier.toString() : ""; - double score = score(docId, subQueryScore); + double score = score(docId, subQueryScore.getValue()); exp.setValue(CombineFunction.toFloat(score)); exp.setDescription("field value function: " + modifierStr + "(" + "doc['" + field + "'].value * factor=" + boostFactor + ")"); diff --git a/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java b/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java index d34d23a9eb4..82c92575c88 100644 --- a/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java +++ b/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())); if (docSet.get(doc)) { filterFunction.function.setNextReader(context); - Explanation functionExplanation = filterFunction.function.explainScore(doc, subQueryExpl.getValue()); + Explanation functionExplanation = filterFunction.function.explainScore(doc, subQueryExpl); double factor = functionExplanation.getValue(); float sc = CombineFunction.toFloat(factor); ComplexExplanation filterExplanation = new ComplexExplanation(true, sc, "function score, product of:"); diff --git a/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java b/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java index 728d9a9b1ac..3df6213794e 100644 --- a/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java +++ b/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java @@ -135,7 +135,7 @@ public class FunctionScoreQuery extends Query { return subQueryExpl; } function.setNextReader(context); - Explanation functionExplanation = function.explainScore(doc, subQueryExpl.getValue()); + Explanation functionExplanation = function.explainScore(doc, subQueryExpl); return combineFunction.explain(getBoost(), subQueryExpl, functionExplanation, maxBoost); } } diff --git a/src/main/java/org/elasticsearch/common/lucene/search/function/RandomScoreFunction.java b/src/main/java/org/elasticsearch/common/lucene/search/function/RandomScoreFunction.java index 276220851fe..9c2ad610cba 100644 --- a/src/main/java/org/elasticsearch/common/lucene/search/function/RandomScoreFunction.java +++ b/src/main/java/org/elasticsearch/common/lucene/search/function/RandomScoreFunction.java @@ -73,7 +73,7 @@ public class RandomScoreFunction extends ScoreFunction { } @Override - public Explanation explainScore(int docId, float subQueryScore) { + public Explanation explainScore(int docId, Explanation subQueryScore) { Explanation exp = new Explanation(); exp.setDescription("random score function (seed: " + originalSeed + ")"); return exp; diff --git a/src/main/java/org/elasticsearch/common/lucene/search/function/ScoreFunction.java b/src/main/java/org/elasticsearch/common/lucene/search/function/ScoreFunction.java index 4bef39c6c47..8d38a6882a3 100644 --- a/src/main/java/org/elasticsearch/common/lucene/search/function/ScoreFunction.java +++ b/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 Explanation explainScore(int docId, float subQueryScore) throws IOException; + public abstract Explanation explainScore(int docId, Explanation subQueryScore) throws IOException; public CombineFunction getDefaultScoreCombiner() { return scoreCombiner; diff --git a/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java b/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java index c1ad415fdf4..5ef188cc3bd 100644 --- a/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java +++ b/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java @@ -107,20 +107,23 @@ public class ScriptScoreFunction extends ScoreFunction { } @Override - public Explanation explainScore(int docId, float subQueryScore) throws IOException { + public Explanation explainScore(int docId, Explanation subQueryScore) throws IOException { Explanation exp; if (script instanceof ExplainableSearchScript) { script.setNextDocId(docId); scorer.docid = docId; - scorer.score = subQueryScore; + scorer.score = subQueryScore.getValue(); exp = ((ExplainableSearchScript) script).explain(subQueryScore); } else { - double score = score(docId, subQueryScore); + double score = score(docId, subQueryScore.getValue()); String explanation = "script score function, computed with script:\"" + sScript; if (params != null) { explanation += "\" and parameters: \n" + params.toString(); } exp = new Explanation(CombineFunction.toFloat(score), explanation); + Explanation scoreExp = new Explanation(subQueryScore.getValue(), "_score: "); + scoreExp.addDetail(subQueryScore); + exp.addDetail(scoreExp); } return exp; } diff --git a/src/main/java/org/elasticsearch/common/lucene/search/function/WeightFactorFunction.java b/src/main/java/org/elasticsearch/common/lucene/search/function/WeightFactorFunction.java index 79576b9ab8c..3e414f8b779 100644 --- a/src/main/java/org/elasticsearch/common/lucene/search/function/WeightFactorFunction.java +++ b/src/main/java/org/elasticsearch/common/lucene/search/function/WeightFactorFunction.java @@ -65,9 +65,9 @@ public class WeightFactorFunction extends ScoreFunction { } @Override - public Explanation explainScore(int docId, float score) throws IOException { + public Explanation explainScore(int docId, Explanation subQueryScore) throws IOException { 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.addDetail(functionExplanation); functionScoreExplanation.addDetail(explainWeight()); @@ -99,7 +99,7 @@ public class WeightFactorFunction extends ScoreFunction { } @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"); } } diff --git a/src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionParser.java b/src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionParser.java index 99a7fde31d8..650a8ff32e4 100644 --- a/src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionParser.java +++ b/src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionParser.java @@ -456,9 +456,9 @@ public abstract class DecayFunctionParser implements ScoreFunctionParser { protected abstract String getFieldName(); @Override - public Explanation explainScore(int docId, float subQueryScore) { + public Explanation explainScore(int docId, Explanation subQueryScore) { ComplexExplanation ce = new ComplexExplanation(); - ce.setValue(CombineFunction.toFloat(score(docId, subQueryScore))); + ce.setValue(CombineFunction.toFloat(score(docId, subQueryScore.getValue()))); ce.setMatch(true); ce.setDescription("Function for field " + getFieldName() + ":"); ce.addDetail(func.explainFunction(getDistanceString(docId), distance(docId), scale)); diff --git a/src/main/java/org/elasticsearch/script/ExplainableSearchScript.java b/src/main/java/org/elasticsearch/script/ExplainableSearchScript.java index d6a6f595b53..d30f23818fe 100644 --- a/src/main/java/org/elasticsearch/script/ExplainableSearchScript.java +++ b/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 + * 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; } \ No newline at end of file diff --git a/src/test/java/org/elasticsearch/search/functionscore/ExplainableScriptTests.java b/src/test/java/org/elasticsearch/search/functionscore/ExplainableScriptTests.java index 8aa93ea88f0..79011b13c6e 100644 --- a/src/test/java/org/elasticsearch/search/functionscore/ExplainableScriptTests.java +++ b/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.SearchType; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.lucene.search.function.CombineFunction; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.fielddata.ScriptDocValues; 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.index.query.FilterBuilders.termFilter; 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.search.builder.SearchSourceBuilder.searchSource; import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; @@ -77,7 +79,7 @@ public class ExplainableScriptTests extends ElasticsearchIntegrationTest { client().admin().indices().prepareRefresh().execute().actionGet(); ensureYellow(); 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); SearchHits hits = response.getHits(); @@ -86,6 +88,8 @@ public class ExplainableScriptTests extends ElasticsearchIntegrationTest { for (SearchHit hit : hits.getHits()) { 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("1.0 = tf(freq=1.0), with freq of")); + assertThat(hit.explanation().getDetails().length, equalTo(2)); idCounter--; } } @@ -105,8 +109,12 @@ public class ExplainableScriptTests extends ElasticsearchIntegrationTest { } @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; } } } diff --git a/src/test/java/org/elasticsearch/search/functionscore/FunctionScoreTests.java b/src/test/java/org/elasticsearch/search/functionscore/FunctionScoreTests.java index cd8cdf07337..ba0d31f428e 100644 --- a/src/test/java/org/elasticsearch/search/functionscore/FunctionScoreTests.java +++ b/src/test/java/org/elasticsearch/search/functionscore/FunctionScoreTests.java @@ -177,7 +177,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest { ).explain(true))).actionGet(); 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( searchRequest().source(