Added `filter` support to `custom_score` query.

Closes #3167
This commit is contained in:
Martijn van Groningen 2013-06-12 22:41:49 +02:00
parent dc0d81b8aa
commit a2de34eead
4 changed files with 60 additions and 13 deletions

View File

@ -26,14 +26,14 @@ import java.io.IOException;
import java.util.Map;
/**
* A query that uses a script to compute the score.
*
*
* A query that uses a script to compute or influence the score of documents that match with the inner query or filter.
*/
public class CustomScoreQueryBuilder extends BaseQueryBuilder implements BoostableQueryBuilder<CustomScoreQueryBuilder> {
private final QueryBuilder queryBuilder;
private final FilterBuilder filterBuilder;
private String script;
private String lang;
@ -43,12 +43,24 @@ public class CustomScoreQueryBuilder extends BaseQueryBuilder implements Boostab
private Map<String, Object> params = null;
/**
* A query that simply applies the boost factor to another query (multiply it).
* 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 to apply the boost factor to.
* @param queryBuilder The query that defines what documents are custom scored by this query
*/
public CustomScoreQueryBuilder(QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
this.filterBuilder = null;
}
/**
* 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;
this.queryBuilder = null;
}
/**
@ -102,8 +114,13 @@ public class CustomScoreQueryBuilder extends BaseQueryBuilder implements Boostab
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(CustomScoreQueryParser.NAME);
builder.field("query");
queryBuilder.toXContent(builder, params);
if (queryBuilder != null) {
builder.field("query");
queryBuilder.toXContent(builder, params);
} else if (filterBuilder != null) {
builder.field("filter");
filterBuilder.toXContent(builder, params);
}
builder.field("script", script);
if (lang != null) {
builder.field("lang", lang);

View File

@ -20,8 +20,9 @@
package org.elasticsearch.index.query;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.ConstantScoreQuery;
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;
@ -55,7 +56,8 @@ public class CustomScoreQueryParser implements QueryParser {
XContentParser parser = parseContext.parser();
Query query = null;
boolean queryFound = false;
Filter filter = null;
boolean queryOrFilterFound = false;
float boost = 1.0f;
String script = null;
String scriptLang = null;
@ -69,7 +71,10 @@ public class CustomScoreQueryParser implements QueryParser {
} else if (token == XContentParser.Token.START_OBJECT) {
if ("query".equals(currentFieldName)) {
query = parseContext.parseInnerQuery();
queryFound = true;
queryOrFilterFound = true;
} else if ("filter".equals(currentFieldName)) {
filter = parseContext.parseInnerFilter();
queryOrFilterFound = true;
} else if ("params".equals(currentFieldName)) {
vars = parser.map();
} else {
@ -87,14 +92,16 @@ public class CustomScoreQueryParser implements QueryParser {
}
}
}
if (!queryFound) {
throw new QueryParsingException(parseContext.index(), "[custom_score] requires 'query' field");
if (!queryOrFilterFound) {
throw new QueryParsingException(parseContext.index(), "[custom_score] requires 'query' or 'filter' field");
}
if (script == null) {
throw new QueryParsingException(parseContext.index(), "[custom_score] requires 'script' field");
}
if (query == null) {
if (query == null && filter == null) {
return null;
} else if (filter != null) {
query = new ConstantScoreQuery(filter);
}
SearchScript searchScript;

View File

@ -520,6 +520,16 @@ public abstract class QueryBuilders {
return new CustomScoreQueryBuilder(queryBuilder);
}
/**
* A query that allows to define a custom scoring script, that defines the score for each document that match
* with the specified filter.
*
* @param filterBuilder The filter that defines which documents are scored by a script.
*/
public static CustomScoreQueryBuilder customScoreQuery(FilterBuilder filterBuilder) {
return new CustomScoreQueryBuilder(filterBuilder);
}
public static CustomFiltersScoreQueryBuilder customFiltersScoreQuery(QueryBuilder queryBuilder) {
return new CustomFiltersScoreQueryBuilder(queryBuilder);
}

View File

@ -309,6 +309,19 @@ public class CustomScoreSearchTests extends AbstractSharedClusterTest {
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 param1 * param2 * _score with filter instead of query");
response = client().search(searchRequest()
.searchType(SearchType.QUERY_THEN_FETCH)
.source(searchSource().explain(true).query(customScoreQuery(termFilter("test", "value")).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());
assertThat(response.getHits().getAt(0).id(), equalTo("1"));
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