diff --git a/docs/reference/query-dsl/geo-bounding-box-query.asciidoc b/docs/reference/query-dsl/geo-bounding-box-query.asciidoc index e8db949bbc6b8..a1b427acf2718 100644 --- a/docs/reference/query-dsl/geo-bounding-box-query.asciidoc +++ b/docs/reference/query-dsl/geo-bounding-box-query.asciidoc @@ -180,6 +180,31 @@ GET /_search -------------------------------------------------- // CONSOLE +[float] +===== Bounding Box as Well-Known Text (WKT) + +[source,js] +-------------------------------------------------- +GET /_search +{ + "query": { + "bool" : { + "must" : { + "match_all" : {} + }, + "filter" : { + "geo_bounding_box" : { + "pin.location" : { + "wkt" : "BBOX (-74.1, -71.12, 40.73, 40.01)" + } + } + } + } + } +} +-------------------------------------------------- +// CONSOLE + [float] ===== Geohash diff --git a/server/src/main/java/org/elasticsearch/common/geo/parsers/GeoWKTParser.java b/server/src/main/java/org/elasticsearch/common/geo/parsers/GeoWKTParser.java index 005caed53a7e9..38643df017943 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/parsers/GeoWKTParser.java +++ b/server/src/main/java/org/elasticsearch/common/geo/parsers/GeoWKTParser.java @@ -63,6 +63,12 @@ private GeoWKTParser() {} public static ShapeBuilder parse(XContentParser parser) throws IOException, ElasticsearchParseException { + return parseExpectedType(parser, null); + } + + /** throws an exception if the parsed geometry type does not match the expected shape type */ + public static ShapeBuilder parseExpectedType(XContentParser parser, final GeoShapeType shapeType) + throws IOException, ElasticsearchParseException { FastStringReader reader = new FastStringReader(parser.text()); try { // setup the tokenizer; configured to read words w/o numbers @@ -77,7 +83,7 @@ public static ShapeBuilder parse(XContentParser parser) tokenizer.wordChars('.', '.'); tokenizer.whitespaceChars(0, ' '); tokenizer.commentChar('#'); - ShapeBuilder builder = parseGeometry(tokenizer); + ShapeBuilder builder = parseGeometry(tokenizer, shapeType); checkEOF(tokenizer); return builder; } finally { @@ -86,8 +92,14 @@ public static ShapeBuilder parse(XContentParser parser) } /** parse geometry from the stream tokenizer */ - private static ShapeBuilder parseGeometry(StreamTokenizer stream) throws IOException, ElasticsearchParseException { + private static ShapeBuilder parseGeometry(StreamTokenizer stream, GeoShapeType shapeType) + throws IOException, ElasticsearchParseException { final GeoShapeType type = GeoShapeType.forName(nextWord(stream)); + if (shapeType != null && shapeType != GeoShapeType.GEOMETRYCOLLECTION) { + if (type.wktName().equals(shapeType.wktName()) == false) { + throw new ElasticsearchParseException("Expected geometry type [{}] but found [{}]", shapeType, type); + } + } switch (type) { case POINT: return parsePoint(stream); @@ -228,9 +240,10 @@ private static GeometryCollectionBuilder parseGeometryCollection(StreamTokenizer if (nextEmptyOrOpen(stream).equals(EMPTY)) { return null; } - GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(parseGeometry(stream)); + GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape( + parseGeometry(stream, GeoShapeType.GEOMETRYCOLLECTION)); while (nextCloserOrComma(stream).equals(COMMA)) { - builder.shape(parseGeometry(stream)); + builder.shape(parseGeometry(stream, null)); } return builder; } diff --git a/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java index c0e57cc45afd9..47dcbaa351454 100644 --- a/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java @@ -31,7 +31,10 @@ import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.geo.GeoHashUtils; import org.elasticsearch.common.geo.GeoPoint; +import org.elasticsearch.common.geo.GeoShapeType; import org.elasticsearch.common.geo.GeoUtils; +import org.elasticsearch.common.geo.builders.EnvelopeBuilder; +import org.elasticsearch.common.geo.parsers.GeoWKTParser; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -62,7 +65,6 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder GeoWKTParser.parseExpectedType(parser, GeoShapeType.POLYGON)); + assertThat(e, hasToString(containsString("Expected geometry type [polygon] but found [point]"))); + } } diff --git a/server/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java index 133057fb8d026..aeaca328ceb7b 100644 --- a/server/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java @@ -406,6 +406,50 @@ public void testFromJson() throws IOException { assertEquals(json, GeoExecType.MEMORY, parsed.type()); } + public void testFromWKT() throws IOException { + String wkt = + "{\n" + + " \"geo_bounding_box\" : {\n" + + " \"pin.location\" : {\n" + + " \"wkt\" : \"BBOX (-74.1, -71.12, 40.73, 40.01)\"\n" + + " },\n" + + " \"validation_method\" : \"STRICT\",\n" + + " \"type\" : \"MEMORY\",\n" + + " \"ignore_unmapped\" : false,\n" + + " \"boost\" : 1.0\n" + + " }\n" + + "}"; + + // toXContent generates the query in geojson only; for now we need to test against the expected + // geojson generated content + String expectedJson = + "{\n" + + " \"geo_bounding_box\" : {\n" + + " \"pin.location\" : {\n" + + " \"top_left\" : [ -74.1, 40.73 ],\n" + + " \"bottom_right\" : [ -71.12, 40.01 ]\n" + + " },\n" + + " \"validation_method\" : \"STRICT\",\n" + + " \"type\" : \"MEMORY\",\n" + + " \"ignore_unmapped\" : false,\n" + + " \"boost\" : 1.0\n" + + " }\n" + + "}"; + + // parse with wkt + GeoBoundingBoxQueryBuilder parsed = (GeoBoundingBoxQueryBuilder) parseQuery(wkt); + // check the builder's generated geojson content against the expected json output + checkGeneratedJson(expectedJson, parsed); + double delta = 0d; + assertEquals(expectedJson, "pin.location", parsed.fieldName()); + assertEquals(expectedJson, -74.1, parsed.topLeft().getLon(), delta); + assertEquals(expectedJson, 40.73, parsed.topLeft().getLat(), delta); + assertEquals(expectedJson, -71.12, parsed.bottomRight().getLon(), delta); + assertEquals(expectedJson, 40.01, parsed.bottomRight().getLat(), delta); + assertEquals(expectedJson, 1.0, parsed.boost(), delta); + assertEquals(expectedJson, GeoExecType.MEMORY, parsed.type()); + } + @Override public void testMustRewrite() throws IOException { assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);