Unify custom scores
=================== The custom boost factor, custom script boost and the filters function query all do the same thing: They take a query and for each found document compute a new score based on the query score and some script, come custom boost factor or a combination of these two. However, the json format for these three functionalities is very different. This makes it hard to add new functions. This commit introduces one keyword <code>function_score</code> for all three functions. The new format can be used to either compute a new score with one function: "function_score": { "(query|filter)": {}, "boost": "boost for the whole query", "function": {} } or allow to combine the newly computed scores "function_score": { "(query|filter)": {}, "boost": "boost for the whole query", "functions": [ { "filter": {}, "function": {} }, { "function": {} } ], "score_mode": "(mult|max|...)" } <code>function</code> here can be either "script_score": { "lang": "lang", "params": { "param1": "value1", "param2": "value2" }, "script": "some script" } or "boost_factor" : number New custom functions can be added via the function score module. Changes --------- The custom boost factor query "custom_boost_factor" : { "query" : { .... }, "boost_factor" : 5.2 } becomes "function_score" : { "query" : { .... }, "boost_factor" : 5.2 } The custom script score "custom_score" : { "query" : { .... }, "params" : { "param1" : 2, "param2" : 3.1 }, "script" : "_score * doc['my_numeric_field'].value / pow(param1, param2)" } becomes "custom_score" : { "query" : { .... }, "script_score" : { "params" : { "param1" : 2, "param2" : 3.1 }, "script" : "_score * doc['my_numeric_field'].value / pow(param1, param2)" } } and the custom filters score query "custom_filters_score" : { "query" : { "match_all" : {} }, "filters" : [ { "filter" : { "range" : { "age" : {"from" : 0, "to" : 10} } }, "boost" : "3" }, { "filter" : { "range" : { "age" : {"from" : 10, "to" : 20} } }, "script" : "_score * doc['my_numeric_field'].value / pow(param1, param2)" } ], "score_mode" : "first", "params" : { "param1" : 2, "param2" : 3.1 } "score_mode" : "first" } becomes: "function_score" : { "query" : { "match_all" : {} }, "functions" : [ { "filter" : { "range" : { "age" : {"from" : 0, "to" : 10} } }, "boost" : "3" }, { "filter" : { "range" : { "age" : {"from" : 10, "to" : 20} } }, "script_score" : { "script" : "_score * doc['my_numeric_field'].value / pow(param1, param2)", "params" : { "param1" : 2, "param2" : 3.1 } } } ], "score_mode" : "first", } Partially closes issue #3423
This commit is contained in:
parent
e1c739fe6f
commit
720b550a94
|
@ -49,7 +49,7 @@ public class BoostScoreFunction implements ScoreFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public float factor(int docId) {
|
||||
public double factor(int docId) {
|
||||
return boost;
|
||||
}
|
||||
|
||||
|
|
|
@ -287,7 +287,7 @@ public class FiltersFunctionScoreQuery extends Query {
|
|||
@Override
|
||||
public float score() throws IOException {
|
||||
int docId = scorer.docID();
|
||||
float factor = 1.0f;
|
||||
double factor = 1.0f;
|
||||
if (scoreMode == ScoreMode.First) {
|
||||
for (int i = 0; i < filterFunctions.length; i++) {
|
||||
if (docSets[i].get(docId)) {
|
||||
|
@ -296,7 +296,7 @@ public class FiltersFunctionScoreQuery extends Query {
|
|||
}
|
||||
}
|
||||
} else if (scoreMode == ScoreMode.Max) {
|
||||
float maxFactor = Float.NEGATIVE_INFINITY;
|
||||
double maxFactor = Double.NEGATIVE_INFINITY;
|
||||
for (int i = 0; i < filterFunctions.length; i++) {
|
||||
if (docSets[i].get(docId)) {
|
||||
maxFactor = Math.max(filterFunctions[i].function.factor(docId), maxFactor);
|
||||
|
@ -306,7 +306,7 @@ public class FiltersFunctionScoreQuery extends Query {
|
|||
factor = maxFactor;
|
||||
}
|
||||
} else if (scoreMode == ScoreMode.Min) {
|
||||
float minFactor = Float.POSITIVE_INFINITY;
|
||||
double minFactor = Double.POSITIVE_INFINITY;
|
||||
for (int i = 0; i < filterFunctions.length; i++) {
|
||||
if (docSets[i].get(docId)) {
|
||||
minFactor = Math.min(filterFunctions[i].function.factor(docId), minFactor);
|
||||
|
@ -322,7 +322,7 @@ public class FiltersFunctionScoreQuery extends Query {
|
|||
}
|
||||
}
|
||||
} else { // Avg / Total
|
||||
float totalFactor = 0.0f;
|
||||
double totalFactor = 0.0f;
|
||||
int count = 0;
|
||||
for (int i = 0; i < filterFunctions.length; i++) {
|
||||
if (docSets[i].get(docId)) {
|
||||
|
@ -341,7 +341,7 @@ public class FiltersFunctionScoreQuery extends Query {
|
|||
factor = maxBoost;
|
||||
}
|
||||
float score = scorer.score();
|
||||
return subQueryBoost * score * factor;
|
||||
return (float)(subQueryBoost * score * factor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -36,12 +36,21 @@ public class FunctionScoreQuery extends Query {
|
|||
|
||||
Query subQuery;
|
||||
final ScoreFunction function;
|
||||
float maxBoost = Float.MAX_VALUE;
|
||||
|
||||
public FunctionScoreQuery(Query subQuery, ScoreFunction function) {
|
||||
this.subQuery = subQuery;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public void setMaxBoost(float maxBoost) {
|
||||
this.maxBoost = maxBoost;
|
||||
}
|
||||
|
||||
public float getMaxBoost() {
|
||||
return this.maxBoost;
|
||||
}
|
||||
|
||||
public Query getSubQuery() {
|
||||
return subQuery;
|
||||
}
|
||||
|
@ -53,7 +62,9 @@ public class FunctionScoreQuery extends Query {
|
|||
@Override
|
||||
public Query rewrite(IndexReader reader) throws IOException {
|
||||
Query newQ = subQuery.rewrite(reader);
|
||||
if (newQ == subQuery) return this;
|
||||
if (newQ == subQuery){
|
||||
return this;
|
||||
}
|
||||
FunctionScoreQuery bq = (FunctionScoreQuery) this.clone();
|
||||
bq.subQuery = newQ;
|
||||
return bq;
|
||||
|
@ -101,7 +112,7 @@ public class FunctionScoreQuery extends Query {
|
|||
return null;
|
||||
}
|
||||
function.setNextReader(context);
|
||||
return new CustomBoostFactorScorer(this, subQueryScorer, function);
|
||||
return new CustomBoostFactorScorer(this, subQueryScorer, function, maxBoost);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -121,18 +132,20 @@ public class FunctionScoreQuery extends Query {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static class CustomBoostFactorScorer extends Scorer {
|
||||
|
||||
private final float subQueryBoost;
|
||||
private final Scorer scorer;
|
||||
private final ScoreFunction function;
|
||||
private final float maxBoost;
|
||||
|
||||
private CustomBoostFactorScorer(CustomBoostFactorWeight w, Scorer scorer, ScoreFunction function) throws IOException {
|
||||
private CustomBoostFactorScorer(CustomBoostFactorWeight w, Scorer scorer, ScoreFunction function, float maxBoost)
|
||||
throws IOException {
|
||||
super(w);
|
||||
this.subQueryBoost = w.getQuery().getBoost();
|
||||
this.scorer = scorer;
|
||||
this.function = function;
|
||||
this.maxBoost = maxBoost;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,7 +165,8 @@ public class FunctionScoreQuery extends Query {
|
|||
|
||||
@Override
|
||||
public float score() throws IOException {
|
||||
return subQueryBoost * function.score(scorer.docID(), scorer.score());
|
||||
float factor = (float)function.score(scorer.docID(), scorer.score());
|
||||
return subQueryBoost * Math.min(maxBoost, factor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -166,7 +180,6 @@ public class FunctionScoreQuery extends Query {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public String toString(String field) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("custom score (").append(subQuery.toString(field)).append(",function=").append(function).append(')');
|
||||
|
@ -175,15 +188,14 @@ public class FunctionScoreQuery extends Query {
|
|||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (getClass() != o.getClass()) return false;
|
||||
if (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.equals(other.function)
|
||||
&& this.maxBoost == other.maxBoost;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return subQuery.hashCode() + 31 * function.hashCode() ^ Float.floatToIntBits(getBoost());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ public interface ScoreFunction {
|
|||
|
||||
float score(int docId, float subQueryScore);
|
||||
|
||||
float factor(int docId);
|
||||
double factor(int docId);
|
||||
|
||||
Explanation explainScore(int docId, Explanation subQueryExpl);
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.common.lucene.search.function;
|
||||
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.elasticsearch.script.ExplainableSearchScript;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ScriptScoreFunction implements ScoreFunction {
|
||||
|
||||
private final String sScript;
|
||||
|
||||
private final Map<String, Object> params;
|
||||
|
||||
private final SearchScript script;
|
||||
|
||||
public ScriptScoreFunction(String sScript, Map<String, Object> params, SearchScript script) {
|
||||
this.sScript = sScript;
|
||||
this.params = params;
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextReader(AtomicReaderContext ctx) {
|
||||
script.setNextReader(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float score(int docId, float subQueryScore) {
|
||||
script.setNextDocId(docId);
|
||||
script.setNextScore(subQueryScore);
|
||||
return script.runAsFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double factor(int docId) {
|
||||
// just the factor, so don't provide _score
|
||||
script.setNextDocId(docId);
|
||||
return script.runAsFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explainScore(int docId, Explanation subQueryExpl) {
|
||||
Explanation exp;
|
||||
if (script instanceof ExplainableSearchScript) {
|
||||
script.setNextDocId(docId);
|
||||
script.setNextScore(subQueryExpl.getValue());
|
||||
exp = ((ExplainableSearchScript) script).explain(subQueryExpl);
|
||||
} else {
|
||||
double score = score(docId, subQueryExpl.getValue());
|
||||
exp = new Explanation((float)score, "script score function: composed of:");
|
||||
exp.addDetail(subQueryExpl);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explainFactor(int docId) {
|
||||
return new Explanation((float)factor(docId), "script_factor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "script[" + sScript + "], params [" + params + "]";
|
||||
}
|
||||
}
|
|
@ -20,13 +20,14 @@
|
|||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A query that simply applies the boost factor to another query (multiply it).
|
||||
*
|
||||
*
|
||||
*
|
||||
* @deprecated use {@link FunctionScoreQueryBuilder} instead.
|
||||
*/
|
||||
public class CustomBoostFactorQueryBuilder extends BaseQueryBuilder {
|
||||
|
||||
|
@ -35,9 +36,11 @@ public class CustomBoostFactorQueryBuilder extends BaseQueryBuilder {
|
|||
private float boostFactor = -1;
|
||||
|
||||
/**
|
||||
* A query that simply applies the boost factor to another query (multiply it).
|
||||
*
|
||||
* @param queryBuilder The query to apply the boost factor to.
|
||||
* A query that simply applies the boost factor to another query (multiply
|
||||
* it).
|
||||
*
|
||||
* @param queryBuilder
|
||||
* The query to apply the boost factor to.
|
||||
*/
|
||||
public CustomBoostFactorQueryBuilder(QueryBuilder queryBuilder) {
|
||||
this.queryBuilder = queryBuilder;
|
||||
|
|
|
@ -25,11 +25,12 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.lucene.search.function.BoostScoreFunction;
|
||||
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @deprecated use {@link FunctionScoreQueryParser} instead.
|
||||
*/
|
||||
public class CustomBoostFactorQueryParser implements QueryParser {
|
||||
|
||||
|
@ -41,7 +42,7 @@ public class CustomBoostFactorQueryParser implements QueryParser {
|
|||
|
||||
@Override
|
||||
public String[] names() {
|
||||
return new String[]{NAME, Strings.toCamelCase(NAME)};
|
||||
return new String[] { NAME, Strings.toCamelCase(NAME) };
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,7 +64,8 @@ public class CustomBoostFactorQueryParser implements QueryParser {
|
|||
query = parseContext.parseInnerQuery();
|
||||
queryFound = true;
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_boost_factor] query does not support [" + currentFieldName + "]");
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_boost_factor] query does not support ["
|
||||
+ currentFieldName + "]");
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("boost_factor".equals(currentFieldName) || "boostFactor".equals(currentFieldName)) {
|
||||
|
@ -71,7 +73,8 @@ public class CustomBoostFactorQueryParser implements QueryParser {
|
|||
} else if ("boost".equals(currentFieldName)) {
|
||||
boost = parser.floatValue();
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_boost_factor] query does not support [" + currentFieldName + "]");
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_boost_factor] query does not support ["
|
||||
+ currentFieldName + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,13 +22,17 @@ package org.elasticsearch.index.query;
|
|||
import com.google.common.collect.Maps;
|
||||
import gnu.trove.list.array.TFloatArrayList;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A query that uses a filters with a script associated with them to compute the score.
|
||||
* A query that uses a filters with a script associated with them to compute the
|
||||
* score.
|
||||
*
|
||||
* @deprecated use {@link FunctionScoreQueryBuilder} instead.
|
||||
*/
|
||||
public class CustomFiltersScoreQueryBuilder extends BaseQueryBuilder implements BoostableQueryBuilder<CustomFiltersScoreQueryBuilder> {
|
||||
|
||||
|
@ -108,8 +112,9 @@ public class CustomFiltersScoreQueryBuilder extends BaseQueryBuilder implements
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the boost for this query. Documents matching this query will (in addition to the normal
|
||||
* weightings) have their score multiplied by the boost provided.
|
||||
* Sets the boost for this query. Documents matching this query will (in
|
||||
* addition to the normal weightings) have their score multiplied by the
|
||||
* boost provided.
|
||||
*/
|
||||
public CustomFiltersScoreQueryBuilder boost(float boost) {
|
||||
this.boost = boost;
|
||||
|
|
|
@ -27,7 +27,9 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.lucene.search.function.BoostScoreFunction;
|
||||
import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery;
|
||||
import org.elasticsearch.common.lucene.search.function.ScoreFunction;
|
||||
import org.elasticsearch.common.lucene.search.function.ScriptScoreFunction;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryParser;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -35,7 +37,7 @@ import java.util.ArrayList;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @deprecated use {@link FunctionScoreQueryParser} instead.
|
||||
*/
|
||||
public class CustomFiltersScoreQueryParser implements QueryParser {
|
||||
|
||||
|
@ -47,7 +49,7 @@ public class CustomFiltersScoreQueryParser implements QueryParser {
|
|||
|
||||
@Override
|
||||
public String[] names() {
|
||||
return new String[]{NAME, Strings.toCamelCase(NAME)};
|
||||
return new String[] { NAME, Strings.toCamelCase(NAME) };
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,7 +81,8 @@ public class CustomFiltersScoreQueryParser implements QueryParser {
|
|||
} else if ("params".equals(currentFieldName)) {
|
||||
vars = parser.map();
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_filters_score] query does not support [" + currentFieldName + "]");
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_filters_score] query does not support ["
|
||||
+ currentFieldName + "]");
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||
if ("filters".equals(currentFieldName)) {
|
||||
|
@ -106,10 +109,12 @@ public class CustomFiltersScoreQueryParser implements QueryParser {
|
|||
}
|
||||
}
|
||||
if (script == null && fboost == -1) {
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_filters_score] missing 'script' or 'boost' in filters array element");
|
||||
throw new QueryParsingException(parseContext.index(),
|
||||
"[custom_filters_score] missing 'script' or 'boost' in filters array element");
|
||||
}
|
||||
if (!filterFound) {
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_filters_score] missing 'filter' in filters array element");
|
||||
throw new QueryParsingException(parseContext.index(),
|
||||
"[custom_filters_score] missing 'filter' in filters array element");
|
||||
}
|
||||
if (filter != null) {
|
||||
filters.add(filter);
|
||||
|
@ -118,7 +123,8 @@ public class CustomFiltersScoreQueryParser implements QueryParser {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_filters_score] query does not support [" + currentFieldName + "]");
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_filters_score] query does not support ["
|
||||
+ currentFieldName + "]");
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("lang".equals(currentFieldName)) {
|
||||
|
@ -140,12 +146,14 @@ public class CustomFiltersScoreQueryParser implements QueryParser {
|
|||
} else if ("first".equals(sScoreMode)) {
|
||||
scoreMode = FiltersFunctionScoreQuery.ScoreMode.First;
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_filters_score] illegal score_mode [" + sScoreMode + "]");
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_filters_score] illegal score_mode [" + sScoreMode
|
||||
+ "]");
|
||||
}
|
||||
} else if ("max_boost".equals(currentFieldName) || "maxBoost".equals(currentFieldName)) {
|
||||
maxBoost = parser.floatValue();
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_filters_score] query does not support [" + currentFieldName + "]");
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_filters_score] query does not support ["
|
||||
+ currentFieldName + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +177,7 @@ public class CustomFiltersScoreQueryParser implements QueryParser {
|
|||
String script = scripts.get(i);
|
||||
if (script != null) {
|
||||
SearchScript searchScript = parseContext.scriptService().search(parseContext.lookup(), scriptLang, script, vars);
|
||||
scoreFunction = new CustomScoreQueryParser.ScriptScoreFunction(script, vars, searchScript);
|
||||
scoreFunction = new ScriptScoreFunction(script, vars, searchScript);
|
||||
} else {
|
||||
scoreFunction = new BoostScoreFunction(boosts.get(i));
|
||||
}
|
||||
|
|
|
@ -26,7 +26,10 @@ import java.io.IOException;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A query that uses a script to compute or influence the score of documents that match with the inner query or filter.
|
||||
* A query that uses a script to compute or influence the score of documents
|
||||
* that match with the inner query or filter.
|
||||
*
|
||||
* @deprecated use {@link FunctionScoreQueryBuilder} instead.
|
||||
*/
|
||||
public class CustomScoreQueryBuilder extends BaseQueryBuilder implements BoostableQueryBuilder<CustomScoreQueryBuilder> {
|
||||
|
||||
|
@ -43,10 +46,13 @@ public class CustomScoreQueryBuilder extends BaseQueryBuilder implements Boostab
|
|||
private Map<String, Object> params = null;
|
||||
|
||||
/**
|
||||
* Constructs a query that defines how the scores are computed or influenced for documents that match with the
|
||||
* specified query by a custom defined script.
|
||||
*
|
||||
* @param queryBuilder The query that defines what documents are custom scored by this query
|
||||
* Constructs a query that defines how the scores are computed or influenced
|
||||
* for documents that match with the specified query by a custom defined
|
||||
* script.
|
||||
*
|
||||
* @param queryBuilder
|
||||
* The query that defines what documents are custom scored by
|
||||
* this query
|
||||
*/
|
||||
public CustomScoreQueryBuilder(QueryBuilder queryBuilder) {
|
||||
this.queryBuilder = queryBuilder;
|
||||
|
@ -54,9 +60,12 @@ public class CustomScoreQueryBuilder extends BaseQueryBuilder implements Boostab
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructs a query that defines how documents are scored that match with the specified filter.
|
||||
*
|
||||
* @param filterBuilder The filter that decides with documents are scored by this query.
|
||||
* Constructs a query that defines how documents are scored that match with
|
||||
* the specified filter.
|
||||
*
|
||||
* @param filterBuilder
|
||||
* The filter that decides with documents are scored by this
|
||||
* query.
|
||||
*/
|
||||
public CustomScoreQueryBuilder(FilterBuilder filterBuilder) {
|
||||
this.filterBuilder = filterBuilder;
|
||||
|
@ -103,8 +112,9 @@ public class CustomScoreQueryBuilder extends BaseQueryBuilder implements Boostab
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the boost for this query. Documents matching this query will (in addition to the normal
|
||||
* weightings) have their score multiplied by the boost provided.
|
||||
* Sets the boost for this query. Documents matching this query will (in
|
||||
* addition to the normal weightings) have their score multiplied by the
|
||||
* boost provided.
|
||||
*/
|
||||
public CustomScoreQueryBuilder boost(float boost) {
|
||||
this.boost = boost;
|
||||
|
|
|
@ -19,24 +19,21 @@
|
|||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
|
||||
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
|
||||
import org.elasticsearch.common.lucene.search.function.ScoreFunction;
|
||||
import org.elasticsearch.common.lucene.search.function.ScriptScoreFunction;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.script.ExplainableSearchScript;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @deprecated use {@link FunctionScoreQueryParser} instead.
|
||||
*/
|
||||
public class CustomScoreQueryParser implements QueryParser {
|
||||
|
||||
|
@ -48,7 +45,7 @@ public class CustomScoreQueryParser implements QueryParser {
|
|||
|
||||
@Override
|
||||
public String[] names() {
|
||||
return new String[]{NAME, Strings.toCamelCase(NAME)};
|
||||
return new String[] { NAME, Strings.toCamelCase(NAME) };
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,7 +75,8 @@ public class CustomScoreQueryParser implements QueryParser {
|
|||
} else if ("params".equals(currentFieldName)) {
|
||||
vars = parser.map();
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_score] query does not support [" + currentFieldName + "]");
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_score] query does not support [" + currentFieldName
|
||||
+ "]");
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("script".equals(currentFieldName)) {
|
||||
|
@ -88,7 +86,8 @@ public class CustomScoreQueryParser implements QueryParser {
|
|||
} else if ("boost".equals(currentFieldName)) {
|
||||
boost = parser.floatValue();
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_score] query does not support [" + currentFieldName + "]");
|
||||
throw new QueryParsingException(parseContext.index(), "[custom_score] query does not support [" + currentFieldName
|
||||
+ "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,64 +113,4 @@ public class CustomScoreQueryParser implements QueryParser {
|
|||
functionScoreQuery.setBoost(boost);
|
||||
return functionScoreQuery;
|
||||
}
|
||||
|
||||
public static class ScriptScoreFunction implements ScoreFunction {
|
||||
|
||||
private final String sScript;
|
||||
|
||||
private final Map<String, Object> params;
|
||||
|
||||
private final SearchScript script;
|
||||
|
||||
public ScriptScoreFunction(String sScript, Map<String, Object> params, SearchScript script) {
|
||||
this.sScript = sScript;
|
||||
this.params = params;
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextReader(AtomicReaderContext ctx) {
|
||||
//LUCENE 4 UPGRADE should this pass on a ARC or just and atomic reader?
|
||||
script.setNextReader(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float score(int docId, float subQueryScore) {
|
||||
script.setNextDocId(docId);
|
||||
script.setNextScore(subQueryScore);
|
||||
return script.runAsFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float factor(int docId) {
|
||||
// just the factor, so don't provide _score
|
||||
script.setNextDocId(docId);
|
||||
return script.runAsFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explainScore(int docId, Explanation subQueryExpl) {
|
||||
Explanation exp;
|
||||
if (script instanceof ExplainableSearchScript) {
|
||||
script.setNextDocId(docId);
|
||||
script.setNextScore(subQueryExpl.getValue());
|
||||
exp = ((ExplainableSearchScript) script).explain(subQueryExpl);
|
||||
} else {
|
||||
float score = score(docId, subQueryExpl.getValue());
|
||||
exp = new Explanation(score, "script score function: composed of:");
|
||||
exp.addDetail(subQueryExpl);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explainFactor(int docId) {
|
||||
return new Explanation(factor(docId), "scriptFactor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "script[" + sScript + "], params [" + params + "]";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,10 +19,11 @@
|
|||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A static factory for simple "import static" usage.
|
||||
|
@ -506,6 +507,7 @@ public abstract class QueryBuilders {
|
|||
* A query that simply applies the boost fact to the wrapped query (multiplies it).
|
||||
*
|
||||
* @param queryBuilder The query to apply the boost factor to.
|
||||
* @deprecated use {@link #functionScoreQuery(QueryBuilder)} instead
|
||||
*/
|
||||
public static CustomBoostFactorQueryBuilder customBoostFactorQuery(QueryBuilder queryBuilder) {
|
||||
return new CustomBoostFactorQueryBuilder(queryBuilder);
|
||||
|
@ -515,6 +517,7 @@ public abstract class QueryBuilders {
|
|||
* A query that allows to define a custom scoring script.
|
||||
*
|
||||
* @param queryBuilder The query to custom score
|
||||
* @deprecated use {@link #functionScoreQuery(QueryBuilder)} instead
|
||||
*/
|
||||
public static CustomScoreQueryBuilder customScoreQuery(QueryBuilder queryBuilder) {
|
||||
return new CustomScoreQueryBuilder(queryBuilder);
|
||||
|
@ -525,15 +528,38 @@ public abstract class QueryBuilders {
|
|||
* with the specified filter.
|
||||
*
|
||||
* @param filterBuilder The filter that defines which documents are scored by a script.
|
||||
* @deprecated use {@link #functionScoreQuery(QueryBuilder)} instead
|
||||
*/
|
||||
public static CustomScoreQueryBuilder customScoreQuery(FilterBuilder filterBuilder) {
|
||||
return new CustomScoreQueryBuilder(filterBuilder);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #functionScoreQuery(QueryBuilder)} instead
|
||||
*/
|
||||
public static CustomFiltersScoreQueryBuilder customFiltersScoreQuery(QueryBuilder queryBuilder) {
|
||||
return new CustomFiltersScoreQueryBuilder(queryBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* A query that allows to define a custom scoring function.
|
||||
*
|
||||
* @param queryBuilder The query to custom score
|
||||
* @param scoreFunctionBuilder The score function used to re-score the query
|
||||
*/
|
||||
public static FunctionScoreQueryBuilder functionScoreQuery(QueryBuilder queryBuilder) {
|
||||
return new FunctionScoreQueryBuilder(queryBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* A query that allows to define a custom scoring function.
|
||||
*
|
||||
* @param filterBuilder The query to custom score
|
||||
* @param scoreFunctionBuilder The score function used to re-score the query
|
||||
*/
|
||||
public static FunctionScoreQueryBuilder functionScoreQuery(FilterBuilder filterBuilder) {
|
||||
return new FunctionScoreQueryBuilder(filterBuilder);
|
||||
}
|
||||
/**
|
||||
* A more like this query that finds documents that are "like" the provided {@link MoreLikeThisQueryBuilder#likeText(String)}
|
||||
* which is checked against the fields the query is constructed with.
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.index.query.functionscore;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.multibindings.Multibinder;
|
||||
import org.elasticsearch.index.query.functionscore.factor.FactorParser;
|
||||
import org.elasticsearch.index.query.functionscore.script.ScriptScoreFunctionParser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class FunctionScoreModule extends AbstractModule {
|
||||
|
||||
private List<Class<? extends ScoreFunctionParser>> parsers = Lists.newArrayList();
|
||||
|
||||
public FunctionScoreModule() {
|
||||
registerParser(FactorParser.class);
|
||||
registerParser(ScriptScoreFunctionParser.class);
|
||||
}
|
||||
|
||||
public void registerParser(Class<? extends ScoreFunctionParser> parser) {
|
||||
parsers.add(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
Multibinder<ScoreFunctionParser> parserMapBinder = Multibinder.newSetBinder(binder(), ScoreFunctionParser.class);
|
||||
for (Class<? extends ScoreFunctionParser> clazz : parsers) {
|
||||
parserMapBinder.addBinding().to(clazz);
|
||||
}
|
||||
bind(ScoreFunctionParserMapper.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.index.query.functionscore;
|
||||
|
||||
import org.elasticsearch.ElasticSearchException;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.query.BaseQueryBuilder;
|
||||
import org.elasticsearch.index.query.BoostableQueryBuilder;
|
||||
import org.elasticsearch.index.query.FilterBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A query that uses a filters with a script associated with them to compute the
|
||||
* score.
|
||||
*/
|
||||
public class FunctionScoreQueryBuilder extends BaseQueryBuilder implements BoostableQueryBuilder<FunctionScoreQueryBuilder> {
|
||||
|
||||
private final QueryBuilder queryBuilder;
|
||||
|
||||
private final FilterBuilder filterBuilder;
|
||||
|
||||
private Float boost;
|
||||
|
||||
private Float maxBoost;
|
||||
|
||||
private String scoreMode;
|
||||
|
||||
private ArrayList<FilterBuilder> filters = new ArrayList<FilterBuilder>();
|
||||
private ArrayList<ScoreFunctionBuilder> scoreFunctions = new ArrayList<ScoreFunctionBuilder>();
|
||||
|
||||
public FunctionScoreQueryBuilder(QueryBuilder queryBuilder) {
|
||||
this.queryBuilder = queryBuilder;
|
||||
this.filterBuilder = null;
|
||||
}
|
||||
|
||||
public FunctionScoreQueryBuilder(FilterBuilder filterBuilder) {
|
||||
this.filterBuilder = filterBuilder;
|
||||
this.queryBuilder = null;
|
||||
}
|
||||
|
||||
public FunctionScoreQueryBuilder add(FilterBuilder filter, ScoreFunctionBuilder scoreFunctionBuilder) {
|
||||
this.filters.add(filter);
|
||||
this.scoreFunctions.add(scoreFunctionBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FunctionScoreQueryBuilder add(ScoreFunctionBuilder scoreFunctionBuilder) {
|
||||
this.filters.add(null);
|
||||
this.scoreFunctions.add(scoreFunctionBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FunctionScoreQueryBuilder scoreMode(String scoreMode) {
|
||||
this.scoreMode = scoreMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FunctionScoreQueryBuilder maxBoost(float maxBoost) {
|
||||
this.maxBoost = maxBoost;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the boost for this query. Documents matching this query will (in
|
||||
* addition to the normal weightings) have their score multiplied by the
|
||||
* boost provided.
|
||||
*/
|
||||
public FunctionScoreQueryBuilder boost(float boost) {
|
||||
this.boost = boost;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(FunctionScoreQueryParser.NAME);
|
||||
if (queryBuilder != null) {
|
||||
builder.field("query");
|
||||
queryBuilder.toXContent(builder, params);
|
||||
} else if (filterBuilder != null) {
|
||||
builder.field("filter");
|
||||
filterBuilder.toXContent(builder, params);
|
||||
} else {
|
||||
throw new ElasticSearchException(FunctionScoreQueryParser.NAME
|
||||
+ " builder requires that either a filter or a query is defined!");
|
||||
}
|
||||
// If there is only one function without a filter, we later want to
|
||||
// create a FunctionScoreQuery.
|
||||
// For this, we only build the scoreFunction.Tthis will be translated to
|
||||
// FunctionScoreQuery in the parser.
|
||||
if (filters.size() == 1 && filters.get(0) == null) {
|
||||
scoreFunctions.get(0).toXContent(builder, params);
|
||||
} else { // in all other cases we build the format needed for a
|
||||
// FiltersFunctionScoreQuery
|
||||
builder.startArray("functions");
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
builder.startObject();
|
||||
builder.field("filter");
|
||||
filters.get(i).toXContent(builder, params);
|
||||
scoreFunctions.get(i).toXContent(builder, params);
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
if (scoreMode != null) {
|
||||
builder.field("score_mode", scoreMode);
|
||||
}
|
||||
if (maxBoost != null) {
|
||||
builder.field("max_boost", maxBoost);
|
||||
}
|
||||
if (boost != null) {
|
||||
builder.field("boost", boost);
|
||||
}
|
||||
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.index.query.functionscore;
|
||||
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.lucene.search.MatchAllDocsFilter;
|
||||
import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
|
||||
import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery;
|
||||
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
|
||||
import org.elasticsearch.common.lucene.search.function.ScoreFunction;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.QueryParseContext;
|
||||
import org.elasticsearch.index.query.QueryParser;
|
||||
import org.elasticsearch.index.query.QueryParsingException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class FunctionScoreQueryParser implements QueryParser {
|
||||
|
||||
public static final String NAME = "function_score";
|
||||
ScoreFunctionParserMapper funtionParserMapper;
|
||||
|
||||
@Inject
|
||||
public FunctionScoreQueryParser(ScoreFunctionParserMapper funtionParserMapper) {
|
||||
this.funtionParserMapper = funtionParserMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] names() {
|
||||
return new String[] { NAME, Strings.toCamelCase(NAME) };
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
Query query = null;
|
||||
float boost = 1.0f;
|
||||
|
||||
FiltersFunctionScoreQuery.ScoreMode scoreMode = FiltersFunctionScoreQuery.ScoreMode.Multiply;
|
||||
ArrayList<FiltersFunctionScoreQuery.FilterFunction> filterFunctions = new ArrayList<FiltersFunctionScoreQuery.FilterFunction>();
|
||||
float maxBoost = Float.MAX_VALUE;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if ("query".equals(currentFieldName)) {
|
||||
query = parseContext.parseInnerQuery();
|
||||
} else if ("filter".equals(currentFieldName)) {
|
||||
query = new XConstantScoreQuery(parseContext.parseInnerFilter());
|
||||
} else if ("score_mode".equals(currentFieldName) || "scoreMode".equals(currentFieldName)) {
|
||||
scoreMode = parseScoreMode(parseContext, parser);
|
||||
} else if ("max_boost".equals(currentFieldName) || "maxBoost".equals(currentFieldName)) {
|
||||
maxBoost = parser.floatValue();
|
||||
} else if ("boost".equals(currentFieldName)) {
|
||||
boost = parser.floatValue();
|
||||
} else if ("functions".equals(currentFieldName)) {
|
||||
currentFieldName = parseFiltersAndFunctions(parseContext, parser, filterFunctions, currentFieldName);
|
||||
} else {
|
||||
// we tru to parse a score function. If there is no score
|
||||
// function for the current field name,
|
||||
// funtionParserMapper.get() will throw an Exception.
|
||||
filterFunctions.add(new FiltersFunctionScoreQuery.FilterFunction(null, funtionParserMapper.get(parseContext.index(),
|
||||
currentFieldName).parse(parseContext, parser)));
|
||||
}
|
||||
}
|
||||
if (query == null) {
|
||||
throw new QueryParsingException(parseContext.index(), NAME + " requires 'query' field");
|
||||
}
|
||||
// if all filter elements returned null, just use the query
|
||||
if (filterFunctions.isEmpty()) {
|
||||
return query;
|
||||
}
|
||||
// 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) {
|
||||
FunctionScoreQuery theQuery = new FunctionScoreQuery(query, filterFunctions.get(0).function);
|
||||
theQuery.setBoost(boost);
|
||||
theQuery.setMaxBoost(maxBoost);
|
||||
return theQuery;
|
||||
// in all other cases we create a FiltersFunctionScoreQuery.
|
||||
} else {
|
||||
FiltersFunctionScoreQuery functionScoreQuery = new FiltersFunctionScoreQuery(query, scoreMode,
|
||||
filterFunctions.toArray(new FiltersFunctionScoreQuery.FilterFunction[filterFunctions.size()]), maxBoost);
|
||||
functionScoreQuery.setBoost(boost);
|
||||
return functionScoreQuery;
|
||||
}
|
||||
}
|
||||
|
||||
private String parseFiltersAndFunctions(QueryParseContext parseContext, XContentParser parser,
|
||||
ArrayList<FiltersFunctionScoreQuery.FilterFunction> filterFunctions, String currentFieldName) throws IOException {
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
Filter filter = null;
|
||||
ScoreFunction scoreFunction = null;
|
||||
if (token != XContentParser.Token.START_OBJECT) {
|
||||
throw new QueryParsingException(parseContext.index(), NAME + ": malformed query, expected a "
|
||||
+ XContentParser.Token.START_OBJECT + " while parsing functions but got a " + token);
|
||||
} else {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else {
|
||||
if ("filter".equals(currentFieldName)) {
|
||||
filter = parseContext.parseInnerFilter();
|
||||
} else {
|
||||
// do not need to check null here,
|
||||
// funtionParserMapper throws exception if parser
|
||||
// non-existent
|
||||
ScoreFunctionParser functionParser = funtionParserMapper.get(parseContext.index(), currentFieldName);
|
||||
scoreFunction = functionParser.parse(parseContext, parser);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (filter == null) {
|
||||
filter = new MatchAllDocsFilter();
|
||||
}
|
||||
filterFunctions.add(new FiltersFunctionScoreQuery.FilterFunction(filter, scoreFunction));
|
||||
|
||||
}
|
||||
return currentFieldName;
|
||||
}
|
||||
|
||||
private FiltersFunctionScoreQuery.ScoreMode parseScoreMode(QueryParseContext parseContext, XContentParser parser) throws IOException {
|
||||
String scoreMode = parser.text();
|
||||
if ("avg".equals(scoreMode)) {
|
||||
return FiltersFunctionScoreQuery.ScoreMode.Avg;
|
||||
} else if ("max".equals(scoreMode)) {
|
||||
return FiltersFunctionScoreQuery.ScoreMode.Max;
|
||||
} else if ("min".equals(scoreMode)) {
|
||||
return FiltersFunctionScoreQuery.ScoreMode.Min;
|
||||
} else if ("total".equals(scoreMode)) {
|
||||
return FiltersFunctionScoreQuery.ScoreMode.Total;
|
||||
} else if ("multiply".equals(scoreMode)) {
|
||||
return FiltersFunctionScoreQuery.ScoreMode.Multiply;
|
||||
} else if ("first".equals(scoreMode)) {
|
||||
return FiltersFunctionScoreQuery.ScoreMode.First;
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), NAME + " illegal score_mode [" + scoreMode + "]");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.index.query.functionscore;
|
||||
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
|
||||
public interface ScoreFunctionBuilder extends ToXContent {
|
||||
|
||||
public String getName();
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.index.query.functionscore;
|
||||
|
||||
import org.elasticsearch.common.lucene.search.function.ScoreFunction;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.QueryParseContext;
|
||||
import org.elasticsearch.index.query.QueryParsingException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface ScoreFunctionParser {
|
||||
|
||||
public ScoreFunction parse(QueryParseContext parseContext, XContentParser parser) throws IOException, QueryParsingException;
|
||||
|
||||
/**
|
||||
* Returns the name of the function, for example "linear", "gauss" etc. This
|
||||
* name is used for registering the parser in
|
||||
* {@link FunctionScoreQueryParser}.
|
||||
* */
|
||||
public String[] getNames();
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.index.query.functionscore;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.query.QueryParsingException;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class ScoreFunctionParserMapper {
|
||||
|
||||
protected ImmutableMap<String, ScoreFunctionParser> functionParsers;
|
||||
|
||||
@Inject
|
||||
public ScoreFunctionParserMapper(Set<ScoreFunctionParser> parsers) {
|
||||
MapBuilder<String, ScoreFunctionParser> builder = MapBuilder.newMapBuilder();
|
||||
for (ScoreFunctionParser scoreFunctionParser : parsers) {
|
||||
for (String name : scoreFunctionParser.getNames()) {
|
||||
builder.put(name, scoreFunctionParser);
|
||||
}
|
||||
}
|
||||
this.functionParsers = builder.immutableMap();
|
||||
}
|
||||
|
||||
public ScoreFunctionParser get(Index index, String parserName) {
|
||||
ScoreFunctionParser functionParser = get(parserName);
|
||||
if (functionParser == null) {
|
||||
throw new QueryParsingException(index, "No function with the name [" + parserName + "] is registered.");
|
||||
}
|
||||
return functionParser;
|
||||
}
|
||||
|
||||
private ScoreFunctionParser get(String parserName) {
|
||||
return functionParsers.get(parserName);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.index.query.functionscore.factor;
|
||||
|
||||
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A query that simply applies the boost factor to another query (multiply it).
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class FactorBuilder implements ScoreFunctionBuilder {
|
||||
|
||||
private Float boostFactor;
|
||||
|
||||
/**
|
||||
* Sets the boost factor for this query.
|
||||
*/
|
||||
public FactorBuilder boostFactor(float boost) {
|
||||
this.boostFactor = new Float(boost);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
if (boostFactor != null) {
|
||||
builder.field("boost_factor", boostFactor.floatValue());
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return FactorParser.NAMES[0];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.index.query.functionscore.factor;
|
||||
|
||||
import org.elasticsearch.index.query.functionscore.ScoreFunctionParser;
|
||||
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.lucene.search.function.BoostScoreFunction;
|
||||
import org.elasticsearch.common.lucene.search.function.ScoreFunction;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.QueryParseContext;
|
||||
import org.elasticsearch.index.query.QueryParsingException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class FactorParser implements ScoreFunctionParser {
|
||||
|
||||
public static String[] NAMES = { "boost_factor", "boostFactor" };
|
||||
|
||||
@Inject
|
||||
public FactorParser() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScoreFunction parse(QueryParseContext parseContext, XContentParser parser) throws IOException, QueryParsingException {
|
||||
float boostFactor = parser.floatValue();
|
||||
return new BoostScoreFunction(boostFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNames() {
|
||||
return NAMES;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.index.query.functionscore.script;
|
||||
|
||||
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A query that uses a script to compute or influence the score of documents
|
||||
* that match with the inner query or filter.
|
||||
*/
|
||||
public class ScriptScoreFunctionBuilder implements ScoreFunctionBuilder {
|
||||
|
||||
private String script;
|
||||
|
||||
private String lang;
|
||||
|
||||
private Map<String, Object> params = null;
|
||||
|
||||
public ScriptScoreFunctionBuilder() {
|
||||
|
||||
}
|
||||
|
||||
public ScriptScoreFunctionBuilder script(String script) {
|
||||
this.script = script;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language of the script.
|
||||
*/
|
||||
public ScriptScoreFunctionBuilder lang(String lang) {
|
||||
this.lang = lang;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional parameters that can be provided to the script.
|
||||
*/
|
||||
public ScriptScoreFunctionBuilder params(Map<String, Object> params) {
|
||||
if (this.params == null) {
|
||||
this.params = params;
|
||||
} else {
|
||||
this.params.putAll(params);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional parameters that can be provided to the script.
|
||||
*/
|
||||
public ScriptScoreFunctionBuilder param(String key, Object value) {
|
||||
if (params == null) {
|
||||
params = Maps.newHashMap();
|
||||
}
|
||||
params.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(getName());
|
||||
builder.field("script", script);
|
||||
if (lang != null) {
|
||||
builder.field("lang", lang);
|
||||
}
|
||||
if (this.params != null) {
|
||||
builder.field("params", this.params);
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return ScriptScoreFunctionParser.NAMES[0];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Licensed to ElasticSearch and Shay Banon 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.index.query.functionscore.script;
|
||||
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.lucene.search.function.ScoreFunction;
|
||||
import org.elasticsearch.common.lucene.search.function.ScriptScoreFunction;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.QueryParseContext;
|
||||
import org.elasticsearch.index.query.QueryParsingException;
|
||||
import org.elasticsearch.index.query.functionscore.ScoreFunctionParser;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ScriptScoreFunctionParser implements ScoreFunctionParser {
|
||||
|
||||
public static String[] NAMES = { "script_score", "scriptScore" };
|
||||
|
||||
@Inject
|
||||
public ScriptScoreFunctionParser() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNames() {
|
||||
return NAMES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScoreFunction parse(QueryParseContext parseContext, XContentParser parser) throws IOException, QueryParsingException {
|
||||
|
||||
String script = null;
|
||||
String scriptLang = null;
|
||||
Map<String, Object> vars = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if ("params".equals(currentFieldName)) {
|
||||
vars = parser.map();
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), NAMES[0] + " query does not support [" + currentFieldName + "]");
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("script".equals(currentFieldName)) {
|
||||
script = parser.text();
|
||||
} else if ("lang".equals(currentFieldName)) {
|
||||
scriptLang = parser.text();
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), NAMES[0] + " query does not support [" + currentFieldName + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (script == null) {
|
||||
throw new QueryParsingException(parseContext.index(), NAMES[0] + " requires 'script' field");
|
||||
}
|
||||
|
||||
SearchScript searchScript;
|
||||
try {
|
||||
searchScript = parseContext.scriptService().search(parseContext.lookup(), scriptLang, script, vars);
|
||||
return new ScriptScoreFunction(script, vars, searchScript);
|
||||
} catch (Exception e) {
|
||||
throw new QueryParsingException(parseContext.index(), NAMES[0] + " the script could not be loaded", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import org.elasticsearch.common.geo.ShapesAvailability;
|
|||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.multibindings.Multibinder;
|
||||
import org.elasticsearch.index.query.*;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryParser;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -104,6 +105,7 @@ public class IndicesQueriesModule extends AbstractModule {
|
|||
qpBinders.addBinding().to(IndicesQueryParser.class).asEagerSingleton();
|
||||
qpBinders.addBinding().to(CommonTermsQueryParser.class).asEagerSingleton();
|
||||
qpBinders.addBinding().to(SpanMultiTermQueryParser.class).asEagerSingleton();
|
||||
qpBinders.addBinding().to(FunctionScoreQueryParser.class).asEagerSingleton();
|
||||
|
||||
if (ShapesAvailability.JTS_AVAILABLE) {
|
||||
qpBinders.addBinding().to(GeoShapeQueryParser.class).asEagerSingleton();
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList;
|
|||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.Module;
|
||||
import org.elasticsearch.common.inject.SpawnModules;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreModule;
|
||||
import org.elasticsearch.search.action.SearchServiceTransportAction;
|
||||
import org.elasticsearch.search.controller.SearchPhaseController;
|
||||
import org.elasticsearch.search.dfs.DfsPhase;
|
||||
|
@ -46,7 +47,7 @@ public class SearchModule extends AbstractModule implements SpawnModules {
|
|||
|
||||
@Override
|
||||
public Iterable<? extends Module> spawnModules() {
|
||||
return ImmutableList.of(new TransportSearchModule(), new FacetModule(), new HighlightModule(), new SuggestModule());
|
||||
return ImmutableList.of(new TransportSearchModule(), new FacetModule(), new HighlightModule(), new SuggestModule(), new FunctionScoreModule());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.elasticsearch.test.integration.search.basic;
|
||||
|
||||
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.elasticsearch.ElasticSearchException;
|
||||
|
@ -29,6 +31,7 @@ import org.elasticsearch.client.Client;
|
|||
import org.elasticsearch.client.Requests;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.query.functionscore.script.ScriptScoreFunctionBuilder;
|
||||
import org.elasticsearch.search.Scroll;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
|
@ -409,4 +412,29 @@ public class TransportTwoNodesSearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
logger.info("Done Testing failed search");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFailedMultiSearchWithWrongQuery_withFunctionScore() throws Exception {
|
||||
prepareData();
|
||||
|
||||
logger.info("Start Testing failed multi search with a wrong query");
|
||||
|
||||
MultiSearchResponse response = client().prepareMultiSearch()
|
||||
// Add custom score query with missing script
|
||||
.add(client().prepareSearch("test").setQuery(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("nid", 1)).add(new ScriptScoreFunctionBuilder())))
|
||||
.add(client().prepareSearch("test").setQuery(QueryBuilders.termQuery("nid", 2)))
|
||||
.add(client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()))
|
||||
.execute().actionGet();
|
||||
assertThat(response.getResponses().length, equalTo(3));
|
||||
assertThat(response.getResponses()[0].getFailureMessage(), notNullValue());
|
||||
|
||||
assertThat(response.getResponses()[1].getFailureMessage(), nullValue());
|
||||
assertThat(response.getResponses()[1].getResponse().getHits().hits().length, equalTo(1));
|
||||
|
||||
assertThat(response.getResponses()[2].getFailureMessage(), nullValue());
|
||||
assertThat(response.getResponses()[2].getResponse().getHits().hits().length, equalTo(10));
|
||||
|
||||
logger.info("Done Testing failed search");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.elasticsearch.test.integration.search.child;
|
||||
|
||||
|
||||
|
||||
import org.elasticsearch.ElasticSearchException;
|
||||
import org.elasticsearch.action.count.CountResponse;
|
||||
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||
|
@ -28,6 +30,7 @@ import org.elasticsearch.action.search.ShardSearchFailure;
|
|||
import org.elasticsearch.common.Priority;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.query.functionscore.script.ScriptScoreFunctionBuilder;
|
||||
import org.elasticsearch.search.facet.terms.TermsFacet;
|
||||
import org.elasticsearch.search.sort.SortBuilders;
|
||||
import org.elasticsearch.search.sort.SortOrder;
|
||||
|
@ -53,8 +56,7 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void multiLevelChild() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(
|
||||
ImmutableSettings.settingsBuilder()
|
||||
|
@ -144,7 +146,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test // see #2744
|
||||
public void test2744() throws ElasticSearchException, IOException {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(
|
||||
|
@ -395,7 +396,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testHasParentFilter() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(
|
||||
ImmutableSettings.settingsBuilder()
|
||||
|
@ -457,7 +457,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void simpleChildQueryWithFlush() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(
|
||||
|
@ -557,7 +556,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void simpleChildQueryWithFlushAnd3Shards() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test").setSettings(
|
||||
ImmutableSettings.settingsBuilder()
|
||||
|
@ -656,7 +654,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testScopedFacet() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(
|
||||
|
@ -707,7 +704,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testDeletedParent() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(
|
||||
|
@ -773,7 +769,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testDfsSearchType() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test").setSettings(
|
||||
ImmutableSettings.settingsBuilder()
|
||||
|
@ -813,7 +808,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testFixAOBEIfTopChildrenIsWrappedInMusNotClause() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test").setSettings(ImmutableSettings.settingsBuilder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0)).execute().actionGet();
|
||||
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
|
||||
|
@ -839,7 +833,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testTopChildrenReSearchBug() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(
|
||||
|
@ -893,7 +886,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testHasChildAndHasParentFailWhenSomeSegmentsDontContainAnyParentOrChildDocs() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test").setSettings(
|
||||
ImmutableSettings.settingsBuilder()
|
||||
|
@ -936,7 +928,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testCountApiUsage() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(
|
||||
|
@ -992,7 +983,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testScoreForParentChildQueries() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.addMapping("child", jsonBuilder()
|
||||
|
@ -1018,79 +1008,8 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
).execute().actionGet();
|
||||
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
|
||||
|
||||
// Parent 1 and its children
|
||||
client().prepareIndex("test", "parent", "1")
|
||||
.setSource("p_field", "p_value1")
|
||||
.execute().actionGet();
|
||||
client().prepareIndex("test", "child", "1")
|
||||
.setSource("c_field1", 1, "c_field2", 0)
|
||||
.setParent("1").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "2")
|
||||
.setSource("c_field1", 1, "c_field2", 0)
|
||||
.setParent("1").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "3")
|
||||
.setSource("c_field1", 2, "c_field2", 0)
|
||||
.setParent("1").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "4")
|
||||
.setSource("c_field1", 2, "c_field2", 0)
|
||||
.setParent("1").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "5")
|
||||
.setSource("c_field1", 1, "c_field2", 1)
|
||||
.setParent("1").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "6")
|
||||
.setSource("c_field1", 1, "c_field2", 2)
|
||||
.setParent("1").execute().actionGet();
|
||||
|
||||
// Parent 2 and its children
|
||||
client().prepareIndex("test", "parent", "2")
|
||||
.setSource("p_field", "p_value2")
|
||||
.execute().actionGet();
|
||||
client().prepareIndex("test", "child", "7")
|
||||
.setSource("c_field1", 3, "c_field2", 0)
|
||||
.setParent("2").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "8")
|
||||
.setSource("c_field1", 1, "c_field2", 1)
|
||||
.setParent("2").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "9")
|
||||
.setSource("c_field1", 1, "c_field2", 1)
|
||||
.setParent("p").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "10")
|
||||
.setSource("c_field1", 1, "c_field2", 1)
|
||||
.setParent("2").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "11")
|
||||
.setSource("c_field1", 1, "c_field2", 1)
|
||||
.setParent("2").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "12")
|
||||
.setSource("c_field1", 1, "c_field2", 2)
|
||||
.setParent("2").execute().actionGet();
|
||||
|
||||
// Parent 3 and its children
|
||||
client().prepareIndex("test", "parent", "3")
|
||||
.setSource("p_field1", "p_value3", "p_field2", 5)
|
||||
.execute().actionGet();
|
||||
client().prepareIndex("test", "child", "13")
|
||||
.setSource("c_field1", 4, "c_field2", 0, "c_field3", 0)
|
||||
.setParent("3").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "14")
|
||||
.setSource("c_field1", 1, "c_field2", 1, "c_field3", 1)
|
||||
.setParent("3").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "15")
|
||||
.setSource("c_field1", 1, "c_field2", 2, "c_field3", 2)
|
||||
.setParent("3").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "16")
|
||||
.setSource("c_field1", 1, "c_field2", 2, "c_field3", 3)
|
||||
.setParent("3").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "17")
|
||||
.setSource("c_field1", 1, "c_field2", 2, "c_field3", 4)
|
||||
.setParent("3").execute().actionGet();
|
||||
client().prepareIndex("test", "child", "18")
|
||||
.setSource("c_field1", 1, "c_field2", 2, "c_field3", 5)
|
||||
.setParent("3").execute().actionGet();
|
||||
client().prepareIndex("test", "child1", "1")
|
||||
.setSource("c_field1", 1, "c_field2", 2, "c_field3", 6)
|
||||
.setParent("3").execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareRefresh().execute().actionGet();
|
||||
indexRandom("test", false, createDocBuilders().toArray(new IndexRequestBuilder[0]));
|
||||
refresh();
|
||||
|
||||
SearchResponse response = client().prepareSearch("test")
|
||||
.setQuery(
|
||||
|
@ -1179,10 +1098,164 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
assertThat(response.getHits().hits()[6].score(), equalTo(5f));
|
||||
}
|
||||
|
||||
List<IndexRequestBuilder> createDocBuilders() {
|
||||
List<IndexRequestBuilder> indexBuilders = new ArrayList<IndexRequestBuilder>();
|
||||
// Parent 1 and its children
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("parent").setId("1").setIndex("test").setSource("p_field", "p_value1"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("1").setIndex("test").setSource("c_field1", 1, "c_field2", 0).setParent("1"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("2").setIndex("test").setSource("c_field1", 1, "c_field2", 0).setParent("1"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("3").setIndex("test").setSource("c_field1", 2, "c_field2", 0).setParent("1"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("4").setIndex("test").setSource("c_field1", 2, "c_field2", 0).setParent("1"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("5").setIndex("test").setSource("c_field1", 1, "c_field2", 1).setParent("1"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("6").setIndex("test").setSource("c_field1", 1, "c_field2", 2).setParent("1"));
|
||||
|
||||
|
||||
// Parent 2 and its children
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("parent").setId("2").setIndex("test").setSource("p_field", "p_value2"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("7").setIndex("test").setSource("c_field1", 3, "c_field2", 0).setParent("2"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("8").setIndex("test").setSource("c_field1", 1, "c_field2", 1).setParent("2"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("9").setIndex("test").setSource("c_field1", 1, "c_field2", 1).setParent("p")); //why "p"????
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("10").setIndex("test").setSource("c_field1", 1, "c_field2", 1).setParent("2"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("11").setIndex("test").setSource("c_field1", 1, "c_field2", 1).setParent("2"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("12").setIndex("test").setSource("c_field1", 1, "c_field2", 2).setParent("2"));
|
||||
|
||||
|
||||
|
||||
// Parent 3 and its children
|
||||
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("parent").setId("3").setIndex("test").setSource("p_field1", "p_value3", "p_field2", 5));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("13").setIndex("test").setSource("c_field1", 4, "c_field2", 0, "c_field3", 0).setParent("3"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("14").setIndex("test").setSource("c_field1", 1, "c_field2", 1, "c_field3", 1).setParent("3"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("15").setIndex("test").setSource("c_field1", 1, "c_field2", 2, "c_field3", 2).setParent("3")); //why "p"????
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("16").setIndex("test").setSource("c_field1", 1, "c_field2", 2, "c_field3", 3).setParent("3"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("17").setIndex("test").setSource("c_field1", 1, "c_field2", 2, "c_field3", 4).setParent("3"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child").setId("18").setIndex("test").setSource("c_field1", 1, "c_field2", 2, "c_field3", 5).setParent("3"));
|
||||
indexBuilders.add(new IndexRequestBuilder(client()).setType("child1").setId("1").setIndex("test").setSource("c_field1", 1, "c_field2", 2, "c_field3", 6).setParent("3"));
|
||||
|
||||
|
||||
return indexBuilders;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScoreForParentChildQueries_withFunctionScore() throws Exception {
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.addMapping("child", jsonBuilder()
|
||||
.startObject()
|
||||
.startObject("type")
|
||||
.startObject("_parent")
|
||||
.field("type", "parent")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
).addMapping("child1", jsonBuilder()
|
||||
.startObject()
|
||||
.startObject("type")
|
||||
.startObject("_parent")
|
||||
.field("type", "parent")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
).setSettings(
|
||||
ImmutableSettings.settingsBuilder()
|
||||
.put("index.number_of_shards", 2)
|
||||
.put("index.number_of_replicas", 0)
|
||||
).execute().actionGet();
|
||||
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
|
||||
|
||||
indexRandom("test", false, createDocBuilders().toArray(new IndexRequestBuilder[0]));
|
||||
refresh();
|
||||
SearchResponse response = client().prepareSearch("test")
|
||||
.setQuery(
|
||||
QueryBuilders.hasChildQuery(
|
||||
"child",
|
||||
QueryBuilders.functionScoreQuery(
|
||||
matchQuery("c_field2", 0)
|
||||
).add(new ScriptScoreFunctionBuilder().script("doc['c_field1'].value")
|
||||
)).scoreType("sum")
|
||||
)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(3l));
|
||||
assertThat(response.getHits().hits()[0].id(), equalTo("1"));
|
||||
assertThat(response.getHits().hits()[0].score(), equalTo(6f));
|
||||
assertThat(response.getHits().hits()[1].id(), equalTo("3"));
|
||||
assertThat(response.getHits().hits()[1].score(), equalTo(4f));
|
||||
assertThat(response.getHits().hits()[2].id(), equalTo("2"));
|
||||
assertThat(response.getHits().hits()[2].score(), equalTo(3f));
|
||||
|
||||
response = client().prepareSearch("test")
|
||||
.setQuery(
|
||||
QueryBuilders.hasChildQuery(
|
||||
"child",
|
||||
QueryBuilders.functionScoreQuery(
|
||||
matchQuery("c_field2", 0)
|
||||
).add(new ScriptScoreFunctionBuilder().script("doc['c_field1'].value")
|
||||
)).scoreType("max")
|
||||
)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(3l));
|
||||
assertThat(response.getHits().hits()[0].id(), equalTo("3"));
|
||||
assertThat(response.getHits().hits()[0].score(), equalTo(4f));
|
||||
assertThat(response.getHits().hits()[1].id(), equalTo("2"));
|
||||
assertThat(response.getHits().hits()[1].score(), equalTo(3f));
|
||||
assertThat(response.getHits().hits()[2].id(), equalTo("1"));
|
||||
assertThat(response.getHits().hits()[2].score(), equalTo(2f));
|
||||
|
||||
response = client().prepareSearch("test")
|
||||
.setQuery(
|
||||
QueryBuilders.hasChildQuery(
|
||||
"child",
|
||||
QueryBuilders.functionScoreQuery(
|
||||
matchQuery("c_field2", 0)
|
||||
).add(new ScriptScoreFunctionBuilder().script("doc['c_field1'].value")
|
||||
)).scoreType("avg")
|
||||
)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(3l));
|
||||
assertThat(response.getHits().hits()[0].id(), equalTo("3"));
|
||||
assertThat(response.getHits().hits()[0].score(), equalTo(4f));
|
||||
assertThat(response.getHits().hits()[1].id(), equalTo("2"));
|
||||
assertThat(response.getHits().hits()[1].score(), equalTo(3f));
|
||||
assertThat(response.getHits().hits()[2].id(), equalTo("1"));
|
||||
assertThat(response.getHits().hits()[2].score(), equalTo(1.5f));
|
||||
|
||||
response = client().prepareSearch("test")
|
||||
.setQuery(
|
||||
QueryBuilders.hasParentQuery(
|
||||
"parent",
|
||||
QueryBuilders.functionScoreQuery(
|
||||
matchQuery("p_field1", "p_value3")
|
||||
).add(new ScriptScoreFunctionBuilder().script("doc['p_field2'].value")
|
||||
)).scoreType("score")
|
||||
)
|
||||
.addSort(SortBuilders.fieldSort("c_field3"))
|
||||
.addSort(SortBuilders.scoreSort())
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(7l));
|
||||
assertThat(response.getHits().hits()[0].id(), equalTo("13"));
|
||||
assertThat(response.getHits().hits()[0].score(), equalTo(5f));
|
||||
assertThat(response.getHits().hits()[1].id(), equalTo("14"));
|
||||
assertThat(response.getHits().hits()[1].score(), equalTo(5f));
|
||||
assertThat(response.getHits().hits()[2].id(), equalTo("15"));
|
||||
assertThat(response.getHits().hits()[2].score(), equalTo(5f));
|
||||
assertThat(response.getHits().hits()[3].id(), equalTo("16"));
|
||||
assertThat(response.getHits().hits()[3].score(), equalTo(5f));
|
||||
assertThat(response.getHits().hits()[4].id(), equalTo("17"));
|
||||
assertThat(response.getHits().hits()[4].score(), equalTo(5f));
|
||||
assertThat(response.getHits().hits()[5].id(), equalTo("18"));
|
||||
assertThat(response.getHits().hits()[5].score(), equalTo(5f));
|
||||
assertThat(response.getHits().hits()[6].id(), equalTo("1"));
|
||||
assertThat(response.getHits().hits()[6].score(), equalTo(5f));
|
||||
}
|
||||
|
||||
@Test
|
||||
// https://github.com/elasticsearch/elasticsearch/issues/2536
|
||||
public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.addMapping("parent", jsonBuilder()
|
||||
|
@ -1242,7 +1315,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testHasChildAndHasParentFilter_withFilter() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test").setSettings(
|
||||
ImmutableSettings.settingsBuilder()
|
||||
|
@ -1287,7 +1359,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
|
||||
@Test
|
||||
public void testSimpleQueryRewrite() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test").setSettings(
|
||||
ImmutableSettings.settingsBuilder()
|
||||
|
@ -1364,7 +1435,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
@Test
|
||||
// See also issue: https://github.com/elasticsearch/elasticsearch/issues/3144
|
||||
public void testReIndexingParentAndChildDocuments() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(
|
||||
|
@ -1444,7 +1514,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
|
|||
@Test
|
||||
// See also issue: https://github.com/elasticsearch/elasticsearch/issues/3203
|
||||
public void testHasChildQueryWithMinimumScore() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.elasticsearch.test.integration.search.customscore;
|
||||
|
||||
|
||||
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.elasticsearch.ElasticSearchException;
|
||||
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
||||
|
@ -26,10 +28,13 @@ import org.elasticsearch.action.search.SearchResponse;
|
|||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.common.Priority;
|
||||
import org.elasticsearch.index.query.FilterBuilders;
|
||||
import org.elasticsearch.index.query.functionscore.factor.FactorBuilder;
|
||||
import org.elasticsearch.index.query.functionscore.script.ScriptScoreFunctionBuilder;
|
||||
import org.elasticsearch.test.integration.AbstractSharedClusterTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.elasticsearch.client.Requests.*;
|
||||
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
||||
|
@ -123,6 +128,76 @@ public class CustomScoreSearchTests extends AbstractSharedClusterTest {
|
|||
assertThat(explanation.getDetails()[1].getDetails()[2].getValue(), equalTo(2f));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testScoreExplainBug_2283_withFunctionScore() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
client().admin().indices().prepareCreate("test").setSettings(settingsBuilder().put("index.number_of_shards", 1)).execute().actionGet();
|
||||
ClusterHealthResponse healthResponse = client().admin().cluster().prepareHealth("test").setWaitForYellowStatus().execute().actionGet();
|
||||
assertThat(healthResponse.isTimedOut(), equalTo(false));
|
||||
|
||||
client().prepareIndex("test", "type", "1").setSource("field", "value1", "color", "red").execute().actionGet();
|
||||
client().prepareIndex("test", "type", "2").setSource("field", "value2", "color", "blue").execute().actionGet();
|
||||
client().prepareIndex("test", "type", "3").setSource("field", "value3", "color", "red").execute().actionGet();
|
||||
client().prepareIndex("test", "type", "4").setSource("field", "value4", "color", "blue").execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareRefresh().execute().actionGet();
|
||||
|
||||
SearchResponse searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(matchAllQuery()).scoreMode("first").add(termFilter("field", "value4"), new ScriptScoreFunctionBuilder().script("2")).add(termFilter("field", "value2"), new ScriptScoreFunctionBuilder().script("3")))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(3.0f));
|
||||
logger.info("--> Hit[0] {} Explanation:\n {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
Explanation explanation = searchResponse.getHits().getAt(0).explanation();
|
||||
assertNotNull(explanation);
|
||||
assertThat(explanation.isMatch(), equalTo(true));
|
||||
assertThat(explanation.getValue(), equalTo(3f));
|
||||
assertThat(explanation.getDescription(), equalTo("custom score, score mode [first]"));
|
||||
|
||||
assertThat(explanation.getDetails().length, equalTo(2));
|
||||
assertThat(explanation.getDetails()[0].isMatch(), equalTo(true));
|
||||
assertThat(explanation.getDetails()[0].getValue(), equalTo(1f));
|
||||
assertThat(explanation.getDetails()[0].getDetails().length, equalTo(2));
|
||||
assertThat(explanation.getDetails()[1].isMatch(), equalTo(true));
|
||||
assertThat(explanation.getDetails()[1].getValue(), equalTo(3f));
|
||||
assertThat(explanation.getDetails()[1].getDetails().length, equalTo(3));
|
||||
|
||||
// Same query but with boost
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(matchAllQuery()).scoreMode("first").add(termFilter("field", "value4"), new ScriptScoreFunctionBuilder().script("2")).add(termFilter("field", "value2"), new ScriptScoreFunctionBuilder().script("3")).boost(2))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(6f));
|
||||
logger.info("--> Hit[0] {} Explanation:\n {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
explanation = searchResponse.getHits().getAt(0).explanation();
|
||||
assertNotNull(explanation);
|
||||
assertThat(explanation.isMatch(), equalTo(true));
|
||||
assertThat(explanation.getValue(), equalTo(6f));
|
||||
assertThat(explanation.getDescription(), equalTo("custom score, score mode [first]"));
|
||||
|
||||
assertThat(explanation.getDetails().length, equalTo(2));
|
||||
assertThat(explanation.getDetails()[0].isMatch(), equalTo(true));
|
||||
assertThat(explanation.getDetails()[0].getValue(), equalTo(1f));
|
||||
assertThat(explanation.getDetails()[0].getDetails().length, equalTo(2));
|
||||
assertThat(explanation.getDetails()[1].isMatch(), equalTo(true));
|
||||
assertThat(explanation.getDetails()[1].getValue(), equalTo(6f));
|
||||
assertThat(explanation.getDetails()[1].getDetails().length, equalTo(3));
|
||||
assertThat(explanation.getDetails()[1].getDetails()[2].getDescription(), equalTo("queryBoost"));
|
||||
assertThat(explanation.getDetails()[1].getDetails()[2].getValue(), equalTo(2f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiValueCustomScriptBoost() throws ElasticSearchException, IOException {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
@ -223,6 +298,108 @@ public class CustomScoreSearchTests extends AbstractSharedClusterTest {
|
|||
assertThat(response.getHits().getAt(1).id(), equalTo("1"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testMultiValueCustomScriptBoost_withFunctionScore() throws ElasticSearchException, IOException {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.setSettings(settingsBuilder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0))
|
||||
.addMapping("type", jsonBuilder().startObject().startObject("type").startObject("properties")
|
||||
.startObject("snum").field("type", "string").endObject()
|
||||
.startObject("dnum").field("type", "double").endObject()
|
||||
.startObject("slnum").field("type", "long").endObject()
|
||||
.startObject("gp").field("type", "geo_point").endObject()
|
||||
.endObject().endObject().endObject())
|
||||
.execute().actionGet();
|
||||
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
|
||||
|
||||
String[] values = new String[100];
|
||||
String[] gp = new String[100];
|
||||
|
||||
long[] lValues = new long[100];
|
||||
double[] dValues = new double[100];
|
||||
int offset = 1;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = ""+ (i + offset);
|
||||
gp[i] = ""+ (i + offset) + ","+ (i + offset);
|
||||
lValues[i] = (i + offset);
|
||||
dValues[i] = (i + offset);
|
||||
}
|
||||
client().index(indexRequest("test").type("type1").id("1")
|
||||
.source(jsonBuilder().startObject().field("test", "value check")
|
||||
.field("snum", values)
|
||||
.field("dnum", dValues)
|
||||
.field("lnum", lValues)
|
||||
.field("gp", gp)
|
||||
.endObject())).actionGet();
|
||||
offset++;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = ""+ (i + offset);
|
||||
gp[i] = ""+ (i + offset) + ","+ (i + offset);
|
||||
lValues[i] = (i + offset);
|
||||
dValues[i] = (i + offset);
|
||||
}
|
||||
client().index(indexRequest("test").type("type1").id("2")
|
||||
.source(jsonBuilder().startObject().field("test", "value check")
|
||||
.field("snum", values)
|
||||
.field("dnum", dValues)
|
||||
.field("lnum", lValues)
|
||||
.field("gp", gp)
|
||||
.endObject())).actionGet();
|
||||
client().admin().indices().refresh(refreshRequest()).actionGet();
|
||||
|
||||
logger.info("running min(doc['num1'].value)");
|
||||
SearchResponse response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termQuery("test", "value")).add(new ScriptScoreFunctionBuilder()
|
||||
.script("c_min = 1000; foreach (x : doc['snum'].values) { c_min = min(Integer.parseInt(x), c_min) } return c_min"))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertThat(response.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(response.getHits().getAt(1).id(), equalTo("1"));
|
||||
|
||||
response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termQuery("test", "value")).add(new ScriptScoreFunctionBuilder()
|
||||
.script("c_min = 1000; foreach (x : doc['lnum'].values) { c_min = min(x, c_min) } return c_min"))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertThat(response.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(response.getHits().getAt(1).id(), equalTo("1"));
|
||||
|
||||
response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termQuery("test", "value")).add(new ScriptScoreFunctionBuilder()
|
||||
.script("c_min = 1000; foreach (x : doc['dnum'].values) { c_min = min(x, c_min) } return c_min"))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertThat(response.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(response.getHits().getAt(1).id(), equalTo("1"));
|
||||
|
||||
response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termQuery("test", "value")).add(new ScriptScoreFunctionBuilder()
|
||||
.script("c_min = 1000; foreach (x : doc['gp'].values) { c_min = min(x.lat, c_min) } return c_min"))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertThat(response.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(response.getHits().getAt(1).id(), equalTo("1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomScriptBoost() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
@ -323,6 +500,108 @@ public class CustomScoreSearchTests extends AbstractSharedClusterTest {
|
|||
assertThat(response.getHits().getAt(1).score(), equalTo(4f)); // _score is always 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testCustomScriptBoost_withFunctionScore() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
client().admin().indices().prepareCreate("test").setSettings(settingsBuilder().put("index.number_of_shards", 1)).execute().actionGet();
|
||||
|
||||
client().index(indexRequest("test").type("type1").id("1")
|
||||
.source(jsonBuilder().startObject().field("test", "value beck").field("num1", 1.0f).endObject())).actionGet();
|
||||
client().index(indexRequest("test").type("type1").id("2")
|
||||
.source(jsonBuilder().startObject().field("test", "value check").field("num1", 2.0f).endObject())).actionGet();
|
||||
client().admin().indices().refresh(refreshRequest()).actionGet();
|
||||
|
||||
logger.info("--- QUERY_THEN_FETCH");
|
||||
|
||||
logger.info("running doc['num1'].value");
|
||||
SearchResponse response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termQuery("test", "value")).add(new ScriptScoreFunctionBuilder().script("doc['num1'].value"))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertThat(response.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(response.getHits().getAt(1).id(), equalTo("1"));
|
||||
|
||||
logger.info("running -doc['num1'].value");
|
||||
response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termQuery("test", "value")).add(new ScriptScoreFunctionBuilder().script("-doc['num1'].value"))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertThat(response.getHits().getAt(0).id(), equalTo("1"));
|
||||
assertThat(response.getHits().getAt(1).id(), equalTo("2"));
|
||||
|
||||
|
||||
logger.info("running pow(doc['num1'].value, 2)");
|
||||
response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termQuery("test", "value")).add(new ScriptScoreFunctionBuilder().script("pow(doc['num1'].value, 2)"))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertThat(response.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(response.getHits().getAt(1).id(), equalTo("1"));
|
||||
|
||||
logger.info("running max(doc['num1'].value, 1)");
|
||||
response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termQuery("test", "value")).add(new ScriptScoreFunctionBuilder().script("max(doc['num1'].value, 1d)"))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertThat(response.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(response.getHits().getAt(1).id(), equalTo("1"));
|
||||
|
||||
logger.info("running doc['num1'].value * _score");
|
||||
response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termQuery("test", "value")).add(new ScriptScoreFunctionBuilder().script("doc['num1'].value * _score"))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertThat(response.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(response.getHits().getAt(1).id(), equalTo("1"));
|
||||
|
||||
logger.info("running param1 * param2 * _score");
|
||||
response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termQuery("test", "value")).add(new ScriptScoreFunctionBuilder().script("param1 * param2 * _score").param("param1", 2).param("param2", 2))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertSearchHits(response, "1", "2");
|
||||
|
||||
|
||||
logger.info("running param1 * param2 * _score with filter instead of query");
|
||||
response = client().search(searchRequest()
|
||||
.searchType(SearchType.QUERY_THEN_FETCH)
|
||||
.source(searchSource().explain(true).query(functionScoreQuery(termFilter("test", "value")).add(new ScriptScoreFunctionBuilder().script("param1 * param2 * _score").param("param1", 2).param("param2", 2))))
|
||||
).actionGet();
|
||||
|
||||
assertThat(response.getHits().totalHits(), equalTo(2l));
|
||||
logger.info("Hit[0] {} Explanation {}", response.getHits().getAt(0).id(), response.getHits().getAt(0).explanation());
|
||||
logger.info("Hit[1] {} Explanation {}", response.getHits().getAt(1).id(), response.getHits().getAt(1).explanation());
|
||||
assertSearchHits(response, "1", "2");
|
||||
assertThat(response.getHits().getAt(0).score(), equalTo(4f)); // _score is always 1
|
||||
assertThat(response.getHits().getAt(1).score(), equalTo(4f)); // _score is always 1
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTriggerBooleanScorer() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
@ -342,6 +621,26 @@ public class CustomScoreSearchTests extends AbstractSharedClusterTest {
|
|||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testTriggerBooleanScorer_withFunctionScore() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
client().admin().indices().prepareCreate("test").setSettings(settingsBuilder().put("index.number_of_shards", 1)).execute().actionGet();
|
||||
|
||||
client().prepareIndex("test", "type", "1").setSource("field", "value1", "color", "red").execute().actionGet();
|
||||
client().prepareIndex("test", "type", "2").setSource("field", "value2", "color", "blue").execute().actionGet();
|
||||
client().prepareIndex("test", "type", "3").setSource("field", "value3", "color", "red").execute().actionGet();
|
||||
client().prepareIndex("test", "type", "4").setSource("field", "value4", "color", "blue").execute().actionGet();
|
||||
client().admin().indices().prepareRefresh().execute().actionGet();
|
||||
SearchResponse searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(fuzzyQuery("field", "value")).add(FilterBuilders.idsFilter("type").addIds("1"), new FactorBuilder().boostFactor( 3)))
|
||||
.execute().actionGet();
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomFiltersScore() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
|
@ -520,4 +819,159 @@ public class CustomScoreSearchTests extends AbstractSharedClusterTest {
|
|||
assertThat(searchResponse.getHits().getAt(3).id(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(3).score(), equalTo(searchResponse.getHits().getAt(3).explanation().getValue()));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCustomFiltersScore_withFunctionScore() throws Exception {
|
||||
client().admin().indices().prepareDelete().execute().actionGet();
|
||||
client().admin().indices().prepareCreate("test").setSettings(settingsBuilder().put("index.number_of_shards", 1)).execute().actionGet();
|
||||
|
||||
client().prepareIndex("test", "type", "1").setSource("field", "value1", "color", "red").execute().actionGet();
|
||||
client().prepareIndex("test", "type", "2").setSource("field", "value2", "color", "blue").execute().actionGet();
|
||||
client().prepareIndex("test", "type", "3").setSource("field", "value3", "color", "red").execute().actionGet();
|
||||
client().prepareIndex("test", "type", "4").setSource("field", "value4", "color", "blue").execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareRefresh().execute().actionGet();
|
||||
|
||||
SearchResponse searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(matchAllQuery()).add(termFilter("field", "value4"), new ScriptScoreFunctionBuilder().script("2")).add(termFilter("field", "value2"), new ScriptScoreFunctionBuilder().script("3")))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(3.0f));
|
||||
logger.info("--> Hit[0] {} Explanation {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
assertThat(searchResponse.getHits().getAt(1).id(), equalTo("4"));
|
||||
assertThat(searchResponse.getHits().getAt(1).score(), equalTo(2.0f));
|
||||
assertThat(searchResponse.getHits().getAt(2).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
assertThat(searchResponse.getHits().getAt(2).score(), equalTo(1.0f));
|
||||
assertThat(searchResponse.getHits().getAt(3).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
assertThat(searchResponse.getHits().getAt(3).score(), equalTo(1.0f));
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(matchAllQuery()).add(termFilter("field", "value4"), new FactorBuilder().boostFactor( 2)).add(termFilter("field", "value2"), new FactorBuilder().boostFactor( 3)))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(3.0f));
|
||||
logger.info("--> Hit[0] {} Explanation {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
assertThat(searchResponse.getHits().getAt(1).id(), equalTo("4"));
|
||||
assertThat(searchResponse.getHits().getAt(1).score(), equalTo(2.0f));
|
||||
assertThat(searchResponse.getHits().getAt(2).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
assertThat(searchResponse.getHits().getAt(2).score(), equalTo(1.0f));
|
||||
assertThat(searchResponse.getHits().getAt(3).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
assertThat(searchResponse.getHits().getAt(3).score(), equalTo(1.0f));
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(matchAllQuery()).scoreMode("total").add(termFilter("field", "value4"), new FactorBuilder().boostFactor( 2)).add(termFilter("field", "value1"), new FactorBuilder().boostFactor( 3)).add(termFilter("color", "red"), new FactorBuilder().boostFactor( 5)))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(8.0f));
|
||||
logger.info("--> Hit[0] {} Explanation {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(matchAllQuery()).scoreMode("max").add(termFilter("field", "value4"), new FactorBuilder().boostFactor( 2)).add(termFilter("field", "value1"), new FactorBuilder().boostFactor( 3)).add(termFilter("color", "red"), new FactorBuilder().boostFactor( 5)))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("1"), equalTo("3"))); // could be both depending on the order of the docs internally (lucene order)
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(5.0f));
|
||||
logger.info("--> Hit[0] {} Explanation {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(matchAllQuery()).scoreMode("avg").add(termFilter("field", "value4"), new FactorBuilder().boostFactor( 2)).add(termFilter("field", "value1"), new FactorBuilder().boostFactor( 3)).add(termFilter("color", "red"), new FactorBuilder().boostFactor( 5)))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("3"));
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(5.0f));
|
||||
logger.info("--> Hit[0] {} Explanation {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
assertThat(searchResponse.getHits().getAt(1).id(), equalTo("1"));
|
||||
assertThat(searchResponse.getHits().getAt(1).score(), equalTo(4.0f));
|
||||
logger.info("--> Hit[1] {} Explanation {}", searchResponse.getHits().getAt(1).id(), searchResponse.getHits().getAt(1).explanation());
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(matchAllQuery()).scoreMode("min").add(termFilter("field", "value4"), new FactorBuilder().boostFactor( 2)).add(termFilter("field", "value1"), new FactorBuilder().boostFactor( 3)).add(termFilter("color", "red"), new FactorBuilder().boostFactor( 5)))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("3"));
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(5.0f));
|
||||
logger.info("--> Hit[0] {} Explanation {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
assertThat(searchResponse.getHits().getAt(1).id(), equalTo("1"));
|
||||
assertThat(searchResponse.getHits().getAt(1).score(), equalTo(3.0f));
|
||||
assertThat(searchResponse.getHits().getAt(2).id(), equalTo("4"));
|
||||
assertThat(searchResponse.getHits().getAt(2).score(), equalTo(2.0f));
|
||||
assertThat(searchResponse.getHits().getAt(3).id(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(3).score(), equalTo(1.0f));
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(matchAllQuery()).scoreMode("multiply").add(termFilter("field", "value4"), new FactorBuilder().boostFactor( 2)).add(termFilter("field", "value1"), new FactorBuilder().boostFactor( 3)).add(termFilter("color", "red"), new FactorBuilder().boostFactor( 5)))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(15.0f));
|
||||
logger.info("--> Hit[0] {} Explanation {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
assertThat(searchResponse.getHits().getAt(1).id(), equalTo("3"));
|
||||
assertThat(searchResponse.getHits().getAt(1).score(), equalTo(5.0f));
|
||||
assertThat(searchResponse.getHits().getAt(2).id(), equalTo("4"));
|
||||
assertThat(searchResponse.getHits().getAt(2).score(), equalTo(2.0f));
|
||||
assertThat(searchResponse.getHits().getAt(3).id(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(3).score(), equalTo(1.0f));
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(termsQuery("field", "value1", "value2", "value3", "value4")).scoreMode("first").add(termFilter("field", "value4"), new FactorBuilder().boostFactor( 2)).add(termFilter("field", "value3"), new FactorBuilder().boostFactor( 3)).add(termFilter("field", "value2"), new FactorBuilder().boostFactor( 4)))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(searchResponse.getHits().getAt(0).explanation().getValue()));
|
||||
logger.info("--> Hit[0] {} Explanation {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
assertThat(searchResponse.getHits().getAt(1).id(), equalTo("3"));
|
||||
assertThat(searchResponse.getHits().getAt(1).score(), equalTo(searchResponse.getHits().getAt(1).explanation().getValue()));
|
||||
assertThat(searchResponse.getHits().getAt(2).id(), equalTo("4"));
|
||||
assertThat(searchResponse.getHits().getAt(2).score(), equalTo(searchResponse.getHits().getAt(2).explanation().getValue()));
|
||||
assertThat(searchResponse.getHits().getAt(3).id(), equalTo("1"));
|
||||
assertThat(searchResponse.getHits().getAt(3).score(), equalTo(searchResponse.getHits().getAt(3).explanation().getValue()));
|
||||
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(functionScoreQuery(termsQuery("field", "value1", "value2", "value3", "value4")).scoreMode("multiply").add(termFilter("field", "value4"), new FactorBuilder().boostFactor( 2)).add(termFilter("field", "value1"), new FactorBuilder().boostFactor( 3)).add(termFilter("color", "red"), new FactorBuilder().boostFactor( 5)))
|
||||
.setExplain(true)
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
|
||||
assertThat(searchResponse.getHits().totalHits(), equalTo(4l));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));
|
||||
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(searchResponse.getHits().getAt(0).explanation().getValue()));
|
||||
logger.info("--> Hit[0] {} Explanation {}", searchResponse.getHits().getAt(0).id(), searchResponse.getHits().getAt(0).explanation());
|
||||
assertThat(searchResponse.getHits().getAt(1).id(), equalTo("3"));
|
||||
assertThat(searchResponse.getHits().getAt(1).score(), equalTo(searchResponse.getHits().getAt(1).explanation().getValue()));
|
||||
assertThat(searchResponse.getHits().getAt(2).id(), equalTo("4"));
|
||||
assertThat(searchResponse.getHits().getAt(2).score(), equalTo(searchResponse.getHits().getAt(2).explanation().getValue()));
|
||||
assertThat(searchResponse.getHits().getAt(3).id(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(3).score(), equalTo(searchResponse.getHits().getAt(3).explanation().getValue()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.elasticsearch.test.integration.search.rescore;
|
||||
|
||||
|
||||
|
||||
import org.apache.lucene.util.English;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
|
@ -28,6 +30,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
|||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.index.query.MatchQueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.query.functionscore.script.ScriptScoreFunctionBuilder;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.SearchHits;
|
||||
import org.elasticsearch.search.rescore.RescoreBuilder;
|
||||
|
@ -37,7 +40,6 @@ import org.junit.Test;
|
|||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
|
@ -443,4 +445,111 @@ public class QueryRescorerTests extends AbstractSharedClusterTest {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testScoring_withFunctionScore() throws Exception {
|
||||
client().admin()
|
||||
.indices()
|
||||
.prepareCreate("test")
|
||||
.addMapping(
|
||||
"type1",
|
||||
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("field1")
|
||||
.field("index", "not_analyzed").field("type", "string").endObject().endObject().endObject().endObject())
|
||||
.setSettings(ImmutableSettings.settingsBuilder()).execute().actionGet();
|
||||
ensureGreen();
|
||||
int numDocs = 1000;
|
||||
|
||||
for (int i = 0; i < numDocs; i++) {
|
||||
client().prepareIndex("test", "type1", String.valueOf(i)).setSource("field1", English.intToEnglish(i)).execute().actionGet();
|
||||
}
|
||||
|
||||
flush();
|
||||
optimize(); // make sure we don't have a background merge running
|
||||
refresh();
|
||||
ensureGreen();
|
||||
|
||||
String[] scoreModes = new String[]{ "max", "min", "avg", "total", "multiply", "" };
|
||||
float primaryWeight = 1.1f;
|
||||
float secondaryWeight = 1.6f;
|
||||
|
||||
for (String scoreMode: scoreModes) {
|
||||
for (int i = 0; i < numDocs - 4; i++) {
|
||||
String[] intToEnglish = new String[] { English.intToEnglish(i), English.intToEnglish(i + 1), English.intToEnglish(i + 2), English.intToEnglish(i + 3) };
|
||||
|
||||
QueryRescorer rescoreQuery = RescoreBuilder
|
||||
.queryRescorer(
|
||||
QueryBuilders.boolQuery()
|
||||
.disableCoord(true)
|
||||
.should(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("field1", intToEnglish[0])).add(new ScriptScoreFunctionBuilder().script("5.0f")))
|
||||
.should(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("field1", intToEnglish[1])).add(new ScriptScoreFunctionBuilder().script("7.0f")))
|
||||
.should(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("field1", intToEnglish[3])).add(new ScriptScoreFunctionBuilder().script("0.0f"))))
|
||||
.setQueryWeight(primaryWeight)
|
||||
.setRescoreQueryWeight(secondaryWeight);
|
||||
|
||||
if (!"".equals(scoreMode)) {
|
||||
rescoreQuery.setScoreMode(scoreMode);
|
||||
}
|
||||
|
||||
SearchResponse rescored = client()
|
||||
.prepareSearch()
|
||||
.setPreference("test") // ensure we hit the same shards for tie-breaking
|
||||
.setQuery(QueryBuilders.boolQuery()
|
||||
.disableCoord(true)
|
||||
.should(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("field1", intToEnglish[0])).add(new ScriptScoreFunctionBuilder().script("2.0f")))
|
||||
.should(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("field1", intToEnglish[1])).add(new ScriptScoreFunctionBuilder().script("3.0f")))
|
||||
.should(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("field1", intToEnglish[2])).add(new ScriptScoreFunctionBuilder().script("5.0f")))
|
||||
.should(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("field1", intToEnglish[3])).add(new ScriptScoreFunctionBuilder().script("0.2f"))))
|
||||
.setFrom(0)
|
||||
.setSize(10)
|
||||
.setRescorer(rescoreQuery)
|
||||
.setRescoreWindow(50).execute().actionGet();
|
||||
|
||||
assertHitCount(rescored, 4);
|
||||
|
||||
if ("total".equals(scoreMode) || "".equals(scoreMode)) {
|
||||
assertFirstHit(rescored, hasId(String.valueOf(i + 1)));
|
||||
assertSecondHit(rescored, hasId(String.valueOf(i)));
|
||||
assertThirdHit(rescored, hasId(String.valueOf(i + 2)));
|
||||
assertThat(rescored.getHits().getHits()[0].getScore(), equalTo(3.0f * primaryWeight + 7.0f * secondaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[1].getScore(), equalTo(2.0f * primaryWeight + 5.0f * secondaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[2].getScore(), equalTo(5.0f * primaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[3].getScore(), equalTo(0.2f * primaryWeight + 0.0f * secondaryWeight));
|
||||
} else if ("max".equals(scoreMode)) {
|
||||
assertFirstHit(rescored, hasId(String.valueOf(i + 1)));
|
||||
assertSecondHit(rescored, hasId(String.valueOf(i)));
|
||||
assertThirdHit(rescored, hasId(String.valueOf(i + 2)));
|
||||
assertThat(rescored.getHits().getHits()[0].getScore(), equalTo(7.0f * secondaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[1].getScore(), equalTo(5.0f * secondaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[2].getScore(), equalTo(5.0f * primaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[3].getScore(), equalTo(0.2f * primaryWeight));
|
||||
} else if ("min".equals(scoreMode)) {
|
||||
assertFirstHit(rescored, hasId(String.valueOf(i + 2)));
|
||||
assertSecondHit(rescored, hasId(String.valueOf(i + 1)));
|
||||
assertThirdHit(rescored, hasId(String.valueOf(i)));
|
||||
assertThat(rescored.getHits().getHits()[0].getScore(), equalTo(5.0f * primaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[1].getScore(), equalTo(3.0f * primaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[2].getScore(), equalTo(2.0f * primaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[3].getScore(), equalTo(0.0f * secondaryWeight));
|
||||
} else if ("avg".equals(scoreMode)) {
|
||||
assertFirstHit(rescored, hasId(String.valueOf(i + 1)));
|
||||
assertSecondHit(rescored, hasId(String.valueOf(i + 2)));
|
||||
assertThirdHit(rescored, hasId(String.valueOf(i)));
|
||||
assertThat(rescored.getHits().getHits()[0].getScore(), equalTo((3.0f * primaryWeight + 7.0f * secondaryWeight) / 2.0f));
|
||||
assertThat(rescored.getHits().getHits()[1].getScore(), equalTo(5.0f * primaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[2].getScore(), equalTo((2.0f * primaryWeight + 5.0f * secondaryWeight) / 2.0f));
|
||||
assertThat(rescored.getHits().getHits()[3].getScore(), equalTo((0.2f * primaryWeight) / 2.0f));
|
||||
} else if ("multiply".equals(scoreMode)) {
|
||||
assertFirstHit(rescored, hasId(String.valueOf(i + 1)));
|
||||
assertSecondHit(rescored, hasId(String.valueOf(i)));
|
||||
assertThirdHit(rescored, hasId(String.valueOf(i + 2)));
|
||||
assertThat(rescored.getHits().getHits()[0].getScore(), equalTo(3.0f * primaryWeight * 7.0f * secondaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[1].getScore(), equalTo(2.0f * primaryWeight * 5.0f * secondaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[2].getScore(), equalTo(5.0f * primaryWeight));
|
||||
assertThat(rescored.getHits().getHits()[3].getScore(), equalTo(0.2f * primaryWeight * 0.0f * secondaryWeight));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.elasticsearch.test.integration.search.sort;
|
||||
|
||||
|
||||
|
||||
import org.elasticsearch.ElasticSearchException;
|
||||
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||
import org.elasticsearch.action.search.SearchPhaseExecutionException;
|
||||
|
@ -28,6 +30,7 @@ import org.elasticsearch.common.settings.ImmutableSettings;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.text.Text;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.index.query.functionscore.script.ScriptScoreFunctionBuilder;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.sort.ScriptSortBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilders;
|
||||
|
@ -179,6 +182,39 @@ public class SimpleSortTests extends AbstractSharedClusterTest {
|
|||
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testScoreSortDirection_withFunctionScore() throws Exception {
|
||||
prepareCreate("test").setSettings(randomSettingsBuilder().put("index.number_of_shards", 1)).execute().actionGet();
|
||||
ensureGreen();
|
||||
|
||||
client().prepareIndex("test", "type", "1").setSource("field", 2).execute().actionGet();
|
||||
client().prepareIndex("test", "type", "2").setSource("field", 1).execute().actionGet();
|
||||
client().prepareIndex("test", "type", "3").setSource("field", 0).execute().actionGet();
|
||||
|
||||
refresh();
|
||||
|
||||
SearchResponse searchResponse = client().prepareSearch("test").setQuery(functionScoreQuery(matchAllQuery()).add(new ScriptScoreFunctionBuilder().script("_source.field"))).execute().actionGet();
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
|
||||
assertThat(searchResponse.getHits().getAt(1).score(), Matchers.lessThan(searchResponse.getHits().getAt(0).score()));
|
||||
assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(2).score(), Matchers.lessThan(searchResponse.getHits().getAt(1).score()));
|
||||
assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3"));
|
||||
|
||||
searchResponse = client().prepareSearch("test").setQuery(functionScoreQuery(matchAllQuery()).add(new ScriptScoreFunctionBuilder().script("_source.field"))).addSort("_score", SortOrder.DESC).execute().actionGet();
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
|
||||
assertThat(searchResponse.getHits().getAt(1).score(), Matchers.lessThan(searchResponse.getHits().getAt(0).score()));
|
||||
assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(2).score(), Matchers.lessThan(searchResponse.getHits().getAt(1).score()));
|
||||
assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3"));
|
||||
|
||||
searchResponse = client().prepareSearch("test").setQuery(functionScoreQuery(matchAllQuery()).add(new ScriptScoreFunctionBuilder().script("_source.field"))).addSort("_score", SortOrder.DESC).execute().actionGet();
|
||||
assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3"));
|
||||
assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("2"));
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIssue2986() {
|
||||
prepareCreate("test").setSettings(getSettings()).execute().actionGet();
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.elasticsearch.index.engine.IndexEngineModule;
|
|||
import org.elasticsearch.index.query.FilterBuilder;
|
||||
import org.elasticsearch.index.query.IndexQueryParserModule;
|
||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreModule;
|
||||
import org.elasticsearch.index.settings.IndexSettingsModule;
|
||||
import org.elasticsearch.index.similarity.SimilarityModule;
|
||||
import org.elasticsearch.indices.InvalidAliasNameException;
|
||||
|
@ -77,6 +78,7 @@ public class IndexAliasesServiceTests {
|
|||
new SettingsModule(ImmutableSettings.Builder.EMPTY_SETTINGS),
|
||||
new IndexEngineModule(ImmutableSettings.Builder.EMPTY_SETTINGS),
|
||||
new IndexCacheModule(ImmutableSettings.Builder.EMPTY_SETTINGS),
|
||||
new FunctionScoreModule(),
|
||||
new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.elasticsearch.test.unit.index.query;
|
||||
|
||||
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.queries.BoostingQuery;
|
||||
|
@ -53,10 +55,9 @@ import org.elasticsearch.index.codec.CodecModule;
|
|||
import org.elasticsearch.index.engine.IndexEngineModule;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.MapperServiceModule;
|
||||
import org.elasticsearch.index.query.IndexQueryParserModule;
|
||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||
import org.elasticsearch.index.query.ParsedQuery;
|
||||
import org.elasticsearch.index.query.QueryStringQueryBuilder;
|
||||
import org.elasticsearch.index.query.*;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreModule;
|
||||
import org.elasticsearch.index.query.functionscore.factor.FactorBuilder;
|
||||
import org.elasticsearch.index.search.NumericRangeFieldDataFilter;
|
||||
import org.elasticsearch.index.search.geo.GeoDistanceFilter;
|
||||
import org.elasticsearch.index.search.geo.GeoPolygonFilter;
|
||||
|
@ -67,6 +68,7 @@ import org.elasticsearch.indices.query.IndicesQueriesModule;
|
|||
import org.elasticsearch.script.ScriptModule;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.threadpool.ThreadPoolModule;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
@ -115,6 +117,7 @@ public class SimpleIndexQueryParserTests {
|
|||
new SimilarityModule(settings),
|
||||
new IndexQueryParserModule(settings),
|
||||
new IndexNameModule(index),
|
||||
new FunctionScoreModule(),
|
||||
new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
@ -1463,6 +1466,19 @@ public class SimpleIndexQueryParserTests {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testCustomBoostFactorQueryBuilder_withFunctionScore() throws IOException {
|
||||
IndexQueryParserService queryParser = queryParser();
|
||||
Query parsedQuery = queryParser.parse(functionScoreQuery(termQuery("name.last", "banon")).add(new FactorBuilder().boostFactor(1.3f))).query();
|
||||
assertThat(parsedQuery, instanceOf(FunctionScoreQuery.class));
|
||||
FunctionScoreQuery functionScoreQuery = (FunctionScoreQuery) parsedQuery;
|
||||
assertThat(((TermQuery) functionScoreQuery.getSubQuery()).getTerm(), equalTo(new Term("name.last", "banon")));
|
||||
assertThat((double) ((BoostScoreFunction) functionScoreQuery.getFunction()).getBoost(), closeTo(1.3, 0.001));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCustomBoostFactorQuery() throws IOException {
|
||||
IndexQueryParserService queryParser = queryParser();
|
||||
|
@ -2225,4 +2241,23 @@ public class SimpleIndexQueryParserTests {
|
|||
assertThat(ectQuery.getHighFreqMinimumNumberShouldMatch(), nullValue());
|
||||
assertThat(ectQuery.getLowFreqMinimumNumberShouldMatch(), equalTo("2"));
|
||||
}
|
||||
|
||||
@Test(expected = QueryParsingException.class)
|
||||
public void assureMalformedThrowsException() throws IOException {
|
||||
IndexQueryParserService queryParser;
|
||||
queryParser = queryParser();
|
||||
String query;
|
||||
query = copyToStringFromClasspath("/org/elasticsearch/test/unit/index/query/faulty-function-score-query.json");
|
||||
Query parsedQuery = queryParser.parse(query).query();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterParsing() throws IOException {
|
||||
IndexQueryParserService queryParser;
|
||||
queryParser = queryParser();
|
||||
String query;
|
||||
query = copyToStringFromClasspath("/org/elasticsearch/test/unit/index/query/function-filter-score-query.json");
|
||||
Query parsedQuery = queryParser.parse(query).query();
|
||||
assertThat((double)(parsedQuery.getBoost()), Matchers.closeTo(3.0, 1.e-7));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"function_score":{
|
||||
"query":{
|
||||
"term":{
|
||||
"name.last":"banon"
|
||||
}
|
||||
},
|
||||
"functions": {
|
||||
{
|
||||
"boost_factor" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
|
||||
{
|
||||
"function_score":{
|
||||
"query":{
|
||||
"term":{
|
||||
"name.last":"banon"
|
||||
}
|
||||
},
|
||||
"functions": [
|
||||
{
|
||||
"boost_factor": 3,
|
||||
"filter": {
|
||||
term:{
|
||||
"name.last":"banon"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"boost_factor": 3
|
||||
},
|
||||
{
|
||||
"boost_factor": 3
|
||||
}
|
||||
],
|
||||
"boost" : 3,
|
||||
"score_mode" : "avg",
|
||||
"max_boost" : 10
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@ import org.elasticsearch.index.codec.CodecModule;
|
|||
import org.elasticsearch.index.engine.IndexEngineModule;
|
||||
import org.elasticsearch.index.query.IndexQueryParserModule;
|
||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreModule;
|
||||
import org.elasticsearch.index.settings.IndexSettingsModule;
|
||||
import org.elasticsearch.index.similarity.SimilarityModule;
|
||||
import org.elasticsearch.indices.query.IndicesQueriesModule;
|
||||
|
@ -77,6 +78,7 @@ public class IndexQueryParserModuleTests {
|
|||
new SimilarityModule(settings),
|
||||
new IndexQueryParserModule(settings),
|
||||
new IndexNameModule(index),
|
||||
new FunctionScoreModule(),
|
||||
new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.elasticsearch.index.codec.CodecModule;
|
|||
import org.elasticsearch.index.engine.IndexEngineModule;
|
||||
import org.elasticsearch.index.query.IndexQueryParserModule;
|
||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreModule;
|
||||
import org.elasticsearch.index.settings.IndexSettingsModule;
|
||||
import org.elasticsearch.index.similarity.SimilarityModule;
|
||||
import org.elasticsearch.indices.query.IndicesQueriesModule;
|
||||
|
@ -75,6 +76,7 @@ public class IndexQueryParserPlugin2Tests {
|
|||
new SimilarityModule(settings),
|
||||
queryParserModule,
|
||||
new IndexNameModule(index),
|
||||
new FunctionScoreModule(),
|
||||
new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.elasticsearch.index.codec.CodecModule;
|
|||
import org.elasticsearch.index.engine.IndexEngineModule;
|
||||
import org.elasticsearch.index.query.IndexQueryParserModule;
|
||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreModule;
|
||||
import org.elasticsearch.index.settings.IndexSettingsModule;
|
||||
import org.elasticsearch.index.similarity.SimilarityModule;
|
||||
import org.elasticsearch.indices.query.IndicesQueriesModule;
|
||||
|
@ -84,6 +85,7 @@ public class IndexQueryParserPluginTests {
|
|||
queryParserModule,
|
||||
new IndexNameModule(index),
|
||||
new CodecModule(settings),
|
||||
new FunctionScoreModule(),
|
||||
new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
|
Loading…
Reference in New Issue