Query DSL: Geo Distance Range filter, closes #856.
This commit is contained in:
parent
7874291c0e
commit
7c38f20056
|
@ -258,6 +258,7 @@ public class IndexQueryParserModule extends AbstractModule {
|
|||
bindings.processXContentQueryFilter(PrefixFilterParser.NAME, PrefixFilterParser.class);
|
||||
bindings.processXContentQueryFilter(ScriptFilterParser.NAME, ScriptFilterParser.class);
|
||||
bindings.processXContentQueryFilter(GeoDistanceFilterParser.NAME, GeoDistanceFilterParser.class);
|
||||
bindings.processXContentQueryFilter(GeoDistanceRangeFilterParser.NAME, GeoDistanceRangeFilterParser.class);
|
||||
bindings.processXContentQueryFilter(GeoBoundingBoxFilterParser.NAME, GeoBoundingBoxFilterParser.class);
|
||||
bindings.processXContentQueryFilter(GeoPolygonFilterParser.NAME, GeoPolygonFilterParser.class);
|
||||
bindings.processXContentQueryFilter(QueryFilterParser.NAME, QueryFilterParser.class);
|
||||
|
|
|
@ -270,6 +270,15 @@ public abstract class FilterBuilders {
|
|||
return new GeoDistanceFilterBuilder(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter to filter based on a specific range from a specific geo location / point.
|
||||
*
|
||||
* @param name The location field name.
|
||||
*/
|
||||
public static GeoDistanceRangeFilterBuilder geoDistanceRangeFilter(String name) {
|
||||
return new GeoDistanceRangeFilterBuilder(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter to filter based on a bounding box defined by top left and bottom right locations / points
|
||||
*
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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.index.query.xcontent;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.search.geo.GeoDistance;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public class GeoDistanceRangeFilterBuilder extends BaseFilterBuilder {
|
||||
|
||||
private final String name;
|
||||
|
||||
private Object from;
|
||||
private Object to;
|
||||
private boolean includeLower = true;
|
||||
private boolean includeUpper = true;
|
||||
|
||||
private double lat;
|
||||
|
||||
private double lon;
|
||||
|
||||
private String geohash;
|
||||
|
||||
private GeoDistance geoDistance;
|
||||
|
||||
private Boolean cache;
|
||||
|
||||
private String filterName;
|
||||
|
||||
public GeoDistanceRangeFilterBuilder(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder point(double lat, double lon) {
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder lat(double lat) {
|
||||
this.lat = lat;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder lon(double lon) {
|
||||
this.lon = lon;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder from(Object from) {
|
||||
this.from = from;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder to(Object to) {
|
||||
this.to = to;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder gt(Object from) {
|
||||
this.from = from;
|
||||
this.includeLower = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder gte(Object from) {
|
||||
this.from = from;
|
||||
this.includeLower = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder lt(Object to) {
|
||||
this.to = to;
|
||||
this.includeUpper = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder lte(Object to) {
|
||||
this.to = to;
|
||||
this.includeUpper = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder includeLower(boolean includeLower) {
|
||||
this.includeLower = includeLower;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder includeUpper(boolean includeUpper) {
|
||||
this.includeUpper = includeUpper;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder geohash(String geohash) {
|
||||
this.geohash = geohash;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceRangeFilterBuilder geoDistance(GeoDistance geoDistance) {
|
||||
this.geoDistance = geoDistance;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filter name for the filter that can be used when searching for matched_filters per hit.
|
||||
*/
|
||||
public GeoDistanceRangeFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the filter be cached or not. Defaults to <tt>false</tt>.
|
||||
*/
|
||||
public GeoDistanceRangeFilterBuilder cache(boolean cache) {
|
||||
this.cache = cache;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(GeoDistanceRangeFilterParser.NAME);
|
||||
if (geohash != null) {
|
||||
builder.field(name, geohash);
|
||||
} else {
|
||||
builder.startArray(name).value(lon).value(lat).endArray();
|
||||
}
|
||||
builder.field("from", from);
|
||||
builder.field("to", to);
|
||||
builder.field("include_lower", includeLower);
|
||||
builder.field("include_upper", includeUpper);
|
||||
if (geoDistance != null) {
|
||||
builder.field("distance_type", geoDistance.name().toLowerCase());
|
||||
}
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
if (cache != null) {
|
||||
builder.field("_cache", cache);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* 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.index.query.xcontent;
|
||||
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.DistanceUnit;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.AbstractIndexComponent;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.mapper.FieldMapper;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType;
|
||||
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldMapper;
|
||||
import org.elasticsearch.index.query.QueryParsingException;
|
||||
import org.elasticsearch.index.search.geo.GeoDistance;
|
||||
import org.elasticsearch.index.search.geo.GeoDistanceRangeFilter;
|
||||
import org.elasticsearch.index.search.geo.GeoHashUtils;
|
||||
import org.elasticsearch.index.settings.IndexSettings;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.index.query.support.QueryParsers.*;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* {
|
||||
* "name.lat" : 1.1,
|
||||
* "name.lon" : 1.2,
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public class GeoDistanceRangeFilterParser extends AbstractIndexComponent implements XContentFilterParser {
|
||||
|
||||
public static final String NAME = "geo_distance_range";
|
||||
|
||||
@Inject public GeoDistanceRangeFilterParser(Index index, @IndexSettings Settings indexSettings) {
|
||||
super(index, indexSettings);
|
||||
}
|
||||
|
||||
@Override public String[] names() {
|
||||
return new String[]{NAME, "geoDistanceRange"};
|
||||
}
|
||||
|
||||
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
XContentParser.Token token;
|
||||
|
||||
boolean cache = false;
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
double lat = 0;
|
||||
double lon = 0;
|
||||
String fieldName = null;
|
||||
Object vFrom = null;
|
||||
Object vTo = null;
|
||||
boolean includeLower = true;
|
||||
boolean includeUpper = true;
|
||||
DistanceUnit unit = DistanceUnit.KILOMETERS; // default unit
|
||||
GeoDistance geoDistance = GeoDistance.ARC;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||
token = parser.nextToken();
|
||||
lon = parser.doubleValue();
|
||||
token = parser.nextToken();
|
||||
lat = parser.doubleValue();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
|
||||
}
|
||||
fieldName = currentFieldName;
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
// the json in the format of -> field : { lat : 30, lon : 12 }
|
||||
String currentName = parser.currentName();
|
||||
fieldName = currentFieldName;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentName = parser.currentName();
|
||||
} else if (token.isValue()) {
|
||||
if (currentName.equals(GeoPointFieldMapper.Names.LAT)) {
|
||||
lat = parser.doubleValue();
|
||||
} else if (currentName.equals(GeoPointFieldMapper.Names.LON)) {
|
||||
lon = parser.doubleValue();
|
||||
} else if (currentName.equals(GeoPointFieldMapper.Names.GEOHASH)) {
|
||||
double[] values = GeoHashUtils.decode(parser.text());
|
||||
lat = values[0];
|
||||
lon = values[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if (currentFieldName.equals("from")) {
|
||||
if (token == XContentParser.Token.VALUE_NULL) {
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
vFrom = parser.text(); // a String
|
||||
} else {
|
||||
vFrom = parser.numberValue(); // a Number
|
||||
}
|
||||
} else if (currentFieldName.equals("to")) {
|
||||
if (token == XContentParser.Token.VALUE_NULL) {
|
||||
}
|
||||
if (token == XContentParser.Token.VALUE_STRING) {
|
||||
vTo = parser.text(); // a String
|
||||
} else {
|
||||
vTo = parser.numberValue(); // a Number
|
||||
}
|
||||
} else if ("include_lower".equals(currentFieldName) || "includeLower".equals(currentFieldName)) {
|
||||
includeLower = parser.booleanValue();
|
||||
} else if ("include_upper".equals(currentFieldName) || "includeUpper".equals(currentFieldName)) {
|
||||
includeUpper = parser.booleanValue();
|
||||
} else if ("gt".equals(currentFieldName)) {
|
||||
if (token == XContentParser.Token.VALUE_NULL) {
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
vFrom = parser.text(); // a String
|
||||
} else {
|
||||
vFrom = parser.numberValue(); // a Number
|
||||
}
|
||||
includeLower = false;
|
||||
} else if ("gte".equals(currentFieldName) || "ge".equals(currentFieldName)) {
|
||||
if (token == XContentParser.Token.VALUE_NULL) {
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
vFrom = parser.text(); // a String
|
||||
} else {
|
||||
vFrom = parser.numberValue(); // a Number
|
||||
}
|
||||
includeLower = true;
|
||||
} else if ("lt".equals(currentFieldName)) {
|
||||
if (token == XContentParser.Token.VALUE_NULL) {
|
||||
}
|
||||
if (token == XContentParser.Token.VALUE_STRING) {
|
||||
vTo = parser.text(); // a String
|
||||
} else {
|
||||
vTo = parser.numberValue(); // a Number
|
||||
}
|
||||
includeUpper = false;
|
||||
} else if ("lte".equals(currentFieldName) || "le".equals(currentFieldName)) {
|
||||
if (token == XContentParser.Token.VALUE_NULL) {
|
||||
}
|
||||
if (token == XContentParser.Token.VALUE_STRING) {
|
||||
vTo = parser.text(); // a String
|
||||
} else {
|
||||
vTo = parser.numberValue(); // a Number
|
||||
}
|
||||
includeUpper = true;
|
||||
} else if (currentFieldName.equals("unit")) {
|
||||
unit = DistanceUnit.fromString(parser.text());
|
||||
} else if (currentFieldName.equals("distance_type") || currentFieldName.equals("distanceType")) {
|
||||
geoDistance = GeoDistance.fromString(parser.text());
|
||||
} else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.LAT_SUFFIX)) {
|
||||
lat = parser.doubleValue();
|
||||
fieldName = currentFieldName.substring(0, currentFieldName.length() - GeoPointFieldMapper.Names.LAT_SUFFIX.length());
|
||||
} else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.LON_SUFFIX)) {
|
||||
lon = parser.doubleValue();
|
||||
fieldName = currentFieldName.substring(0, currentFieldName.length() - GeoPointFieldMapper.Names.LON_SUFFIX.length());
|
||||
} else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.GEOHASH_SUFFIX)) {
|
||||
double[] values = GeoHashUtils.decode(parser.text());
|
||||
lat = values[0];
|
||||
lon = values[1];
|
||||
fieldName = currentFieldName.substring(0, currentFieldName.length() - GeoPointFieldMapper.Names.GEOHASH_SUFFIX.length());
|
||||
} else if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
} else if ("_cache".equals(currentFieldName)) {
|
||||
cache = parser.booleanValue();
|
||||
} 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 = currentFieldName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double from;
|
||||
double to;
|
||||
if (vFrom instanceof Number) {
|
||||
from = unit.toMiles(((Number) vFrom).doubleValue());
|
||||
} else {
|
||||
from = DistanceUnit.parse((String) vFrom, unit, DistanceUnit.MILES);
|
||||
}
|
||||
if (vTo instanceof Number) {
|
||||
to = unit.toMiles(((Number) vTo).doubleValue());
|
||||
} else {
|
||||
to = DistanceUnit.parse((String) vTo, unit, DistanceUnit.MILES);
|
||||
}
|
||||
|
||||
MapperService mapperService = parseContext.mapperService();
|
||||
FieldMapper mapper = mapperService.smartNameFieldMapper(fieldName);
|
||||
if (mapper == null) {
|
||||
throw new QueryParsingException(index, "failed to find geo_point field [" + fieldName + "]");
|
||||
}
|
||||
if (mapper.fieldDataType() != GeoPointFieldDataType.TYPE) {
|
||||
throw new QueryParsingException(index, "field [" + fieldName + "] is not a geo_point field");
|
||||
}
|
||||
fieldName = mapper.names().indexName();
|
||||
|
||||
Filter filter = new GeoDistanceRangeFilter(lat, lon, from, to, includeLower, includeUpper, geoDistance, fieldName, parseContext.indexCache().fieldData());
|
||||
if (cache) {
|
||||
filter = parseContext.cacheFilter(filter);
|
||||
}
|
||||
filter = wrapSmartNameFilter(filter, parseContext.smartFieldMappers(fieldName), parseContext);
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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.index.search.geo;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.search.DocIdSet;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
import org.elasticsearch.common.lucene.docset.GetDocSet;
|
||||
import org.elasticsearch.common.unit.DistanceUnit;
|
||||
import org.elasticsearch.index.cache.field.data.FieldDataCache;
|
||||
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData;
|
||||
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public class GeoDistanceRangeFilter extends Filter {
|
||||
|
||||
private final double lat;
|
||||
|
||||
private final double lon;
|
||||
|
||||
private final double inclusiveLowerPoint; // in miles
|
||||
private final double inclusiveUpperPoint; // in miles
|
||||
|
||||
private final GeoDistance geoDistance;
|
||||
|
||||
private final String fieldName;
|
||||
|
||||
private final FieldDataCache fieldDataCache;
|
||||
|
||||
public GeoDistanceRangeFilter(double lat, double lon, Double lowerVal, Double upperVal, boolean includeLower, boolean includeUpper, GeoDistance geoDistance, String fieldName, FieldDataCache fieldDataCache) {
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
this.geoDistance = geoDistance;
|
||||
this.fieldName = fieldName;
|
||||
this.fieldDataCache = fieldDataCache;
|
||||
|
||||
if (lowerVal != null) {
|
||||
double f = lowerVal.doubleValue();
|
||||
long i = NumericUtils.doubleToSortableLong(f);
|
||||
inclusiveLowerPoint = NumericUtils.sortableLongToDouble(includeLower ? i : (i + 1L));
|
||||
} else {
|
||||
inclusiveLowerPoint = Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
if (upperVal != null) {
|
||||
double f = upperVal.doubleValue();
|
||||
long i = NumericUtils.doubleToSortableLong(f);
|
||||
inclusiveUpperPoint = NumericUtils.sortableLongToDouble(includeUpper ? i : (i - 1L));
|
||||
} else {
|
||||
inclusiveUpperPoint = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
|
||||
public double lat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
public double lon() {
|
||||
return lon;
|
||||
}
|
||||
|
||||
public GeoDistance geoDistance() {
|
||||
return geoDistance;
|
||||
}
|
||||
|
||||
public String fieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
@Override public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
|
||||
final GeoPointFieldData fieldData = (GeoPointFieldData) fieldDataCache.cache(GeoPointFieldDataType.TYPE, reader, fieldName);
|
||||
return new GetDocSet(reader.maxDoc()) {
|
||||
|
||||
@Override public boolean isCacheable() {
|
||||
// not cacheable for several reasons:
|
||||
// 1. It is only relevant when _cache is set to true, and then, we really want to create in mem bitset
|
||||
// 2. Its already fast without in mem bitset, since it works with field data
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean get(int doc) throws IOException {
|
||||
if (!fieldData.hasValue(doc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fieldData.multiValued()) {
|
||||
double[] lats = fieldData.latValues(doc);
|
||||
double[] lons = fieldData.lonValues(doc);
|
||||
for (int i = 0; i < lats.length; i++) {
|
||||
double d = geoDistance.calculate(lat, lon, lats[i], lons[i], DistanceUnit.MILES);
|
||||
if (d >= inclusiveLowerPoint && d <= inclusiveUpperPoint) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
double d = geoDistance.calculate(lat, lon, fieldData.latValue(doc), fieldData.lonValue(doc), DistanceUnit.MILES);
|
||||
if (d >= inclusiveLowerPoint && d <= inclusiveUpperPoint) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
GeoDistanceRangeFilter filter = (GeoDistanceRangeFilter) o;
|
||||
|
||||
if (Double.compare(filter.inclusiveLowerPoint, inclusiveLowerPoint) != 0) return false;
|
||||
if (Double.compare(filter.inclusiveUpperPoint, inclusiveUpperPoint) != 0) return false;
|
||||
if (Double.compare(filter.lat, lat) != 0) return false;
|
||||
if (Double.compare(filter.lon, lon) != 0) return false;
|
||||
if (fieldName != null ? !fieldName.equals(filter.fieldName) : filter.fieldName != null) return false;
|
||||
if (geoDistance != filter.geoDistance) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = lat != +0.0d ? Double.doubleToLongBits(lat) : 0L;
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
temp = lon != +0.0d ? Double.doubleToLongBits(lon) : 0L;
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = inclusiveLowerPoint != +0.0d ? Double.doubleToLongBits(inclusiveLowerPoint) : 0L;
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = inclusiveUpperPoint != +0.0d ? Double.doubleToLongBits(inclusiveUpperPoint) : 0L;
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + (geoDistance != null ? geoDistance.hashCode() : 0);
|
||||
result = 31 * result + (fieldName != null ? fieldName.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -140,6 +140,15 @@ public class GeoDistanceTests extends AbstractNodesTests {
|
|||
assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5")));
|
||||
}
|
||||
|
||||
searchResponse = client.prepareSearch() // from NY
|
||||
.setQuery(filteredQuery(matchAllQuery(), geoDistanceRangeFilter("location").from("1.0km").to("2.0km").point(40.7143528, -74.0059731)))
|
||||
.execute().actionGet();
|
||||
assertThat(searchResponse.hits().getTotalHits(), equalTo(2l));
|
||||
assertThat(searchResponse.hits().hits().length, equalTo(2));
|
||||
for (SearchHit hit : searchResponse.hits()) {
|
||||
assertThat(hit.id(), anyOf(equalTo("4"), equalTo("5")));
|
||||
}
|
||||
|
||||
// SORTING
|
||||
|
||||
searchResponse = client.prepareSearch().setQuery(matchAllQuery())
|
||||
|
|
Loading…
Reference in New Issue