From 02c29433d1c50441fae029715c249b6983872aa2 Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Tue, 27 Jul 2021 18:34:21 +0200 Subject: [PATCH] Vector tiles: order hits by geometry size by default (#75621) _mvt end point gives priority to geometries with larger area --- .../painless/spi/org.elasticsearch.txt | 2 + .../test/painless/50_script_doc_values.yml | 14 +++++ .../common/geo}/SphericalMercatorUtils.java | 12 +++-- .../index/fielddata/ScriptDocValues.java | 14 +++++ .../geo/SphericalMercatorUtilTests.java | 53 +++++++++++++++++++ .../AbstractAtomicGeoShapeShapeFieldData.java | 13 +++++ .../test/70_script_doc_values.yml | 16 ++++++ .../xpack/vectortile/VectorTileRestIT.java | 53 ++++++++++++++----- .../vectortile/feature/FeatureFactory.java | 1 + .../feature/SimpleFeatureFactory.java | 1 + .../vectortile/rest/VectorTileRequest.java | 31 +++++++++-- .../FeatureFactoriesConsistencyTests.java | 1 + .../rest/VectorTileRequestTests.java | 11 +++- 13 files changed, 197 insertions(+), 25 deletions(-) rename {x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature => server/src/main/java/org/elasticsearch/common/geo}/SphericalMercatorUtils.java (75%) create mode 100644 server/src/test/java/org/elasticsearch/common/geo/SphericalMercatorUtilTests.java diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt index 1adda3bcef102..5ff8535c967a1 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt @@ -158,6 +158,8 @@ class org.elasticsearch.index.fielddata.ScriptDocValues$Geometry { int getDimensionalType() org.elasticsearch.common.geo.GeoPoint getCentroid() org.elasticsearch.common.geo.GeoBoundingBox getBoundingBox() + double getMercatorWidth() + double getMercatorHeight() } class org.elasticsearch.index.fielddata.ScriptDocValues$GeoPoints { diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml index 7c7a7390107ee..29d526a7c7187 100644 --- a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml @@ -182,6 +182,20 @@ setup: source: "doc['geo_point'].getDimensionalType()" - match: { hits.hits.0.fields.type.0: 0 } + - do: + search: + rest_total_hits_as_int: true + body: + script_fields: + width: + script: + source: "doc['geo_point'].getMercatorWidth()" + height: + script: + source: "doc['geo_point'].getMercatorHeight()" + - match: { hits.hits.0.fields.width.0: 0.0 } + - match: { hits.hits.0.fields.height.0: 0.0 } + --- "ip": - do: diff --git a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/SphericalMercatorUtils.java b/server/src/main/java/org/elasticsearch/common/geo/SphericalMercatorUtils.java similarity index 75% rename from x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/SphericalMercatorUtils.java rename to server/src/main/java/org/elasticsearch/common/geo/SphericalMercatorUtils.java index da0949582d36c..a406b86d01916 100644 --- a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/SphericalMercatorUtils.java +++ b/server/src/main/java/org/elasticsearch/common/geo/SphericalMercatorUtils.java @@ -1,20 +1,22 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ -package org.elasticsearch.xpack.vectortile.feature; +package org.elasticsearch.common.geo; import org.elasticsearch.geometry.Rectangle; /** * Utility functions to transforms WGS84 coordinates into spherical mercator. */ -class SphericalMercatorUtils { +public class SphericalMercatorUtils { - private static double MERCATOR_FACTOR = 20037508.34 / 180.0; + public static final double MERCATOR_BOUNDS = 20037508.34; + private static final double MERCATOR_FACTOR = MERCATOR_BOUNDS / 180.0; /** * Transforms WGS84 longitude to a Spherical mercator longitude diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java b/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java index 3f35a3f8e9609..86d007072197e 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java @@ -255,6 +255,10 @@ public abstract static class Geometry extends ScriptDocValues { public abstract GeoBoundingBox getBoundingBox(); /** Returns the centroid of this geometry */ public abstract GeoPoint getCentroid(); + /** Returns the width of the bounding box diagonal in the spherical Mercator projection (meters) */ + public abstract double getMercatorWidth(); + /** Returns the height of the bounding box diagonal in the spherical Mercator projection (meters) */ + public abstract double getMercatorHeight(); } public static final class GeoPoints extends Geometry { @@ -418,6 +422,16 @@ public GeoPoint getCentroid() { return size() == 0 ? null : centroid; } + @Override + public double getMercatorWidth() { + return 0; + } + + @Override + public double getMercatorHeight() { + return 0; + } + @Override public GeoBoundingBox getBoundingBox() { return size() == 0 ? null : boundingBox; diff --git a/server/src/test/java/org/elasticsearch/common/geo/SphericalMercatorUtilTests.java b/server/src/test/java/org/elasticsearch/common/geo/SphericalMercatorUtilTests.java new file mode 100644 index 0000000000000..f2ef80922a5bd --- /dev/null +++ b/server/src/test/java/org/elasticsearch/common/geo/SphericalMercatorUtilTests.java @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.common.geo; + +import org.elasticsearch.geo.GeometryTestUtils; +import org.elasticsearch.geometry.Rectangle; +import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils; +import org.elasticsearch.test.ESTestCase; +import org.hamcrest.Matchers; + +import static org.elasticsearch.common.geo.SphericalMercatorUtils.MERCATOR_BOUNDS; +import static org.elasticsearch.common.geo.SphericalMercatorUtils.lonToSphericalMercator; +import static org.elasticsearch.common.geo.SphericalMercatorUtils.latToSphericalMercator; +import static org.elasticsearch.common.geo.SphericalMercatorUtils.recToSphericalMercator; + +public class SphericalMercatorUtilTests extends ESTestCase { + + public void testLon() { + assertThat(lonToSphericalMercator(180.0), Matchers.equalTo(MERCATOR_BOUNDS)); + assertThat(lonToSphericalMercator(-180.0), Matchers.equalTo(-MERCATOR_BOUNDS)); + assertThat(lonToSphericalMercator(0.0), Matchers.equalTo(0.0)); + final double lon = lonToSphericalMercator(GeometryTestUtils.randomLon()); + assertThat(lon, Matchers.greaterThanOrEqualTo(-MERCATOR_BOUNDS)); + assertThat(lon, Matchers.lessThanOrEqualTo(MERCATOR_BOUNDS)); + } + + public void testLat() { + assertThat(latToSphericalMercator(GeoTileUtils.LATITUDE_MASK), Matchers.closeTo(MERCATOR_BOUNDS, 1e-7)); + assertThat(latToSphericalMercator(-GeoTileUtils.LATITUDE_MASK), Matchers.closeTo(-MERCATOR_BOUNDS, 1e-7)); + assertThat(latToSphericalMercator(0.0), Matchers.closeTo(0, 1e-7)); + final double lat = latToSphericalMercator(randomValueOtherThanMany( + l -> l >= GeoTileUtils.LATITUDE_MASK || l <= -GeoTileUtils.LATITUDE_MASK, + GeometryTestUtils::randomLat + )); + assertThat(lat, Matchers.greaterThanOrEqualTo(-MERCATOR_BOUNDS)); + assertThat(lat, Matchers.lessThanOrEqualTo(MERCATOR_BOUNDS)); + } + + public void testRectangle() { + Rectangle rect = GeometryTestUtils.randomRectangle(); + Rectangle mercatorRect = recToSphericalMercator(rect); + assertThat(mercatorRect.getMinX(), Matchers.equalTo(lonToSphericalMercator(rect.getMinX()))); + assertThat(mercatorRect.getMaxX(), Matchers.equalTo(lonToSphericalMercator(rect.getMaxX()))); + assertThat(mercatorRect.getMinY(), Matchers.equalTo(latToSphericalMercator(rect.getMinY()))); + assertThat(mercatorRect.getMaxY(), Matchers.equalTo(latToSphericalMercator(rect.getMaxY()))); + } +} diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/plain/AbstractAtomicGeoShapeShapeFieldData.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/plain/AbstractAtomicGeoShapeShapeFieldData.java index 59ed40ad9041f..62b0abedd788d 100644 --- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/plain/AbstractAtomicGeoShapeShapeFieldData.java +++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/plain/AbstractAtomicGeoShapeShapeFieldData.java @@ -19,6 +19,9 @@ import java.util.Collection; import java.util.Collections; +import static org.elasticsearch.common.geo.SphericalMercatorUtils.latToSphericalMercator; +import static org.elasticsearch.common.geo.SphericalMercatorUtils.lonToSphericalMercator; + public abstract class AbstractAtomicGeoShapeShapeFieldData implements LeafGeoShapeFieldData { @Override @@ -88,6 +91,16 @@ public GeoPoint getCentroid() { return value == null ? null : centroid; } + @Override + public double getMercatorWidth() { + return lonToSphericalMercator(boundingBox.right()) - lonToSphericalMercator(boundingBox.left()); + } + + @Override + public double getMercatorHeight() { + return latToSphericalMercator(boundingBox.top()) - latToSphericalMercator(boundingBox.bottom()); + } + @Override public GeoBoundingBox getBoundingBox() { return value == null ? null : boundingBox; diff --git a/x-pack/plugin/spatial/src/yamlRestTest/resources/rest-api-spec/test/70_script_doc_values.yml b/x-pack/plugin/spatial/src/yamlRestTest/resources/rest-api-spec/test/70_script_doc_values.yml index ecde387a7a4cf..482eb1661fcdb 100644 --- a/x-pack/plugin/spatial/src/yamlRestTest/resources/rest-api-spec/test/70_script_doc_values.yml +++ b/x-pack/plugin/spatial/src/yamlRestTest/resources/rest-api-spec/test/70_script_doc_values.yml @@ -91,3 +91,19 @@ setup: source: "doc['geo_shape'].get(0)" - match: { error.root_cause.0.reason: "cannot write xcontent for geo_shape doc value" } + +--- +"diagonal length": + - do: + search: + rest_total_hits_as_int: true + body: + script_fields: + width: + script: + source: "doc['geo_shape'].getMercatorWidth()" + height: + script: + source: "doc['geo_shape'].getMercatorHeight()" + - match: { hits.hits.0.fields.width.0: 389.62170283915475 } + - match: { hits.hits.0.fields.height.0: 333.37976840604097 } diff --git a/x-pack/plugin/vector-tile/src/javaRestTest/java/org/elasticsearch/xpack/vectortile/VectorTileRestIT.java b/x-pack/plugin/vector-tile/src/javaRestTest/java/org/elasticsearch/xpack/vectortile/VectorTileRestIT.java index 695d0ed13e6cd..9cb0a08100464 100644 --- a/x-pack/plugin/vector-tile/src/javaRestTest/java/org/elasticsearch/xpack/vectortile/VectorTileRestIT.java +++ b/x-pack/plugin/vector-tile/src/javaRestTest/java/org/elasticsearch/xpack/vectortile/VectorTileRestIT.java @@ -237,7 +237,7 @@ public void testBasicGet() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 4096, 33, 1); assertLayer(tile, AGGS_LAYER, 4096, 1, 1); - assertLayer(tile, META_LAYER, 4096, 1, 14); + assertLayer(tile, META_LAYER, 4096, 1, 13); } public void testIndexAllGet() throws Exception { @@ -248,7 +248,7 @@ public void testIndexAllGet() throws Exception { // 33 points, 1 polygon and two from geometry collection assertLayer(tile, HITS_LAYER, 4096, 36, 1); assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 1); - assertLayer(tile, META_LAYER, 4096, 1, 14); + assertLayer(tile, META_LAYER, 4096, 1, 13); } public void testExtent() throws Exception { @@ -258,7 +258,7 @@ public void testExtent() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 256, 33, 1); assertLayer(tile, AGGS_LAYER, 256, 1, 1); - assertLayer(tile, META_LAYER, 256, 1, 14); + assertLayer(tile, META_LAYER, 256, 1, 13); } public void testExtentURL() throws Exception { @@ -271,7 +271,7 @@ public void testExtentURL() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 512, 33, 1); assertLayer(tile, AGGS_LAYER, 512, 1, 1); - assertLayer(tile, META_LAYER, 512, 1, 14); + assertLayer(tile, META_LAYER, 512, 1, 13); } public void testExactBounds() throws Exception { @@ -327,7 +327,7 @@ public void testGridPrecision() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 4096, 33, 1); assertLayer(tile, AGGS_LAYER, 4096, 1, 1); - assertLayer(tile, META_LAYER, 4096, 1, 14); + assertLayer(tile, META_LAYER, 4096, 1, 13); } { final Request mvtRequest = new Request(getHttpMethod(), INDEX_POINTS + "/_mvt/location/" + z + "/" + x + "/" + y); @@ -345,7 +345,7 @@ public void testGridType() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 4096, 33, 1); assertLayer(tile, AGGS_LAYER, 4096, 1, 1); - assertLayer(tile, META_LAYER, 4096, 1, 14); + assertLayer(tile, META_LAYER, 4096, 1, 13); assertFeatureType(tile, AGGS_LAYER, VectorTile.Tile.GeomType.POINT); } { @@ -355,7 +355,7 @@ public void testGridType() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 4096, 33, 1); assertLayer(tile, AGGS_LAYER, 4096, 1, 1); - assertLayer(tile, META_LAYER, 4096, 1, 14); + assertLayer(tile, META_LAYER, 4096, 1, 13); assertFeatureType(tile, AGGS_LAYER, VectorTile.Tile.GeomType.POLYGON); } { @@ -376,7 +376,7 @@ public void testGridTypeURL() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 4096, 33, 1); assertLayer(tile, AGGS_LAYER, 4096, 1, 1); - assertLayer(tile, META_LAYER, 4096, 1, 14); + assertLayer(tile, META_LAYER, 4096, 1, 13); assertFeatureType(tile, AGGS_LAYER, VectorTile.Tile.GeomType.POLYGON); } @@ -386,7 +386,7 @@ public void testNoAggLayer() throws Exception { final VectorTile.Tile tile = execute(mvtRequest); assertThat(tile.getLayersCount(), Matchers.equalTo(2)); assertLayer(tile, HITS_LAYER, 4096, 33, 1); - assertLayer(tile, META_LAYER, 4096, 1, 9); + assertLayer(tile, META_LAYER, 4096, 1, 8); } public void testNoAggLayerURL() throws Exception { @@ -398,7 +398,7 @@ public void testNoAggLayerURL() throws Exception { final VectorTile.Tile tile = execute(mvtRequest); assertThat(tile.getLayersCount(), Matchers.equalTo(2)); assertLayer(tile, HITS_LAYER, 4096, 33, 1); - assertLayer(tile, META_LAYER, 4096, 1, 9); + assertLayer(tile, META_LAYER, 4096, 1, 8); } public void testNoHitsLayer() throws Exception { @@ -419,6 +419,31 @@ public void testNoHitsLayerURL() throws Exception { assertLayer(tile, META_LAYER, 4096, 1, 13); } + public void testDefaultSort() throws Exception { + { + final Request mvtRequest = new Request(getHttpMethod(), INDEX_POINTS_SHAPES + "/_mvt/location/" + z + "/" + x + "/" + y); + mvtRequest.setJsonEntity("{\"size\": 100 }"); + final VectorTile.Tile tile = execute(mvtRequest); + assertThat(tile.getLayersCount(), Matchers.equalTo(3)); + assertLayer(tile, HITS_LAYER, 4096, 34, 1); + final VectorTile.Tile.Layer layer = getLayer(tile, HITS_LAYER); + assertThat(layer.getFeatures(0).getType(), Matchers.equalTo(VectorTile.Tile.GeomType.POLYGON)); + assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 1); + assertLayer(tile, META_LAYER, 4096, 1, 13); + } + { + final Request mvtRequest = new Request(getHttpMethod(), INDEX_POINTS_SHAPES + "/_mvt/location/" + z + "/" + x + "/" + y); + mvtRequest.setJsonEntity("{\"size\": 100, \"sort\" : []}"); // override default sort + final VectorTile.Tile tile = execute(mvtRequest); + assertThat(tile.getLayersCount(), Matchers.equalTo(3)); + assertLayer(tile, HITS_LAYER, 4096, 34, 1); + final VectorTile.Tile.Layer layer = getLayer(tile, HITS_LAYER); + assertThat(layer.getFeatures(0).getType(), Matchers.equalTo(VectorTile.Tile.GeomType.POINT)); + assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 1); + assertLayer(tile, META_LAYER, 4096, 1, 14); + } + } + public void testRuntimeFieldWithSort() throws Exception { String runtimeMapping = "\"runtime_mappings\": {\n" + " \"width\": {\n" @@ -498,7 +523,7 @@ public void testBasicQueryGet() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 4096, 1, 1); assertLayer(tile, AGGS_LAYER, 4096, 1, 1); - assertLayer(tile, META_LAYER, 4096, 1, 14); + assertLayer(tile, META_LAYER, 4096, 1, 13); } public void testBasicShape() throws Exception { @@ -507,7 +532,7 @@ public void testBasicShape() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 4096, 1, 1); assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 1); - assertLayer(tile, META_LAYER, 4096, 1, 14); + assertLayer(tile, META_LAYER, 4096, 1, 13); } public void testWithFields() throws Exception { @@ -517,7 +542,7 @@ public void testWithFields() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 4096, 1, 3); assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 1); - assertLayer(tile, META_LAYER, 4096, 1, 14); + assertLayer(tile, META_LAYER, 4096, 1, 13); } public void testMinAgg() throws Exception { @@ -537,7 +562,7 @@ public void testMinAgg() throws Exception { assertThat(tile.getLayersCount(), Matchers.equalTo(3)); assertLayer(tile, HITS_LAYER, 4096, 1, 1); assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 2); - assertLayer(tile, META_LAYER, 4096, 1, 19); + assertLayer(tile, META_LAYER, 4096, 1, 18); } private String getHttpMethod() { diff --git a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/FeatureFactory.java b/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/FeatureFactory.java index e35bafe1591cb..223fcc601edbd 100644 --- a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/FeatureFactory.java +++ b/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/FeatureFactory.java @@ -15,6 +15,7 @@ import com.wdtinc.mapbox_vector_tile.build.MvtLayerParams; import com.wdtinc.mapbox_vector_tile.build.MvtLayerProps; +import org.elasticsearch.common.geo.SphericalMercatorUtils; import org.elasticsearch.geometry.Circle; import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.GeometryCollection; diff --git a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/SimpleFeatureFactory.java b/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/SimpleFeatureFactory.java index 71b8002e34d75..5d65213b98f48 100644 --- a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/SimpleFeatureFactory.java +++ b/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/SimpleFeatureFactory.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.vectortile.feature; import org.apache.lucene.util.BitUtil; +import org.elasticsearch.common.geo.SphericalMercatorUtils; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Rectangle; diff --git a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/VectorTileRequest.java b/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/VectorTileRequest.java index 2c2fb275287f4..80a8f2b1edf67 100644 --- a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/VectorTileRequest.java +++ b/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/VectorTileRequest.java @@ -16,6 +16,7 @@ import org.elasticsearch.index.query.AbstractQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.script.Script; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.PipelineAggregationBuilder; @@ -27,7 +28,9 @@ import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.fetch.subphase.FieldAndFormat; +import org.elasticsearch.search.sort.ScriptSortBuilder; import org.elasticsearch.search.sort.SortBuilder; +import org.elasticsearch.search.sort.SortOrder; import java.io.IOException; import java.util.ArrayList; @@ -71,14 +74,11 @@ private static GRID_TYPE fromString(String type) { } protected static class Defaults { - // TODO: Should it be SearchService.DEFAULT_SIZE? public static final int SIZE = 10000; public static final List FETCH = emptyList(); public static final Map RUNTIME_MAPPINGS = emptyMap(); public static final QueryBuilder QUERY = null; public static final AggregatorFactories.Builder AGGS = null; - public static final List> SORT = emptyList(); - // TODO: Should it be 0, no aggs by default? public static final int GRID_PRECISION = 8; public static final GRID_TYPE GRID_TYPE = VectorTileRequest.GRID_TYPE.GRID; public static final int EXTENT = 4096; @@ -161,6 +161,14 @@ static VectorTileRequest parseRestRequest(RestRequest restRequest) throws IOExce return request; } + private static final String SCRIPT = "" + + "ScriptDocValues.Geometry geometry = doc[params." + + FIELD_PARAM + + "];" + + "double w = geometry.getMercatorWidth();" + + "double h = geometry.getMercatorHeight();" + + "return h * h + w * w;"; + private final String[] indexes; private final String field; private final int x; @@ -175,7 +183,7 @@ static VectorTileRequest parseRestRequest(RestRequest restRequest) throws IOExce private int extent = Defaults.EXTENT; private AggregatorFactories.Builder aggBuilder = Defaults.AGGS; private List fields = Defaults.FETCH; - private List> sortBuilders = Defaults.SORT; + private List> sortBuilders; private boolean exact_bounds = Defaults.EXACT_BOUNDS; private VectorTileRequest(String[] indexes, String field, int z, int x, int y) { @@ -317,7 +325,20 @@ private void setAggBuilder(AggregatorFactories.Builder aggBuilder) { } public List> getSortBuilders() { - return sortBuilders; + if (sortBuilders == null) { + if (size == 0) { + // no need to add sorting + return List.of(); + } + return List.of( + new ScriptSortBuilder( + new Script(Script.DEFAULT_SCRIPT_TYPE, Script.DEFAULT_SCRIPT_LANG, SCRIPT, Map.of(FIELD_PARAM, getField())), + ScriptSortBuilder.ScriptSortType.NUMBER + ).order(SortOrder.DESC) + ); + } else { + return sortBuilders; + } } private void setSortBuilders(List> sortBuilders) { diff --git a/x-pack/plugin/vector-tile/src/test/java/org/elasticsearch/xpack/vectortile/feature/FeatureFactoriesConsistencyTests.java b/x-pack/plugin/vector-tile/src/test/java/org/elasticsearch/xpack/vectortile/feature/FeatureFactoriesConsistencyTests.java index c3a65137c68b3..d120fe6145452 100644 --- a/x-pack/plugin/vector-tile/src/test/java/org/elasticsearch/xpack/vectortile/feature/FeatureFactoriesConsistencyTests.java +++ b/x-pack/plugin/vector-tile/src/test/java/org/elasticsearch/xpack/vectortile/feature/FeatureFactoriesConsistencyTests.java @@ -11,6 +11,7 @@ import com.wdtinc.mapbox_vector_tile.adapt.jts.UserDataIgnoreConverter; import org.apache.lucene.geo.GeoTestUtil; +import org.elasticsearch.common.geo.SphericalMercatorUtils; import org.elasticsearch.geometry.MultiPoint; import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Rectangle; diff --git a/x-pack/plugin/vector-tile/src/test/java/org/elasticsearch/xpack/vectortile/rest/VectorTileRequestTests.java b/x-pack/plugin/vector-tile/src/test/java/org/elasticsearch/xpack/vectortile/rest/VectorTileRequestTests.java index c98a8bc8bfd83..de27514c72f0e 100644 --- a/x-pack/plugin/vector-tile/src/test/java/org/elasticsearch/xpack/vectortile/rest/VectorTileRequestTests.java +++ b/x-pack/plugin/vector-tile/src/test/java/org/elasticsearch/xpack/vectortile/rest/VectorTileRequestTests.java @@ -25,6 +25,8 @@ import org.elasticsearch.search.aggregations.metrics.AvgAggregationBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.FieldSortBuilder; +import org.elasticsearch.search.sort.ScriptSortBuilder; +import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.FakeRestRequest; import org.hamcrest.Matchers; @@ -53,7 +55,6 @@ public void testDefaults() throws IOException { assertThat(vectorTileRequest.getGridPrecision(), Matchers.equalTo(VectorTileRequest.Defaults.GRID_PRECISION)); assertThat(vectorTileRequest.getExactBounds(), Matchers.equalTo(VectorTileRequest.Defaults.EXACT_BOUNDS)); assertThat(vectorTileRequest.getRuntimeMappings(), Matchers.equalTo(VectorTileRequest.Defaults.RUNTIME_MAPPINGS)); - assertThat(vectorTileRequest.getSortBuilders(), Matchers.equalTo(VectorTileRequest.Defaults.SORT)); assertThat(vectorTileRequest.getQueryBuilder(), Matchers.equalTo(VectorTileRequest.Defaults.QUERY)); }); } @@ -144,6 +145,14 @@ public void testFieldRuntimeMappings() throws IOException { }); } + public void testDefaultFieldSort() throws IOException { + assertRestRequest((builder) -> {}, (vectorTileRequest) -> { + assertThat(vectorTileRequest.getSortBuilders(), Matchers.iterableWithSize(1)); + ScriptSortBuilder sortBuilder = (ScriptSortBuilder) vectorTileRequest.getSortBuilders().get(0); + assertThat(sortBuilder.order(), Matchers.equalTo(SortOrder.DESC)); + }); + } + public void testFieldSort() throws IOException { final String sortName = randomAlphaOfLength(10); assertRestRequest(