Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function score: Apply min_score to sub query score if no function provided #10326

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.apache.lucene.util.ToStringUtils;

import java.io.IOException;
import java.util.Objects;
import java.util.Set;

/**
Expand All @@ -43,7 +44,7 @@ public class FunctionScoreQuery extends Query {
public FunctionScoreQuery(Query subQuery, ScoreFunction function, Float minScore) {
this.subQuery = subQuery;
this.function = function;
this.combineFunction = function.getDefaultScoreCombiner();
this.combineFunction = function == null? combineFunction.MULT : function.getDefaultScoreCombiner();
this.minScore = minScore;
}

Expand Down Expand Up @@ -124,7 +125,9 @@ public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOExcept
if (subQueryScorer == null) {
return null;
}
function.setNextReader(context);
if (function != null) {
function.setNextReader(context);
}
return new FunctionFactorScorer(this, subQueryScorer, function, maxBoost, combineFunction, minScore);
}

Expand All @@ -134,9 +137,13 @@ public Explanation explain(LeafReaderContext context, int doc) throws IOExceptio
if (!subQueryExpl.isMatch()) {
return subQueryExpl;
}
function.setNextReader(context);
Explanation functionExplanation = function.explainScore(doc, subQueryExpl);
return combineFunction.explain(getBoost(), subQueryExpl, functionExplanation, maxBoost);
if (function != null) {
function.setNextReader(context);
Explanation functionExplanation = function.explainScore(doc, subQueryExpl);
return combineFunction.explain(getBoost(), subQueryExpl, functionExplanation, maxBoost);
} else {
return subQueryExpl;
}
}
}

Expand All @@ -153,8 +160,12 @@ private FunctionFactorScorer(CustomBoostFactorWeight w, Scorer scorer, ScoreFunc
@Override
public float innerScore() throws IOException {
float score = scorer.score();
return scoreCombiner.combine(subQueryBoost, score,
function.score(scorer.docID(), score), maxBoost);
if (function == null) {
return subQueryBoost * score;
} else {
return scoreCombiner.combine(subQueryBoost, score,
function.score(scorer.docID(), score), maxBoost);
}
}
}

Expand All @@ -171,12 +182,12 @@ public boolean equals(Object o) {
if (o == null || getClass() != o.getClass())
return false;
FunctionScoreQuery other = (FunctionScoreQuery) o;
return this.getBoost() == other.getBoost() && this.subQuery.equals(other.subQuery) && this.function.equals(other.function)
return this.getBoost() == other.getBoost() && this.subQuery.equals(other.subQuery) && (this.function != null ? this.function.equals(other.function) : other.function == null)
&& this.maxBoost == other.maxBoost;
}

@Override
public int hashCode() {
return subQuery.hashCode() + 31 * function.hashCode() ^ Float.floatToIntBits(getBoost());
return subQuery.hashCode() + 31 * Objects.hashCode(function) ^ Float.floatToIntBits(getBoost());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars

FiltersFunctionScoreQuery.ScoreMode scoreMode = FiltersFunctionScoreQuery.ScoreMode.Multiply;
ArrayList<FiltersFunctionScoreQuery.FilterFunction> filterFunctions = new ArrayList<>();
float maxBoost = Float.MAX_VALUE;
Float maxBoost = null;
Float minScore = null;

String currentFieldName = null;
Expand Down Expand Up @@ -157,13 +157,17 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
query = new FilteredQuery(query, filter);
}
// if all filter elements returned null, just use the query
if (filterFunctions.isEmpty()) {
if (filterFunctions.isEmpty() && combineFunction == null) {
return query;
}
if (maxBoost == null) {
maxBoost = Float.MAX_VALUE;
}
// handle cases where only one score function and no filter was
// provided. In this case we create a FunctionScoreQuery.
if (filterFunctions.size() == 1 && (filterFunctions.get(0).filter == null || filterFunctions.get(0).filter instanceof MatchAllDocsFilter)) {
FunctionScoreQuery theQuery = new FunctionScoreQuery(query, filterFunctions.get(0).function, minScore);
if (filterFunctions.size() == 0 || filterFunctions.size() == 1 && (filterFunctions.get(0).filter == null || filterFunctions.get(0).filter instanceof MatchAllDocsFilter)) {
ScoreFunction function = filterFunctions.size() == 0 ? null : filterFunctions.get(0).function;
FunctionScoreQuery theQuery = new FunctionScoreQuery(query, function, minScore);
if (combineFunction != null) {
theQuery.setCombineFunction(combineFunction);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,5 +557,40 @@ public void testFilterAndQueryGiven() throws IOException, ExecutionException, In
assertThat(Float.parseFloat(hit.getId()), equalTo(hit.getScore()));
}
}

@Test
public void testWithEmptyFunctions() throws IOException, ExecutionException, InterruptedException {
assertAcked(prepareCreate("test"));
ensureYellow();
index("test", "testtype", "1", jsonBuilder().startObject().field("text", "test text").endObject());
refresh();

// make sure that min_score works if functions is empty, see https://github.com/elastic/elasticsearch/issues/10253
float termQueryScore = 0.19178301f;
testMinScoreApplied("sum", termQueryScore);
testMinScoreApplied("avg", termQueryScore);
testMinScoreApplied("max", termQueryScore);
testMinScoreApplied("min", termQueryScore);
testMinScoreApplied("multiply", termQueryScore);
testMinScoreApplied("replace", termQueryScore);
}

protected void testMinScoreApplied(String boostMode, float expectedScore) throws InterruptedException, ExecutionException {
SearchResponse response = client().search(
searchRequest().source(
searchSource().explain(true).query(
functionScoreQuery(termQuery("text", "text")).boostMode(boostMode).setMinScore(0.1f)))).get();
assertSearchResponse(response);
assertThat(response.getHits().totalHits(), equalTo(1l));
assertThat(response.getHits().getAt(0).getScore(), equalTo(expectedScore));

response = client().search(
searchRequest().source(
searchSource().explain(true).query(
functionScoreQuery(termQuery("text", "text")).boostMode(boostMode).setMinScore(2f)))).get();

assertSearchResponse(response);
assertThat(response.getHits().totalHits(), equalTo(0l));
}
}