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

Geohex aggregation on geo_shape field #91956

Merged
merged 21 commits into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from 20 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
5 changes: 5 additions & 0 deletions docs/changelog/91956.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 91956
summary: Geohex aggregation on `geo_shape` field
area: Geo
type: feature
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.elasticsearch.xcontent.XContentFactory;
import org.elasticsearch.xcontent.XContentType;
import org.elasticsearch.xpack.spatial.LocalStateSpatialPlugin;
import org.elasticsearch.xpack.spatial.common.H3CartesianUtil;
import org.elasticsearch.xpack.spatial.index.mapper.GeoShapeWithDocValuesFieldMapper;
import org.elasticsearch.xpack.spatial.index.query.GeoGridQueryBuilder;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.GeoHexGridAggregationBuilder;
Expand Down Expand Up @@ -87,6 +88,10 @@ public void testGeoShapeGeoTile() throws IOException {
);
}

public void testGeoShapeGeoHex() throws IOException {
doTestGeohexGrid(GeoShapeWithDocValuesFieldMapper.CONTENT_TYPE, () -> GeometryTestUtils.randomGeometryWithoutCircle(0, false));
}

private void doTestGeohashGrid(String fieldType, Supplier<Geometry> randomGeometriesSupplier) throws IOException {
doTestGrid(
1,
Expand Down Expand Up @@ -124,43 +129,13 @@ private void doTestGeohexGrid(String fieldType, Supplier<Geometry> randomGeometr
}
return points;
},
this::toGeoHexRectangle,
h3 -> H3CartesianUtil.toBoundingBox(H3.stringToH3(h3)),
GeoHexGridAggregationBuilder::new,
(s1, s2) -> new GeoGridQueryBuilder(s1).setGridId(GeoGridQueryBuilder.Grid.GEOHEX, s2),
randomGeometriesSupplier
);
}

private Rectangle toGeoHexRectangle(String bucketKey) {
final long h3 = H3.stringToH3(bucketKey);
final CellBoundary boundary = H3.h3ToGeoBoundary(h3);
double minLat = Double.POSITIVE_INFINITY;
double minLon = Double.POSITIVE_INFINITY;
double maxLat = Double.NEGATIVE_INFINITY;
double maxLon = Double.NEGATIVE_INFINITY;
for (int i = 0; i < boundary.numPoints(); i++) {
final double boundaryLat = boundary.getLatLon(i).getLatDeg();
final double boundaryLon = boundary.getLatLon(i).getLonDeg();
minLon = Math.min(minLon, boundaryLon);
maxLon = Math.max(maxLon, boundaryLon);
minLat = Math.min(minLat, boundaryLat);
maxLat = Math.max(maxLat, boundaryLat);
}
final int resolution = H3.getResolution(h3);
if (H3.geoToH3(90, 0, resolution) == h3) {
// north pole
return new Rectangle(-180d, 180d, 90, minLat);
} else if (H3.geoToH3(-90, 0, resolution) == h3) {
// south pole
return new Rectangle(-180d, 180d, maxLat, -90);
} else if (maxLon - minLon > 180d) {
// crosses dateline
return new Rectangle(maxLon, minLon, maxLat, minLat);
} else {
return new Rectangle(minLon, maxLon, maxLat, minLat);
}
}

private void doTestGrid(
int minPrecision,
int maxPrecision,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,19 @@
import org.elasticsearch.xpack.spatial.search.aggregations.GeoLineAggregationBuilder;
import org.elasticsearch.xpack.spatial.search.aggregations.InternalGeoLine;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.BoundedGeoHashGridTiler;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.BoundedGeoHexGridTiler;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.BoundedGeoTileGridTiler;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.GeoGridTiler;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.GeoHexCellIdSource;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.GeoHexGridAggregationBuilder;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.GeoHexGridAggregator;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.GeoShapeCellIdSource;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.GeoShapeHashGridAggregator;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.GeoShapeHexGridAggregator;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.GeoShapeTileGridAggregator;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.InternalGeoHexGrid;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.UnboundedGeoHashGridTiler;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.UnboundedGeoHexGridTiler;
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.UnboundedGeoTileGridTiler;
import org.elasticsearch.xpack.spatial.search.aggregations.metrics.CartesianBoundsAggregationBuilder;
import org.elasticsearch.xpack.spatial.search.aggregations.metrics.CartesianBoundsAggregator;
Expand Down Expand Up @@ -310,12 +313,9 @@ private void registerGeoShapeGridAggregators(ValuesSourceRegistry.Builder builde
collectsFromSingleBucket,
metadata) -> {
if (GEO_GRID_AGG_FEATURE.check(getLicenseState())) {
final GeoGridTiler tiler;
if (geoBoundingBox.isUnbounded()) {
tiler = new UnboundedGeoHashGridTiler(precision);
} else {
tiler = new BoundedGeoHashGridTiler(precision, geoBoundingBox);
}
final GeoGridTiler tiler = geoBoundingBox.isUnbounded()
? new UnboundedGeoHashGridTiler(precision)
: new BoundedGeoHashGridTiler(precision, geoBoundingBox);
GeoShapeCellIdSource cellIdSource = new GeoShapeCellIdSource((GeoShapeValuesSource) valuesSource, tiler);
GeoShapeHashGridAggregator agg = new GeoShapeHashGridAggregator(
name,
Expand Down Expand Up @@ -353,12 +353,9 @@ private void registerGeoShapeGridAggregators(ValuesSourceRegistry.Builder builde
collectsFromSingleBucket,
metadata) -> {
if (GEO_GRID_AGG_FEATURE.check(getLicenseState())) {
final GeoGridTiler tiler;
if (geoBoundingBox.isUnbounded()) {
tiler = new UnboundedGeoTileGridTiler(precision);
} else {
tiler = new BoundedGeoTileGridTiler(precision, geoBoundingBox);
}
final GeoGridTiler tiler = geoBoundingBox.isUnbounded()
? new UnboundedGeoTileGridTiler(precision)
: new BoundedGeoTileGridTiler(precision, geoBoundingBox);
GeoShapeCellIdSource cellIdSource = new GeoShapeCellIdSource((GeoShapeValuesSource) valuesSource, tiler);
GeoShapeTileGridAggregator agg = new GeoShapeTileGridAggregator(
name,
Expand All @@ -379,6 +376,46 @@ private void registerGeoShapeGridAggregators(ValuesSourceRegistry.Builder builde
},
true
);

builder.register(
GeoHexGridAggregationBuilder.REGISTRY_KEY,
GeoShapeValuesSourceType.instance(),
(
name,
factories,
valuesSource,
precision,
geoBoundingBox,
requiredSize,
shardSize,
context,
parent,
collectsFromSingleBucket,
metadata) -> {
if (GEO_GRID_AGG_FEATURE.check(getLicenseState())) {
final GeoGridTiler tiler = geoBoundingBox.isUnbounded()
? new UnboundedGeoHexGridTiler(precision)
: new BoundedGeoHexGridTiler(precision, geoBoundingBox);
GeoShapeCellIdSource cellIdSource = new GeoShapeCellIdSource((GeoShapeValuesSource) valuesSource, tiler);
GeoShapeHexGridAggregator agg = new GeoShapeHexGridAggregator(
name,
factories,
cellIdSource,
requiredSize,
shardSize,
context,
parent,
collectsFromSingleBucket,
metadata
);
// this would ideally be something set in an immutable way on the ValuesSource
cellIdSource.setCircuitBreakerConsumer(agg::addRequestBytes);
return agg;
}
throw LicenseUtils.newComplianceException("geohex_grid aggregation on geo_shape fields");
},
true
);
}

private static void registerValueCountAggregator(ValuesSourceRegistry.Builder builder) {
Expand Down
Loading