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
This commit is contained in:
Britta Weber 2015-02-23 17:50:59 +01:00
parent e43f3a941b
commit 3add12a970
12 changed files with 34 additions and 21 deletions

View File

@ -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;

View File

@ -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 + ")");

View File

@ -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:");

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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");
}
}

View File

@ -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));

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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(