Add support for multipoint geoshape queries (#52133) (#52553)

Currently multi-point queries are not supported when indexing your data using BKD-backed geoshape strategy. This commit removes this limitation.
This commit is contained in:
Ignacio Vera 2020-02-21 07:45:53 +01:00 committed by GitHub
parent 4bc7545e43
commit 107f00a4ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 66 deletions

View File

@ -134,16 +134,10 @@ and will be removed in a future version.
*IMPORTANT NOTES*
The following features are not yet supported with the new indexing approach:
`CONTAINS` relation query - when using the new default vector indexing strategy, `geo_shape`
queries with `relation` defined as `contains` are supported for indices created with
ElasticSearch 7.5.0 or higher.
* `geo_shape` query with `MultiPoint` geometry types - Elasticsearch currently prevents searching
geo_shape fields with a MultiPoint geometry type to avoid a brute force linear search
over each individual point. For now, if this is absolutely needed, this can be achieved
using a `bool` query with each individual point.
* `CONTAINS` relation query - when using the new default vector indexing strategy, `geo_shape`
queries with `relation` defined as `contains` are supported for indices created with
ElasticSearch 7.5.0 or higher.
[[prefix-trees]]
[float]

View File

@ -28,7 +28,6 @@ import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.Version;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.geometry.Circle;
import org.elasticsearch.geometry.Geometry;
@ -106,13 +105,7 @@ public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper
occur = BooleanClause.Occur.SHOULD;
}
for (Geometry shape : collection) {
if (shape instanceof MultiPoint) {
// Flatten multi-points
// We do not support multi-point queries?
visit(bqb, (GeometryCollection<?>) shape);
} else {
bqb.add(shape.visit(this), occur);
}
bqb.add(shape.visit(this), occur);
}
}
@ -139,8 +132,11 @@ public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper
@Override
public Query visit(MultiPoint multiPoint) {
throw new QueryShardException(context, "Field [" + fieldName + "] does not support " + GeoShapeType.MULTIPOINT +
" queries");
double[][] points = new double[multiPoint.size()][2];
for (int i = 0; i < multiPoint.size(); i++) {
points[i] = new double[] {multiPoint.get(i).getLat(), multiPoint.get(i).getLon()};
}
return LatLonShape.newPointQuery(fieldName, relation.getLuceneRelation(), points);
}
@Override
@ -161,8 +157,8 @@ public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper
// intersects is more efficient.
luceneRelation = ShapeField.QueryRelation.INTERSECTS;
}
return LatLonShape.newBoxQuery(fieldName, luceneRelation,
point.getY(), point.getY(), point.getX(), point.getX());
return LatLonShape.newPointQuery(fieldName, luceneRelation,
new double[] {point.getY(), point.getX()});
}
@Override

View File

@ -84,9 +84,8 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
}
protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
// LatLonShape does not support MultiPoint queries
RandomShapeGenerator.ShapeType shapeType =
randomFrom(ShapeType.POINT, ShapeType.LINESTRING, ShapeType.MULTILINESTRING, ShapeType.POLYGON);
randomFrom(ShapeType.POINT, ShapeType.MULTIPOINT, ShapeType.LINESTRING, ShapeType.MULTILINESTRING, ShapeType.POLYGON);
ShapeBuilder<?, ?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null, shapeType);
GeoShapeQueryBuilder builder;
clearShapeFields();
@ -111,11 +110,20 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
}
}
if (randomBoolean()) {
if (shapeType == ShapeType.LINESTRING || shapeType == ShapeType.MULTILINESTRING) {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS));
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 {
// LatLonShape does not support CONTAINS:
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.WITHIN));
if (shapeType == ShapeType.LINESTRING || shapeType == ShapeType.MULTILINESTRING) {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS));
} else {
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.WITHIN));
}
}
}

View File

@ -206,23 +206,8 @@ public class GeoShapeQueryTests extends GeoQueryTests {
}
public void testRandomGeoCollectionQuery() throws Exception {
boolean usePrefixTrees = randomBoolean();
// Create a random geometry collection to index.
GeometryCollectionBuilder gcb;
if (usePrefixTrees) {
gcb = RandomShapeGenerator.createGeometryCollection(random());
} else {
// vector strategy does not yet support multipoint queries
gcb = new GeometryCollectionBuilder();
int numShapes = RandomNumbers.randomIntBetween(random(), 1, 4);
for (int i = 0; i < numShapes; ++i) {
ShapeBuilder shape;
do {
shape = RandomShapeGenerator.createShape(random());
} while (shape instanceof MultiPointBuilder);
gcb.shape(shape);
}
}
GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
org.apache.lucene.geo.Polygon randomPoly = GeoTestUtil.nextPolygon();
assumeTrue("Skipping the check for the polygon with a degenerated dimension",
@ -234,10 +219,9 @@ public class GeoShapeQueryTests extends GeoQueryTests {
}
gcb.shape(new PolygonBuilder(cb));
logger.info("Created Random GeometryCollection containing {} shapes using {} tree", gcb.numShapes(),
usePrefixTrees ? "geohash" : "quadtree");
logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
XContentBuilder mapping = createPrefixTreeMapping(usePrefixTrees ? "geohash" : "quadtree");
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();
ensureGreen();
@ -321,8 +305,7 @@ public class GeoShapeQueryTests extends GeoQueryTests {
}
public void testGeometryCollectionRelations() throws Exception {
XContentBuilder mapping = createPrefixTreeMapping(LegacyGeoShapeFieldMapper.DeprecatedParameters.PrefixTrees.GEOHASH);
XContentBuilder mapping = createDefaultMapping();
createIndex("test", Settings.builder().put("index.number_of_shards", 1).build(), "doc", mapping);
EnvelopeBuilder envelopeBuilder = new EnvelopeBuilder(new Coordinate(-10, 10), new Coordinate(10, -10));
@ -441,13 +424,13 @@ public class GeoShapeQueryTests extends GeoQueryTests {
public void testReusableBuilder() throws IOException {
PolygonBuilder polygon = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(170, -10).coordinate(190, -10).coordinate(190, 10).coordinate(170, 10).close())
.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(175, -5).coordinate(185, -5).coordinate(185, 5)
.coordinate(175, 5).close()));
.coordinate(170, -10).coordinate(190, -10).coordinate(190, 10).coordinate(170, 10).close())
.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(175, -5).coordinate(185, -5).coordinate(185, 5)
.coordinate(175, 5).close()));
assertUnmodified(polygon);
LineStringBuilder linestring = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(170, -10).coordinate(190, -10).coordinate(190, 10).coordinate(170, 10).close());
.coordinate(170, -10).coordinate(190, -10).coordinate(190, 10).coordinate(170, 10).close());
assertUnmodified(linestring);
}
@ -534,13 +517,9 @@ public class GeoShapeQueryTests extends GeoQueryTests {
GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
if (randomBoolean()) {
client().admin().indices().prepareCreate("test").addMapping("type", "geo", "type=geo_shape")
.execute().actionGet();
} else {
client().admin().indices().prepareCreate("test").addMapping("type", "geo", "type=geo_shape,tree=quadtree")
.execute().actionGet();
}
XContentBuilder builder = createRandomMapping();
client().admin().indices().prepareCreate("test").addMapping("type", builder)
.execute().actionGet();
XContentBuilder docSource = gcb.toXContent(jsonBuilder().startObject().field("geo"), null).endObject();
client().prepareIndex("test", "type", "1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
@ -696,13 +675,9 @@ public class GeoShapeQueryTests extends GeoQueryTests {
logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
if (randomBoolean()) {
client().admin().indices().prepareCreate("test")
.addMapping("type", "geo", "type=geo_shape").get();
} else {
client().admin().indices().prepareCreate("test")
.addMapping("type", "geo", "type=geo_shape,tree=quadtree").get();
}
XContentBuilder builder = createRandomMapping();
client().admin().indices().prepareCreate("test")
.addMapping("type", builder).get();
XContentBuilder docSource = gcb.toXContent(jsonBuilder().startObject().field("geo"), null).endObject();
client().prepareIndex("test", "type", "1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();