function_score: remove explanation of query score from functions
The score is explained already, it should not be again explained per function. Also, remove explanation from parameter list of ScoreFunction#explainScore() and leave only the score. This also removes ExplainableSearchScript which is not used anywhere and was the only reason to have the Explanation in the parameter anyway. closes #7245
This commit is contained in:
parent
99b0faed14
commit
9addac8300
|
@ -49,7 +49,7 @@ public class BoostScoreFunction extends ScoreFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Explanation explainScore(int docId, Explanation subQueryExpl) {
|
public Explanation explainScore(int docId, float 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;
|
||||||
|
|
|
@ -70,14 +70,13 @@ public class FieldValueFactorFunction extends ScoreFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Explanation explainScore(int docId, Explanation subQueryExpl) {
|
public Explanation explainScore(int docId, float 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, subQueryExpl.getValue());
|
double score = score(docId, subQueryScore);
|
||||||
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 + ")");
|
||||||
exp.addDetail(subQueryExpl);
|
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,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);
|
Explanation functionExplanation = filterFunction.function.explainScore(doc, subQueryExpl.getValue());
|
||||||
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:");
|
||||||
|
|
|
@ -131,7 +131,7 @@ public class FunctionScoreQuery extends Query {
|
||||||
return subQueryExpl;
|
return subQueryExpl;
|
||||||
}
|
}
|
||||||
function.setNextReader(context);
|
function.setNextReader(context);
|
||||||
Explanation functionExplanation = function.explainScore(doc, subQueryExpl);
|
Explanation functionExplanation = function.explainScore(doc, subQueryExpl.getValue());
|
||||||
return combineFunction.explain(getBoost(), subQueryExpl, functionExplanation, maxBoost);
|
return combineFunction.explain(getBoost(), subQueryExpl, functionExplanation, maxBoost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,10 +44,9 @@ public class RandomScoreFunction extends ScoreFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Explanation explainScore(int docId, Explanation subQueryExpl) {
|
public Explanation explainScore(int docId, float subQueryScore) {
|
||||||
Explanation exp = new Explanation();
|
Explanation exp = new Explanation();
|
||||||
exp.setDescription("random score function (seed: " + prng.originalSeed + ")");
|
exp.setDescription("random score function (seed: " + prng.originalSeed + ")");
|
||||||
exp.addDetail(subQueryExpl);
|
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ public abstract class ScoreFunction {
|
||||||
|
|
||||||
public abstract double score(int docId, float subQueryScore);
|
public abstract double score(int docId, float subQueryScore);
|
||||||
|
|
||||||
public abstract Explanation explainScore(int docId, Explanation subQueryExpl);
|
public abstract Explanation explainScore(int docId, float subQueryScore);
|
||||||
|
|
||||||
public CombineFunction getDefaultScoreCombiner() {
|
public CombineFunction getDefaultScoreCombiner() {
|
||||||
return scoreCombiner;
|
return scoreCombiner;
|
||||||
|
|
|
@ -22,7 +22,6 @@ package org.elasticsearch.common.lucene.search.function;
|
||||||
import org.apache.lucene.index.AtomicReaderContext;
|
import org.apache.lucene.index.AtomicReaderContext;
|
||||||
import org.apache.lucene.search.Explanation;
|
import org.apache.lucene.search.Explanation;
|
||||||
import org.apache.lucene.search.Scorer;
|
import org.apache.lucene.search.Scorer;
|
||||||
import org.elasticsearch.script.ExplainableSearchScript;
|
|
||||||
import org.elasticsearch.script.SearchScript;
|
import org.elasticsearch.script.SearchScript;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -101,18 +100,14 @@ public class ScriptScoreFunction extends ScoreFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Explanation explainScore(int docId, Explanation subQueryExpl) {
|
public Explanation explainScore(int docId, float subQueryScore) {
|
||||||
Explanation exp;
|
Explanation exp;
|
||||||
if (script instanceof ExplainableSearchScript) {
|
double score = score(docId, subQueryScore);
|
||||||
script.setNextDocId(docId);
|
String explanation = "script score function, computed with script:\"" + sScript;
|
||||||
scorer.docid = docId;
|
if (params != null) {
|
||||||
scorer.score = subQueryExpl.getValue();
|
explanation += "\" and parameters: \n" + params.toString();
|
||||||
exp = ((ExplainableSearchScript) script).explain(subQueryExpl);
|
|
||||||
} else {
|
|
||||||
double score = score(docId, subQueryExpl.getValue());
|
|
||||||
exp = new Explanation(CombineFunction.toFloat(score), "script score function: composed of:");
|
|
||||||
exp.addDetail(subQueryExpl);
|
|
||||||
}
|
}
|
||||||
|
exp = new Explanation(CombineFunction.toFloat(score), explanation);
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -455,9 +455,9 @@ public abstract class DecayFunctionParser implements ScoreFunctionParser {
|
||||||
protected abstract String getFieldName();
|
protected abstract String getFieldName();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Explanation explainScore(int docId, Explanation subQueryExpl) {
|
public Explanation explainScore(int docId, float subQueryScore) {
|
||||||
ComplexExplanation ce = new ComplexExplanation();
|
ComplexExplanation ce = new ComplexExplanation();
|
||||||
ce.setValue(CombineFunction.toFloat(score(docId, subQueryExpl.getValue())));
|
ce.setValue(CombineFunction.toFloat(score(docId, subQueryScore)));
|
||||||
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));
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.script;
|
|
||||||
|
|
||||||
import org.apache.lucene.search.Explanation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To be implemented by {@link SearchScript} which can provided an {@link Explanation} of the score
|
|
||||||
*/
|
|
||||||
public interface ExplainableSearchScript extends SearchScript {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the explanation of the current document being scored
|
|
||||||
*
|
|
||||||
* @param subQueryExpl the explanation of the subQuery
|
|
||||||
*/
|
|
||||||
Explanation explain(Explanation subQueryExpl);
|
|
||||||
|
|
||||||
}
|
|
|
@ -27,8 +27,6 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A search script.
|
* A search script.
|
||||||
*
|
|
||||||
* @see {@link ExplainableSearchScript} for script which can explain a score
|
|
||||||
*/
|
*/
|
||||||
public interface SearchScript extends ExecutableScript, ReaderContextAware, ScorerAware {
|
public interface SearchScript extends ExecutableScript, ReaderContextAware, ScorerAware {
|
||||||
|
|
||||||
|
|
|
@ -183,11 +183,10 @@ public class RandomScoreFunctionTests extends ElasticsearchTestCase {
|
||||||
function.score(0, 1.0f);
|
function.score(0, 1.0f);
|
||||||
|
|
||||||
// Generate the randomScore explanation
|
// Generate the randomScore explanation
|
||||||
Explanation randomExplanation = function.explainScore(0, subExplanation);
|
Explanation randomExplanation = function.explainScore(0, subExplanation.getValue());
|
||||||
|
|
||||||
// Original seed should be there
|
// Original seed should be there
|
||||||
assertThat(randomExplanation.getDescription(), containsString("" + seed));
|
assertThat(randomExplanation.getDescription(), containsString("" + seed));
|
||||||
assertThat(randomExplanation.getDetails(), arrayContaining(subExplanation));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.search.functionscore;
|
||||||
|
|
||||||
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
|
import org.elasticsearch.action.search.SearchType;
|
||||||
|
import org.elasticsearch.test.ElasticsearchIntegrationTest;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import static org.elasticsearch.client.Requests.searchRequest;
|
||||||
|
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||||
|
import static org.elasticsearch.index.query.QueryBuilders.functionScoreQuery;
|
||||||
|
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||||
|
import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.*;
|
||||||
|
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
|
||||||
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
|
||||||
|
public class FunctionScoreTests extends ElasticsearchIntegrationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExplainQueryOnlyOnce() throws IOException, ExecutionException, InterruptedException {
|
||||||
|
assertAcked(prepareCreate("test").addMapping(
|
||||||
|
"type1",
|
||||||
|
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
|
||||||
|
.endObject().startObject("num").field("type", "float").endObject().endObject().endObject().endObject()));
|
||||||
|
ensureYellow();
|
||||||
|
|
||||||
|
client().prepareIndex()
|
||||||
|
.setType("type1")
|
||||||
|
.setId("1")
|
||||||
|
.setIndex("test")
|
||||||
|
.setSource(
|
||||||
|
jsonBuilder().startObject().field("test", "value").field("num", 10).endObject()).get();
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
SearchResponse response = client().search(
|
||||||
|
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
|
||||||
|
searchSource().explain(true).query(
|
||||||
|
functionScoreQuery(termQuery("test", "value")).add(gaussDecayFunction("num", 5, 5)).add(exponentialDecayFunction("num", 5, 5)).add(linearDecayFunction("num", 5, 5))))).get();
|
||||||
|
String explanation = response.getHits().getAt(0).explanation().toString();
|
||||||
|
|
||||||
|
checkQueryExplanationAppearsOnlyOnce(explanation);
|
||||||
|
response = client().search(
|
||||||
|
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
|
||||||
|
searchSource().explain(true).query(
|
||||||
|
functionScoreQuery(termQuery("test", "value")).add(fieldValueFactorFunction("num"))))).get();
|
||||||
|
explanation = response.getHits().getAt(0).explanation().toString();
|
||||||
|
checkQueryExplanationAppearsOnlyOnce(explanation);
|
||||||
|
|
||||||
|
response = client().search(
|
||||||
|
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
|
||||||
|
searchSource().explain(true).query(
|
||||||
|
functionScoreQuery(termQuery("test", "value")).add(randomFunction(10))))).get();
|
||||||
|
explanation = response.getHits().getAt(0).explanation().toString();
|
||||||
|
|
||||||
|
checkQueryExplanationAppearsOnlyOnce(explanation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkQueryExplanationAppearsOnlyOnce(String explanation) {
|
||||||
|
// use some substring of the query explanation and see if it appears twice
|
||||||
|
String queryExplanation = "idf(docFreq=1, maxDocs=1)";
|
||||||
|
int queryExplanationIndex = explanation.indexOf(queryExplanation, 0);
|
||||||
|
assertThat(queryExplanationIndex, greaterThan(-1));
|
||||||
|
queryExplanationIndex = explanation.indexOf(queryExplanation, queryExplanationIndex + 1);
|
||||||
|
assertThat(queryExplanationIndex, equalTo(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue