diff --git a/modules/benchmark/micro/src/main/java/org/elasticsearch/benchmark/search/geo/GeoDistanceSearchBenchmark.java b/modules/benchmark/micro/src/main/java/org/elasticsearch/benchmark/search/geo/GeoDistanceSearchBenchmark.java index 402b125d73f..71d74f9e24f 100644 --- a/modules/benchmark/micro/src/main/java/org/elasticsearch/benchmark/search/geo/GeoDistanceSearchBenchmark.java +++ b/modules/benchmark/micro/src/main/java/org/elasticsearch/benchmark/search/geo/GeoDistanceSearchBenchmark.java @@ -107,23 +107,40 @@ public class GeoDistanceSearchBenchmark { System.err.println("--> Warming up (ARC) - optimize_bbox"); long start = System.currentTimeMillis(); for (int i = 0; i < NUM_WARM; i++) { - run(client, GeoDistance.ARC, true); + run(client, GeoDistance.ARC, "memory"); } long totalTime = System.currentTimeMillis() - start; - System.err.println("--> Warmup (ARC) - optimize_bbox " + (totalTime / NUM_WARM) + "ms"); + System.err.println("--> Warmup (ARC) - optimize_bbox (memory) " + (totalTime / NUM_WARM) + "ms"); - System.err.println("--> Perf (ARC) - optimize_bbox"); + System.err.println("--> Perf (ARC) - optimize_bbox (memory)"); start = System.currentTimeMillis(); for (int i = 0; i < NUM_RUNS; i++) { - run(client, GeoDistance.ARC, true); + run(client, GeoDistance.ARC, "memory"); } totalTime = System.currentTimeMillis() - start; System.err.println("--> Perf (ARC) - optimize_bbox " + (totalTime / NUM_RUNS) + "ms"); + System.err.println("--> Warming up (ARC) - optimize_bbox (indexed)"); + start = System.currentTimeMillis(); + for (int i = 0; i < NUM_WARM; i++) { + run(client, GeoDistance.ARC, "indexed"); + } + totalTime = System.currentTimeMillis() - start; + System.err.println("--> Warmup (ARC) - optimize_bbox (indexed) " + (totalTime / NUM_WARM) + "ms"); + + System.err.println("--> Perf (ARC) - optimize_bbox (indexed)"); + start = System.currentTimeMillis(); + for (int i = 0; i < NUM_RUNS; i++) { + run(client, GeoDistance.ARC, "indexed"); + } + totalTime = System.currentTimeMillis() - start; + System.err.println("--> Perf (ARC) - optimize_bbox (indexed) " + (totalTime / NUM_RUNS) + "ms"); + + System.err.println("--> Warming up (ARC) - no optimize_bbox"); start = System.currentTimeMillis(); for (int i = 0; i < NUM_WARM; i++) { - run(client, GeoDistance.ARC, false); + run(client, GeoDistance.ARC, "none"); } totalTime = System.currentTimeMillis() - start; System.err.println("--> Warmup (ARC) - no optimize_bbox " + (totalTime / NUM_WARM) + "ms"); @@ -131,7 +148,7 @@ public class GeoDistanceSearchBenchmark { System.err.println("--> Perf (ARC) - no optimize_bbox"); start = System.currentTimeMillis(); for (int i = 0; i < NUM_RUNS; i++) { - run(client, GeoDistance.ARC, false); + run(client, GeoDistance.ARC, "none"); } totalTime = System.currentTimeMillis() - start; System.err.println("--> Perf (ARC) - no optimize_bbox " + (totalTime / NUM_RUNS) + "ms"); @@ -139,7 +156,7 @@ public class GeoDistanceSearchBenchmark { System.err.println("--> Warming up (PLANE)"); start = System.currentTimeMillis(); for (int i = 0; i < NUM_WARM; i++) { - run(client, GeoDistance.PLANE, true); + run(client, GeoDistance.PLANE, "memory"); } totalTime = System.currentTimeMillis() - start; System.err.println("--> Warmup (PLANE) " + (totalTime / NUM_WARM) + "ms"); @@ -147,7 +164,7 @@ public class GeoDistanceSearchBenchmark { System.err.println("--> Perf (PLANE)"); start = System.currentTimeMillis(); for (int i = 0; i < NUM_RUNS; i++) { - run(client, GeoDistance.PLANE, true); + run(client, GeoDistance.PLANE, "memory"); } totalTime = System.currentTimeMillis() - start; System.err.println("--> Perf (PLANE) " + (totalTime / NUM_RUNS) + "ms"); @@ -155,7 +172,7 @@ public class GeoDistanceSearchBenchmark { node.close(); } - public static void run(Client client, GeoDistance geoDistance, boolean optimizeBbox) { + public static void run(Client client, GeoDistance geoDistance, String optimizeBbox) { client.prepareSearch() // from NY .setSearchType(SearchType.COUNT) .setQuery(filteredQuery(matchAllQuery(), geoDistanceFilter("location") diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java index 727af98244b..b927db76294 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java @@ -164,6 +164,10 @@ public class DoubleFieldMapper extends NumberFieldMapper { includeLower, includeUpper); } + public Filter rangeFilter(Double lowerTerm, Double upperTerm, boolean includeLower, boolean includeUpper) { + return NumericRangeFilter.newDoubleRange(names.indexName(), precisionStep, lowerTerm, upperTerm, includeLower, includeUpper); + } + @Override public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) { return NumericRangeFieldDataFilter.newDoubleRange(fieldDataCache, names.indexName(), lowerTerm == null ? null : Double.parseDouble(lowerTerm), diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/geo/GeoPointFieldMapper.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/geo/GeoPointFieldMapper.java index a953c63c239..7a76dc94cb6 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/geo/GeoPointFieldMapper.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/geo/GeoPointFieldMapper.java @@ -36,6 +36,7 @@ import org.elasticsearch.index.mapper.MergeMappingException; import org.elasticsearch.index.mapper.ObjectMapperListener; import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.core.AbstractFieldMapper; +import org.elasticsearch.index.mapper.core.DoubleFieldMapper; import org.elasticsearch.index.mapper.core.NumberFieldMapper; import org.elasticsearch.index.mapper.core.StringFieldMapper; import org.elasticsearch.index.mapper.object.ArrayValueMapperParser; @@ -147,8 +148,8 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser { .index(Field.Index.NOT_ANALYZED).omitNorms(true).omitTermFreqAndPositions(true).includeInAll(false).store(store).build(context); - NumberFieldMapper latMapper = null; - NumberFieldMapper lonMapper = null; + DoubleFieldMapper latMapper = null; + DoubleFieldMapper lonMapper = null; context.path().add(name); if (enableLatLon) { @@ -158,8 +159,8 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser { latMapperBuilder.precisionStep(precisionStep); lonMapperBuilder.precisionStep(precisionStep); } - latMapper = (NumberFieldMapper) latMapperBuilder.includeInAll(false).store(store).build(context); - lonMapper = (NumberFieldMapper) lonMapperBuilder.includeInAll(false).store(store).build(context); + latMapper = (DoubleFieldMapper) latMapperBuilder.includeInAll(false).store(store).build(context); + lonMapper = (DoubleFieldMapper) lonMapperBuilder.includeInAll(false).store(store).build(context); } StringFieldMapper geohashMapper = null; if (enableGeoHash) { @@ -226,13 +227,13 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser { private final int precision; - private final NumberFieldMapper latMapper; + private final DoubleFieldMapper latMapper; - private final NumberFieldMapper lonMapper; + private final DoubleFieldMapper lonMapper; private final StringFieldMapper geohashMapper; - private final StringFieldMapper geoStringMapper; + private final GeoStringFieldMapper geoStringMapper; private final boolean validateLon; private final boolean validateLat; @@ -241,7 +242,7 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser { private final boolean normalizeLat; public GeoPointFieldMapper(String name, ContentPath.Type pathType, boolean enableLatLon, boolean enableGeoHash, Integer precisionStep, int precision, - NumberFieldMapper latMapper, NumberFieldMapper lonMapper, StringFieldMapper geohashMapper, StringFieldMapper geoStringMapper, + DoubleFieldMapper latMapper, DoubleFieldMapper lonMapper, StringFieldMapper geohashMapper, GeoStringFieldMapper geoStringMapper, boolean validateLon, boolean validateLat, boolean normalizeLon, boolean normalizeLat) { this.name = name; @@ -256,6 +257,8 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser { this.geoStringMapper = geoStringMapper; this.geohashMapper = geohashMapper; + this.geoStringMapper.geoMapper = this; + this.validateLat = validateLat; this.validateLon = validateLon; @@ -267,6 +270,18 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser { return this.name; } + public DoubleFieldMapper latMapper() { + return latMapper; + } + + public DoubleFieldMapper lonMapper() { + return lonMapper; + } + + public boolean isEnableLatLon() { + return enableLatLon; + } + @Override public void parse(ParseContext context) throws IOException { ContentPath.Type origPathType = context.path().pathType(); context.path().pathType(pathType); @@ -536,6 +551,8 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser { } } + GeoPointFieldMapper geoMapper; + public GeoStringFieldMapper(Names names, Field.Index index, Field.Store store, Field.TermVector termVector, float boost, boolean omitNorms, boolean omitTermFreqAndPositions, String nullValue, NamedAnalyzer indexAnalyzer, NamedAnalyzer searchAnalyzer) { super(names, index, store, termVector, boost, omitNorms, omitTermFreqAndPositions, nullValue, indexAnalyzer, searchAnalyzer); } @@ -543,5 +560,9 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser { @Override public FieldDataType fieldDataType() { return GeoPointFieldDataType.TYPE; } + + public GeoPointFieldMapper geoMapper() { + return geoMapper; + } } } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxFilterBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxFilterBuilder.java index 506666d9f26..db8204c6861 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxFilterBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxFilterBuilder.java @@ -44,6 +44,8 @@ public class GeoBoundingBoxFilterBuilder extends BaseFilterBuilder { private String filterName; + private String type; + public GeoBoundingBoxFilterBuilder(String name) { this.name = name; } @@ -105,6 +107,15 @@ public class GeoBoundingBoxFilterBuilder extends BaseFilterBuilder { return this; } + /** + * Sets the type of executing of the geo bounding box. Can be either `memory` or `indexed`. Defaults + * to `memory`. + */ + public GeoBoundingBoxFilterBuilder type(String type) { + this.type = type; + return this; + } + @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(GeoBoundingBoxFilterParser.NAME); @@ -135,6 +146,9 @@ public class GeoBoundingBoxFilterBuilder extends BaseFilterBuilder { if (cacheKey != null) { builder.field("_cache_key", cacheKey); } + if (type != null) { + builder.field("type", type); + } builder.endObject(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxFilterParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxFilterParser.java index 842500be768..869fd48f949 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxFilterParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxFilterParser.java @@ -25,11 +25,11 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.cache.filter.support.CacheKeyFilter; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MapperService; -import org.elasticsearch.index.mapper.geo.GeoPointFieldDataType; import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper; -import org.elasticsearch.index.search.geo.GeoBoundingBoxFilter; import org.elasticsearch.index.search.geo.GeoHashUtils; import org.elasticsearch.index.search.geo.GeoUtils; +import org.elasticsearch.index.search.geo.InMemoryGeoBoundingBoxFilter; +import org.elasticsearch.index.search.geo.IndexedGeoBoundingBoxFilter; import org.elasticsearch.index.search.geo.Point; import java.io.IOException; @@ -64,6 +64,9 @@ public class GeoBoundingBoxFilterParser implements FilterParser { XContentParser.Token token; boolean normalizeLon = true; boolean normalizeLat = true; + + String type = "memory"; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); @@ -151,6 +154,8 @@ public class GeoBoundingBoxFilterParser implements FilterParser { } else if ("normalize".equals(currentFieldName)) { normalizeLat = parser.booleanValue(); normalizeLon = parser.booleanValue(); + } else if ("type".equals(currentFieldName)) { + type = parser.text(); } } } @@ -170,13 +175,22 @@ public class GeoBoundingBoxFilterParser implements FilterParser { if (mapper == null) { throw new QueryParsingException(parseContext.index(), "failed to find geo_point field [" + fieldName + "]"); } - if (mapper.fieldDataType() != GeoPointFieldDataType.TYPE) { + if (!(mapper instanceof GeoPointFieldMapper.GeoStringFieldMapper)) { throw new QueryParsingException(parseContext.index(), "field [" + fieldName + "] is not a geo_point field"); } + GeoPointFieldMapper geoMapper = ((GeoPointFieldMapper.GeoStringFieldMapper) mapper).geoMapper(); + fieldName = mapper.names().indexName(); + Filter filter; + if ("indexed".equals(type)) { + filter = IndexedGeoBoundingBoxFilter.create(topLeft, bottomRight, geoMapper); + } else if ("memory".equals(type)) { + filter = new InMemoryGeoBoundingBoxFilter(topLeft, bottomRight, fieldName, parseContext.indexCache().fieldData()); + } else { + throw new QueryParsingException(parseContext.index(), "geo bounding box type [" + type + "] not supported, either 'indexed' or 'memory' are allowed"); + } - Filter filter = new GeoBoundingBoxFilter(topLeft, bottomRight, fieldName, parseContext.indexCache().fieldData()); if (cache) { filter = parseContext.cacheFilter(filter, cacheKey); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceFilterBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceFilterBuilder.java index f4295cc32ae..ef3cd4b34ed 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceFilterBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceFilterBuilder.java @@ -42,7 +42,7 @@ public class GeoDistanceFilterBuilder extends BaseFilterBuilder { private GeoDistance geoDistance; - private Boolean optimizeBbox; + private String optimizeBbox; private Boolean cache; private String cacheKey; @@ -89,7 +89,7 @@ public class GeoDistanceFilterBuilder extends BaseFilterBuilder { return this; } - public GeoDistanceFilterBuilder optimizeBbox(boolean optimizeBbox) { + public GeoDistanceFilterBuilder optimizeBbox(String optimizeBbox) { this.optimizeBbox = optimizeBbox; return this; } @@ -127,7 +127,7 @@ public class GeoDistanceFilterBuilder extends BaseFilterBuilder { builder.field("distance_type", geoDistance.name().toLowerCase()); } if (optimizeBbox != null) { - builder.field("optimize_bbox", optimizeBbox.booleanValue()); + builder.field("optimize_bbox", optimizeBbox); } if (filterName != null) { builder.field("_name", filterName); diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceFilterParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceFilterParser.java index 774ea8b91a1..9789d5ea880 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceFilterParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceFilterParser.java @@ -74,7 +74,7 @@ public class GeoDistanceFilterParser implements FilterParser { Object vDistance = null; DistanceUnit unit = DistanceUnit.KILOMETERS; // default unit GeoDistance geoDistance = GeoDistance.ARC; - boolean optimizeBbox = true; + String optimizeBbox = "memory"; boolean normalizeLon = true; boolean normalizeLat = true; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { @@ -137,7 +137,7 @@ public class GeoDistanceFilterParser implements FilterParser { } else if ("_cache_key".equals(currentFieldName) || "_cacheKey".equals(currentFieldName)) { cacheKey = new CacheKeyFilter.Key(parser.text()); } else if ("optimize_bbox".equals(currentFieldName) || "optimizeBbox".equals(currentFieldName)) { - optimizeBbox = parser.booleanValue(); + optimizeBbox = parser.textOrNull(); } else if ("normalize".equals(currentFieldName)) { normalizeLat = parser.booleanValue(); normalizeLon = parser.booleanValue(); @@ -180,9 +180,10 @@ public class GeoDistanceFilterParser implements FilterParser { if (mapper.fieldDataType() != GeoPointFieldDataType.TYPE) { throw new QueryParsingException(parseContext.index(), "field [" + fieldName + "] is not a geo_point field"); } + GeoPointFieldMapper geoMapper = ((GeoPointFieldMapper.GeoStringFieldMapper) mapper).geoMapper(); fieldName = mapper.names().indexName(); - Filter filter = new GeoDistanceFilter(lat, lon, distance, geoDistance, fieldName, parseContext.indexCache().fieldData(), optimizeBbox); + Filter filter = new GeoDistanceFilter(lat, lon, distance, geoDistance, fieldName, geoMapper, parseContext.indexCache().fieldData(), optimizeBbox); if (cache) { filter = parseContext.cacheFilter(filter, cacheKey); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceRangeFilterBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceRangeFilterBuilder.java index a4774938873..d3e32e2e135 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceRangeFilterBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceRangeFilterBuilder.java @@ -49,6 +49,8 @@ public class GeoDistanceRangeFilterBuilder extends BaseFilterBuilder { private String filterName; + private String optimizeBbox; + public GeoDistanceRangeFilterBuilder(String name) { this.name = name; } @@ -123,6 +125,11 @@ public class GeoDistanceRangeFilterBuilder extends BaseFilterBuilder { return this; } + public GeoDistanceRangeFilterBuilder optimizeBbox(String optimizeBbox) { + this.optimizeBbox = optimizeBbox; + return this; + } + /** * Sets the filter name for the filter that can be used when searching for matched_filters per hit. */ @@ -158,6 +165,9 @@ public class GeoDistanceRangeFilterBuilder extends BaseFilterBuilder { if (geoDistance != null) { builder.field("distance_type", geoDistance.name().toLowerCase()); } + if (optimizeBbox != null) { + builder.field("optimize_bbox", optimizeBbox); + } if (filterName != null) { builder.field("_name", filterName); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceRangeFilterParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceRangeFilterParser.java index 11268859f3d..9615bb4b465 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceRangeFilterParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/GeoDistanceRangeFilterParser.java @@ -76,7 +76,7 @@ public class GeoDistanceRangeFilterParser implements FilterParser { boolean includeUpper = true; DistanceUnit unit = DistanceUnit.KILOMETERS; // default unit GeoDistance geoDistance = GeoDistance.ARC; - boolean optimizeBbox = true; + String optimizeBbox = "memory"; boolean normalizeLon = true; boolean normalizeLat = true; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { @@ -186,7 +186,7 @@ public class GeoDistanceRangeFilterParser implements FilterParser { } else if ("_cache_key".equals(currentFieldName) || "_cacheKey".equals(currentFieldName)) { cacheKey = new CacheKeyFilter.Key(parser.text()); } else if ("optimize_bbox".equals(currentFieldName) || "optimizeBbox".equals(currentFieldName)) { - optimizeBbox = parser.booleanValue(); + optimizeBbox = parser.textOrNull(); } else if ("normalize".equals(currentFieldName)) { normalizeLat = parser.booleanValue(); normalizeLon = parser.booleanValue(); @@ -237,9 +237,10 @@ public class GeoDistanceRangeFilterParser implements FilterParser { if (mapper.fieldDataType() != GeoPointFieldDataType.TYPE) { throw new QueryParsingException(parseContext.index(), "field [" + fieldName + "] is not a geo_point field"); } + GeoPointFieldMapper geoMapper = ((GeoPointFieldMapper.GeoStringFieldMapper) mapper).geoMapper(); fieldName = mapper.names().indexName(); - Filter filter = new GeoDistanceRangeFilter(lat, lon, from, to, includeLower, includeUpper, geoDistance, fieldName, parseContext.indexCache().fieldData(), optimizeBbox); + Filter filter = new GeoDistanceRangeFilter(lat, lon, from, to, includeLower, includeUpper, geoDistance, fieldName, geoMapper, parseContext.indexCache().fieldData(), optimizeBbox); if (cache) { filter = parseContext.cacheFilter(filter, cacheKey); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistance.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistance.java index 57fef026765..205249704d9 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistance.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistance.java @@ -131,12 +131,12 @@ public enum GeoDistance { maxLon = MAX_LON; } - Point left = new Point(Math.toDegrees(minLat), Math.toDegrees(minLon)); - Point right = new Point(Math.toDegrees(maxLat), Math.toDegrees(maxLon)); + Point topLeft = new Point(Math.toDegrees(maxLat), Math.toDegrees(minLon)); + Point bottomRight = new Point(Math.toDegrees(minLat), Math.toDegrees(maxLon)); if (minLon > maxLon) { - return new Meridian180DistanceBoundingCheck(left, right); + return new Meridian180DistanceBoundingCheck(topLeft, bottomRight); } - return new SimpleDistanceBoundingCheck(left, right); + return new SimpleDistanceBoundingCheck(topLeft, bottomRight); } public static GeoDistance fromString(String s) { @@ -158,6 +158,10 @@ public enum GeoDistance { public static interface DistanceBoundingCheck { boolean isWithin(double targetLatitude, double targetLongitude); + + Point topLeft(); + + Point bottomRight(); } public static AlwaysDistanceBoundingCheck ALWAYS_INSTANCE = new AlwaysDistanceBoundingCheck(); @@ -166,36 +170,60 @@ public enum GeoDistance { @Override public boolean isWithin(double targetLatitude, double targetLongitude) { return true; } + + @Override public Point topLeft() { + return null; + } + + @Override public Point bottomRight() { + return null; + } } public static class Meridian180DistanceBoundingCheck implements DistanceBoundingCheck { - private final Point left; - private final Point right; + private final Point topLeft; + private final Point bottomRight; - public Meridian180DistanceBoundingCheck(Point left, Point right) { - this.left = left; - this.right = right; + public Meridian180DistanceBoundingCheck(Point topLeft, Point bottomRight) { + this.topLeft = topLeft; + this.bottomRight = bottomRight; } @Override public boolean isWithin(double targetLatitude, double targetLongitude) { - return (targetLatitude >= left.lat && targetLatitude <= right.lat) && - (targetLongitude >= left.lon || targetLongitude <= right.lon); + return (targetLatitude >= bottomRight.lat && targetLatitude <= topLeft.lat) && + (targetLongitude >= topLeft.lon || targetLongitude <= bottomRight.lon); + } + + @Override public Point topLeft() { + return topLeft; + } + + @Override public Point bottomRight() { + return bottomRight; } } public static class SimpleDistanceBoundingCheck implements DistanceBoundingCheck { - private final Point left; - private final Point right; + private final Point topLeft; + private final Point bottomRight; - public SimpleDistanceBoundingCheck(Point left, Point right) { - this.left = left; - this.right = right; + public SimpleDistanceBoundingCheck(Point topLeft, Point bottomRight) { + this.topLeft = topLeft; + this.bottomRight = bottomRight; } @Override public boolean isWithin(double targetLatitude, double targetLongitude) { - return (targetLatitude >= left.lat && targetLatitude <= right.lat) && - (targetLongitude >= left.lon && targetLongitude <= right.lon); + return (targetLatitude >= bottomRight.lat && targetLatitude <= topLeft.lat) && + (targetLongitude >= topLeft.lon && targetLongitude <= bottomRight.lon); + } + + @Override public Point topLeft() { + return topLeft; + } + + @Override public Point bottomRight() { + return bottomRight; } } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistanceFilter.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistanceFilter.java index 0605d825f96..f25630e4dc5 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistanceFilter.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistanceFilter.java @@ -22,16 +22,21 @@ 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.elasticsearch.ElasticSearchIllegalArgumentException; +import org.elasticsearch.common.collect.ImmutableList; +import org.elasticsearch.common.lucene.docset.AndDocSet; +import org.elasticsearch.common.lucene.docset.DocSet; +import org.elasticsearch.common.lucene.docset.DocSets; 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.geo.GeoPointFieldData; import org.elasticsearch.index.mapper.geo.GeoPointFieldDataType; +import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper; import java.io.IOException; /** - * @author kimchy (shay.banon) */ public class GeoDistanceFilter extends Filter { @@ -48,10 +53,11 @@ public class GeoDistanceFilter extends Filter { private final FieldDataCache fieldDataCache; private final GeoDistance.FixedSourceDistance fixedSourceDistance; - private final GeoDistance.DistanceBoundingCheck distanceBoundingCheck; + private GeoDistance.DistanceBoundingCheck distanceBoundingCheck; + private final Filter boundingBoxFilter; - public GeoDistanceFilter(double lat, double lon, double distance, GeoDistance geoDistance, String fieldName, FieldDataCache fieldDataCache, - boolean optimizeBbox) { + public GeoDistanceFilter(double lat, double lon, double distance, GeoDistance geoDistance, String fieldName, GeoPointFieldMapper mapper, FieldDataCache fieldDataCache, + String optimizeBbox) { this.lat = lat; this.lon = lon; this.distance = distance; @@ -60,7 +66,20 @@ public class GeoDistanceFilter extends Filter { this.fieldDataCache = fieldDataCache; this.fixedSourceDistance = geoDistance.fixedSourceDistance(lat, lon, DistanceUnit.MILES); - this.distanceBoundingCheck = optimizeBbox ? GeoDistance.distanceBoundingCheck(lat, lon, distance, DistanceUnit.MILES) : GeoDistance.ALWAYS_INSTANCE; + if (optimizeBbox != null && !"none".equals(optimizeBbox)) { + distanceBoundingCheck = GeoDistance.distanceBoundingCheck(lat, lon, distance, DistanceUnit.MILES); + if ("memory".equals(optimizeBbox)) { + boundingBoxFilter = null; + } else if ("indexed".equals(optimizeBbox)) { + boundingBoxFilter = IndexedGeoBoundingBoxFilter.create(distanceBoundingCheck.topLeft(), distanceBoundingCheck.bottomRight(), mapper); + distanceBoundingCheck = GeoDistance.ALWAYS_INSTANCE; // fine, we do the bounding box check using the filter + } else { + throw new ElasticSearchIllegalArgumentException("type [" + optimizeBbox + "] for bounding box optimization not supported"); + } + } else { + distanceBoundingCheck = GeoDistance.ALWAYS_INSTANCE; + boundingBoxFilter = null; + } } public double lat() { @@ -84,8 +103,21 @@ public class GeoDistanceFilter extends Filter { } @Override public DocIdSet getDocIdSet(IndexReader reader) throws IOException { + DocSet boundingBoxDocSet = null; + if (boundingBoxFilter != null) { + DocIdSet docIdSet = boundingBoxFilter.getDocIdSet(reader); + if (docIdSet == null) { + return null; + } + boundingBoxDocSet = DocSets.convert(reader, docIdSet); + } final GeoPointFieldData fieldData = (GeoPointFieldData) fieldDataCache.cache(GeoPointFieldDataType.TYPE, reader, fieldName); - return new GeoDistanceDocSet(reader.maxDoc(), fieldData, fixedSourceDistance, distanceBoundingCheck, distance); + GeoDistanceDocSet distDocSet = new GeoDistanceDocSet(reader.maxDoc(), fieldData, fixedSourceDistance, distanceBoundingCheck, distance); + if (boundingBoxDocSet == null) { + return distDocSet; + } else { + return new AndDocSet(ImmutableList.of(boundingBoxDocSet, distDocSet)); + } } @Override diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistanceRangeFilter.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistanceRangeFilter.java index 1e3c87eb2e2..cd7fd7a3b41 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistanceRangeFilter.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoDistanceRangeFilter.java @@ -23,11 +23,17 @@ 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.ElasticSearchIllegalArgumentException; +import org.elasticsearch.common.collect.ImmutableList; +import org.elasticsearch.common.lucene.docset.AndDocSet; +import org.elasticsearch.common.lucene.docset.DocSet; +import org.elasticsearch.common.lucene.docset.DocSets; 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.geo.GeoPointFieldData; import org.elasticsearch.index.mapper.geo.GeoPointFieldDataType; +import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper; import java.io.IOException; @@ -44,14 +50,15 @@ public class GeoDistanceRangeFilter extends Filter { private final GeoDistance geoDistance; private final GeoDistance.FixedSourceDistance fixedSourceDistance; - private final GeoDistance.DistanceBoundingCheck distanceBoundingCheck; + private GeoDistance.DistanceBoundingCheck distanceBoundingCheck; + private final Filter boundingBoxFilter; 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, - boolean optimizeBbox) { + public GeoDistanceRangeFilter(double lat, double lon, Double lowerVal, Double upperVal, boolean includeLower, boolean includeUpper, GeoDistance geoDistance, String fieldName, GeoPointFieldMapper mapper, FieldDataCache fieldDataCache, + String optimizeBbox) { this.lat = lat; this.lon = lon; this.geoDistance = geoDistance; @@ -73,10 +80,23 @@ public class GeoDistanceRangeFilter extends Filter { inclusiveUpperPoint = NumericUtils.sortableLongToDouble(includeUpper ? i : (i - 1L)); } else { inclusiveUpperPoint = Double.POSITIVE_INFINITY; - optimizeBbox = false; + optimizeBbox = null; } - this.distanceBoundingCheck = optimizeBbox ? GeoDistance.distanceBoundingCheck(lat, lon, inclusiveUpperPoint, DistanceUnit.MILES) : GeoDistance.ALWAYS_INSTANCE; + if (optimizeBbox != null && !"none".equals(optimizeBbox)) { + distanceBoundingCheck = GeoDistance.distanceBoundingCheck(lat, lon, inclusiveUpperPoint, DistanceUnit.MILES); + if ("memory".equals(optimizeBbox)) { + boundingBoxFilter = null; + } else if ("indexed".equals(optimizeBbox)) { + boundingBoxFilter = IndexedGeoBoundingBoxFilter.create(distanceBoundingCheck.topLeft(), distanceBoundingCheck.bottomRight(), mapper); + distanceBoundingCheck = GeoDistance.ALWAYS_INSTANCE; // fine, we do the bounding box check using the filter + } else { + throw new ElasticSearchIllegalArgumentException("type [" + optimizeBbox + "] for bounding box optimization not supported"); + } + } else { + distanceBoundingCheck = GeoDistance.ALWAYS_INSTANCE; + boundingBoxFilter = null; + } } public double lat() { @@ -96,8 +116,21 @@ public class GeoDistanceRangeFilter extends Filter { } @Override public DocIdSet getDocIdSet(IndexReader reader) throws IOException { + DocSet boundingBoxDocSet = null; + if (boundingBoxFilter != null) { + DocIdSet docIdSet = boundingBoxFilter.getDocIdSet(reader); + if (docIdSet == null) { + return null; + } + boundingBoxDocSet = DocSets.convert(reader, docIdSet); + } final GeoPointFieldData fieldData = (GeoPointFieldData) fieldDataCache.cache(GeoPointFieldDataType.TYPE, reader, fieldName); - return new GeoDistanceRangeDocSet(reader.maxDoc(), fieldData, fixedSourceDistance, distanceBoundingCheck, inclusiveLowerPoint, inclusiveUpperPoint); + GeoDistanceRangeDocSet distDocSet = new GeoDistanceRangeDocSet(reader.maxDoc(), fieldData, fixedSourceDistance, distanceBoundingCheck, inclusiveLowerPoint, inclusiveUpperPoint); + if (boundingBoxDocSet == null) { + return distDocSet; + } else { + return new AndDocSet(ImmutableList.of(boundingBoxDocSet, distDocSet)); + } } @Override diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoBoundingBoxFilter.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/InMemoryGeoBoundingBoxFilter.java similarity index 87% rename from modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoBoundingBoxFilter.java rename to modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/InMemoryGeoBoundingBoxFilter.java index ea3cc61c0bc..11d09d25c3c 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/GeoBoundingBoxFilter.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/InMemoryGeoBoundingBoxFilter.java @@ -32,7 +32,7 @@ import java.io.IOException; /** * @author kimchy (shay.banon) */ -public class GeoBoundingBoxFilter extends Filter { +public class InMemoryGeoBoundingBoxFilter extends Filter { private final Point topLeft; @@ -42,7 +42,7 @@ public class GeoBoundingBoxFilter extends Filter { private final FieldDataCache fieldDataCache; - public GeoBoundingBoxFilter(Point topLeft, Point bottomRight, String fieldName, FieldDataCache fieldDataCache) { + public InMemoryGeoBoundingBoxFilter(Point topLeft, Point bottomRight, String fieldName, FieldDataCache fieldDataCache) { this.topLeft = topLeft; this.bottomRight = bottomRight; this.fieldName = fieldName; @@ -66,18 +66,18 @@ public class GeoBoundingBoxFilter extends Filter { //checks to see if bounding box crosses 180 degrees if (topLeft.lon > bottomRight.lon) { - return new LeftGeoBoundingBoxDocSet(reader.maxDoc(), fieldData, topLeft, bottomRight); + return new Meridian180GeoBoundingBoxDocSet(reader.maxDoc(), fieldData, topLeft, bottomRight); } else { - return new RightGeoBoundingBoxDocSet(reader.maxDoc(), fieldData, topLeft, bottomRight); + return new GeoBoundingBoxDocSet(reader.maxDoc(), fieldData, topLeft, bottomRight); } } - public static class LeftGeoBoundingBoxDocSet extends GetDocSet { + public static class Meridian180GeoBoundingBoxDocSet extends GetDocSet { private final GeoPointFieldData fieldData; private final Point topLeft; private final Point bottomRight; - public LeftGeoBoundingBoxDocSet(int maxDoc, GeoPointFieldData fieldData, Point topLeft, Point bottomRight) { + public Meridian180GeoBoundingBoxDocSet(int maxDoc, GeoPointFieldData fieldData, Point topLeft, Point bottomRight) { super(maxDoc); this.fieldData = fieldData; this.topLeft = topLeft; @@ -120,12 +120,12 @@ public class GeoBoundingBoxFilter extends Filter { } } - public static class RightGeoBoundingBoxDocSet extends GetDocSet { + public static class GeoBoundingBoxDocSet extends GetDocSet { private final GeoPointFieldData fieldData; private final Point topLeft; private final Point bottomRight; - public RightGeoBoundingBoxDocSet(int maxDoc, GeoPointFieldData fieldData, Point topLeft, Point bottomRight) { + public GeoBoundingBoxDocSet(int maxDoc, GeoPointFieldData fieldData, Point topLeft, Point bottomRight) { super(maxDoc); this.fieldData = fieldData; this.topLeft = topLeft; diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/IndexedGeoBoundingBoxFilter.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/IndexedGeoBoundingBoxFilter.java new file mode 100644 index 00000000000..908a6fbe230 --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/search/geo/IndexedGeoBoundingBoxFilter.java @@ -0,0 +1,159 @@ +/* + * 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.OpenBitSet; +import org.elasticsearch.ElasticSearchIllegalArgumentException; +import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper; + +import java.io.IOException; + +/** + */ +public class IndexedGeoBoundingBoxFilter { + + public static Filter create(Point topLeft, Point bottomRight, GeoPointFieldMapper fieldMapper) { + if (!fieldMapper.isEnableLatLon()) { + throw new ElasticSearchIllegalArgumentException("lat/lon is not enabled (indexed) for field [" + fieldMapper.name() + "], can't use indexed filter on it"); + } + //checks to see if bounding box crosses 180 degrees + if (topLeft.lon > bottomRight.lon) { + return new LeftGeoBoundingBoxFilter(topLeft, bottomRight, fieldMapper); + } else { + return new RightGeoBoundingBoxFilter(topLeft, bottomRight, fieldMapper); + } + } + + static class LeftGeoBoundingBoxFilter extends Filter { + + final Filter lonFilter1; + final Filter lonFilter2; + final Filter latFilter; + + public LeftGeoBoundingBoxFilter(Point topLeft, Point bottomRight, GeoPointFieldMapper fieldMapper) { + lonFilter1 = fieldMapper.lonMapper().rangeFilter(null, bottomRight.lon, true, true); + lonFilter2 = fieldMapper.lonMapper().rangeFilter(topLeft.lon, null, true, true); + latFilter = fieldMapper.latMapper().rangeFilter(bottomRight.lat, topLeft.lat, true, true); + } + + @Override public OpenBitSet getDocIdSet(IndexReader reader) throws IOException { + OpenBitSet main; + DocIdSet set = lonFilter1.getDocIdSet(reader); + if (set == null || set == DocIdSet.EMPTY_DOCIDSET) { + main = null; + } else { + main = (OpenBitSet) set; + } + + set = lonFilter2.getDocIdSet(reader); + if (set == null || set == DocIdSet.EMPTY_DOCIDSET) { + if (main == null) { + return null; + } else { + // nothing to do here, we remain with the main one + } + } else { + if (main == null) { + main = (OpenBitSet) set; + } else { + main.or((OpenBitSet) set); + } + } + + set = latFilter.getDocIdSet(reader); + if (set == null || set == DocIdSet.EMPTY_DOCIDSET) { + return null; + } + main.and((OpenBitSet) set); + return main; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + LeftGeoBoundingBoxFilter that = (LeftGeoBoundingBoxFilter) o; + + if (latFilter != null ? !latFilter.equals(that.latFilter) : that.latFilter != null) return false; + if (lonFilter1 != null ? !lonFilter1.equals(that.lonFilter1) : that.lonFilter1 != null) return false; + if (lonFilter2 != null ? !lonFilter2.equals(that.lonFilter2) : that.lonFilter2 != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = lonFilter1 != null ? lonFilter1.hashCode() : 0; + result = 31 * result + (lonFilter2 != null ? lonFilter2.hashCode() : 0); + result = 31 * result + (latFilter != null ? latFilter.hashCode() : 0); + return result; + } + } + + static class RightGeoBoundingBoxFilter extends Filter { + + final Filter lonFilter; + final Filter latFilter; + + public RightGeoBoundingBoxFilter(Point topLeft, Point bottomRight, GeoPointFieldMapper fieldMapper) { + lonFilter = fieldMapper.lonMapper().rangeFilter(topLeft.lon, bottomRight.lon, true, true); + latFilter = fieldMapper.latMapper().rangeFilter(bottomRight.lat, topLeft.lat, true, true); + } + + @Override public OpenBitSet getDocIdSet(IndexReader reader) throws IOException { + OpenBitSet main; + DocIdSet set = lonFilter.getDocIdSet(reader); + if (set == null || set == DocIdSet.EMPTY_DOCIDSET) { + return null; + } + main = (OpenBitSet) set; + set = latFilter.getDocIdSet(reader); + if (set == null || set == DocIdSet.EMPTY_DOCIDSET) { + return null; + } + main.and((OpenBitSet) set); + return main; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RightGeoBoundingBoxFilter that = (RightGeoBoundingBoxFilter) o; + + if (latFilter != null ? !latFilter.equals(that.latFilter) : that.latFilter != null) return false; + if (lonFilter != null ? !lonFilter.equals(that.lonFilter) : that.lonFilter != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = lonFilter != null ? lonFilter.hashCode() : 0; + result = 31 * result + (latFilter != null ? latFilter.hashCode() : 0); + return result; + } + } +} diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/SimpleIndexQueryParserTests.java b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/SimpleIndexQueryParserTests.java index 1563038e73a..a13ebb5c759 100644 --- a/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/SimpleIndexQueryParserTests.java +++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/SimpleIndexQueryParserTests.java @@ -51,9 +51,9 @@ import org.elasticsearch.index.engine.IndexEngineModule; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperServiceModule; import org.elasticsearch.index.search.NumericRangeFieldDataFilter; -import org.elasticsearch.index.search.geo.GeoBoundingBoxFilter; import org.elasticsearch.index.search.geo.GeoDistanceFilter; import org.elasticsearch.index.search.geo.GeoPolygonFilter; +import org.elasticsearch.index.search.geo.InMemoryGeoBoundingBoxFilter; import org.elasticsearch.index.settings.IndexSettingsModule; import org.elasticsearch.index.similarity.SimilarityModule; import org.elasticsearch.indices.query.IndicesQueriesModule; @@ -1531,7 +1531,7 @@ public class SimpleIndexQueryParserTests { assertThat(parsedQuery.query(), instanceOf(DeletionAwareConstantScoreQuery.class)); assertThat(parsedQuery.namedFilters().containsKey("test"), equalTo(true)); DeletionAwareConstantScoreQuery constantScoreQuery = (DeletionAwareConstantScoreQuery) parsedQuery.query(); - GeoBoundingBoxFilter filter = (GeoBoundingBoxFilter) constantScoreQuery.getFilter(); + InMemoryGeoBoundingBoxFilter filter = (InMemoryGeoBoundingBoxFilter) constantScoreQuery.getFilter(); assertThat(filter.fieldName(), equalTo("location")); assertThat(filter.topLeft().lat, closeTo(40, 0.00001)); assertThat(filter.topLeft().lon, closeTo(-70, 0.00001)); @@ -1546,7 +1546,7 @@ public class SimpleIndexQueryParserTests { Query parsedQuery = queryParser.parse(query).query(); assertThat(parsedQuery, instanceOf(DeletionAwareConstantScoreQuery.class)); DeletionAwareConstantScoreQuery constantScoreQuery = (DeletionAwareConstantScoreQuery) parsedQuery; - GeoBoundingBoxFilter filter = (GeoBoundingBoxFilter) constantScoreQuery.getFilter(); + InMemoryGeoBoundingBoxFilter filter = (InMemoryGeoBoundingBoxFilter) constantScoreQuery.getFilter(); assertThat(filter.fieldName(), equalTo("location")); assertThat(filter.topLeft().lat, closeTo(40, 0.00001)); assertThat(filter.topLeft().lon, closeTo(-70, 0.00001)); @@ -1560,7 +1560,7 @@ public class SimpleIndexQueryParserTests { Query parsedQuery = queryParser.parse(query).query(); assertThat(parsedQuery, instanceOf(DeletionAwareConstantScoreQuery.class)); DeletionAwareConstantScoreQuery constantScoreQuery = (DeletionAwareConstantScoreQuery) parsedQuery; - GeoBoundingBoxFilter filter = (GeoBoundingBoxFilter) constantScoreQuery.getFilter(); + InMemoryGeoBoundingBoxFilter filter = (InMemoryGeoBoundingBoxFilter) constantScoreQuery.getFilter(); assertThat(filter.fieldName(), equalTo("location")); assertThat(filter.topLeft().lat, closeTo(40, 0.00001)); assertThat(filter.topLeft().lon, closeTo(-70, 0.00001)); @@ -1574,7 +1574,7 @@ public class SimpleIndexQueryParserTests { Query parsedQuery = queryParser.parse(query).query(); assertThat(parsedQuery, instanceOf(DeletionAwareConstantScoreQuery.class)); DeletionAwareConstantScoreQuery constantScoreQuery = (DeletionAwareConstantScoreQuery) parsedQuery; - GeoBoundingBoxFilter filter = (GeoBoundingBoxFilter) constantScoreQuery.getFilter(); + InMemoryGeoBoundingBoxFilter filter = (InMemoryGeoBoundingBoxFilter) constantScoreQuery.getFilter(); assertThat(filter.fieldName(), equalTo("location")); assertThat(filter.topLeft().lat, closeTo(40, 0.00001)); assertThat(filter.topLeft().lon, closeTo(-70, 0.00001)); @@ -1588,7 +1588,7 @@ public class SimpleIndexQueryParserTests { Query parsedQuery = queryParser.parse(query).query(); assertThat(parsedQuery, instanceOf(DeletionAwareConstantScoreQuery.class)); DeletionAwareConstantScoreQuery constantScoreQuery = (DeletionAwareConstantScoreQuery) parsedQuery; - GeoBoundingBoxFilter filter = (GeoBoundingBoxFilter) constantScoreQuery.getFilter(); + InMemoryGeoBoundingBoxFilter filter = (InMemoryGeoBoundingBoxFilter) constantScoreQuery.getFilter(); assertThat(filter.fieldName(), equalTo("location")); assertThat(filter.topLeft().lat, closeTo(40, 0.00001)); assertThat(filter.topLeft().lon, closeTo(-70, 0.00001)); diff --git a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/geo/GeoBoundingBoxTests.java b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/geo/GeoBoundingBoxTests.java index 3c930d3b655..f26a7692e86 100644 --- a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/geo/GeoBoundingBoxTests.java +++ b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/geo/GeoBoundingBoxTests.java @@ -118,7 +118,15 @@ public class GeoBoundingBoxTests extends AbstractNodesTests { assertThat(searchResponse.hits().getTotalHits(), equalTo(2l)); assertThat(searchResponse.hits().hits().length, equalTo(2)); for (SearchHit hit : searchResponse.hits()) { - System.err.println("-->" + hit.id()); + assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("5"))); + } + + searchResponse = client.prepareSearch() // from NY + .setQuery(filteredQuery(matchAllQuery(), geoBoundingBoxFilter("location").topLeft(40.73, -74.1).bottomRight(40.717, -73.99).type("indexed"))) + .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("1"), equalTo("3"), equalTo("5"))); } } @@ -183,6 +191,12 @@ public class GeoBoundingBoxTests extends AbstractNodesTests { assertThat(searchResponse.hits().getTotalHits(), equalTo(1l)); assertThat(searchResponse.hits().hits().length, equalTo(1)); assertThat(searchResponse.hits().getAt(0).id(), equalTo("2")); + searchResponse = client.prepareSearch() + .setQuery(filteredQuery(matchAllQuery(), geoBoundingBoxFilter("location").topLeft(41, -11).bottomRight(40, 9).type("indexed"))) + .execute().actionGet(); + assertThat(searchResponse.hits().getTotalHits(), equalTo(1l)); + assertThat(searchResponse.hits().hits().length, equalTo(1)); + assertThat(searchResponse.hits().getAt(0).id(), equalTo("2")); searchResponse = client.prepareSearch() .setQuery(filteredQuery(matchAllQuery(), geoBoundingBoxFilter("location").topLeft(41, -9).bottomRight(40, 11))) @@ -190,6 +204,12 @@ public class GeoBoundingBoxTests extends AbstractNodesTests { assertThat(searchResponse.hits().getTotalHits(), equalTo(1l)); assertThat(searchResponse.hits().hits().length, equalTo(1)); assertThat(searchResponse.hits().getAt(0).id(), equalTo("3")); + searchResponse = client.prepareSearch() + .setQuery(filteredQuery(matchAllQuery(), geoBoundingBoxFilter("location").topLeft(41, -9).bottomRight(40, 11).type("indexed"))) + .execute().actionGet(); + assertThat(searchResponse.hits().getTotalHits(), equalTo(1l)); + assertThat(searchResponse.hits().hits().length, equalTo(1)); + assertThat(searchResponse.hits().getAt(0).id(), equalTo("3")); searchResponse = client.prepareSearch() .setQuery(filteredQuery(matchAllQuery(), geoBoundingBoxFilter("location").topLeft(11, 171).bottomRight(1, -169))) @@ -197,6 +217,12 @@ public class GeoBoundingBoxTests extends AbstractNodesTests { assertThat(searchResponse.hits().getTotalHits(), equalTo(1l)); assertThat(searchResponse.hits().hits().length, equalTo(1)); assertThat(searchResponse.hits().getAt(0).id(), equalTo("5")); + searchResponse = client.prepareSearch() + .setQuery(filteredQuery(matchAllQuery(), geoBoundingBoxFilter("location").topLeft(11, 171).bottomRight(1, -169).type("indexed"))) + .execute().actionGet(); + assertThat(searchResponse.hits().getTotalHits(), equalTo(1l)); + assertThat(searchResponse.hits().hits().length, equalTo(1)); + assertThat(searchResponse.hits().getAt(0).id(), equalTo("5")); searchResponse = client.prepareSearch() .setQuery(filteredQuery(matchAllQuery(), geoBoundingBoxFilter("location").topLeft(9, 169).bottomRight(-1, -171))) @@ -204,6 +230,12 @@ public class GeoBoundingBoxTests extends AbstractNodesTests { assertThat(searchResponse.hits().getTotalHits(), equalTo(1l)); assertThat(searchResponse.hits().hits().length, equalTo(1)); assertThat(searchResponse.hits().getAt(0).id(), equalTo("9")); + searchResponse = client.prepareSearch() + .setQuery(filteredQuery(matchAllQuery(), geoBoundingBoxFilter("location").topLeft(9, 169).bottomRight(-1, -171).type("indexed"))) + .execute().actionGet(); + assertThat(searchResponse.hits().getTotalHits(), equalTo(1l)); + assertThat(searchResponse.hits().hits().length, equalTo(1)); + assertThat(searchResponse.hits().getAt(0).id(), equalTo("9")); } @Test public void limit2BoundingBoxTest() throws Exception { @@ -240,6 +272,12 @@ public class GeoBoundingBoxTests extends AbstractNodesTests { geoBoundingBoxFilter("location").topLeft(74.579421999999994, 143.5).bottomRight(-66.668903999999998, 113.96875)) ).execute().actionGet(); assertThat(searchResponse.hits().totalHits(), equalTo(1l)); + searchResponse = client.prepareSearch() + .setQuery( + filteredQuery(termQuery("userid", 880), + geoBoundingBoxFilter("location").topLeft(74.579421999999994, 143.5).bottomRight(-66.668903999999998, 113.96875).type("indexed")) + ).execute().actionGet(); + assertThat(searchResponse.hits().totalHits(), equalTo(1l)); searchResponse = client.prepareSearch() .setQuery( @@ -247,6 +285,12 @@ public class GeoBoundingBoxTests extends AbstractNodesTests { geoBoundingBoxFilter("location").topLeft(74.579421999999994, 143.5).bottomRight(-66.668903999999998, 113.96875)) ).execute().actionGet(); assertThat(searchResponse.hits().totalHits(), equalTo(1l)); + searchResponse = client.prepareSearch() + .setQuery( + filteredQuery(termQuery("userid", 534), + geoBoundingBoxFilter("location").topLeft(74.579421999999994, 143.5).bottomRight(-66.668903999999998, 113.96875).type("indexed")) + ).execute().actionGet(); + assertThat(searchResponse.hits().totalHits(), equalTo(1l)); } } 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 eefe52dda1e..9700255ceec 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 @@ -38,7 +38,6 @@ import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; /** - * @author kimchy (shay.banon) */ public class GeoDistanceTests extends AbstractNodesTests { @@ -122,6 +121,14 @@ public class GeoDistanceTests extends AbstractNodesTests { for (SearchHit hit : searchResponse.hits()) { assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5"), equalTo("6"))); } + searchResponse = client.prepareSearch() // from NY + .setQuery(filteredQuery(matchAllQuery(), geoDistanceFilter("location").distance("3km").point(40.7143528, -74.0059731).optimizeBbox("indexed"))) + .execute().actionGet(); + assertThat(searchResponse.hits().getTotalHits(), equalTo(5l)); + assertThat(searchResponse.hits().hits().length, equalTo(5)); + for (SearchHit hit : searchResponse.hits()) { + assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5"), equalTo("6"))); + } // now with a PLANE type searchResponse = client.prepareSearch() // from NY @@ -143,6 +150,14 @@ public class GeoDistanceTests extends AbstractNodesTests { for (SearchHit hit : searchResponse.hits()) { assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5"))); } + searchResponse = client.prepareSearch() // from NY + .setQuery(filteredQuery(matchAllQuery(), geoDistanceFilter("location").distance("2km").point(40.7143528, -74.0059731).optimizeBbox("indexed"))) + .execute().actionGet(); + assertThat(searchResponse.hits().getTotalHits(), equalTo(4l)); + assertThat(searchResponse.hits().hits().length, equalTo(4)); + for (SearchHit hit : searchResponse.hits()) { + assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5"))); + } searchResponse = client.prepareSearch() // from NY .setQuery(filteredQuery(matchAllQuery(), geoDistanceFilter("location").distance("1.242mi").point(40.7143528, -74.0059731))) @@ -152,6 +167,14 @@ public class GeoDistanceTests extends AbstractNodesTests { for (SearchHit hit : searchResponse.hits()) { assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5"))); } + searchResponse = client.prepareSearch() // from NY + .setQuery(filteredQuery(matchAllQuery(), geoDistanceFilter("location").distance("1.242mi").point(40.7143528, -74.0059731).optimizeBbox("indexed"))) + .execute().actionGet(); + assertThat(searchResponse.hits().getTotalHits(), equalTo(4l)); + assertThat(searchResponse.hits().hits().length, equalTo(4)); + for (SearchHit hit : searchResponse.hits()) { + 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))) @@ -161,6 +184,14 @@ public class GeoDistanceTests extends AbstractNodesTests { for (SearchHit hit : searchResponse.hits()) { assertThat(hit.id(), anyOf(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).optimizeBbox("indexed"))) + .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