Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions docs/reference/search/request/sort.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,9 @@ GET /_search
"pin.location" : [-70, 40],
"order" : "asc",
"unit" : "km",
"mode" : "min",
"distance_type" : "arc"
"mode" : "min",
"distance_type" : "arc",
"ignore_unmapped": true
}
}
],
Expand Down Expand Up @@ -317,6 +318,12 @@ GET /_search

The unit to use when computing sort values. The default is `m` (meters).


`ignore_unmapped`::

Indicates if the unmapped field should be treated as a missing value. Setting it to `true` is equivalent to specifying
an `unmapped_type` in the field sort. The default is `false` (unmapped field are causing the search to fail).

NOTE: geo distance sorting does not support configurable missing values: the
distance will always be considered equal to +Infinity+ when a document does not
have values for the field that is used for distance computation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public class GeoDistanceSortBuilder extends SortBuilder<GeoDistanceSortBuilder>
private static final ParseField DISTANCE_TYPE_FIELD = new ParseField("distance_type");
private static final ParseField VALIDATION_METHOD_FIELD = new ParseField("validation_method");
private static final ParseField SORTMODE_FIELD = new ParseField("mode", "sort_mode");
private static final ParseField IGNORE_UNMAPPED = new ParseField("ignore_unmapped");

private final String fieldName;
private final List<GeoPoint> points = new ArrayList<>();
Expand All @@ -97,6 +98,8 @@ public class GeoDistanceSortBuilder extends SortBuilder<GeoDistanceSortBuilder>

private GeoValidationMethod validation = DEFAULT_VALIDATION;

private boolean ignoreUnmapped = false;

/**
* Constructs a new distance based sort on a geo point like field.
*
Expand Down Expand Up @@ -152,6 +155,7 @@ public GeoDistanceSortBuilder(String fieldName, String ... geohashes) {
this.nestedPath = original.nestedPath;
this.validation = original.validation;
this.nestedSort = original.nestedSort;
this.ignoreUnmapped = original.ignoreUnmapped;
}

/**
Expand All @@ -171,6 +175,10 @@ public GeoDistanceSortBuilder(StreamInput in) throws IOException {
nestedSort = in.readOptionalWriteable(NestedSortBuilder::new);
}
validation = GeoValidationMethod.readFromStream(in);
// TODO: Change to 6_4_0 after backport
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
ignoreUnmapped = in.readBoolean();
}
}

@Override
Expand All @@ -187,6 +195,10 @@ public void writeTo(StreamOutput out) throws IOException {
out.writeOptionalWriteable(nestedSort);
}
validation.writeTo(out);
// TODO: Change to 6_4_0 after backport
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
out.writeBoolean(ignoreUnmapped);
}
}

/**
Expand Down Expand Up @@ -374,6 +386,18 @@ public GeoDistanceSortBuilder setNestedSort(final NestedSortBuilder nestedSort)
return this;
}

/**
* Returns true if unmapped geo fields should be treated as located at an infinite distance
*/
public boolean ignoreUnmapped() {
return ignoreUnmapped;
}

public GeoDistanceSortBuilder ignoreUnmapped(boolean ignoreUnmapped) {
this.ignoreUnmapped = ignoreUnmapped;
return this;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
Expand Down Expand Up @@ -403,6 +427,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.field(NESTED_FIELD.getPreferredName(), nestedSort);
}
builder.field(VALIDATION_METHOD_FIELD.getPreferredName(), validation);
builder.field(IGNORE_UNMAPPED.getPreferredName(), ignoreUnmapped);

builder.endObject();
builder.endObject();
Expand Down Expand Up @@ -434,14 +459,15 @@ public boolean equals(Object object) {
Objects.equals(nestedFilter, other.nestedFilter) &&
Objects.equals(nestedPath, other.nestedPath) &&
Objects.equals(validation, other.validation) &&
Objects.equals(nestedSort, other.nestedSort);
Objects.equals(nestedSort, other.nestedSort) &&
ignoreUnmapped == other.ignoreUnmapped;
}

@Override
public int hashCode() {
return Objects.hash(this.fieldName, this.points, this.geoDistance,
this.unit, this.sortMode, this.order, this.nestedFilter,
this.nestedPath, this.validation, this.nestedSort);
this.nestedPath, this.validation, this.nestedSort, this.ignoreUnmapped);
}

/**
Expand All @@ -465,6 +491,7 @@ public static GeoDistanceSortBuilder fromXContent(XContentParser parser, String
String nestedPath = null;
NestedSortBuilder nestedSort = null;
GeoValidationMethod validation = null;
boolean ignoreUnmapped = false;

XContentParser.Token token;
String currentName = parser.currentName();
Expand Down Expand Up @@ -509,6 +536,8 @@ public static GeoDistanceSortBuilder fromXContent(XContentParser parser, String
} else if (NESTED_PATH_FIELD.match(currentName, parser.getDeprecationHandler())) {
DEPRECATION_LOGGER.deprecated("[nested_path] has been deprecated in favour of the [nested] parameter");
nestedPath = parser.text();
} else if (IGNORE_UNMAPPED.match(currentName, parser.getDeprecationHandler())) {
ignoreUnmapped = parser.booleanValue();
} else if (token == Token.VALUE_STRING){
if (fieldName != null && fieldName.equals(currentName) == false) {
throw new ParsingException(
Expand Down Expand Up @@ -554,6 +583,7 @@ public static GeoDistanceSortBuilder fromXContent(XContentParser parser, String
if (validation != null) {
result.validation(validation);
}
result.ignoreUnmapped(ignoreUnmapped);
return result;
}

Expand Down Expand Up @@ -596,8 +626,11 @@ public SortFieldAndFormat build(QueryShardContext context) throws IOException {

MappedFieldType fieldType = context.fieldMapper(fieldName);
if (fieldType == null) {
throw new IllegalArgumentException("failed to find mapper for [" + fieldName
+ "] for geo distance based sort");
if (ignoreUnmapped) {
fieldType = context.getMapperService().unmappedFieldType("geo_point");
} else {
throw new IllegalArgumentException("failed to find mapper for [" + fieldName + "] for geo distance based sort");
}
}
final IndexGeoPointFieldData geoIndexFieldData = context.getForField(fieldType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;


public class GeoDistanceIT extends ESIntegTestCase {
Expand Down Expand Up @@ -406,4 +407,57 @@ public void testGeoDistanceFilter() throws IOException {
assertHitCount(result, 1);
}

public void testDistanceSortingWithUnmappedField() throws Exception {
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("type1").startObject("properties")
.startObject("locations").field("type", "geo_point");
xContentBuilder.endObject().endObject().endObject().endObject();
assertAcked(prepareCreate("test1").addMapping("type1", xContentBuilder));
assertAcked(prepareCreate("test2"));
ensureGreen();

client().prepareIndex("test1", "type1", "1")
.setSource(jsonBuilder().startObject().array("names", "Times Square", "Tribeca").startArray("locations")
// to NY: 5.286 km
.startObject().field("lat", 40.759011).field("lon", -73.9844722).endObject()
// to NY: 0.4621 km
.startObject().field("lat", 40.718266).field("lon", -74.007819).endObject().endArray().endObject())
.execute().actionGet();

client().prepareIndex("test2", "type1", "2")
.setSource(jsonBuilder().startObject().array("names", "Wall Street", "Soho").endObject())
.execute().actionGet();

refresh();

// Order: Asc
SearchResponse searchResponse = client().prepareSearch("test1", "test2").setQuery(matchAllQuery())
.addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).ignoreUnmapped(true).order(SortOrder.ASC)).execute()
.actionGet();

assertHitCount(searchResponse, 2);
assertOrderedSearchHits(searchResponse, "1", "2");
assertThat(((Number) searchResponse.getHits().getAt(0).getSortValues()[0]).doubleValue(), closeTo(462.1d, 10d));
assertThat(((Number) searchResponse.getHits().getAt(1).getSortValues()[0]).doubleValue(), equalTo(Double.POSITIVE_INFINITY));

// Order: Desc
searchResponse = client().prepareSearch("test1", "test2").setQuery(matchAllQuery())
.addSort(
SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).ignoreUnmapped(true).order(SortOrder.DESC)
).execute()
.actionGet();

// Doc with missing geo point is first, is consistent with 0.20.x
assertHitCount(searchResponse, 2);
assertOrderedSearchHits(searchResponse, "2", "1");
assertThat(((Number) searchResponse.getHits().getAt(0).getSortValues()[0]).doubleValue(), equalTo(Double.POSITIVE_INFINITY));
assertThat(((Number) searchResponse.getHits().getAt(1).getSortValues()[0]).doubleValue(), closeTo(5286d, 10d));

// Make sure that by default the unmapped fields continue to fail
searchResponse = client().prepareSearch("test1", "test2").setQuery(matchAllQuery())
.addSort( SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).order(SortOrder.DESC)).execute()
.actionGet();
assertThat(searchResponse.getFailedShards(), greaterThan(0));
assertHitCount(searchResponse, 1);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.DocValueFormat;
import org.elasticsearch.search.MultiValueMode;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.geo.RandomGeoGenerator;

import java.io.IOException;
Expand Down Expand Up @@ -121,6 +122,9 @@ public static GeoDistanceSortBuilder randomGeoDistanceSortBuilder() {
}
}
}
if (randomBoolean()) {
result.ignoreUnmapped(result.ignoreUnmapped() == false);
}
return result;
}

Expand Down Expand Up @@ -154,7 +158,7 @@ private static GeoDistance geoDistance(GeoDistance original) {
@Override
protected GeoDistanceSortBuilder mutate(GeoDistanceSortBuilder original) throws IOException {
GeoDistanceSortBuilder result = new GeoDistanceSortBuilder(original);
int parameter = randomIntBetween(0, 7);
int parameter = randomIntBetween(0, 8);
switch (parameter) {
case 0:
while (Arrays.deepEquals(original.points(), result.points())) {
Expand Down Expand Up @@ -194,6 +198,9 @@ protected GeoDistanceSortBuilder mutate(GeoDistanceSortBuilder original) throws
case 7:
result.validation(randomValueOtherThan(result.validation(), () -> randomFrom(GeoValidationMethod.values())));
break;
case 8:
result.ignoreUnmapped(result.ignoreUnmapped() == false);
break;
}
return result;
}
Expand Down