Skip to content

Commit

Permalink
function_score: use query and filter together
Browse files Browse the repository at this point in the history
Before, if filter and query was defined for function_score, then the
filter was silently ignored. Now, if both is defined then function score
query wraps this in a filtered_query.

closes elastic#8638
  • Loading branch information
brwe committed Nov 26, 2014
1 parent eba761e commit 71bea9e
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 3 deletions.
20 changes: 20 additions & 0 deletions src/main/java/org/elasticsearch/index/query/QueryBuilders.java
Expand Up @@ -473,6 +473,26 @@ public static FunctionScoreQueryBuilder functionScoreQuery(FilterBuilder filterB
return new FunctionScoreQueryBuilder(filterBuilder);
}

/**
* A query that allows to define a custom scoring function.
*
* @param queryBuilder The query to custom score
* @param filterBuilder The filterBuilder to custom score
*/
public static FunctionScoreQueryBuilder functionScoreQuery(QueryBuilder queryBuilder, FilterBuilder filterBuilder) {
return new FunctionScoreQueryBuilder(queryBuilder, filterBuilder);
}

/**
* A query that allows to define a custom scoring function.
*
* @param queryBuilder The query to custom score
* @param filterBuilder The filterBuilder to custom score
*/
public static FunctionScoreQueryBuilder functionScoreQuery(QueryBuilder queryBuilder, FilterBuilder filterBuilder, ScoreFunctionBuilder function) {
return (new FunctionScoreQueryBuilder(queryBuilder, filterBuilder)).add(function);
}

/**
* 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.
Expand Down
Expand Up @@ -61,6 +61,11 @@ public FunctionScoreQueryBuilder(FilterBuilder filterBuilder) {
this.queryBuilder = null;
}

public FunctionScoreQueryBuilder(QueryBuilder queryBuilder, FilterBuilder filterBuilder) {
this.filterBuilder = filterBuilder;
this.queryBuilder = queryBuilder;
}

public FunctionScoreQueryBuilder() {
this.filterBuilder = null;
this.queryBuilder = null;
Expand Down Expand Up @@ -130,7 +135,8 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
if (queryBuilder != null) {
builder.field("query");
queryBuilder.toXContent(builder, params);
} else if (filterBuilder != null) {
}
if (filterBuilder != null) {
builder.field("filter");
filterBuilder.toXContent(builder, params);
}
Expand Down
Expand Up @@ -23,6 +23,7 @@
import com.google.common.collect.ImmutableMap.Builder;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.FilteredQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.ParseField;
Expand Down Expand Up @@ -84,6 +85,7 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
XContentParser parser = parseContext.parser();

Query query = null;
Filter filter = null;
float boost = 1.0f;

FiltersFunctionScoreQuery.ScoreMode scoreMode = FiltersFunctionScoreQuery.ScoreMode.Multiply;
Expand All @@ -104,7 +106,7 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
} else if ("query".equals(currentFieldName)) {
query = parseContext.parseInnerQuery();
} else if ("filter".equals(currentFieldName)) {
query = new ConstantScoreQuery(parseContext.parseInnerFilter());
filter = parseContext.parseInnerFilter();
} else if ("score_mode".equals(currentFieldName) || "scoreMode".equals(currentFieldName)) {
scoreMode = parseScoreMode(parseContext, parser);
} else if ("boost_mode".equals(currentFieldName) || "boostMode".equals(currentFieldName)) {
Expand Down Expand Up @@ -144,9 +146,15 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
singleFunctionName = currentFieldName;
}
}
if (query == null) {
if (query == null && filter == null) {
query = Queries.newMatchAllQuery();
}
if (query == null && filter != null) {
query = new ConstantScoreQuery(filter);
}
if (query != null && filter != null) {
query = new FilteredQuery(query, filter);
}
// if all filter elements returned null, just use the query
if (filterFunctions.isEmpty()) {
return query;
Expand Down
Expand Up @@ -20,6 +20,9 @@
package org.elasticsearch.search.functionscore;

import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.geo.GeoPoint;
Expand All @@ -28,11 +31,14 @@
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.weight.WeightBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;

import static org.elasticsearch.client.Requests.searchRequest;
Expand Down Expand Up @@ -433,5 +439,40 @@ public void testScriptScoresWithAgg() throws IOException {
assertThat(((Terms) response.getAggregations().asMap().get("score_agg")).getBuckets().get(0).getKeyAsNumber().floatValue(), is(1f));
assertThat(((Terms) response.getAggregations().asMap().get("score_agg")).getBuckets().get(0).getDocCount(), is(1l));
}

@Test
public void testFilterAndQueryGiven() throws IOException, ExecutionException, InterruptedException {
assertAcked(prepareCreate("test").addMapping(
"type",
jsonBuilder().startObject().startObject("type").startObject("properties")
.startObject("filter_field").field("type", "string").endObject()
.startObject("query_field").field("type", "string").endObject()
.startObject("num").field("type", "float").endObject().endObject().endObject().endObject()));
ensureYellow();

List<IndexRequestBuilder> indexRequests = new ArrayList<>();
for (int i = 0; i < 20; i++) {
indexRequests.add(
client().prepareIndex()
.setType("type")
.setId(Integer.toString(i))
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("query_field", Integer.toString(i % 3)).field("filter_field", Integer.toString(i % 2)).field("num", i).endObject()));
}

indexRandom(true, true, indexRequests);

SearchResponse response = client().search(
searchRequest().source(
searchSource().query(
functionScoreQuery(termQuery("query_field", "0"), termFilter("filter_field", "0"), scriptFunction("doc['num'].value")).boostMode("replace")))).get();

assertSearchResponse(response);
assertThat(response.getHits().totalHits(), equalTo(4l));
for (SearchHit hit : response.getHits().getHits()) {
assertThat(Float.parseFloat(hit.getId()), equalTo(hit.getScore()));
}
}
}

0 comments on commit 71bea9e

Please sign in to comment.