Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add value ranges for GeoTile aggregation metrics metrics in the meta layer #71611

Merged
merged 7 commits into from
Apr 22, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,22 @@
import org.elasticsearch.rest.RestResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestResponseListener;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregatorFactories;
import org.elasticsearch.search.aggregations.PipelineAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils;
import org.elasticsearch.search.aggregations.metrics.AvgAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.CardinalityAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.MaxAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.MinAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FieldAndFormat;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
Expand Down Expand Up @@ -226,9 +234,31 @@ public AggregatorFactories.Builder getAggBuilder() {
}

public void setAggBuilder(AggregatorFactories.Builder aggBuilder) {
// TODO: validation
for (AggregationBuilder aggregation : aggBuilder.getAggregatorFactories()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's wait with this. I think I have some idea on how to make this more generic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still wonder if 1) this should responsibility of the callers and 2) if we want to add these pipeline aggs, we need to throw an exception if we don't recognize the agg

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My hope is that a user of this API does not need to be an expert on Elasticsearch so we can provide this information from them. I am hoping to only support a subset of the aggregations, the ones that make sense.

final String type = aggregation.getType();
switch (type) {
case MinAggregationBuilder.NAME:
case MaxAggregationBuilder.NAME:
case AvgAggregationBuilder.NAME:
case SumAggregationBuilder.NAME:
case CardinalityAggregationBuilder.NAME:
break;
default:
// top term and percentile should be supported
throw new IllegalArgumentException("Unsupported aggregation of type [" + type + "]");
}
}
for (PipelineAggregationBuilder aggregation : aggBuilder.getPipelineAggregatorFactories()) {
// should not have pipeline aggregations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

final String type = aggregation.getType();
throw new IllegalArgumentException("Unsupported pipeline aggregation of type [" + type + "]");
}
this.aggBuilder = aggBuilder;
}

public Collection<AggregationBuilder> getAggregations() {
return aggBuilder == null ? emptyList() : aggBuilder.getAggregatorFactories();
}
}

protected AbstractVectorTileSearchAction(Supplier<R> emptyRequestProvider) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils;
Expand All @@ -34,8 +35,12 @@
import org.elasticsearch.search.aggregations.metrics.MinAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.NumericMetricsAggregation;
import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder;
import org.elasticsearch.search.aggregations.pipeline.InternalBucketMetricValue;
import org.elasticsearch.search.aggregations.pipeline.MaxBucketPipelineAggregationBuilder;
import org.elasticsearch.search.aggregations.pipeline.MinBucketPipelineAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.FieldAndFormat;

import java.util.Collection;
import java.util.List;

import static org.elasticsearch.rest.RestRequest.Method.GET;
Expand All @@ -49,8 +54,11 @@ public class RestVectorTileAction extends AbstractVectorTileSearchAction<Abstrac
private static final String GRID_FIELD = "grid";
private static final String BOUNDS_FIELD = "bounds";

private static final String COUNT_TAG = "count";
private static final String ID_TAG = "id";
private static final String COUNT_TAG = "_count";
private static final String ID_TAG = "_id";

private static final String COUNT_MIN = "_count.min";
private static final String COUNT_MAX = "_count.max";

public RestVectorTileAction() {
super(Request::new);
Expand Down Expand Up @@ -112,6 +120,15 @@ private static SearchRequestBuilder searchBuilder(SearchRequestBuilder searchReq
aBuilder.subAggregations(request.getAggBuilder());
}
searchRequestBuilder.addAggregation(aBuilder);
searchRequestBuilder.addAggregation(new MaxBucketPipelineAggregationBuilder(COUNT_MAX, GRID_FIELD + "._count"));
searchRequestBuilder.addAggregation(new MinBucketPipelineAggregationBuilder(COUNT_MIN, GRID_FIELD + "._count"));
final Collection<AggregationBuilder> aggregations = request.getAggregations();
for (AggregationBuilder aggregation : aggregations) {
searchRequestBuilder.addAggregation(
new MaxBucketPipelineAggregationBuilder(aggregation.getName() + ".max", GRID_FIELD + ">" + aggregation.getName()));
searchRequestBuilder.addAggregation(
new MinBucketPipelineAggregationBuilder(aggregation.getName() + ".min", GRID_FIELD + ">" + aggregation.getName()));
}
}
if (request.getExactBounds()) {
final GeoBoundsAggregationBuilder boundsBuilder =
Expand Down Expand Up @@ -210,6 +227,27 @@ private VectorTile.Tile.Layer.Builder getMetaLayer(SearchResponse response, Requ
addPropertyToFeature(featureBuilder, layerProps, "_shards.failed", response.getFailedShards());
addPropertyToFeature(featureBuilder, layerProps, "hits.total.value", response.getHits().getTotalHits().value);
addPropertyToFeature(featureBuilder, layerProps, "hits.total.relation", response.getHits().getTotalHits().relation.name());
if (response.getAggregations() != null) {
final InternalBucketMetricValue countMinVal = response.getAggregations().get(COUNT_MIN);
if (countMinVal != null && Double.isFinite(countMinVal.value())) {
addPropertyToFeature(featureBuilder, layerProps, countMinVal.getName(), (int) countMinVal.value());
}
final InternalBucketMetricValue countMaxVal = response.getAggregations().get(COUNT_MAX);
if (countMaxVal != null && Double.isFinite(countMaxVal.value())) {
addPropertyToFeature(featureBuilder, layerProps, countMaxVal.getName(), (int) countMaxVal.value());
}
final Collection<AggregationBuilder> aggregations = request.getAggregations();
for (AggregationBuilder aggregation : aggregations) {
final InternalBucketMetricValue aggMinVal = response.getAggregations().get(aggregation.getName() + ".min");
if (aggMinVal != null && Double.isFinite(aggMinVal.value())) {
addPropertyToFeature(featureBuilder, layerProps, "aggs." + aggMinVal.getName(), aggMinVal.value());
}
final InternalBucketMetricValue aggMaxVal = response.getAggregations().get(aggregation.getName() + ".max");
if (aggMaxVal != null && Double.isFinite(aggMaxVal.value())) {
addPropertyToFeature(featureBuilder, layerProps, "aggs." + aggMaxVal.getName(), aggMaxVal.value());
}
}
}
metaLayerBuilder.addFeatures(featureBuilder);
addPropertiesToLayer(metaLayerBuilder, layerProps);
return metaLayerBuilder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ public void box(VectorTile.Tile.Feature.Builder featureBuilder, double minLon, d
}

private int lat(double lat) {
return (int) (pointYScale * (VectorTileUtils.latToSphericalMercator(lat) - rectangle.getMinY())) + extent;
return (int) Math.round(pointYScale * (VectorTileUtils.latToSphericalMercator(lat) - rectangle.getMinY())) + extent;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should still probably get this in.

}

private int lon(double lon) {
return (int) (pointXScale * (VectorTileUtils.lonToSphericalMercator(lon) - rectangle.getMinX()));
return (int) Math.round(pointXScale * (VectorTileUtils.lonToSphericalMercator(lon) - rectangle.getMinX()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,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, 7);
assertLayer(tile, META_LAYER, 4096, 1, 9);

}

Expand All @@ -152,7 +152,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, 7);
assertLayer(tile, META_LAYER, 4096, 1, 9);
}
{
final Request mvtRequest = new Request(HttpGet.METHOD_NAME, INDEX_POINTS + "/_mvt/location/" + z + "/" + x + "/" + y);
Expand All @@ -170,7 +170,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, 7);
assertLayer(tile, META_LAYER, 4096, 1, 9);
}
{
final Request mvtRequest = new Request(HttpGet.METHOD_NAME, INDEX_POINTS + "/_mvt/location/" + z + "/" + x + "/" + y);
Expand All @@ -179,7 +179,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, 7);
assertLayer(tile, META_LAYER, 4096, 1, 9);
}
{
final Request mvtRequest = new Request(HttpGet.METHOD_NAME, INDEX_POINTS + "/_mvt/location/" + z + "/" + x + "/" + y);
Expand All @@ -204,7 +204,7 @@ public void testNoHitsLayer() throws Exception {
final VectorTile.Tile tile = execute(mvtRequest);
assertThat(tile.getLayersCount(), Matchers.equalTo(2));
assertLayer(tile, AGGS_LAYER, 4096, 1, 1);
assertLayer(tile, META_LAYER, 4096, 1, 7);
assertLayer(tile, META_LAYER, 4096, 1, 9);
}

public void testBasicQueryGet() throws Exception {
Expand All @@ -222,7 +222,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, 7);
assertLayer(tile, META_LAYER, 4096, 1, 9);
}

public void testBasicShape() throws Exception {
Expand All @@ -231,7 +231,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, 7);
assertLayer(tile, META_LAYER, 4096, 1, 9);
}

public void testWithFields() throws Exception {
Expand All @@ -241,7 +241,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, 7);
assertLayer(tile, META_LAYER, 4096, 1, 9);
}

public void testMinAgg() throws Exception {
Expand All @@ -259,7 +259,43 @@ 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, 7);
assertLayer(tile, META_LAYER, 4096, 1, 11);
}

public void testUnsupportedAgg() {
{
final Request mvtRequest = new Request(HttpGet.METHOD_NAME, INDEX_SHAPES + "/_mvt/location/" + z + "/" + x + "/" + y);
mvtRequest.setJsonEntity("{\n" +
" \"aggs\": {\n" +
" \"minVal\": {\n" +
" \"terms\": {\n" +
" \"field\": \"name\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
final ResponseException ex = expectThrows(ResponseException.class, () -> execute(mvtRequest));
assertThat(ex.getResponse().getStatusLine().getStatusCode(), Matchers.equalTo(HttpStatus.SC_BAD_REQUEST));
}
{
final Request mvtRequest = new Request(HttpGet.METHOD_NAME, INDEX_SHAPES + "/_mvt/location/"+ z + "/" + x + "/" + y);
mvtRequest.setJsonEntity("{\n" +
" \"aggs\": {\n" +
" \"minVal\": {\n" +
" \"min\": {\n" +
" \"field\": \"value1\"\n" +
" }\n" +
" },\n" +
" \"maxBucket\": {\n" +
" \"max_bucket\": {\n" +
" \"buckets_path\": \"minVal\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
final ResponseException ex = expectThrows(ResponseException.class, () -> execute(mvtRequest));
assertThat(ex.getResponse().getStatusLine().getStatusCode(), Matchers.equalTo(HttpStatus.SC_BAD_REQUEST));
}
}

private void assertLayer(VectorTile.Tile tile, String name, int extent, int numFeatures, int numTags) {
Expand Down