Geo shape query vs geo point backport (#53774)

Backport to 7x

Enable geo_shape query to work on geo_point fields for shapes: circle, polygon, multipolygon, rectangle see: #48928
Co-Authored-By:  @iverase
This commit is contained in:
Dominic Page 2020-03-19 13:00:36 +01:00 committed by GitHub
parent 8f4a3eb07f
commit b0884baf46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1039 additions and 201 deletions

View File

@ -18,9 +18,11 @@ Finds documents with geo-points within the specified distance of a central point
Find documents with geo-points within the specified polygon.
<<query-dsl-geo-shape-query,`geo_shape`>> query::
Finds documents with geo-shapes which either intersect, are contained by, or do not intersect with the specified
geo-shape.
Finds documents with:
* `geo-shapes` which either intersect, are contained by, or do not intersect
with the specified geo-shape
* `geo-points` which intersect the specified
geo-shape
include::geo-bounding-box-query.asciidoc[]

View File

@ -4,9 +4,9 @@
<titleabbrev>Geo-shape</titleabbrev>
++++
Filter documents indexed using the `geo_shape` type.
Filter documents indexed using the `geo_shape` or `geo_point` type.
Requires the <<geo-shape,`geo_shape` Mapping>>.
Requires the <<geo-shape,`geo_shape` Mapping>> or the <<geo-point,`geo_point` Mapping>>.
The `geo_shape` query uses the same grid square representation as the
`geo_shape` mapping to find documents that have a shape that intersects
@ -142,7 +142,7 @@ GET /example/_search
The <<spatial-strategy, geo_shape strategy>> mapping parameter determines
which spatial relation operators may be used at search time.
The following is a complete list of spatial relation operators available:
The following is a complete list of spatial relation operators available when searching a field of type `geo_shape`:
* `INTERSECTS` - (default) Return all documents whose `geo_shape` field
intersects the query geometry.
@ -153,6 +153,11 @@ is within the query geometry.
* `CONTAINS` - Return all documents whose `geo_shape` field
contains the query geometry.
When searching a field of type `geo_point` there is a single supported spatial relation operator:
* `INTERSECTS` - (default) Return all documents whose `geo_point` field
intersects the query geometry.
[float]
==== Ignore Unmapped
@ -162,6 +167,15 @@ querying multiple indexes which might have different mappings. When set to
`false` (the default value) the query will throw an exception if the field
is not mapped.
==== Shape Types supported for Geo-Point
When searching a field of type `geo_point` the following shape types are not supported:
* `POINT`
* `LINE`
* `MULTIPOINT`
* `MULTILINE`
==== Notes
Geo-shape queries on geo-shapes implemented with <<prefix-trees, `PrefixTrees`>> will not be executed if
<<query-dsl-allow-expensive-queries, `search.allow_expensive_queries`>> is set to false.

View File

@ -64,6 +64,6 @@ public class LinearRing extends Line {
public String toString() {
return "linearring(x=" + Arrays.toString(getX()) +
", y=" + Arrays.toString(getY()) +
(hasZ() ? ", z=" + Arrays.toString(getZ()) : "");
(hasZ() ? ", z=" + Arrays.toString(getZ()) : "") + ")";
}
}

View File

@ -26,8 +26,6 @@ import org.apache.lucene.search.TermQuery;
import org.elasticsearch.Version;
import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
import org.elasticsearch.common.settings.Settings;
@ -35,7 +33,6 @@ import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper.DeprecatedParameters;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.QueryShardException;
@ -91,19 +88,6 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
}
/**
* interface representing a query builder that generates a query from the given shape
*/
public interface QueryProcessor {
Query process(Geometry shape, String fieldName, ShapeRelation relation, QueryShardContext context);
@Deprecated
default Query process(Geometry shape, String fieldName, SpatialStrategy strategy, ShapeRelation relation,
QueryShardContext context) {
return process(shape, fieldName, relation, context);
}
}
public abstract static class Builder<T extends Builder, Y extends AbstractGeometryFieldMapper>
extends FieldMapper.Builder<T, Y> {
protected Boolean coerce;
@ -272,14 +256,14 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
}
}
public abstract static class AbstractGeometryFieldType<Parsed, Processed> extends MappedFieldType {
public abstract static class AbstractGeometryFieldType<Parsed, Processed> extends AbstractSearchableGeometryFieldType {
protected Orientation orientation = Defaults.ORIENTATION.value();
protected Indexer<Parsed, Processed> geometryIndexer;
protected Parser<Parsed> geometryParser;
protected QueryProcessor geometryQueryBuilder;
protected AbstractGeometryFieldType() {
setIndexOptions(IndexOptions.DOCS);
@ -339,14 +323,6 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
protected Parser<Parsed> geometryParser() {
return geometryParser;
}
public void setGeometryQueryBuilder(QueryProcessor geometryQueryBuilder) {
this.geometryQueryBuilder = geometryQueryBuilder;
}
public QueryProcessor geometryQueryBuilder() {
return geometryQueryBuilder;
}
}
protected Explicit<Boolean> coerce;

View File

@ -0,0 +1,63 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.mapper;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.index.query.QueryShardContext;
/**
* a base class for geometry types that support shape query builder
*/
public abstract class AbstractSearchableGeometryFieldType extends MappedFieldType {
protected QueryProcessor geometryQueryBuilder;
protected AbstractSearchableGeometryFieldType() {
}
protected AbstractSearchableGeometryFieldType(AbstractSearchableGeometryFieldType ref) {
super(ref);
}
public void setGeometryQueryBuilder(QueryProcessor geometryQueryBuilder) {
this.geometryQueryBuilder = geometryQueryBuilder;
}
public QueryProcessor geometryQueryBuilder() {
return geometryQueryBuilder;
}
/**
* interface representing a query builder that generates a query from the given shape
*/
public interface QueryProcessor {
Query process(Geometry shape, String fieldName, ShapeRelation relation, QueryShardContext context);
@Deprecated
default Query process(Geometry shape, String fieldName, SpatialStrategy strategy, ShapeRelation relation,
QueryShardContext context) {
return process(shape, fieldName, relation, context);
}
}
}

View File

@ -40,6 +40,7 @@ import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.plain.AbstractLatLonPointDVIndexFieldData;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.QueryShardException;
import org.elasticsearch.index.query.VectorGeoPointShapeQueryProcessor;
import java.io.IOException;
import java.util.ArrayList;
@ -121,6 +122,14 @@ public class GeoPointFieldMapper extends FieldMapper implements ArrayValueMapper
ignoreMalformed, ignoreZValue, copyTo);
}
@Override
protected void setupFieldType(BuilderContext context) {
super.setupFieldType(context);
GeoPointFieldType fieldType = (GeoPointFieldType)fieldType();
fieldType.setGeometryQueryBuilder(new VectorGeoPointShapeQueryProcessor());
}
@Override
public GeoPointFieldMapper build(BuilderContext context) {
return build(context, name, fieldType, defaultFieldType, context.indexSettings(),
@ -210,7 +219,7 @@ public class GeoPointFieldMapper extends FieldMapper implements ArrayValueMapper
throw new UnsupportedOperationException("Parsing is implemented in parse(), this method should NEVER be called");
}
public static class GeoPointFieldType extends MappedFieldType {
public static class GeoPointFieldType extends AbstractSearchableGeometryFieldType {
public GeoPointFieldType() {
}
@ -245,7 +254,8 @@ public class GeoPointFieldMapper extends FieldMapper implements ArrayValueMapper
@Override
public Query termQuery(Object value, QueryShardContext context) {
throw new QueryShardException(context, "Geo fields do not support exact searching, use dedicated geo queries instead: ["
throw new QueryShardException(context,
"Geo fields do not support exact searching, use dedicated geo queries instead: ["
+ name() + "]");
}
}

View File

@ -24,6 +24,7 @@ import org.apache.lucene.document.LatLonShape;
import org.apache.lucene.index.IndexableField;
import org.elasticsearch.common.geo.GeoLineDecomposer;
import org.elasticsearch.common.geo.GeoPolygonDecomposer;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.geometry.Circle;
import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.geometry.GeometryCollection;
@ -64,7 +65,7 @@ public final class GeoShapeIndexer implements AbstractGeometryFieldMapper.Indexe
return geometry.visit(new GeometryVisitor<Geometry, RuntimeException>() {
@Override
public Geometry visit(Circle circle) {
throw new UnsupportedOperationException("CIRCLE geometry is not supported");
throw new UnsupportedOperationException(GeoShapeType.CIRCLE + " geometry is not supported");
}
@Override

View File

@ -372,11 +372,9 @@ public abstract class AbstractGeometryQueryBuilder<QB extends AbstractGeometryQu
}
/** list of content types this shape query is compatible with */
protected abstract List validContentTypes();
protected abstract List<String> validContentTypes();
/** builds the appropriate lucene shape query */
protected abstract Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType);
/** returns expected content type for this query */
protected abstract String queryFieldType();
/** writes the xcontent specific to this shape query */
protected abstract void doShapeQueryXContent(XContentBuilder builder, Params params) throws IOException;
/** creates a new ShapeQueryBuilder from the provided field name and shape builder */
@ -400,7 +398,9 @@ public abstract class AbstractGeometryQueryBuilder<QB extends AbstractGeometryQu
if (ignoreUnmapped) {
return new MatchNoDocsQuery();
} else {
throw new QueryShardException(context, "failed to find " + queryFieldType() + " field [" + fieldName + "]");
throw new QueryShardException(context, "failed to find "
+ String.join(" or ", validContentTypes())
+ " field [" + fieldName + "]");
}
}

View File

@ -35,12 +35,14 @@ import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType;
import org.elasticsearch.index.mapper.GeoPointFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
@ -57,6 +59,12 @@ public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQ
private SpatialStrategy strategy;
protected static final List<String> validContentTypes =
Collections.unmodifiableList(
Arrays.asList(
GeoShapeFieldMapper.CONTENT_TYPE,
GeoPointFieldMapper.CONTENT_TYPE));
/**
* Creates a new GeoShapeQueryBuilder whose Query will be against the given
* field name using the given Shape
@ -181,13 +189,8 @@ public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQ
}
@Override
protected List validContentTypes() {
return Arrays.asList(GeoShapeFieldMapper.CONTENT_TYPE);
}
@Override
public String queryFieldType() {
return GeoShapeFieldMapper.CONTENT_TYPE;
protected List<String> validContentTypes() {
return validContentTypes;
}
@Override
@ -210,12 +213,15 @@ public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQ
@Override
public Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType) {
if (fieldType.typeName().equals(GeoShapeFieldMapper.CONTENT_TYPE) == false) {
if (validContentTypes().contains(fieldType.typeName()) == false) {
throw new QueryShardException(context,
"Field [" + fieldName + "] is not of type [" + queryFieldType() + "] but of type [" + fieldType.typeName() + "]");
"Field [" + fieldName + "] is of unsupported type [" + fieldType.typeName() + "]. ["
+ NAME + "] query supports the following types ["
+ String.join(",", validContentTypes()) + "]");
}
final AbstractGeometryFieldMapper.AbstractGeometryFieldType ft = (AbstractGeometryFieldMapper.AbstractGeometryFieldType) fieldType;
final AbstractSearchableGeometryFieldType ft =
(AbstractSearchableGeometryFieldType) fieldType;
return new ConstantScoreQuery(ft.geometryQueryBuilder().process(shape, fieldName, strategy, relation, context));
}

View File

@ -53,6 +53,7 @@ import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Polygon;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.shape.Shape;
@ -62,7 +63,7 @@ import java.util.List;
import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES;
public class LegacyGeoShapeQueryProcessor implements AbstractGeometryFieldMapper.QueryProcessor {
public class LegacyGeoShapeQueryProcessor implements AbstractSearchableGeometryFieldType.QueryProcessor {
private AbstractGeometryFieldMapper.AbstractGeometryFieldType ft;

View File

@ -0,0 +1,191 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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;
import org.apache.lucene.document.LatLonDocValuesField;
import org.apache.lucene.document.LatLonPoint;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexOrDocValuesQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.geo.GeoPolygonDecomposer;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.geometry.Circle;
import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.geometry.GeometryCollection;
import org.elasticsearch.geometry.GeometryVisitor;
import org.elasticsearch.geometry.LinearRing;
import org.elasticsearch.geometry.MultiLine;
import org.elasticsearch.geometry.MultiPoint;
import org.elasticsearch.geometry.MultiPolygon;
import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Polygon;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.geometry.ShapeType;
import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType;
import org.elasticsearch.index.mapper.GeoPointFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import java.util.ArrayList;
import static org.elasticsearch.index.mapper.GeoShapeIndexer.toLucenePolygon;
public class VectorGeoPointShapeQueryProcessor implements AbstractSearchableGeometryFieldType.QueryProcessor {
@Override
public Query process(Geometry shape, String fieldName, ShapeRelation relation, QueryShardContext context) {
validateIsGeoPointFieldType(fieldName, context);
// geo points only support intersects
if (relation != ShapeRelation.INTERSECTS) {
throw new QueryShardException(context,
relation+ " query relation not supported for Field [" + fieldName + "].");
}
// wrap geoQuery as a ConstantScoreQuery
return getVectorQueryFromShape(shape, fieldName, relation, context);
}
private void validateIsGeoPointFieldType(String fieldName, QueryShardContext context) {
MappedFieldType fieldType = context.fieldMapper(fieldName);
if (fieldType instanceof GeoPointFieldMapper.GeoPointFieldType == false) {
throw new QueryShardException(context, "Expected " + GeoPointFieldMapper.CONTENT_TYPE
+ " field type for Field [" + fieldName + "] but found " + fieldType.typeName());
}
}
protected Query getVectorQueryFromShape(
Geometry queryShape, String fieldName, ShapeRelation relation, QueryShardContext context) {
ShapeVisitor shapeVisitor = new ShapeVisitor(context, fieldName, relation);
return queryShape.visit(shapeVisitor);
}
private class ShapeVisitor implements GeometryVisitor<Query, RuntimeException> {
QueryShardContext context;
MappedFieldType fieldType;
String fieldName;
ShapeRelation relation;
ShapeVisitor(QueryShardContext context, String fieldName, ShapeRelation relation) {
this.context = context;
this.fieldType = context.fieldMapper(fieldName);
this.fieldName = fieldName;
this.relation = relation;
}
@Override
public Query visit(Circle circle) {
Query query = LatLonPoint.newDistanceQuery(
fieldName, circle.getLat(), circle.getLon(), circle.getRadiusMeters());
if (fieldType.hasDocValues()) {
Query dvQuery = LatLonDocValuesField.newSlowDistanceQuery(
fieldName, circle.getLat(), circle.getLon(), circle.getRadiusMeters());
query = new IndexOrDocValuesQuery(query, dvQuery);
}
return query;
}
@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 = BooleanClause.Occur.FILTER;
for (Geometry shape : collection) {
bqb.add(shape.visit(this), occur);
}
}
@Override
public Query visit(org.elasticsearch.geometry.Line line) {
throw new QueryShardException(context, "Field [" + fieldName + "] does not support "
+ GeoShapeType.LINESTRING + " queries");
}
@Override
// don't think this is called directly
public Query visit(LinearRing ring) {
throw new QueryShardException(context, "Field [" + fieldName + "] does not support "
+ ShapeType.LINEARRING + " queries");
}
@Override
public Query visit(MultiLine multiLine) {
throw new QueryShardException(context, "Field [" + fieldName + "] does not support "
+ GeoShapeType.MULTILINESTRING + " queries");
}
@Override
public Query visit(MultiPoint multiPoint) {
throw new QueryShardException(context, "Field [" + fieldName + "] does not support "
+ GeoShapeType.MULTIPOINT + " queries");
}
// helper for visit(MultiPolygon multiPolygon) and visit(Polygon polygon)
private Query visit(ArrayList<Polygon> collector) {
org.apache.lucene.geo.Polygon[] lucenePolygons =
new org.apache.lucene.geo.Polygon[collector.size()];
for (int i = 0; i < collector.size(); i++) {
lucenePolygons[i] = toLucenePolygon(collector.get(i));
}
Query query = LatLonPoint.newPolygonQuery(fieldName, lucenePolygons);
if (fieldType.hasDocValues()) {
Query dvQuery = LatLonDocValuesField.newSlowPolygonQuery(fieldName, lucenePolygons);
query = new IndexOrDocValuesQuery(query, dvQuery);
}
return query;
}
@Override
public Query visit(MultiPolygon multiPolygon) {
ArrayList<org.elasticsearch.geometry.Polygon> collector = new ArrayList<>();
GeoPolygonDecomposer.decomposeMultiPolygon(multiPolygon, true, collector);
return visit(collector);
}
@Override
public Query visit(Point point) {
// not currently supported
throw new QueryShardException(context, "Field [" + fieldName + "] does not support " + GeoShapeType.POINT +
" queries");
}
@Override
public Query visit(Polygon polygon) {
ArrayList<org.elasticsearch.geometry.Polygon> collector = new ArrayList<>();
GeoPolygonDecomposer.decomposePolygon(polygon, true, collector);
return visit(collector);
}
@Override
public Query visit(Rectangle r) {
Query query = LatLonPoint.newBoxQuery(fieldName, r.getMinY(), r.getMaxY(), r.getMinX(), r.getMaxX());
if (fieldType.hasDocValues()) {
Query dvQuery = LatLonDocValuesField.newSlowBoxQuery(
fieldName, r.getMinY(), r.getMaxY(), r.getMinX(), r.getMaxX());
query = new IndexOrDocValuesQuery(query, dvQuery);
}
return query;
}
}
}

View File

@ -39,14 +39,14 @@ import org.elasticsearch.geometry.MultiPoint;
import org.elasticsearch.geometry.MultiPolygon;
import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.index.mapper.MappedFieldType;
import static org.elasticsearch.index.mapper.GeoShapeIndexer.toLucenePolygon;
public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper.QueryProcessor {
public class VectorGeoShapeQueryProcessor implements AbstractSearchableGeometryFieldType.QueryProcessor {
@Override
public Query process(Geometry shape, String fieldName, ShapeRelation relation, QueryShardContext context) {
@ -59,7 +59,8 @@ public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper
return getVectorQueryFromShape(shape, fieldName, relation, context);
}
protected Query getVectorQueryFromShape(Geometry queryShape, String fieldName, ShapeRelation relation, QueryShardContext context) {
protected Query getVectorQueryFromShape(
Geometry queryShape, String fieldName, ShapeRelation relation, QueryShardContext context) {
GeoShapeIndexer geometryIndexer = new GeoShapeIndexer(true, fieldName);
Geometry processedShape = geometryIndexer.prepareForIndexing(queryShape);
@ -85,7 +86,7 @@ public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper
@Override
public Query visit(Circle circle) {
throw new QueryShardException(context, "Field [" + fieldName + "] found and unknown shape Circle");
throw new QueryShardException(context, "Field [" + fieldName + "] found an unknown shape Circle");
}
@Override
@ -117,7 +118,7 @@ public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper
@Override
public Query visit(LinearRing ring) {
throw new QueryShardException(context, "Field [" + fieldName + "] found and unsupported shape LinearRing");
throw new QueryShardException(context, "Field [" + fieldName + "] found an unsupported shape LinearRing");
}
@Override

View File

@ -35,7 +35,6 @@ import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Polygon;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
@ -52,7 +51,7 @@ public class GeometryIndexerTests extends ESTestCase {
public void testCircle() {
UnsupportedOperationException ex =
expectThrows(UnsupportedOperationException.class, () -> indexer.prepareForIndexing(new Circle(2, 1, 3)));
assertEquals("CIRCLE geometry is not supported", ex.getMessage());
assertEquals(GeoShapeType.CIRCLE + " geometry is not supported", ex.getMessage());
}
public void testCollection() {

View File

@ -0,0 +1,65 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.test.geo.RandomShapeGenerator;
public class GeoShapeQueryBuilderGeoPointTests extends GeoShapeQueryBuilderTests {
protected String fieldName() {
return GEO_POINT_FIELD_NAME;
}
protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
RandomShapeGenerator.ShapeType shapeType = RandomShapeGenerator.ShapeType.POLYGON;
ShapeBuilder<?, ?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null, shapeType);
GeoShapeQueryBuilder builder;
clearShapeFields();
if (indexedShape == false) {
builder = new GeoShapeQueryBuilder(fieldName(), shape);
} else {
indexedShapeToReturn = shape;
indexedShapeId = randomAlphaOfLengthBetween(3, 20);
builder = new GeoShapeQueryBuilder(fieldName(), indexedShapeId);
if (randomBoolean()) {
indexedShapeIndex = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapeIndex(indexedShapeIndex);
}
if (randomBoolean()) {
indexedShapePath = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapePath(indexedShapePath);
}
if (randomBoolean()) {
indexedShapeRouting = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapeRouting(indexedShapeRouting);
}
}
if (randomBoolean()) {
builder.relation(randomFrom(ShapeRelation.INTERSECTS));
}
if (randomBoolean()) {
builder.ignoreUnmapped(randomBoolean());
}
return builder;
}
}

View File

@ -0,0 +1,84 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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;
import org.elasticsearch.Version;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.test.geo.RandomShapeGenerator;
public class GeoShapeQueryBuilderGeoShapeTests extends GeoShapeQueryBuilderTests {
protected String fieldName() {
return GEO_SHAPE_FIELD_NAME;
}
protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
RandomShapeGenerator.ShapeType shapeType = randomFrom(
RandomShapeGenerator.ShapeType.POINT,
RandomShapeGenerator.ShapeType.MULTIPOINT,
RandomShapeGenerator.ShapeType.LINESTRING,
RandomShapeGenerator.ShapeType.MULTILINESTRING,
RandomShapeGenerator.ShapeType.POLYGON);
ShapeBuilder<?, ?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null, shapeType);
GeoShapeQueryBuilder builder;
clearShapeFields();
if (indexedShape == false) {
builder = new GeoShapeQueryBuilder(fieldName(), shape);
} else {
indexedShapeToReturn = shape;
indexedShapeId = randomAlphaOfLengthBetween(3, 20);
builder = new GeoShapeQueryBuilder(fieldName(), indexedShapeId);
if (randomBoolean()) {
indexedShapeIndex = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapeIndex(indexedShapeIndex);
}
if (randomBoolean()) {
indexedShapePath = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapePath(indexedShapePath);
}
if (randomBoolean()) {
indexedShapeRouting = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapeRouting(indexedShapeRouting);
}
}
if (randomBoolean()) {
QueryShardContext context = createShardContext();
if (context.indexVersionCreated().onOrAfter(Version.V_7_5_0)) { // CONTAINS is only supported from version 7.5
if (shapeType == RandomShapeGenerator.ShapeType.LINESTRING || shapeType == RandomShapeGenerator.ShapeType.MULTILINESTRING) {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.CONTAINS));
} else {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS,
ShapeRelation.WITHIN, ShapeRelation.CONTAINS));
}
} else {
if (shapeType == RandomShapeGenerator.ShapeType.LINESTRING || shapeType == RandomShapeGenerator.ShapeType.MULTILINESTRING) {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS));
} else {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.WITHIN));
}
}
}
if (randomBoolean()) {
builder.ignoreUnmapped(randomBoolean());
}
return builder;
}
}

View File

@ -29,7 +29,6 @@ import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
@ -49,13 +48,15 @@ import org.locationtech.jts.geom.Coordinate;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.containsString;
import static org.apache.lucene.util.LuceneTestCase.random;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.matchesPattern;
public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQueryBuilder> {
public abstract class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQueryBuilder> {
protected static String indexedShapeId;
protected static String indexedShapeType;
@ -64,9 +65,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
protected static String indexedShapeRouting;
protected static ShapeBuilder<?, ?, ?> indexedShapeToReturn;
protected String fieldName() {
return GEO_SHAPE_FIELD_NAME;
}
protected abstract String fieldName();
@Override
protected Settings createTestIndexSettings() {
@ -83,55 +82,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
return doCreateTestQueryBuilder(randomBoolean());
}
protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
RandomShapeGenerator.ShapeType shapeType =
randomFrom(ShapeType.POINT, ShapeType.MULTIPOINT, ShapeType.LINESTRING, ShapeType.MULTILINESTRING, ShapeType.POLYGON);
ShapeBuilder<?, ?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null, shapeType);
GeoShapeQueryBuilder builder;
clearShapeFields();
if (indexedShape == false) {
builder = new GeoShapeQueryBuilder(fieldName(), shape);
} else {
indexedShapeToReturn = shape;
indexedShapeId = randomAlphaOfLengthBetween(3, 20);
indexedShapeType = randomBoolean() ? randomAlphaOfLengthBetween(3, 20) : null;
builder = new GeoShapeQueryBuilder(fieldName(), indexedShapeId, indexedShapeType);
if (randomBoolean()) {
indexedShapeIndex = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapeIndex(indexedShapeIndex);
}
if (randomBoolean()) {
indexedShapePath = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapePath(indexedShapePath);
}
if (randomBoolean()) {
indexedShapeRouting = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapeRouting(indexedShapeRouting);
}
}
if (randomBoolean()) {
QueryShardContext context = createShardContext();
if (context.indexVersionCreated().onOrAfter(Version.V_7_5_0)) { // CONTAINS is only supported from version 7.5
if (shapeType == ShapeType.LINESTRING || shapeType == ShapeType.MULTILINESTRING) {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.CONTAINS));
} else {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS,
ShapeRelation.WITHIN, ShapeRelation.CONTAINS));
}
} else {
if (shapeType == ShapeType.LINESTRING || shapeType == ShapeType.MULTILINESTRING) {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS));
} else {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.WITHIN));
}
}
}
if (randomBoolean()) {
builder.ignoreUnmapped(randomBoolean());
}
return builder;
}
abstract GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape);
@Override
protected GetResponse executeGet(GetRequest getRequest) {
@ -282,7 +233,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
new GeoShapeQueryBuilder("unmapped", shape.buildGeometry());
failingQueryBuilder.ignoreUnmapped(false);
QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(createShardContext()));
assertThat(e.getMessage(), containsString("failed to find geo_shape field [unmapped]"));
assertThat(e.getMessage(), matchesPattern("failed to find .*geo_shape.* field \\[unmapped\\]"));
}
public void testWrongFieldType() throws IOException {
@ -292,7 +243,8 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
new GeoShapeQueryBuilder(STRING_FIELD_NAME, shape) :
new GeoShapeQueryBuilder(STRING_FIELD_NAME, shape.buildGeometry());
QueryShardException e = expectThrows(QueryShardException.class, () -> queryBuilder.toQuery(createShardContext()));
assertThat(e.getMessage(), containsString("Field [mapped_string] is not of type [geo_shape] but of type [text]"));
assertThat(e.getMessage(), matchesPattern("Field \\[mapped_string\\] is of unsupported type \\[text\\]." +
" \\[geo_shape\\] query supports the following types \\[.*geo_shape.*\\]"));
}
public void testSerializationFailsUnlessFetched() throws IOException {

View File

@ -19,8 +19,28 @@
package org.elasticsearch.search.geo;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
import org.elasticsearch.common.geo.builders.LineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.geometry.Line;
import org.elasticsearch.geometry.LinearRing;
import org.elasticsearch.geometry.MultiLine;
import org.elasticsearch.geometry.MultiPoint;
import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.index.query.GeoShapeQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import static org.hamcrest.Matchers.containsString;
public class GeoPointShapeQueryTests extends GeoQueryTests {
@ -37,16 +57,130 @@ public class GeoPointShapeQueryTests extends GeoQueryTests {
@Override
protected XContentBuilder createDefaultMapping() throws Exception {
XContentBuilder xcb = XContentFactory.jsonBuilder().startObject()
.startObject("properties").startObject("location")
.startObject("properties").startObject(defaultGeoFieldName)
.field("type", "geo_point")
.endObject().endObject().endObject();
return xcb;
}
@Override
public void testIndexPointsFilterRectangle(String mapping) throws Exception {
//super.testIndexPointsFilterRectangle(Strings.toString(createDefaultMapping()));
public void testProcessRelationSupport() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate("test").addMapping("_doc", xcb).get();
ensureGreen();
Rectangle rectangle = new Rectangle(-35, -25, -25, -35);
for (ShapeRelation shapeRelation : ShapeRelation.values()) {
if (shapeRelation.equals(ShapeRelation.INTERSECTS) == false) {
try {
client().prepareSearch("test")
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, rectangle)
.relation(shapeRelation))
.get();
} catch (SearchPhaseExecutionException e) {
assertThat(e.getCause().getMessage(),
containsString(shapeRelation
+ " query relation not supported for Field [" + defaultGeoFieldName + "]"));
}
}
}
}
public void testQueryLine() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate("test").addMapping("_doc", xcb).get();
ensureGreen();
Line line = new Line(new double[]{-25, -25}, new double[]{-35, -35});
try {
client().prepareSearch("test")
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, line)).get();
} catch (
SearchPhaseExecutionException e) {
assertThat(e.getCause().getMessage(),
containsString("does not support " + GeoShapeType.LINESTRING + " queries"));
}
}
public void testQueryLinearRing() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate("test").addMapping("_doc", xcb).get();
ensureGreen();
LinearRing linearRing = new LinearRing(new double[]{-25,-35,-25}, new double[]{-25,-35,-25});
try {
// LinearRing extends Line implements Geometry: expose the build process
GeoShapeQueryBuilder queryBuilder = new GeoShapeQueryBuilder(defaultGeoFieldName, linearRing);
SearchRequestBuilder searchRequestBuilder = new SearchRequestBuilder(client(), SearchAction.INSTANCE);
searchRequestBuilder.setQuery(queryBuilder);
searchRequestBuilder.setIndices("test");
searchRequestBuilder.get();
} catch (
SearchPhaseExecutionException e) {
assertThat(e.getCause().getMessage(),
containsString("Field [" + defaultGeoFieldName + "] does not support LINEARRING queries"));
}
}
public void testQueryMultiLine() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate("test").addMapping("_doc", xcb).get();
ensureGreen();
CoordinatesBuilder coords1 = new CoordinatesBuilder()
.coordinate(-35,-35)
.coordinate(-25,-25);
CoordinatesBuilder coords2 = new CoordinatesBuilder()
.coordinate(-15,-15)
.coordinate(-5,-5);
LineStringBuilder lsb1 = new LineStringBuilder(coords1);
LineStringBuilder lsb2 = new LineStringBuilder(coords2);
MultiLineStringBuilder mlb = new MultiLineStringBuilder().linestring(lsb1).linestring(lsb2);
MultiLine multiline = (MultiLine) mlb.buildGeometry();
try {
client().prepareSearch("test")
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiline)).get();
} catch (Exception e) {
assertThat(e.getCause().getMessage(),
containsString("does not support " + GeoShapeType.MULTILINESTRING + " queries"));
}
}
public void testQueryMultiPoint() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate("test").addMapping("_doc", xcb).get();
ensureGreen();
MultiPointBuilder mpb = new MultiPointBuilder().coordinate(-35,-25).coordinate(-15,-5);
MultiPoint multiPoint = mpb.buildGeometry();
try {
client().prepareSearch("test")
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiPoint)).get();
} catch (Exception e) {
assertThat(e.getCause().getMessage(),
containsString("does not support " + GeoShapeType.MULTIPOINT + " queries"));
}
}
public void testQueryPoint() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate("test").addMapping("_doc", xcb).get();
ensureGreen();
PointBuilder pb = new PointBuilder().coordinate(-35, -25);
Point point = pb.buildGeometry();
try {
client().prepareSearch("test")
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, point)).get();
} catch (Exception e) {
assertThat(e.getCause().getMessage(),
containsString("does not support " + GeoShapeType.POINT + " queries"));
}
}
}

View File

@ -21,78 +21,421 @@ package org.elasticsearch.search.geo;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.builders.CircleBuilder;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
import org.elasticsearch.common.geo.builders.MultiPolygonBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.index.query.GeoShapeQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.test.ESSingleNodeTestCase;
import org.locationtech.jts.geom.Coordinate;
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.geoIntersectionQuery;
import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
public abstract class GeoQueryTests extends ESSingleNodeTestCase {
protected abstract XContentBuilder createTypedMapping() throws Exception;
protected abstract XContentBuilder createDefaultMapping() throws Exception;
static String defaultGeoFieldName = "geo";
static String defaultIndexName = "test";
public void testNullShape() throws Exception {
String mapping = Strings.toString(createTypedMapping());
client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate(defaultIndexName).addMapping("_doc", xcb).get();
ensureGreen();
client().prepareIndex("test", "type1", "aNullshape").setSource("{\"location\": null}", XContentType.JSON)
client().prepareIndex(defaultIndexName,"_doc")
.setId("aNullshape")
.setSource("{\"geo\": null}", XContentType.JSON)
.setRefreshPolicy(IMMEDIATE).get();
GetResponse result = client().prepareGet("test", "type1", "aNullshape").get();
GetResponse result = client().prepareGet(defaultIndexName,"_doc","aNullshape").get();
assertThat(result.getField("location"), nullValue());
}
public void testIndexPointsFilterRectangle(String mapping) throws Exception {
client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
public void testIndexPointsFilterRectangle() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate(defaultIndexName).addMapping("_doc", xcb).get();
ensureGreen();
client().prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject()
.field("name", "Document 1")
.startObject("geo")
.field("type", "point")
.startArray("coordinates").value(-30).value(-30).endArray()
.endObject()
client().prepareIndex(defaultIndexName,"_doc").setId("1").setSource(jsonBuilder()
.startObject()
.field("name", "Document 1")
.field(defaultGeoFieldName, "POINT(-30 -30)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex("test", "type1", "2").setSource(jsonBuilder().startObject()
.field("name", "Document 2")
.startObject("geo")
.field("type", "point")
.startArray("coordinates").value(-45).value(-50).endArray()
.endObject()
client().prepareIndex(defaultIndexName,"_doc").setId("2").setSource(jsonBuilder()
.startObject()
.field("name", "Document 2")
.field(defaultGeoFieldName, "POINT(-45 -50)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
EnvelopeBuilder shape = new EnvelopeBuilder(new Coordinate(-45, 45), new Coordinate(45, -45));
SearchResponse searchResponse = client().prepareSearch("test")
.setQuery(geoIntersectionQuery("geo", shape))
GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(shape);
Geometry geometry = builder.buildGeometry().get(0);
SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
.relation(ShapeRelation.INTERSECTS))
.get();
assertSearchResponse(searchResponse);
assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
assertThat(searchResponse.getHits().getHits().length, equalTo(1));
assertHitCount(searchResponse, 1);
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
searchResponse = client().prepareSearch("test")
.setQuery(geoShapeQuery("geo", shape))
// default query, without specifying relation (expect intersects)
searchResponse = client().prepareSearch(defaultIndexName)
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry))
.get();
assertSearchResponse(searchResponse);
assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
assertThat(searchResponse.getHits().getHits().length, equalTo(1));
assertHitCount(searchResponse, 1);
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
}
public void testIndexPointsCircle() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate(defaultIndexName).addMapping("_doc", xcb).get();
ensureGreen();
client().prepareIndex(defaultIndexName,"_doc").setId("1").setSource(jsonBuilder()
.startObject()
.field("name", "Document 1")
.field(defaultGeoFieldName, "POINT(-30 -30)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("2").setSource(jsonBuilder()
.startObject()
.field("name", "Document 2")
.field(defaultGeoFieldName, "POINT(-45 -50)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
CircleBuilder shape = new CircleBuilder().center(new Coordinate(-30, -30)).radius("100m");
GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(shape);
Geometry geometry = builder.buildGeometry().get(0);
try {
client().prepareSearch(defaultIndexName)
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
.relation(ShapeRelation.INTERSECTS))
.get();
} catch (
Exception e) {
assertThat(e.getCause().getMessage(),
containsString("failed to create query: "
+ GeoShapeType.CIRCLE + " geometry is not supported"));
}
}
public void testIndexPointsPolygon() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate(defaultIndexName).addMapping("_doc", xcb).get();
ensureGreen();
client().prepareIndex(defaultIndexName,"_doc").setId("1").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(-30 -30)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("2").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(-45 -50)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
CoordinatesBuilder cb = new CoordinatesBuilder();
cb.coordinate(new Coordinate(-35, -35))
.coordinate(new Coordinate(-35, -25))
.coordinate(new Coordinate(-25, -25))
.coordinate(new Coordinate(-25, -35))
.coordinate(new Coordinate(-35, -35));
PolygonBuilder shape = new PolygonBuilder(cb);
GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(shape);
Geometry geometry = builder.buildGeometry();
SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
.relation(ShapeRelation.INTERSECTS))
.get();
assertSearchResponse(searchResponse);
assertHitCount(searchResponse, 1);
SearchHits searchHits = searchResponse.getHits();
assertThat(searchHits.getAt(0).getId(), equalTo("1"));
}
public void testIndexPointsMultiPolygon() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate(defaultIndexName).addMapping("_doc", xcb).get();
ensureGreen();
client().prepareIndex(defaultIndexName,"_doc").setId("1").setSource(jsonBuilder()
.startObject()
.field("name", "Document 1")
.field(defaultGeoFieldName, "POINT(-30 -30)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("2").setSource(jsonBuilder()
.startObject()
.field("name", "Document 2")
.field(defaultGeoFieldName, "POINT(-40 -40)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("3").setSource(jsonBuilder()
.startObject()
.field("name", "Document 3")
.field(defaultGeoFieldName, "POINT(-50 -50)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
CoordinatesBuilder encloseDocument1Cb = new CoordinatesBuilder();
encloseDocument1Cb.coordinate(new Coordinate(-35, -35))
.coordinate(new Coordinate(-35, -25))
.coordinate(new Coordinate(-25, -25))
.coordinate(new Coordinate(-25, -35))
.coordinate(new Coordinate(-35, -35));
PolygonBuilder encloseDocument1Shape = new PolygonBuilder(encloseDocument1Cb);
CoordinatesBuilder encloseDocument2Cb = new CoordinatesBuilder();
encloseDocument2Cb.coordinate(new Coordinate(-55, -55))
.coordinate(new Coordinate(-55, -45))
.coordinate(new Coordinate(-45, -45))
.coordinate(new Coordinate(-45, -55))
.coordinate(new Coordinate(-55, -55));
PolygonBuilder encloseDocument2Shape = new PolygonBuilder(encloseDocument2Cb);
MultiPolygonBuilder mp = new MultiPolygonBuilder();
mp.polygon(encloseDocument1Shape).polygon(encloseDocument2Shape);
GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(mp);
Geometry geometry = builder.buildGeometry();
SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
.relation(ShapeRelation.INTERSECTS))
.get();
assertSearchResponse(searchResponse);
assertHitCount(searchResponse, 2);
assertThat(searchResponse.getHits().getAt(0).getId(), not(equalTo("2")));
assertThat(searchResponse.getHits().getAt(1).getId(), not(equalTo("2")));
}
public void testIndexPointsRectangle() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate(defaultIndexName).addMapping("_doc", xcb).get();
ensureGreen();
client().prepareIndex(defaultIndexName,"_doc").setId("1").setSource(jsonBuilder()
.startObject()
.field("name", "Document 1")
.field(defaultGeoFieldName, "POINT(-30 -30)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("2").setSource(jsonBuilder()
.startObject()
.field("name", "Document 2")
.field(defaultGeoFieldName, "POINT(-45 -50)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
Rectangle rectangle = new Rectangle(-50, -40, -45, -55);
SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, rectangle)
.relation(ShapeRelation.INTERSECTS))
.get();
assertSearchResponse(searchResponse);
assertHitCount(searchResponse, 1);
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2"));
}
public void testIndexPointsIndexedRectangle() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate(defaultIndexName).addMapping(defaultIndexName, xcb).get();
ensureGreen();
client().prepareIndex(defaultIndexName,"_doc").setId("point1").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(-30 -30)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("point2").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(-45 -50)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
String indexedShapeIndex = "indexed_query_shapes";
String indexedShapePath = "shape";
xcb = XContentFactory.jsonBuilder().startObject()
.startObject("properties").startObject(indexedShapePath)
.field("type", "geo_shape")
.endObject()
.endObject()
.endObject();
client().admin().indices().prepareCreate(indexedShapeIndex).addMapping(defaultIndexName, xcb).get();
ensureGreen();
client().prepareIndex(indexedShapeIndex,"_doc").setId("shape1").setSource(jsonBuilder()
.startObject()
.field(indexedShapePath, "BBOX(-50, -40, -45, -55)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(indexedShapeIndex,"_doc").setId("shape2").setSource(jsonBuilder()
.startObject()
.field(indexedShapePath, "BBOX(-60, -50, -50, -60)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, "shape1")
.relation(ShapeRelation.INTERSECTS)
.indexedShapeIndex(indexedShapeIndex)
.indexedShapePath(indexedShapePath))
.get();
assertSearchResponse(searchResponse);
assertHitCount(searchResponse, 1);
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("point2"));
searchResponse = client().prepareSearch(defaultIndexName)
.setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, "shape2")
.relation(ShapeRelation.INTERSECTS)
.indexedShapeIndex(indexedShapeIndex)
.indexedShapePath(indexedShapePath))
.get();
assertSearchResponse(searchResponse);
assertHitCount(searchResponse, 0);
}
public void testRectangleSpanningDateline() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate("test").addMapping("_doc", xcb).get();
ensureGreen();
client().prepareIndex(defaultIndexName,"_doc").setId("1").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(-169 0)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("2").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(-179 0)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("3").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(171 0)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
Rectangle rectangle = new Rectangle(
169, -178, 1, -1);
GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery("geo", rectangle);
SearchResponse response = client().prepareSearch("test").setQuery(geoShapeQueryBuilder).get();
assertHitCount(response, 2);
SearchHits searchHits = response.getHits();
assertThat(searchHits.getAt(0).getId(),not(equalTo("1")));
assertThat(searchHits.getAt(1).getId(),not(equalTo("1")));
}
public void testPolygonSpanningDateline() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate("test").addMapping("_doc", xcb).get();
ensureGreen();
client().prepareIndex(defaultIndexName,"_doc").setId("1").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(-169 7)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("2").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(-179 7)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("3").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(179 7)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("4").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(171 7)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
PolygonBuilder polygon = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-177, 10)
.coordinate(177, 10)
.coordinate(177, 5)
.coordinate(-177, 5)
.coordinate(-177, 10));
GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery("geo", polygon.buildGeometry());
geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS);
SearchResponse response = client().prepareSearch("test").setQuery(geoShapeQueryBuilder).get();
assertHitCount(response, 2);
SearchHits searchHits = response.getHits();
assertThat(searchHits.getAt(0).getId(),not(equalTo("1")));
assertThat(searchHits.getAt(1).getId(),not(equalTo("1")));
assertThat(searchHits.getAt(0).getId(),not(equalTo("4")));
assertThat(searchHits.getAt(1).getId(),not(equalTo("4")));
}
public void testMultiPolygonSpanningDateline() throws Exception {
XContentBuilder xcb = createDefaultMapping();
client().admin().indices().prepareCreate("test").addMapping("_doc", xcb).get();
ensureGreen();
client().prepareIndex(defaultIndexName,"_doc").setId("1").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(-169 7)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("2").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(-179 7)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
client().prepareIndex(defaultIndexName,"_doc").setId("3").setSource(jsonBuilder()
.startObject()
.field(defaultGeoFieldName, "POINT(171 7)")
.endObject()).setRefreshPolicy(IMMEDIATE).get();
MultiPolygonBuilder multiPolygon = new MultiPolygonBuilder()
.polygon(new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-167, 10)
.coordinate(-171, 10)
.coordinate(171, 5)
.coordinate(-167, 5)
.coordinate(-167, 10)))
.polygon(new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-177, 10)
.coordinate(177, 10)
.coordinate(177, 5)
.coordinate(-177, 5)
.coordinate(-177, 10)));
GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery(
"geo",
multiPolygon.buildGeometry());
geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS);
SearchResponse response = client().prepareSearch("test").setQuery(geoShapeQueryBuilder).get();
assertHitCount(response, 2);
SearchHits searchHits = response.getHits();
assertThat(searchHits.getAt(0).getId(),not(equalTo("3")));
assertThat(searchHits.getAt(1).getId(),not(equalTo("3")));
}
}

View File

@ -52,6 +52,8 @@ import org.locationtech.spatial4j.shape.Rectangle;
import java.io.IOException;
import java.util.Locale;
import static com.carrotsearch.randomizedtesting.RandomizedTest.assumeTrue;
import static org.apache.lucene.util.LuceneTestCase.random;
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.geoIntersectionQuery;
@ -62,9 +64,11 @@ import static org.elasticsearch.test.geo.RandomShapeGenerator.xRandomPoint;
import static org.elasticsearch.test.geo.RandomShapeGenerator.xRandomRectangle;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.not;
public class GeoShapeQueryTests extends GeoQueryTests {
protected static final String[] PREFIX_TREES = new String[] {
@ -74,7 +78,7 @@ public class GeoShapeQueryTests extends GeoQueryTests {
@Override
protected XContentBuilder createTypedMapping() throws Exception {
XContentBuilder xcb = XContentFactory.jsonBuilder().startObject().startObject("type1")
XContentBuilder xcb = XContentFactory.jsonBuilder().startObject()
.startObject("properties").startObject("geo")
.field("type", "geo_shape");
if (randomBoolean()) {
@ -121,10 +125,6 @@ public class GeoShapeQueryTests extends GeoQueryTests {
return xcb;
}
public void testIndexPointsFilterRectangle() throws Exception {
super.testIndexPointsFilterRectangle(Strings.toString(createTypedMapping()));
}
public void testShapeFetchingPath() throws Exception {
createIndex("shapes");
client().admin().indices().prepareCreate("test").addMapping("type", "geo", "type=geo_shape").get();
@ -219,8 +219,6 @@ public class GeoShapeQueryTests extends GeoQueryTests {
}
gcb.shape(new PolygonBuilder(cb));
logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
XContentBuilder mapping = createRandomMapping();
Settings settings = Settings.builder().put("index.number_of_shards", 1).build();
client().admin().indices().prepareCreate("test").addMapping("_doc",mapping).setSettings(settings).get();
@ -237,8 +235,7 @@ public class GeoShapeQueryTests extends GeoQueryTests {
geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS);
SearchResponse result = client().prepareSearch("test").setQuery(geoShapeQueryBuilder).get();
assertSearchResponse(result);
assertTrue("query: " + geoShapeQueryBuilder.toString() + " doc: " + Strings.toString(docSource),
result.getHits().getTotalHits().value > 0);
assertThat(result.getHits().getHits().length, greaterThan(0));
}
// Test for issue #34418
@ -299,9 +296,9 @@ public class GeoShapeQueryTests extends GeoQueryTests {
SearchResponse response = client().prepareSearch("test")
.setQuery(querySupplier.get())
.get();
assertEquals(2, response.getHits().getTotalHits().value);
assertNotEquals("1", response.getHits().getAt(0).getId());
assertNotEquals("1", response.getHits().getAt(1).getId());
assertHitCount(response, 2);
assertThat(response.getHits().getAt(0).getId(), not(equalTo("1")));
assertThat(response.getHits().getAt(1).getId(), not(equalTo("1")));
}
public void testGeometryCollectionRelations() throws Exception {
@ -322,15 +319,15 @@ public class GeoShapeQueryTests extends GeoQueryTests {
SearchResponse response = client().prepareSearch("test")
.setQuery(geoShapeQuery("geo", builder.buildGeometry()).relation(ShapeRelation.CONTAINS))
.get();
assertEquals(1, response.getHits().getTotalHits().value);
assertHitCount(response, 1);
response = client().prepareSearch("test")
.setQuery(geoShapeQuery("geo", builder.buildGeometry()).relation(ShapeRelation.INTERSECTS))
.get();
assertEquals(1, response.getHits().getTotalHits().value);
assertHitCount(response, 1);
response = client().prepareSearch("test")
.setQuery(geoShapeQuery("geo", builder.buildGeometry()).relation(ShapeRelation.DISJOINT))
.get();
assertEquals(0, response.getHits().getTotalHits().value);
assertHitCount(response, 0);
}
// A geometry collection that is partially within the indexed shape
{
@ -340,15 +337,15 @@ public class GeoShapeQueryTests extends GeoQueryTests {
SearchResponse response = client().prepareSearch("test")
.setQuery(geoShapeQuery("geo", builder.buildGeometry()).relation(ShapeRelation.CONTAINS))
.get();
assertEquals(0, response.getHits().getTotalHits().value);
assertHitCount(response, 0);
response = client().prepareSearch("test")
.setQuery(geoShapeQuery("geo", builder.buildGeometry()).relation(ShapeRelation.INTERSECTS))
.get();
assertEquals(1, response.getHits().getTotalHits().value);
assertHitCount(response, 1);
response = client().prepareSearch("test")
.setQuery(geoShapeQuery("geo", builder.buildGeometry()).relation(ShapeRelation.DISJOINT))
.get();
assertEquals(0, response.getHits().getTotalHits().value);
assertHitCount(response, 0);
}
{
// A geometry collection that is disjoint with the indexed shape
@ -358,15 +355,15 @@ public class GeoShapeQueryTests extends GeoQueryTests {
SearchResponse response = client().prepareSearch("test")
.setQuery(geoShapeQuery("geo", builder.buildGeometry()).relation(ShapeRelation.CONTAINS))
.get();
assertEquals(0, response.getHits().getTotalHits().value);
assertHitCount(response, 0);
response = client().prepareSearch("test")
.setQuery(geoShapeQuery("geo", builder.buildGeometry()).relation(ShapeRelation.INTERSECTS))
.get();
assertEquals(0, response.getHits().getTotalHits().value);
assertHitCount(response, 0);
response = client().prepareSearch("test")
.setQuery(geoShapeQuery("geo", builder.buildGeometry()).relation(ShapeRelation.DISJOINT))
.get();
assertEquals(1, response.getHits().getTotalHits().value);
assertHitCount(response, 1);
}
}
@ -401,13 +398,12 @@ public class GeoShapeQueryTests extends GeoQueryTests {
.get();
assertSearchResponse(searchResponse);
assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
assertThat(searchResponse.getHits().getHits().length, equalTo(1));
assertHitCount(searchResponse, 1);
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("blakely"));
}
public void testIndexedShapeReferenceSourceDisabled() throws Exception {
XContentBuilder mapping = createTypedMapping();
XContentBuilder mapping = createDefaultMapping();
client().admin().indices().prepareCreate("test").addMapping("type1", mapping).get();
createIndex("shapes", Settings.EMPTY, "shape_type", "_source", "enabled=false");
ensureGreen();
@ -417,9 +413,13 @@ public class GeoShapeQueryTests extends GeoQueryTests {
client().prepareIndex("shapes", "shape_type", "Big_Rectangle").setSource(jsonBuilder().startObject()
.field("shape", shape).endObject()).setRefreshPolicy(IMMEDIATE).get();
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> client().prepareSearch("test")
.setQuery(geoIntersectionQuery("geo", "Big_Rectangle")).get());
assertThat(e.getMessage(), containsString("source disabled"));
try {
client().prepareSearch("test")
.setQuery(geoIntersectionQuery("geo", "Big_Rectangle")).get();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(),
containsString("source disabled"));
}
}
public void testReusableBuilder() throws IOException {
@ -509,13 +509,12 @@ public class GeoShapeQueryTests extends GeoQueryTests {
SearchResponse response = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
.setPostFilter(filter).get();
assertSearchResponse(response);
assertThat(response.getHits().getTotalHits().value, greaterThan(0L));
assertThat(response.getHits().getHits().length, greaterThan(0));
}
public void testExistsQuery() throws Exception {
// Create a random geometry collection.
GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
XContentBuilder builder = createRandomMapping();
client().admin().indices().prepareCreate("test").addMapping("type", builder)
@ -560,7 +559,7 @@ public class GeoShapeQueryTests extends GeoQueryTests {
.setQuery(geoIntersectionQuery("location", shape))
.get();
assertEquals(1, response.getHits().getTotalHits().value);
assertHitCount(response, 1);
}
public void testPointsOnlyExplicit() throws Exception {
@ -594,11 +593,11 @@ public class GeoShapeQueryTests extends GeoQueryTests {
.setQuery(matchAllQuery())
.get();
assertEquals(2, response.getHits().getTotalHits().value);
assertHitCount(response, 2);
}
public void testIndexedShapeReference() throws Exception {
String mapping = Strings.toString(createTypedMapping());
String mapping = Strings.toString(createDefaultMapping());
client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
createIndex("shapes");
ensureGreen();
@ -620,8 +619,7 @@ public class GeoShapeQueryTests extends GeoQueryTests {
.get();
assertSearchResponse(searchResponse);
assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
assertThat(searchResponse.getHits().getHits().length, equalTo(1));
assertHitCount(searchResponse, 1);
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
searchResponse = client().prepareSearch("test")
@ -629,8 +627,7 @@ public class GeoShapeQueryTests extends GeoQueryTests {
.get();
assertSearchResponse(searchResponse);
assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
assertThat(searchResponse.getHits().getHits().length, equalTo(1));
assertHitCount(searchResponse, 1);
assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
}
@ -660,7 +657,7 @@ public class GeoShapeQueryTests extends GeoQueryTests {
SearchResponse response = client().prepareSearch("test")
.setQuery(geoShapeQuery("alias", shape))
.get();
assertEquals(1, response.getHits().getTotalHits().value);
assertHitCount(response, 1);
}
public void testQueryRandomGeoCollection() throws Exception {
@ -673,8 +670,6 @@ public class GeoShapeQueryTests extends GeoQueryTests {
}
gcb.shape(new PolygonBuilder(cb));
logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
XContentBuilder builder = createRandomMapping();
client().admin().indices().prepareCreate("test")
.addMapping("type", builder).get();

View File

@ -27,6 +27,7 @@ import org.elasticsearch.xpack.spatial.index.mapper.ShapeFieldMapper;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
@ -45,6 +46,9 @@ public class ShapeQueryBuilder extends AbstractGeometryQueryBuilder<ShapeQueryBu
static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Types are deprecated in [geo_shape] queries. " +
"The type should no longer be specified in the [indexed_shape] section.";
protected static final List<String> validContentTypes =
Collections.unmodifiableList(Arrays.asList(ShapeFieldMapper.CONTENT_TYPE));
/**
* Creates a new GeoShapeQueryBuilder whose Query will be against the given
* field name using the given Shape
@ -117,23 +121,20 @@ public class ShapeQueryBuilder extends AbstractGeometryQueryBuilder<ShapeQueryBu
return new ShapeQueryBuilder(fieldName, shapeSupplier, indexedShapeId, indexedShapeType);
}
@Override
public String queryFieldType() {
return ShapeFieldMapper.CONTENT_TYPE;
}
@Override
@SuppressWarnings({ "rawtypes" })
protected List validContentTypes() {
return Arrays.asList(ShapeFieldMapper.CONTENT_TYPE);
protected List<String> validContentTypes(){
return validContentTypes;
}
@Override
@SuppressWarnings({ "rawtypes" })
public Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType) {
if (fieldType.typeName().equals(ShapeFieldMapper.CONTENT_TYPE) == false) {
List<String> validContentTypes = validContentTypes();
if (validContentTypes.contains(fieldType.typeName()) == false) {
throw new QueryShardException(context,
"Field [" + fieldName + "] is not of type [" + queryFieldType() + "] but of type [" + fieldType.typeName() + "]");
"Field [" + fieldName + "] is not of type [" + String.join(" or ", validContentTypes())
+ "] but of type [" + fieldType.typeName() + "]");
}
final AbstractGeometryFieldMapper.AbstractGeometryFieldType ft = (AbstractGeometryFieldMapper.AbstractGeometryFieldType) fieldType;

View File

@ -28,7 +28,7 @@ import org.elasticsearch.geometry.MultiPolygon;
import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Polygon;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.QueryShardException;
@ -36,7 +36,7 @@ import org.elasticsearch.xpack.spatial.index.mapper.ShapeFieldMapper;
import static org.elasticsearch.xpack.spatial.index.mapper.ShapeIndexer.toLucenePolygon;
public class ShapeQueryProcessor implements AbstractGeometryFieldMapper.QueryProcessor {
public class ShapeQueryProcessor implements AbstractSearchableGeometryFieldType.QueryProcessor {
@Override
public Query process(Geometry shape, String fieldName, ShapeRelation relation, QueryShardContext context) {