From ff8d743337e548ec268f9f2dc1923fc2ce7a05fd Mon Sep 17 00:00:00 2001 From: kimchy Date: Sun, 8 Aug 2010 21:46:11 +0300 Subject: [PATCH] add geo distance sort option, refactor java level sort API to have sort builders (similar to facets) --- .../action/search/SearchRequestBuilder.java | 27 +-- .../lucene/geo/GeoDistanceDataComparator.java | 183 ++++++++++++++++++ .../rest/action/search/RestSearchAction.java | 7 +- .../search/builder/SearchSourceBuilder.java | 144 ++------------ .../geodistance/GeoDistanceFacetBuilder.java | 2 +- .../search/query/SortParseElement.java | 173 ++++++++++++++--- .../search/sort/FieldSortBuilder.java | 51 +++++ .../search/sort/GeoDistanceSortBuilder.java | 109 +++++++++++ .../search/sort/ScoreSortBuilder.java | 45 +++++ .../search/sort/ScriptSortBuilder.java | 72 +++++++ .../search/sort/SortBuilder.java | 30 +++ .../search/sort/SortBuilders.java | 42 ++++ .../elasticsearch/search/sort/SortOrder.java | 28 +++ .../TransportTwoServersSearchTests.java | 5 +- .../TwoInstanceEmbeddedSearchTests.java | 5 +- ...ceUnbalancedShardsEmbeddedSearchTests.java | 5 +- .../search/geo/GeoDistanceTests.java | 32 +++ .../scriptfield/ScriptFieldSearchTests.java | 6 +- .../scriptfilter/ScriptFilterSearchTests.java | 8 +- .../search/sort/SimpleSortTests.java | 29 +-- 20 files changed, 798 insertions(+), 205 deletions(-) create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/common/lucene/geo/GeoDistanceDataComparator.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/GeoDistanceSortBuilder.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/ScoreSortBuilder.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/ScriptSortBuilder.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortBuilder.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortBuilders.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortOrder.java diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/client/action/search/SearchRequestBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/client/action/search/SearchRequestBuilder.java index 29620d37b41..39edf32c6ee 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/client/action/search/SearchRequestBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/client/action/search/SearchRequestBuilder.java @@ -33,8 +33,9 @@ import org.elasticsearch.search.Scroll; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.facets.AbstractFacetBuilder; import org.elasticsearch.search.highlight.HighlightBuilder; +import org.elasticsearch.search.sort.SortBuilder; +import org.elasticsearch.search.sort.SortOrder; -import javax.annotation.Nullable; import java.util.Map; /** @@ -262,32 +263,18 @@ public class SearchRequestBuilder extends BaseRequestBuilder params) { - sourceBuilder().sortScript(script, type, order, params); + public SearchRequestBuilder addSort(SortBuilder sort) { + sourceBuilder().sort(sort); return this; } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/common/lucene/geo/GeoDistanceDataComparator.java b/modules/elasticsearch/src/main/java/org/elasticsearch/common/lucene/geo/GeoDistanceDataComparator.java new file mode 100644 index 00000000000..44351ffa4d8 --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/common/lucene/geo/GeoDistanceDataComparator.java @@ -0,0 +1,183 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.geo; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.FieldComparator; +import org.apache.lucene.search.FieldComparatorSource; +import org.elasticsearch.ElasticSearchIllegalArgumentException; +import org.elasticsearch.common.unit.DistanceUnit; +import org.elasticsearch.index.cache.field.data.FieldDataCache; +import org.elasticsearch.index.field.data.FieldData; +import org.elasticsearch.index.field.data.NumericFieldData; +import org.elasticsearch.index.mapper.FieldMapper; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.mapper.xcontent.XContentGeoPointFieldMapper; + +import java.io.IOException; + +/** + * @author kimchy (shay.banon) + */ +// LUCENE MONITOR: Monitor against FieldComparator.Double +public class GeoDistanceDataComparator extends FieldComparator { + + public static FieldComparatorSource comparatorSource(String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance, + FieldDataCache fieldDataCache, MapperService mapperService) { + return new InnerSource(fieldName, lat, lon, unit, geoDistance, fieldDataCache, mapperService); + } + + private static class InnerSource extends FieldComparatorSource { + + protected final String fieldName; + + protected final double lat; + + protected final double lon; + + protected final DistanceUnit unit; + + protected final GeoDistance geoDistance; + + protected final FieldDataCache fieldDataCache; + + private final MapperService mapperService; + + private InnerSource(String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance, + FieldDataCache fieldDataCache, MapperService mapperService) { + this.fieldName = fieldName; + this.lat = lat; + this.lon = lon; + this.unit = unit; + this.geoDistance = geoDistance; + this.fieldDataCache = fieldDataCache; + this.mapperService = mapperService; + } + + @Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException { + return new GeoDistanceDataComparator(numHits, fieldname, lat, lon, unit, geoDistance, fieldDataCache, mapperService); + } + } + + protected final String fieldName; + + protected final String indexLatFieldName; + + protected final String indexLonFieldName; + + protected final double lat; + + protected final double lon; + + protected final DistanceUnit unit; + + protected final GeoDistance geoDistance; + + protected final FieldDataCache fieldDataCache; + + protected final FieldData.Type fieldDataType; + + protected NumericFieldData latFieldData; + + protected NumericFieldData lonFieldData; + + + private final double[] values; + private double bottom; + + public GeoDistanceDataComparator(int numHits, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance, + FieldDataCache fieldDataCache, MapperService mapperService) { + values = new double[numHits]; + + this.fieldName = fieldName; + this.lat = lat; + this.lon = lon; + this.unit = unit; + this.geoDistance = geoDistance; + this.fieldDataCache = fieldDataCache; + + FieldMapper mapper = mapperService.smartNameFieldMapper(fieldName + XContentGeoPointFieldMapper.Names.LAT_SUFFIX); + if (mapper == null) { + throw new ElasticSearchIllegalArgumentException("No mapping found for field [" + fieldName + "] for geo distance sort"); + } + this.indexLatFieldName = mapper.names().indexName(); + + mapper = mapperService.smartNameFieldMapper(fieldName + XContentGeoPointFieldMapper.Names.LON_SUFFIX); + if (mapper == null) { + throw new ElasticSearchIllegalArgumentException("No mapping found for field [" + fieldName + "] for geo distance sort"); + } + this.indexLonFieldName = mapper.names().indexName(); + this.fieldDataType = mapper.fieldDataType(); + } + + @Override public void setNextReader(IndexReader reader, int docBase) throws IOException { + latFieldData = (NumericFieldData) fieldDataCache.cache(fieldDataType, reader, indexLatFieldName); + lonFieldData = (NumericFieldData) fieldDataCache.cache(fieldDataType, reader, indexLonFieldName); + } + + @Override public int compare(int slot1, int slot2) { + final double v1 = values[slot1]; + final double v2 = values[slot2]; + if (v1 > v2) { + return 1; + } else if (v1 < v2) { + return -1; + } else { + return 0; + } + } + + @Override public int compareBottom(int doc) { + double distance; + if (!latFieldData.hasValue(doc) || !lonFieldData.hasValue(doc)) { + // is this true? push this to the "end" + distance = Double.MAX_VALUE; + } else { + distance = geoDistance.calculate(lat, lon, latFieldData.doubleValue(doc), lonFieldData.doubleValue(doc), unit); + } + final double v2 = distance; + if (bottom > v2) { + return 1; + } else if (bottom < v2) { + return -1; + } else { + return 0; + } + } + + @Override public void copy(int slot, int doc) { + double distance; + if (!latFieldData.hasValue(doc) || !lonFieldData.hasValue(doc)) { + // is this true? push this to the "end" + distance = Double.MAX_VALUE; + } else { + distance = geoDistance.calculate(lat, lon, latFieldData.doubleValue(doc), lonFieldData.doubleValue(doc), unit); + } + values[slot] = distance; + } + + @Override public void setBottom(final int bottom) { + this.bottom = values[bottom]; + } + + @Override public Comparable value(int slot) { + return Double.valueOf(values[slot]); + } +} diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java b/modules/elasticsearch/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java index 8239f5de608..d151a5bbd58 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java @@ -34,6 +34,7 @@ import org.elasticsearch.rest.*; import org.elasticsearch.rest.action.support.RestActions; import org.elasticsearch.search.Scroll; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.sort.SortOrder; import java.io.IOException; import java.util.regex.Pattern; @@ -191,11 +192,9 @@ public class RestSearchAction extends BaseRestHandler { String sortField = sort.substring(0, delimiter); String reverse = sort.substring(delimiter + 1); if ("asc".equals(reverse)) { - searchSourceBuilder.sort(sortField, SearchSourceBuilder.Order.ASC); + searchSourceBuilder.sort(sortField, SortOrder.ASC); } else if ("desc".equals(reverse)) { - searchSourceBuilder.sort(sortField, SearchSourceBuilder.Order.DESC); - } else { - searchSourceBuilder.sort(sortField, reverse.equals("reverse")); + searchSourceBuilder.sort(sortField, SortOrder.DESC); } } else { searchSourceBuilder.sort(sort); diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java index dd791d0f1be..835172bec91 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java @@ -31,16 +31,15 @@ import org.elasticsearch.common.xcontent.builder.XContentBuilder; import org.elasticsearch.index.query.xcontent.XContentQueryBuilder; import org.elasticsearch.search.facets.AbstractFacetBuilder; import org.elasticsearch.search.highlight.HighlightBuilder; -import org.elasticsearch.search.query.SortParseElement; +import org.elasticsearch.search.sort.SortBuilder; +import org.elasticsearch.search.sort.SortBuilders; +import org.elasticsearch.search.sort.SortOrder; -import javax.annotation.Nullable; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; -import static org.elasticsearch.common.collect.Lists.*; - /** * A search source builder allowing to easily build search source. Simple construction * using {@link org.elasticsearch.search.builder.SearchSourceBuilder#searchSource()}. @@ -50,11 +49,6 @@ import static org.elasticsearch.common.collect.Lists.*; */ public class SearchSourceBuilder implements ToXContent { - public static enum Order { - ASC, - DESC - } - /** * A static factory method to construct a new search source. */ @@ -81,9 +75,7 @@ public class SearchSourceBuilder implements ToXContent { private Boolean explain; - private List sortFields; - - private List sortScripts; + private List sorts; private List fieldNames; @@ -159,18 +151,8 @@ public class SearchSourceBuilder implements ToXContent { * @param name The name of the field * @param order The sort ordering */ - public SearchSourceBuilder sort(String name, Order order) { - boolean reverse = false; - if (name.equals(SortParseElement.SCORE_FIELD_NAME)) { - if (order == Order.ASC) { - reverse = true; - } - } else { - if (order == Order.DESC) { - reverse = true; - } - } - return sort(name, reverse); + public SearchSourceBuilder sort(String name, SortOrder order) { + return sort(SortBuilders.fieldSort(name).order(order)); } /** @@ -179,36 +161,17 @@ public class SearchSourceBuilder implements ToXContent { * @param name The name of the field to sort by */ public SearchSourceBuilder sort(String name) { - return sort(name, false); + return sort(SortBuilders.fieldSort(name)); } /** - * Adds a sort script. - * - * @param script The script to execute. - * @param type The type of the result (can either be "string" or "number"). - * @param order The order. - * @param params Optional parameters to the script. + * Adds a sort builder. */ - public SearchSourceBuilder sortScript(String script, String type, Order order, @Nullable Map params) { - if (sortScripts == null) { - sortScripts = Lists.newArrayList(); + public SearchSourceBuilder sort(SortBuilder sort) { + if (sorts == null) { + sorts = Lists.newArrayList(); } - sortScripts.add(new ScriptSortTuple(script, type, params, order == Order.DESC)); - return this; - } - - /** - * Add a sort against the given field name and if it should be revered or not. - * - * @param name The name of the field to sort by - * @param reverse Should the sort be reversed or not - */ - public SearchSourceBuilder sort(String name, boolean reverse) { - if (sortFields == null) { - sortFields = newArrayListWithCapacity(2); - } - sortFields.add(new SortTuple(name, reverse)); + sorts.add(sort); return this; } @@ -378,35 +341,14 @@ public class SearchSourceBuilder implements ToXContent { builder.endObject(); } - if (sortFields != null || sortScripts != null) { - builder.field("sort"); - builder.startObject(); - if (sortFields != null) { - for (SortTuple sortTuple : sortFields) { - builder.field(sortTuple.fieldName()); - builder.startObject(); - if (sortTuple.reverse()) { - builder.field("reverse", true); - } - builder.endObject(); - } + if (sorts != null) { + builder.startArray("sort"); + for (SortBuilder sort : sorts) { + builder.startObject(); + sort.toXContent(builder, params); + builder.endObject(); } - if (sortScripts != null) { - for (ScriptSortTuple scriptSort : sortScripts) { - builder.startObject("_script"); - builder.field("script", scriptSort.script()); - builder.field("type", scriptSort.type()); - if (scriptSort.params() != null) { - builder.field("params"); - builder.map(scriptSort.params()); - } - if (scriptSort.reverse()) { - builder.field("reverse", true); - } - builder.endObject(); - } - } - builder.endObject(); + builder.endArray(); } if (indexBoost != null) { @@ -457,52 +399,4 @@ public class SearchSourceBuilder implements ToXContent { return params; } } - - private static class SortTuple { - private final String fieldName; - private final boolean reverse; - - private SortTuple(String fieldName, boolean reverse) { - this.fieldName = fieldName; - this.reverse = reverse; - } - - public String fieldName() { - return fieldName; - } - - public boolean reverse() { - return reverse; - } - } - - private static class ScriptSortTuple { - private final String script; - private final String type; - private final Map params; - private final boolean reverse; - - private ScriptSortTuple(String script, String type, Map params, boolean reverse) { - this.script = script; - this.type = type; - this.params = params; - this.reverse = reverse; - } - - public String script() { - return script; - } - - public String type() { - return type; - } - - public Map params() { - return params; - } - - public boolean reverse() { - return reverse; - } - } } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetBuilder.java index 3ad8afb192c..8c97564968a 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetBuilder.java @@ -140,7 +140,7 @@ public class GeoDistanceFacetBuilder extends AbstractFacetBuilder { } /** - * The geo distance type used to compute the distnace. + * The geo distance type used to compute the distance. */ public GeoDistanceFacetBuilder geoDistance(GeoDistance geoDistance) { this.geoDistance = geoDistance; diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/query/SortParseElement.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/query/SortParseElement.java index 00d82b3b012..a772dec1f61 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/query/SortParseElement.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/query/SortParseElement.java @@ -23,12 +23,17 @@ import org.apache.lucene.search.FieldComparatorSource; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.elasticsearch.common.collect.Lists; +import org.elasticsearch.common.lucene.geo.GeoDistance; +import org.elasticsearch.common.lucene.geo.GeoDistanceDataComparator; +import org.elasticsearch.common.lucene.geo.GeoHashUtils; +import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.field.function.FieldsFunction; import org.elasticsearch.index.field.function.script.ScriptFieldsFunction; import org.elasticsearch.index.field.function.sort.DoubleFieldsFunctionDataComparator; import org.elasticsearch.index.field.function.sort.StringFieldsFunctionDataComparator; import org.elasticsearch.index.mapper.FieldMapper; +import org.elasticsearch.index.mapper.xcontent.XContentGeoPointFieldMapper; import org.elasticsearch.search.SearchParseElement; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.internal.SearchContext; @@ -50,6 +55,7 @@ public class SortParseElement implements SearchParseElement { public static final String SCRIPT_FIELD_NAME = "_script"; public static final String SCORE_FIELD_NAME = "_score"; public static final String DOC_FIELD_NAME = "_doc"; + public static final String GEO_DISTANCE_FIELD_NAME = "_geo_distance"; public SortParseElement() { } @@ -60,6 +66,7 @@ public class SortParseElement implements SearchParseElement { if (token == XContentParser.Token.START_ARRAY) { while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { if (token == XContentParser.Token.START_OBJECT) { + //TODO move to pluggable parsers, similar to facets. Builders already exists... addCompoundSortField(parser, context, sortFields); } else if (token == XContentParser.Token.VALUE_STRING) { addSortField(context, sortFields, parser.text(), false); @@ -80,9 +87,6 @@ public class SortParseElement implements SearchParseElement { String fieldName = parser.currentName(); boolean reverse = false; String innerJsonName = null; - String script = null; - String type = null; - Map params = null; token = parser.nextToken(); if (token == XContentParser.Token.VALUE_STRING) { String direction = parser.text(); @@ -91,6 +95,11 @@ public class SortParseElement implements SearchParseElement { } else if (direction.equals("desc")) { reverse = !SCORE_FIELD_NAME.equals(fieldName); } + addSortField(context, sortFields, fieldName, reverse); + } else if (GEO_DISTANCE_FIELD_NAME.equals(fieldName)) { + addGeoDistanceSortField(parser, context, sortFields); + } else if (SCRIPT_FIELD_NAME.equals(fieldName)) { + addScriptSortField(parser, context, sortFields); } else { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { @@ -104,34 +113,9 @@ public class SortParseElement implements SearchParseElement { } else if ("desc".equals(parser.text())) { reverse = !SCORE_FIELD_NAME.equals(fieldName); } - } else if ("script".equals(innerJsonName)) { - script = parser.text(); - } else if ("type".equals(innerJsonName)) { - type = parser.text(); - } else if ("params".equals(innerJsonName)) { - params = parser.map(); } } } - } - if (SCRIPT_FIELD_NAME.equals(fieldName)) { - if (script == null) { - throw new SearchParseException(context, "_script sorting requires setting the script to sort by"); - } - if (type == null) { - throw new SearchParseException(context, "_script sorting requires setting the type of the script"); - } - FieldsFunction fieldsFunction = new ScriptFieldsFunction(script, context.scriptService(), context.mapperService(), context.fieldDataCache()); - FieldComparatorSource fieldComparatorSource; - if ("string".equals(type)) { - fieldComparatorSource = StringFieldsFunctionDataComparator.comparatorSource(fieldsFunction, params); - } else if ("number".equals(type)) { - fieldComparatorSource = DoubleFieldsFunctionDataComparator.comparatorSource(fieldsFunction, params); - } else { - throw new SearchParseException(context, "custom script sort type [" + type + "] not supported"); - } - sortFields.add(new SortField(fieldName, fieldComparatorSource, reverse)); - } else { addSortField(context, sortFields, fieldName, reverse); } } @@ -159,4 +143,137 @@ public class SortParseElement implements SearchParseElement { sortFields.add(new SortField(fieldName, fieldMapper.fieldDataType().newFieldComparatorSource(context.fieldDataCache()), reverse)); } } + + /** + *
+     * "_script" : {
+     *      "script" : "doc[...]",
+     *      "order" : "asc"
+     * }
+     * 
+ */ + private void addScriptSortField(XContentParser parser, SearchContext context, List sortFields) throws IOException { + String script = null; + String type = null; + Map params = null; + boolean reverse = false; + + XContentParser.Token token; + String currentName = parser.currentName(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentName = parser.currentName(); + } else if (token.isValue()) { + if ("reverse".equals(currentName)) { + reverse = parser.booleanValue(); + } else if ("order".equals(currentName)) { + reverse = "desc".equals(parser.text()); + } else if ("script".equals(currentName)) { + script = parser.text(); + } else if ("type".equals(currentName)) { + type = parser.text(); + } else if ("params".equals(currentName)) { + params = parser.map(); + } + } + } + + if (script == null) { + throw new SearchParseException(context, "_script sorting requires setting the script to sort by"); + } + if (type == null) { + throw new SearchParseException(context, "_script sorting requires setting the type of the script"); + } + FieldsFunction fieldsFunction = new ScriptFieldsFunction(script, context.scriptService(), context.mapperService(), context.fieldDataCache()); + FieldComparatorSource fieldComparatorSource; + if ("string".equals(type)) { + fieldComparatorSource = StringFieldsFunctionDataComparator.comparatorSource(fieldsFunction, params); + } else if ("number".equals(type)) { + fieldComparatorSource = DoubleFieldsFunctionDataComparator.comparatorSource(fieldsFunction, params); + } else { + throw new SearchParseException(context, "custom script sort type [" + type + "] not supported"); + } + sortFields.add(new SortField("_script", fieldComparatorSource, reverse)); + } + + /** + *
+     * "_geo_distance" : {
+     *      "pin.location" : {
+     *
+     *      },
+     *      "order" : "asc"
+     * }
+     * 
+ */ + private void addGeoDistanceSortField(XContentParser parser, SearchContext context, List sortFields) throws IOException { + String fieldName = null; + double lat = Double.NaN; + double lon = Double.NaN; + DistanceUnit unit = DistanceUnit.KILOMETERS; + GeoDistance geoDistance = GeoDistance.ARC; + boolean reverse = false; + + + XContentParser.Token token; + String currentName = parser.currentName(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentName = parser.currentName(); + } else if (token == XContentParser.Token.START_ARRAY) { + token = parser.nextToken(); + lat = parser.doubleValue(); + token = parser.nextToken(); + lon = parser.doubleValue(); + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + + } + fieldName = currentName; + } else if (token == XContentParser.Token.START_OBJECT) { + // the json in the format of -> field : { lat : 30, lon : 12 } + fieldName = currentName; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentName = parser.currentName(); + } else if (token.isValue()) { + if (currentName.equals(XContentGeoPointFieldMapper.Names.LAT)) { + lat = parser.doubleValue(); + } else if (currentName.equals(XContentGeoPointFieldMapper.Names.LON)) { + lon = parser.doubleValue(); + } else if (currentName.equals(XContentGeoPointFieldMapper.Names.GEOHASH)) { + double[] values = GeoHashUtils.decode(parser.text()); + lat = values[0]; + lon = values[1]; + } + } + } + } else if (token.isValue()) { + if ("reverse".equals(currentName)) { + reverse = parser.booleanValue(); + } else if ("order".equals(currentName)) { + reverse = "desc".equals(parser.text()); + } else if (currentName.equals("unit")) { + unit = DistanceUnit.fromString(parser.text()); + } else if (currentName.equals("distance_type") || currentName.equals("distanceType")) { + geoDistance = GeoDistance.fromString(parser.text()); + } else { + // assume the value is the actual value + String value = parser.text(); + int comma = value.indexOf(','); + if (comma != -1) { + lat = Double.parseDouble(value.substring(0, comma).trim()); + lon = Double.parseDouble(value.substring(comma + 1).trim()); + } else { + double[] values = GeoHashUtils.decode(value); + lat = values[0]; + lon = values[1]; + } + + fieldName = currentName; + } + } + } + + sortFields.add(new SortField(fieldName, GeoDistanceDataComparator.comparatorSource(fieldName, lat, lon, unit, geoDistance, context.fieldDataCache(), context.mapperService()), reverse)); + } } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java new file mode 100644 index 00000000000..f1f8c621a71 --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java @@ -0,0 +1,51 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.search.sort; + +import org.elasticsearch.common.xcontent.builder.XContentBuilder; + +import java.io.IOException; + +/** + * @author kimchy (shay.banon) + */ +public class FieldSortBuilder extends SortBuilder { + + private final String fieldName; + + private SortOrder order; + + public FieldSortBuilder(String fieldName) { + this.fieldName = fieldName; + } + + public FieldSortBuilder order(SortOrder order) { + this.order = order; + return this; + } + + @Override public void toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(fieldName); + if (order == SortOrder.DESC) { + builder.field("reverse", true); + } + builder.endObject(); + } +} diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/GeoDistanceSortBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/GeoDistanceSortBuilder.java new file mode 100644 index 00000000000..db0b9f6b54e --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/GeoDistanceSortBuilder.java @@ -0,0 +1,109 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.search.sort; + +import org.elasticsearch.common.lucene.geo.GeoDistance; +import org.elasticsearch.common.unit.DistanceUnit; +import org.elasticsearch.common.xcontent.builder.XContentBuilder; + +import java.io.IOException; + +/** + * @author kimchy (shay.banon) + */ +public class GeoDistanceSortBuilder extends SortBuilder { + + final String fieldName; + + private double lat; + private double lon; + private String geohash; + + private GeoDistance geoDistance; + private DistanceUnit unit; + private SortOrder order; + + public GeoDistanceSortBuilder(String fieldName) { + this.fieldName = fieldName; + } + + /** + * The point to create the range distance facets from. + * + * @param lat latitude. + * @param lon longitude. + */ + public GeoDistanceSortBuilder point(double lat, double lon) { + this.lat = lat; + this.lon = lon; + return this; + } + + /** + * The geohash of the geo point to create the range distance facets from. + */ + public GeoDistanceSortBuilder geohash(String geohash) { + this.geohash = geohash; + return this; + } + + /** + * The geo distance type used to compute the distance. + */ + public GeoDistanceSortBuilder geoDistance(GeoDistance geoDistance) { + this.geoDistance = geoDistance; + return this; + } + + /** + * The distance unit to use. Defaults to {@link org.elasticsearch.common.unit.DistanceUnit#KILOMETERS} + */ + public GeoDistanceSortBuilder unit(DistanceUnit unit) { + this.unit = unit; + return this; + } + + public GeoDistanceSortBuilder order(SortOrder order) { + this.order = order; + return this; + } + + @Override public void toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject("_geo_distance"); + + if (geohash != null) { + builder.field(fieldName, geohash); + } else { + builder.startArray(fieldName).value(lat).value(lon).endArray(); + } + + if (unit != null) { + builder.field("unit", unit); + } + if (geoDistance != null) { + builder.field("distance_type", geoDistance.name().toLowerCase()); + } + if (order == SortOrder.DESC) { + builder.field("reverse", true); + } + + builder.endObject(); + } +} diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/ScoreSortBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/ScoreSortBuilder.java new file mode 100644 index 00000000000..8f21ddc779e --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/ScoreSortBuilder.java @@ -0,0 +1,45 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.search.sort; + +import org.elasticsearch.common.xcontent.builder.XContentBuilder; + +import java.io.IOException; + +/** + * @author kimchy (shay.banon) + */ +public class ScoreSortBuilder extends SortBuilder { + + private SortOrder order; + + public ScoreSortBuilder order(SortOrder order) { + this.order = order; + return this; + } + + @Override public void toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject("_score"); + if (order == SortOrder.ASC) { + builder.field("reverse", true); + } + builder.endObject(); + } +} diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/ScriptSortBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/ScriptSortBuilder.java new file mode 100644 index 00000000000..464343e4925 --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/ScriptSortBuilder.java @@ -0,0 +1,72 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.search.sort; + +import org.elasticsearch.common.collect.Maps; +import org.elasticsearch.common.xcontent.builder.XContentBuilder; + +import java.io.IOException; +import java.util.Map; + +/** + * @author kimchy (shay.banon) + */ +public class ScriptSortBuilder extends SortBuilder { + + private final String script; + + private final String type; + + private SortOrder order; + + private Map params; + + public ScriptSortBuilder(String script, String type) { + this.script = script; + this.type = type; + } + + public ScriptSortBuilder param(String name, Object value) { + if (params == null) { + params = Maps.newHashMap(); + } + params.put(name, value); + return this; + } + + public ScriptSortBuilder order(SortOrder order) { + this.order = order; + return this; + } + + @Override public void toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject("_script"); + builder.field("script", script); + builder.field("type", type); + if (order == SortOrder.DESC) { + builder.field("reverse", true); + } + if (this.params != null) { + builder.field("params"); + builder.map(this.params); + } + builder.endObject(); + } +} diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortBuilder.java new file mode 100644 index 00000000000..95cc0be557d --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortBuilder.java @@ -0,0 +1,30 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.search.sort; + +import org.elasticsearch.common.xcontent.ToXContent; + +/** + * @author kimchy (shay.banon) + */ +public abstract class SortBuilder implements ToXContent { + + public abstract SortBuilder order(SortOrder order); +} diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortBuilders.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortBuilders.java new file mode 100644 index 00000000000..95792219124 --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortBuilders.java @@ -0,0 +1,42 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.search.sort; + +/** + * @author kimchy (shay.banon) + */ +public class SortBuilders { + + public static ScoreSortBuilder scoreSort() { + return new ScoreSortBuilder(); + } + + public static FieldSortBuilder fieldSort(String field) { + return new FieldSortBuilder(field); + } + + public static ScriptSortBuilder scriptSort(String script, String type) { + return new ScriptSortBuilder(script, type); + } + + public static GeoDistanceSortBuilder geoDistanceSort(String fieldName) { + return new GeoDistanceSortBuilder(fieldName); + } +} diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortOrder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortOrder.java new file mode 100644 index 00000000000..b633820a980 --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/sort/SortOrder.java @@ -0,0 +1,28 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.search.sort; + +/** + * @author kimchy (shay.banon) + */ +public enum SortOrder { + ASC, + DESC +} diff --git a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TransportTwoServersSearchTests.java b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TransportTwoServersSearchTests.java index cc6e1398ab9..1451c809786 100644 --- a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TransportTwoServersSearchTests.java +++ b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TransportTwoServersSearchTests.java @@ -32,6 +32,7 @@ import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.facets.FacetBuilders; import org.elasticsearch.search.facets.query.QueryFacet; +import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.integration.AbstractNodesTests; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; @@ -110,7 +111,7 @@ public class TransportTwoServersSearchTests extends AbstractNodesTests { @Test public void testDfsQueryThenFetchWithSort() throws Exception { SearchSourceBuilder source = searchSource() .query(termQuery("multi", "test")) - .from(0).size(60).explain(true).sort("age", false); + .from(0).size(60).explain(true).sort("age", SortOrder.ASC); SearchResponse searchResponse = client.search(searchRequest("test").source(source).searchType(DFS_QUERY_THEN_FETCH).scroll(new Scroll(timeValueMinutes(10)))).actionGet(); assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0)); @@ -162,7 +163,7 @@ public class TransportTwoServersSearchTests extends AbstractNodesTests { @Test public void testQueryThenFetchWithSort() throws Exception { SearchSourceBuilder source = searchSource() .query(termQuery("multi", "test")) - .from(0).size(60).explain(true).sort("age", false); + .from(0).size(60).explain(true).sort("age", SortOrder.ASC); SearchResponse searchResponse = client.search(searchRequest("test").source(source).searchType(QUERY_THEN_FETCH).scroll(new Scroll(timeValueMinutes(10)))).actionGet(); assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0)); diff --git a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TwoInstanceEmbeddedSearchTests.java b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TwoInstanceEmbeddedSearchTests.java index 5ceb3279178..f9a928ac323 100644 --- a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TwoInstanceEmbeddedSearchTests.java +++ b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TwoInstanceEmbeddedSearchTests.java @@ -47,6 +47,7 @@ import org.elasticsearch.search.internal.InternalSearchResponse; import org.elasticsearch.search.query.QuerySearchRequest; import org.elasticsearch.search.query.QuerySearchResult; import org.elasticsearch.search.query.QuerySearchResultProvider; +import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.integration.AbstractNodesTests; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; @@ -178,7 +179,7 @@ public class TwoInstanceEmbeddedSearchTests extends AbstractNodesTests { @Test public void testDfsQueryThenFetchWithSort() throws Exception { SearchSourceBuilder sourceBuilder = searchSource() .query(termQuery("multi", "test")) - .from(0).size(60).explain(true).sort("age", false); + .from(0).size(60).explain(true).sort("age", SortOrder.ASC); List dfsResults = newArrayList(); for (ShardsIterator shardsIt : indicesService.searchShards(clusterService.state(), new String[]{"test"}, null)) { @@ -320,7 +321,7 @@ public class TwoInstanceEmbeddedSearchTests extends AbstractNodesTests { @Test public void testSimpleFacets() { SearchSourceBuilder sourceBuilder = searchSource() .query(termQuery("multi", "test")) - .from(0).size(20).explain(true).sort("age", false) + .from(0).size(20).explain(true).sort("age", SortOrder.ASC) .facet(FacetBuilders.queryFacet("all", termQuery("multi", "test"))) .facet(FacetBuilders.queryFacet("test1", termQuery("name", "test1"))); diff --git a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TwoInstanceUnbalancedShardsEmbeddedSearchTests.java b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TwoInstanceUnbalancedShardsEmbeddedSearchTests.java index 27de7db196d..b73f743a18f 100644 --- a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TwoInstanceUnbalancedShardsEmbeddedSearchTests.java +++ b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/TwoInstanceUnbalancedShardsEmbeddedSearchTests.java @@ -52,6 +52,7 @@ import org.elasticsearch.search.internal.InternalSearchResponse; import org.elasticsearch.search.query.QuerySearchRequest; import org.elasticsearch.search.query.QuerySearchResult; import org.elasticsearch.search.query.QuerySearchResultProvider; +import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.integration.AbstractNodesTests; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; @@ -183,7 +184,7 @@ public class TwoInstanceUnbalancedShardsEmbeddedSearchTests extends AbstractNode @Test public void testDfsQueryFetchWithSort() throws Exception { SearchSourceBuilder sourceBuilder = searchSource() .query(termQuery("multi", "test")) - .from(0).size(60).explain(true).sort("age", false); + .from(0).size(60).explain(true).sort("age", SortOrder.ASC); List dfsResults = newArrayList(); for (ShardsIterator shardsIt : indicesService.searchShards(clusterService.state(), new String[]{"test"}, null)) { @@ -326,7 +327,7 @@ public class TwoInstanceUnbalancedShardsEmbeddedSearchTests extends AbstractNode @Test public void testSimpleFacets() { SearchSourceBuilder sourceBuilder = searchSource() .query(termQuery("multi", "test")) - .from(0).size(20).explain(true).sort("age", false) + .from(0).size(20).explain(true).sort("age", SortOrder.ASC) .facet(queryFacet("all").query(termQuery("multi", "test"))) .facet(queryFacet("test1").query(termQuery("name", "test1"))); diff --git a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/geo/GeoDistanceTests.java b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/geo/GeoDistanceTests.java index 8ece5b95cb4..a0b8ceabbba 100644 --- a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/geo/GeoDistanceTests.java +++ b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/geo/GeoDistanceTests.java @@ -22,6 +22,8 @@ package org.elasticsearch.test.integration.search.geo; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.sort.SortBuilders; +import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.integration.AbstractNodesTests; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; @@ -133,5 +135,35 @@ public class GeoDistanceTests extends AbstractNodesTests { for (SearchHit hit : searchResponse.hits()) { assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5"))); } + + // SORTING + + searchResponse = client.prepareSearch().setQuery(matchAllQuery()) + .addSort(SortBuilders.geoDistanceSort("location").point(40.7143528, -74.0059731).order(SortOrder.ASC)) + .execute().actionGet(); + + assertThat(searchResponse.hits().getTotalHits(), equalTo(7l)); + assertThat(searchResponse.hits().hits().length, equalTo(7)); + assertThat(searchResponse.hits().getAt(0).id(), equalTo("1")); + assertThat(searchResponse.hits().getAt(1).id(), equalTo("3")); + assertThat(searchResponse.hits().getAt(2).id(), equalTo("4")); + assertThat(searchResponse.hits().getAt(3).id(), equalTo("5")); + assertThat(searchResponse.hits().getAt(4).id(), equalTo("6")); + assertThat(searchResponse.hits().getAt(5).id(), equalTo("2")); + assertThat(searchResponse.hits().getAt(6).id(), equalTo("7")); + + searchResponse = client.prepareSearch().setQuery(matchAllQuery()) + .addSort(SortBuilders.geoDistanceSort("location").point(40.7143528, -74.0059731).order(SortOrder.DESC)) + .execute().actionGet(); + + assertThat(searchResponse.hits().getTotalHits(), equalTo(7l)); + assertThat(searchResponse.hits().hits().length, equalTo(7)); + assertThat(searchResponse.hits().getAt(6).id(), equalTo("1")); + assertThat(searchResponse.hits().getAt(5).id(), equalTo("3")); + assertThat(searchResponse.hits().getAt(4).id(), equalTo("4")); + assertThat(searchResponse.hits().getAt(3).id(), equalTo("5")); + assertThat(searchResponse.hits().getAt(2).id(), equalTo("6")); + assertThat(searchResponse.hits().getAt(1).id(), equalTo("2")); + assertThat(searchResponse.hits().getAt(0).id(), equalTo("7")); } } diff --git a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/scriptfield/ScriptFieldSearchTests.java b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/scriptfield/ScriptFieldSearchTests.java index 882fe8d9133..0618940c7db 100644 --- a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/scriptfield/ScriptFieldSearchTests.java +++ b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/scriptfield/ScriptFieldSearchTests.java @@ -22,6 +22,7 @@ package org.elasticsearch.test.integration.search.scriptfield; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.collect.MapBuilder; +import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.integration.AbstractNodesTests; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; @@ -32,7 +33,6 @@ import java.util.Map; import static org.elasticsearch.client.Requests.*; import static org.elasticsearch.common.xcontent.XContentFactory.*; import static org.elasticsearch.index.query.xcontent.QueryBuilders.*; -import static org.elasticsearch.search.builder.SearchSourceBuilder.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; @@ -77,7 +77,7 @@ public class ScriptFieldSearchTests extends AbstractNodesTests { logger.info("running doc['num1'].value"); SearchResponse response = client.prepareSearch() .setQuery(matchAllQuery()) - .addSort("num1", Order.ASC) + .addSort("num1", SortOrder.ASC) .addScriptField("sNum1", "doc['num1'].value") .addScriptField("date1", "doc['date'].date.millis") .execute().actionGet(); @@ -98,7 +98,7 @@ public class ScriptFieldSearchTests extends AbstractNodesTests { Map params = MapBuilder.newMapBuilder().put("factor", 2.0).map(); response = client.prepareSearch() .setQuery(matchAllQuery()) - .addSort("num1", Order.ASC) + .addSort("num1", SortOrder.ASC) .addScriptField("sNum1", "doc['num1'].value * factor", params) .execute().actionGet(); diff --git a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/scriptfilter/ScriptFilterSearchTests.java b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/scriptfilter/ScriptFilterSearchTests.java index e829b4bf4f9..335ffb6a8a1 100644 --- a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/scriptfilter/ScriptFilterSearchTests.java +++ b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/scriptfilter/ScriptFilterSearchTests.java @@ -21,6 +21,7 @@ package org.elasticsearch.test.integration.search.scriptfilter; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; +import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.integration.AbstractNodesTests; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; @@ -30,7 +31,6 @@ import static org.elasticsearch.client.Requests.*; import static org.elasticsearch.common.xcontent.XContentFactory.*; import static org.elasticsearch.index.query.xcontent.FilterBuilders.*; import static org.elasticsearch.index.query.xcontent.QueryBuilders.*; -import static org.elasticsearch.search.builder.SearchSourceBuilder.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; @@ -75,7 +75,7 @@ public class ScriptFilterSearchTests extends AbstractNodesTests { logger.info("running doc['num1'].value > 1"); SearchResponse response = client.prepareSearch() .setQuery(filtered(matchAllQuery(), scriptFilter("doc['num1'].value > 1"))) - .addSort("num1", Order.ASC) + .addSort("num1", SortOrder.ASC) .addScriptField("sNum1", "doc['num1'].value") .execute().actionGet(); @@ -88,7 +88,7 @@ public class ScriptFilterSearchTests extends AbstractNodesTests { logger.info("running doc['num1'].value > param1"); response = client.prepareSearch() .setQuery(filtered(matchAllQuery(), scriptFilter("doc['num1'].value > param1").addParam("param1", 2))) - .addSort("num1", Order.ASC) + .addSort("num1", SortOrder.ASC) .addScriptField("sNum1", "doc['num1'].value") .execute().actionGet(); @@ -99,7 +99,7 @@ public class ScriptFilterSearchTests extends AbstractNodesTests { logger.info("running doc['num1'].value > param1"); response = client.prepareSearch() .setQuery(filtered(matchAllQuery(), scriptFilter("doc['num1'].value > param1").addParam("param1", -1))) - .addSort("num1", Order.ASC) + .addSort("num1", SortOrder.ASC) .addScriptField("sNum1", "doc['num1'].value") .execute().actionGet(); diff --git a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/sort/SimpleSortTests.java b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/sort/SimpleSortTests.java index 0c1b1ff924d..4d60ea5d377 100644 --- a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/sort/SimpleSortTests.java +++ b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/sort/SimpleSortTests.java @@ -22,7 +22,7 @@ package org.elasticsearch.test.integration.search.sort; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.client.Client; -import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.integration.AbstractNodesTests; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; @@ -30,6 +30,7 @@ import org.testng.annotations.Test; import static org.elasticsearch.common.xcontent.XContentFactory.*; import static org.elasticsearch.index.query.xcontent.QueryBuilders.*; +import static org.elasticsearch.search.sort.SortBuilders.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; @@ -83,7 +84,7 @@ public class SimpleSortTests extends AbstractNodesTests { SearchResponse searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSort("svalue", SearchSourceBuilder.Order.ASC) + .addSort("svalue", SortOrder.ASC) .execute().actionGet(); assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); @@ -95,7 +96,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSortScript("doc['svalue'].value", "string", SearchSourceBuilder.Order.ASC) + .addSort(scriptSort("doc['svalue'].value", "string").order(SortOrder.ASC)) .execute().actionGet(); assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); @@ -105,7 +106,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSort("svalue", SearchSourceBuilder.Order.DESC) + .addSort("svalue", SortOrder.DESC) .execute().actionGet(); assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); @@ -115,7 +116,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSortScript("doc['svalue'].value", "string", SearchSourceBuilder.Order.DESC) + .addSort(scriptSort("doc['svalue'].value", "string").order(SortOrder.DESC)) .execute().actionGet(); assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); @@ -125,7 +126,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSort("ivalue", SearchSourceBuilder.Order.ASC) + .addSort("ivalue", SortOrder.ASC) .execute().actionGet(); assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); @@ -137,7 +138,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSortScript("doc['ivalue'].value", "number", SearchSourceBuilder.Order.ASC) + .addSort(scriptSort("doc['ivalue'].value", "number").order(SortOrder.ASC)) .execute().actionGet(); assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); @@ -147,7 +148,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSort("ivalue", SearchSourceBuilder.Order.DESC) + .addSort("ivalue", SortOrder.DESC) .execute().actionGet(); assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); @@ -159,7 +160,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSortScript("doc['ivalue'].value", "number", SearchSourceBuilder.Order.DESC) + .addSort(scriptSort("doc['ivalue'].value", "string").order(SortOrder.DESC)) .execute().actionGet(); assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); @@ -169,7 +170,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSort("dvalue", SearchSourceBuilder.Order.ASC) + .addSort("dvalue", SortOrder.ASC) .execute().actionGet(); assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); @@ -179,7 +180,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSort("dvalue", SearchSourceBuilder.Order.DESC) + .addSort("dvalue", SortOrder.DESC) .execute().actionGet(); assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); @@ -217,7 +218,7 @@ public class SimpleSortTests extends AbstractNodesTests { SearchResponse searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSort("svalue", SearchSourceBuilder.Order.ASC) + .addSort("svalue", SortOrder.ASC) .execute().actionGet(); if (searchResponse.failedShards() > 0) { @@ -236,7 +237,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) .addScriptField("id", "doc['id'].value") - .addSort("svalue", SearchSourceBuilder.Order.DESC) + .addSort("svalue", SortOrder.DESC) .execute().actionGet(); if (searchResponse.failedShards() > 0) { @@ -256,7 +257,7 @@ public class SimpleSortTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(termQuery("id", "2")) .addScriptField("id", "doc['id'].value") - .addSort("svalue", SearchSourceBuilder.Order.DESC) + .addSort("svalue", SortOrder.DESC) .execute().actionGet(); if (searchResponse.failedShards() > 0) {