Change the `sort` boolean option in percolate api to the sort dsl available in search api.

Closes #4625
This commit is contained in:
Martijn van Groningen 2014-01-06 14:54:44 +01:00
parent 0973b2863c
commit 7e341cefd0
8 changed files with 107 additions and 24 deletions

View File

@ -193,8 +193,10 @@ occurred for the filter to included the latest percolate queries.
* `track_scores` - Whether the `_score` is included for each match. The is based on the query and represents how the query matched
to the percolate query's metadata and *not* how the document being percolated matched to the query. The `query` option
is required for this option. Defaults to `false`.
* `sort` - Whether the matches should be sorted by the `_score`. Similar to the `score` option, but also sorts the
matches. The `size` and `query` option are required for this option. Defaults to `false`.
* `sort` - Define a sort specification like in the search api. Currently only sorting `_score` reverse (default relevancy)
is supported. Other sort fields will throw an exception. The `size` and `query` option are required for this setting. Like
`track_score` the score is based on the query and represents how the query matched to the percolate query's metadata
and *not* how the document being percolated matched to the query.
* `facets` - Allows facet definitions to be included. The facets are based on the matching percolator queries. See facet
documentation how to define facets.
* `aggs` - Allows aggregation definitions to be included. The aggregations are based on the matching percolator queries,

View File

@ -33,6 +33,7 @@ import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import java.util.Map;
@ -107,13 +108,21 @@ public class PercolateRequestBuilder extends BroadcastOperationRequestBuilder<Pe
}
/**
* Similar as {@link #setScore(boolean)}, but also sort by the score.
* Similar as {@link #setScore(boolean)}, but whether to sort by the score descending.
*/
public PercolateRequestBuilder setSort(boolean sort) {
public PercolateRequestBuilder setSortByScore(boolean sort) {
sourceBuilder().setSort(sort);
return this;
}
/**
* Adds
*/
public PercolateRequestBuilder addSort(SortBuilder sort) {
sourceBuilder().addSort(sort);
return this;
}
/**
* Whether to compute a score for each match and include it in the response. The score is based on
* {@link #setPercolateQuery(QueryBuilder)}}.

View File

@ -31,6 +31,8 @@ import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilderException;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import java.io.IOException;
import java.util.HashMap;
@ -47,6 +49,7 @@ public class PercolateSourceBuilder implements ToXContent {
private FilterBuilder filterBuilder;
private Integer size;
private Boolean sort;
private List<SortBuilder> sorts;
private Boolean trackScores;
private HighlightBuilder highlightBuilder;
private List<FacetBuilder> facets;
@ -105,10 +108,25 @@ public class PercolateSourceBuilder implements ToXContent {
}
/**
* Similar as {@link #setTrackScores(boolean)}, but also sort by the score.
* Similar as {@link #setTrackScores(boolean)}, but whether to sort by the score descending.
*/
public PercolateSourceBuilder setSort(boolean sort) {
this.sort = sort;
if (sort) {
addSort(new ScoreSortBuilder());
} else {
this.sorts = null;
}
return this;
}
/**
* Adds a sort builder. Only sorting by score desc is supported.
*/
public PercolateSourceBuilder addSort(SortBuilder sort) {
if (sorts == null) {
sorts = Lists.newArrayList();
}
sorts.add(sort);
return this;
}
@ -178,8 +196,14 @@ public class PercolateSourceBuilder implements ToXContent {
if (size != null) {
builder.field("size", size);
}
if (sort != null) {
builder.field("sort", sort);
if (sorts != null) {
builder.startArray("sort");
for (SortBuilder sort : sorts) {
builder.startObject();
sort.toXContent(builder, params);
builder.endObject();
}
builder.endArray();
}
if (trackScores != null) {
builder.field("track_scores", trackScores);

View File

@ -84,7 +84,7 @@ public class PercolateContext extends SearchContext {
public boolean limit;
public int size;
public boolean sort;
public boolean doSort;
public byte percolatorTypeId;
private boolean trackScores;
@ -112,6 +112,7 @@ public class PercolateContext extends SearchContext {
private SearchContextFacets facets;
private SearchContextAggregations aggregations;
private QuerySearchResult querySearchResult;
private Sort sort;
public PercolateContext(PercolateShardRequest request, SearchShardTarget searchShardTarget, IndexShard indexShard, IndexService indexService, CacheRecycler cacheRecycler, PageCacheRecycler pageCacheRecycler) {
this.request = request;
@ -518,12 +519,13 @@ public class PercolateContext extends SearchContext {
@Override
public SearchContext sort(Sort sort) {
throw new UnsupportedOperationException();
this.sort = sort;
return this;
}
@Override
public Sort sort() {
throw new UnsupportedOperationException();
return sort;
}
@Override

View File

@ -88,6 +88,7 @@ import org.elasticsearch.search.highlight.HighlightField;
import org.elasticsearch.search.highlight.HighlightPhase;
import org.elasticsearch.search.highlight.SearchContextHighlight;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.sort.SortParseElement;
import java.io.IOException;
import java.util.ArrayList;
@ -114,6 +115,7 @@ public class PercolatorService extends AbstractComponent {
private final FacetPhase facetPhase;
private final HighlightPhase highlightPhase;
private final AggregationPhase aggregationPhase;
private final SortParseElement sortParseElement;
@Inject
public PercolatorService(Settings settings, IndicesService indicesService, CacheRecycler cacheRecycler, PageCacheRecycler pageCacheRecycler,
@ -127,6 +129,7 @@ public class PercolatorService extends AbstractComponent {
this.highlightPhase = highlightPhase;
this.facetPhase = facetPhase;
this.aggregationPhase = aggregationPhase;
this.sortParseElement = new SortParseElement();
final long maxReuseBytes = settings.getAsBytesSize("indices.memory.memory_index.size_per_thread", new ByteSizeValue(1, ByteSizeUnit.MB)).bytes();
cache = new CloseableThreadLocal<MemoryIndex>() {
@ -176,11 +179,11 @@ public class PercolatorService extends AbstractComponent {
throw new ElasticsearchIllegalArgumentException("Nothing to percolate");
}
if (context.percolateQuery() == null && (context.trackScores() || context.sort || context.facets() != null || context.aggregations() != null)) {
if (context.percolateQuery() == null && (context.trackScores() || context.doSort || context.facets() != null || context.aggregations() != null)) {
context.percolateQuery(new MatchAllDocsQuery());
}
if (context.sort && !context.limit) {
if (context.doSort && !context.limit) {
throw new ElasticsearchIllegalArgumentException("Can't sort if size isn't specified");
}
@ -214,7 +217,7 @@ public class PercolatorService extends AbstractComponent {
if (request.onlyCount()) {
action = context.percolateQuery() != null ? queryCountPercolator : countPercolator;
} else {
if (context.sort) {
if (context.doSort) {
action = topMatchingPercolator;
} else if (context.percolateQuery() != null) {
action = context.trackScores() ? scoringPercolator : queryPercolator;
@ -293,9 +296,15 @@ public class PercolatorService extends AbstractComponent {
}
Filter filter = documentIndexService.queryParserService().parseInnerFilter(parser).filter();
context.percolateQuery(new XConstantScoreQuery(filter));
} else if ("sort".equals(currentFieldName)) {
parseSort(parser, context);
} else if (element != null) {
element.parse(parser, context);
}
} else if (token == XContentParser.Token.START_ARRAY) {
if ("sort".equals(currentFieldName)) {
parseSort(parser, context);
}
} else if (token == null) {
break;
} else if (token.isValue()) {
@ -306,7 +315,7 @@ public class PercolatorService extends AbstractComponent {
throw new ElasticsearchParseException("size is set to [" + context.size + "] and is expected to be higher or equal to 0");
}
} else if ("sort".equals(currentFieldName)) {
context.sort = parser.booleanValue();
parseSort(parser, context);
} else if ("track_scores".equals(currentFieldName) || "trackScores".equals(currentFieldName)) {
context.trackScores(parser.booleanValue());
}
@ -359,6 +368,16 @@ public class PercolatorService extends AbstractComponent {
return doc;
}
private void parseSort(XContentParser parser, PercolateContext context) throws Exception {
sortParseElement.parse(parser, context);
// null, means default sorting by relevancy
if (context.sort() == null) {
context.doSort = true;
} else {
throw new ElasticsearchParseException("Only _score desc is supported");
}
}
private ParsedDocument parseFetchedDoc(BytesReference fetchedDoc, IndexService documentIndexService, String type) {
ParsedDocument doc = null;
XContentParser parser = null;

View File

@ -47,7 +47,7 @@ import java.util.List;
*/
public class SortParseElement implements SearchParseElement {
private static final SortField SORT_SCORE = new SortField(null, SortField.Type.SCORE);
public static final SortField SORT_SCORE = new SortField(null, SortField.Type.SCORE);
private static final SortField SORT_SCORE_REVERSE = new SortField(null, SortField.Type.SCORE, true);
private static final SortField SORT_DOC = new SortField(null, SortField.Type.DOC);
private static final SortField SORT_DOC_REVERSE = new SortField(null, SortField.Type.DOC, true);

View File

@ -91,7 +91,7 @@ public class PercolatorFacetsAndAggregationsTests extends ElasticsearchIntegrati
if (randomBoolean()) {
percolateRequestBuilder.setScore(true);
} else {
percolateRequestBuilder.setSort(true).setSize(numQueries);
percolateRequestBuilder.setSortByScore(true).setSize(numQueries);
}
boolean countOnly = randomBoolean();

View File

@ -42,7 +42,9 @@ import org.elasticsearch.index.engine.VersionConflictEngineException;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.factor.FactorBuilder;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
@ -1181,7 +1183,7 @@ public class PercolatorTests extends ElasticsearchIntegrationTest {
for (int i = 0; i < runs; i++) {
int size = randomIntBetween(1, 10);
PercolateResponse response = client().preparePercolate().setIndices("my-index").setDocumentType("my-type")
.setSort(true)
.setSortByScore(true)
.setSize(size)
.setPercolateDoc(docBuilder().setDoc("field", "value"))
.setPercolateQuery(QueryBuilders.functionScoreQuery(matchAllQuery(), scriptFunction("doc['level'].value")))
@ -1205,7 +1207,7 @@ public class PercolatorTests extends ElasticsearchIntegrationTest {
NavigableSet<Integer> levels = controlMap.get(value);
int size = randomIntBetween(1, levels.size());
PercolateResponse response = client().preparePercolate().setIndices("my-index").setDocumentType("my-type")
.setSort(true)
.setSortByScore(true)
.setSize(size)
.setPercolateDoc(docBuilder().setDoc("field", "value"))
.setPercolateQuery(QueryBuilders.functionScoreQuery(matchQuery("field1", value), scriptFunction("doc['level'].value")))
@ -1237,7 +1239,7 @@ public class PercolatorTests extends ElasticsearchIntegrationTest {
refresh();
PercolateResponse response = client().preparePercolate().setIndices("my-index").setDocumentType("my-type")
.setSort(true)
.setSortByScore(true)
.setSize(2)
.setPercolateDoc(docBuilder().setDoc("field", "value"))
.setPercolateQuery(QueryBuilders.functionScoreQuery(matchAllQuery(), scriptFunction("doc['level'].value")))
@ -1249,7 +1251,7 @@ public class PercolatorTests extends ElasticsearchIntegrationTest {
assertThat(response.getMatches()[1].getScore(), equalTo(1f));
response = client().preparePercolate().setIndices("my-index").setDocumentType("my-type")
.setSort(true)
.setSortByScore(true)
.setPercolateDoc(docBuilder().setDoc("field", "value"))
.setPercolateQuery(QueryBuilders.functionScoreQuery(matchAllQuery(), scriptFunction("doc['level'].value")))
.execute().actionGet();
@ -1262,13 +1264,38 @@ public class PercolatorTests extends ElasticsearchIntegrationTest {
assertThat(response.getShardFailures()[1].reason(), containsString("Can't sort if size isn't specified"));
}
@Test
public void testPercolateSorting_unsupportedField() throws Exception {
client().admin().indices().prepareCreate("my-index").execute().actionGet();
ensureGreen();
client().prepareIndex("my-index", PercolatorService.TYPE_NAME, "1")
.setSource(jsonBuilder().startObject().field("query", matchAllQuery()).field("level", 1).endObject())
.execute().actionGet();
client().prepareIndex("my-index", PercolatorService.TYPE_NAME, "2")
.setSource(jsonBuilder().startObject().field("query", matchAllQuery()).field("level", 2).endObject())
.execute().actionGet();
refresh();
PercolateResponse response = client().preparePercolate().setIndices("my-index").setDocumentType("my-type")
.setSize(2)
.setPercolateDoc(docBuilder().setDoc("field", "value"))
.setPercolateQuery(QueryBuilders.functionScoreQuery(matchAllQuery(), scriptFunction("doc['level'].value")))
.addSort(SortBuilders.fieldSort("level"))
.get();
assertThat(response.getShardFailures().length, equalTo(5));
assertThat(response.getShardFailures()[0].status(), equalTo(RestStatus.BAD_REQUEST));
assertThat(response.getShardFailures()[0].reason(), containsString("Only _score desc is supported"));
}
@Test
public void testPercolateOnEmptyIndex() throws Exception {
client().admin().indices().prepareCreate("my-index").execute().actionGet();
ensureGreen();
PercolateResponse response = client().preparePercolate().setIndices("my-index").setDocumentType("my-type")
.setSort(true)
.setSortByScore(true)
.setSize(2)
.setPercolateDoc(docBuilder().setDoc("field", "value"))
.setPercolateQuery(QueryBuilders.functionScoreQuery(matchAllQuery(), scriptFunction("doc['level'].value")))
@ -1288,7 +1315,7 @@ public class PercolatorTests extends ElasticsearchIntegrationTest {
.execute().actionGet();
PercolateResponse response = client().preparePercolate().setIndices("my-index").setDocumentType("my-type")
.setSort(true)
.setSortByScore(true)
.setSize(2)
.setPercolateDoc(docBuilder().setDoc("field", "value"))
.setPercolateQuery(QueryBuilders.functionScoreQuery(matchAllQuery(), scriptFunction("doc['level'].value")))
@ -1449,7 +1476,7 @@ public class PercolatorTests extends ElasticsearchIntegrationTest {
.setPercolateDoc(docBuilder().setDoc(jsonBuilder().startObject().field("field1", "The quick brown fox jumps over the lazy dog").endObject()))
.setHighlightBuilder(new HighlightBuilder().field("field1"))
.setPercolateQuery(functionScoreQuery(matchAllQuery()).add(new FactorBuilder().boostFactor(5.5f)))
.setSort(true)
.setSortByScore(true)
.execute().actionGet();
assertMatchCount(response, 5l);
assertThat(response.getMatches(), arrayWithSize(5));