diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeIndexer.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeIndexer.java index 2df89bee955..7346a94c5f1 100644 --- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeIndexer.java +++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeIndexer.java @@ -6,8 +6,6 @@ package org.elasticsearch.xpack.spatial.index.mapper; import org.apache.lucene.document.XYShape; -import org.apache.lucene.geo.XYLine; -import org.apache.lucene.geo.XYPolygon; import org.apache.lucene.index.IndexableField; import org.elasticsearch.geometry.Circle; import org.elasticsearch.geometry.Geometry; @@ -75,8 +73,7 @@ public class ShapeIndexer implements AbstractGeometryFieldMapper.Indexer { - QueryShardContext context; - String fieldName; - ShapeRelation relation; - - ShapeVisitor(QueryShardContext context, String fieldName, ShapeRelation relation) { - this.context = context; - this.fieldName = fieldName; - this.relation = relation; - } - - @Override - public Query visit(Circle circle) { - throw new QueryShardException(context, "Field [" + fieldName + "] found and unknown shape Circle"); - } - - @Override - public Query visit(GeometryCollection collection) { - BooleanQuery.Builder bqb = new BooleanQuery.Builder(); - visit(bqb, collection); - return bqb.build(); - } - - private void visit(BooleanQuery.Builder bqb, GeometryCollection collection) { - BooleanClause.Occur occur; - if (relation == ShapeRelation.CONTAINS || relation == ShapeRelation.DISJOINT) { - // all shapes must be disjoint / must be contained in relation to the indexed shape. - occur = BooleanClause.Occur.MUST; - } else { - // at least one shape must intersect / contain the indexed shape. - occur = BooleanClause.Occur.SHOULD; - } - for (Geometry shape : collection) { - bqb.add(shape.visit(this), occur); - } - } - - @Override - public Query visit(Line line) { - return XYShape.newLineQuery(fieldName, relation.getLuceneRelation(), - new XYLine(doubleArrayToFloatArray(line.getX()), doubleArrayToFloatArray(line.getY()))); - } - - @Override - public Query visit(LinearRing ring) { - throw new QueryShardException(context, "Field [" + fieldName + "] found and unsupported shape LinearRing"); - } - - @Override - public Query visit(MultiLine multiLine) { - XYLine[] lines = new XYLine[multiLine.size()]; - for (int i=0; i geometries = visitor.geometries(); + if (geometries.size() == 0) { + return new MatchNoDocsQuery(); } + return XYShape.newGeometryQuery(fieldName, relation.getLuceneRelation(), + geometries.toArray(new XYGeometry[geometries.size()])); } - private static float[] doubleArrayToFloatArray(double[] array) { - float[] result = new float[array.length]; - for (int i = 0; i < array.length; ++i) { - result[i] = (float) array[i]; + private static class LuceneGeometryCollector implements GeometryVisitor { + private final List geometries = new ArrayList<>(); + private final String name; + private final QueryShardContext context; + + private LuceneGeometryCollector(String name, QueryShardContext context) { + this.name = name; + this.context = context; + } + + List geometries() { + return geometries; + } + + @Override + public Void visit(Circle circle) { + if (circle.isEmpty() == false) { + geometries.add(ShapeUtils.toLuceneXYCircle(circle)); + } + return null; + } + + @Override + public Void visit(GeometryCollection collection) { + for (Geometry shape : collection) { + shape.visit(this); + } + return null; + } + + @Override + public Void visit(Line line) { + if (line.isEmpty() == false) { + geometries.add(ShapeUtils.toLuceneXYLine(line)); + } + return null; + } + + @Override + public Void visit(LinearRing ring) { + throw new QueryShardException(context, "Field [" + name + "] found and unsupported shape LinearRing"); + } + + @Override + public Void visit(MultiLine multiLine) { + for (Line line : multiLine) { + visit(line); + } + return null; + } + + @Override + public Void visit(MultiPoint multiPoint) { + for (Point point : multiPoint) { + visit(point); + } + return null; + } + + @Override + public Void visit(MultiPolygon multiPolygon) { + for (Polygon polygon : multiPolygon) { + visit(polygon); + } + return null; + } + + @Override + public Void visit(Point point) { + if (point.isEmpty() == false) { + geometries.add(ShapeUtils.toLuceneXYPoint(point)); + } + return null; + + } + + @Override + public Void visit(Polygon polygon) { + if (polygon.isEmpty() == false) { + geometries.add(ShapeUtils.toLuceneXYPolygon(polygon)); + } + return null; + } + + @Override + public Void visit(Rectangle r) { + if (r.isEmpty() == false) { + geometries.add(ShapeUtils.toLuceneXYRectangle(r)); + } + return null; } - return result; } } diff --git a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTests.java b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTests.java index 09f2919a764..2b81d96eb30 100644 --- a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTests.java +++ b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTests.java @@ -10,11 +10,13 @@ import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.geo.GeoJson; import org.elasticsearch.common.geo.ShapeRelation; +import org.elasticsearch.common.geo.builders.CircleBuilder; import org.elasticsearch.common.geo.builders.EnvelopeBuilder; import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder; import org.elasticsearch.common.geo.builders.MultiPointBuilder; import org.elasticsearch.common.geo.builders.PointBuilder; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; @@ -337,4 +339,42 @@ public class ShapeQueryTests extends ESSingleNodeTestCase { assertEquals(1, response.getHits().getTotalHits().value); } } + + public void testDistanceQuery() throws Exception { + client().admin().indices().prepareCreate("test_distance").addMapping("type", "location", "type=shape") + .execute().actionGet(); + ensureGreen(); + + CircleBuilder circleBuilder = new CircleBuilder().center(new Coordinate(1, 0)).radius(10, DistanceUnit.METERS); + + client().index(new IndexRequest("test_distance") + .source(jsonBuilder().startObject().field("location", new PointBuilder(2, 2)).endObject()) + .setRefreshPolicy(IMMEDIATE)).actionGet(); + client().index(new IndexRequest("test_distance") + .source(jsonBuilder().startObject().field("location", new PointBuilder(3, 1)).endObject()) + .setRefreshPolicy(IMMEDIATE)).actionGet(); + client().index(new IndexRequest("test_distance") + .source(jsonBuilder().startObject().field("location", new PointBuilder(-20, -30)).endObject()) + .setRefreshPolicy(IMMEDIATE)).actionGet(); + client().index(new IndexRequest("test_distance") + .source(jsonBuilder().startObject().field("location", new PointBuilder(20, 30)).endObject()) + .setRefreshPolicy(IMMEDIATE)).actionGet(); + + SearchResponse response = client().prepareSearch("test_distance") + .setQuery(new ShapeQueryBuilder("location", circleBuilder.buildGeometry()).relation(ShapeRelation.WITHIN)) + .get(); + assertEquals(2, response.getHits().getTotalHits().value); + response = client().prepareSearch("test_distance") + .setQuery(new ShapeQueryBuilder("location", circleBuilder.buildGeometry()).relation(ShapeRelation.INTERSECTS)) + .get(); + assertEquals(2, response.getHits().getTotalHits().value); + response = client().prepareSearch("test_distance") + .setQuery(new ShapeQueryBuilder("location", circleBuilder.buildGeometry()).relation(ShapeRelation.DISJOINT)) + .get(); + assertEquals(2, response.getHits().getTotalHits().value); + response = client().prepareSearch("test_distance") + .setQuery(new ShapeQueryBuilder("location", circleBuilder.buildGeometry()).relation(ShapeRelation.CONTAINS)) + .get(); + assertEquals(0, response.getHits().getTotalHits().value); + } }