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:
Britta Weber 2013-07-26 19:09:18 +02:00
parent e1c739fe6f
commit 720b550a94
36 changed files with 1882 additions and 218 deletions

View File

@ -49,7 +49,7 @@ public class BoostScoreFunction implements ScoreFunction {
}
@Override
public float factor(int docId) {
public double factor(int docId) {
return boost;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,15 @@
{
"function_score":{
"query":{
"term":{
"name.last":"banon"
}
},
"functions": {
{
"boost_factor" : 3
}
}
}
}
}

View File

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

View File

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

View File

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

View File

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