Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
50d9ce2
New GeoHexGrid aggregation
iverase Jan 24, 2022
16dc137
fix docs
iverase Jan 24, 2022
96bc189
Add include so page publishes
jrodewig Jan 24, 2022
23abea9
Merge branch 'master' into GeoHexGrid
elasticmachine Jan 24, 2022
2330f62
Update docs/reference/aggregations/bucket/geohexgrid-aggregation.asci…
iverase Jan 24, 2022
baa26df
Update docs/reference/aggregations/bucket/geohexgrid-aggregation.asci…
iverase Jan 24, 2022
cf529cf
Update docs/reference/aggregations/bucket/geohexgrid-aggregation.asci…
iverase Jan 24, 2022
2f83ca9
Update docs/reference/aggregations/bucket/geohexgrid-aggregation.asci…
iverase Jan 24, 2022
af890d2
Update docs/reference/aggregations/bucket/geohexgrid-aggregation.asci…
iverase Jan 24, 2022
4aad240
Update docs/reference/aggregations/bucket/geohexgrid-aggregation.asci…
iverase Jan 24, 2022
10356b2
Update docs/reference/aggregations/bucket/geohexgrid-aggregation.asci…
iverase Jan 24, 2022
0e734af
Update docs/reference/aggregations/bucket/geohexgrid-aggregation.asci…
iverase Jan 24, 2022
3a425db
Merge branch 'master' into GeoHexGrid
iverase Jan 26, 2022
98e48cc
iter in docs
iverase Jan 26, 2022
e04baf3
Update docs/changelog/82924.yaml
iverase Jan 26, 2022
0af195d
yaml editing
iverase Jan 26, 2022
5b23ee6
Update docs/changelog/82924.yaml
iverase Jan 26, 2022
157c2a9
fix link
iverase Jan 26, 2022
aef42db
Merge branch 'GeoHexGrid' of github.com:iverase/elasticsearch into Ge…
iverase Jan 26, 2022
1eebba3
Additional edits and fixes
jrodewig Jan 26, 2022
73f09bb
minor edit
jrodewig Jan 26, 2022
c67e4bb
fix test
iverase Jan 26, 2022
9c096d8
Merge branch 'GeoHexGrid' of github.com:iverase/elasticsearch into Ge…
iverase Jan 26, 2022
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
2 changes: 2 additions & 0 deletions docs/reference/aggregations/bucket.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ include::bucket/geodistance-aggregation.asciidoc[]

include::bucket/geohashgrid-aggregation.asciidoc[]

include::bucket/geohexgrid-aggregation.asciidoc[]

include::bucket/geotilegrid-aggregation.asciidoc[]

include::bucket/global-aggregation.asciidoc[]
Expand Down
245 changes: 245 additions & 0 deletions docs/reference/aggregations/bucket/geohexgrid-aggregation.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
[role="xpack"]
[[search-aggregations-bucket-geohexgrid-aggregation]]
=== Geohex grid aggregation
++++
<titleabbrev>Geohex grid</titleabbrev>
++++

A multi-bucket aggregation that groups <<geo-point,`geo_point`>>
values into buckets that represent a grid.
The resulting grid can be sparse and only
contains cells that have matching data. Each cell corresponds to a
https://h3geo.org/docs/core-library/h3Indexing#h3-cell-indexp[H3 cell index] a
and labeled using the https://h3geo.org/docs/core-library/h3Indexing#h3index-representation
[H3Index representation].

* High precision keys have a larger range for x and y, and represent tiles that
cover only a small area.
* Low precision keys have a smaller range for x and y, and represent tiles that
each cover a large area.

See https://h3geo.org/docs/core-library/restable[the table od cell areas for H3]
resolutions on how precision (zoom) correlates to size on the ground.
Precision for this aggregation can be between 0 and 15, inclusive.

WARNING: The highest-precision geohex for precision 15 produces cells that cover
less than a 10cm by 10cm of land and so high-precision requests can be very
costly in terms of RAM and result sizes. Please see the example below on how
to first filter the aggregation to a smaller geographic area before requesting
high-levels of detail.

You can only use `geohex_grid` to aggregate an explicitly mapped `geo_point`.
If the `geo_point` field contains an array, `geohex_grid` aggregates all the array values.


==== Simple low-precision request

[source,console,id=geohexgrid-aggregation-example]
--------------------------------------------------
PUT /museums
{
"mappings": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}

POST /museums/_bulk?refresh
{"index":{"_id":1}}
{"location": "52.374081,4.912350", "name": "NEMO Science Museum"}
{"index":{"_id":2}}
{"location": "52.369219,4.901618", "name": "Museum Het Rembrandthuis"}
{"index":{"_id":3}}
{"location": "52.371667,4.914722", "name": "Nederlands Scheepvaartmuseum"}
{"index":{"_id":4}}
{"location": "51.222900,4.405200", "name": "Letterenhuis"}
{"index":{"_id":5}}
{"location": "48.861111,2.336389", "name": "Musée du Louvre"}
{"index":{"_id":6}}
{"location": "48.860000,2.327000", "name": "Musée d'Orsay"}

POST /museums/_search?size=0
{
"aggregations": {
"large-grid": {
"geohex_grid": {
"field": "location",
"precision": 4
}
}
}
}
--------------------------------------------------

Response:

[source,console-result]
--------------------------------------------------
{
...
"aggregations": {
"large-grid": {
"buckets": [
{
"key": "841969dffffffff",
"doc_count": 3
},
{
"key": "841fb47ffffffff",
"doc_count": 2
},
{
"key": "841fa4dffffffff",
"doc_count": 1
}
]
}
}
}
--------------------------------------------------
// TESTRESPONSE[s/\.\.\./"took": $body.took,"_shards": $body._shards,"hits":$body.hits,"timed_out":false,/]

==== High-precision requests

When requesting detailed buckets (typically for displaying a "zoomed in" map)
a filter like <<query-dsl-geo-bounding-box-query,geo_bounding_box>> should be
applied to narrow the subject area otherwise potentially millions of buckets
will be created and returned.

[source,console]
--------------------------------------------------
POST /museums/_search?size=0
{
"aggregations": {
"zoomed-in": {
"filter": {
"geo_bounding_box": {
"location": {
"top_left": "52.4, 4.9",
"bottom_right": "52.3, 5.0"
}
}
},
"aggregations": {
"zoom1": {
"geohex_grid": {
"field": "location",
"precision": 12
}
}
}
}
}
}
--------------------------------------------------
// TEST[continued]

[source,console-result]
--------------------------------------------------
{
...
"aggregations": {
"zoomed-in": {
"doc_count": 3,
"zoom1": {
"buckets": [
{
"key": "8c1969c9b2617ff",
"doc_count": 1
},
{
"key": "8c1969526d753ff",
"doc_count": 1
},
{
"key": "8c1969526d26dff",
"doc_count": 1
}
]
}
}
}
}
--------------------------------------------------
// TESTRESPONSE[s/\.\.\./"took": $body.took,"_shards": $body._shards,"hits":$body.hits,"timed_out":false,/]

==== Requests with additional bounding box filtering

The `geohex_grid` aggregation supports an optional `bounds` parameter
that restricts the cells considered to those that intersects the
bounds provided. The `bounds` parameter accepts the bounding box in
all the same <<query-dsl-geo-bounding-box-query-accepted-formats,accepted formats>> of the
bounds specified in the Geo Bounding Box Query. This bounding box can be used with or
without an additional `geo_bounding_box` query for filtering the points prior to aggregating.
It is an independent bounding box that can intersect with, be equal to, or be disjoint
to any additional `geo_bounding_box` queries defined in the context of the aggregation.

[source,console,id=geohexgrid-aggregation-with-bounds]
--------------------------------------------------
POST /museums/_search?size=0
{
"aggregations": {
"tiles-in-bounds": {
"geohex_grid": {
"field": "location",
"precision": 12,
"bounds": {
"top_left": "52.4, 4.9",
"bottom_right": "52.3, 5.0"
}
}
}
}
}
--------------------------------------------------
// TEST[continued]

[source,console-result]
--------------------------------------------------
{
...
"aggregations": {
"tiles-in-bounds": {
"buckets": [
{
"key": "8c1969c9b2617ff",
"doc_count": 1
},
{
"key": "8c1969526d753ff",
"doc_count": 1
},
{
"key": "8c1969526d26dff",
"doc_count": 1
}
]
}
}
}
--------------------------------------------------
// TESTRESPONSE[s/\.\.\./"took": $body.took,"_shards": $body._shards,"hits":$body.hits,"timed_out":false,/]

==== Options

[horizontal]
field:: Mandatory. The name of the field indexed with GeoPoints.

precision:: Optional. The integer zoom of the key used to define
cells/buckets in the results. Defaults to 6.
Values outside of [0,15] will be rejected.

bounds:: Optional. The bounding box to filter the points in the bucket.

size:: Optional. The maximum number of geohex buckets to return
(defaults to 10,000). When results are trimmed, buckets are
prioritised based on the volumes of documents they contain.

shard_size:: Optional. To allow for more accurate counting of the top cells
returned in the final result the aggregation defaults to
returning `max(10,(size x number-of-shards))` buckets from each
shard. If this heuristic is undesirable, the number considered
from each shard can be over-ridden using this parameter.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void writeTo(StreamOutput out) throws IOException {
aggregations.writeTo(out);
}

protected long hashAsLong() {
public long hashAsLong() {
return hashAsLong;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static ObjectParser<ParsedGeoGrid, Void> createParser(
return parser;
}

protected void setName(String name) {
public void setName(String name) {
super.setName(name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ private SpatialStatsAction() {
* Items to track. Serialized by ordinals. Append only, don't remove or change order of items in this list.
*/
public enum Item {
GEOLINE
GEOLINE,
GEOHEX
}

public static class Request extends BaseNodesRequest<Request> implements ToXContentObject {
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugin/spatial/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies {
compileOnly project(path: ':modules:legacy-geo')
compileOnly project(':modules:lang-painless:spi')
compileOnly project(path: xpackModule('core'))
api project(":libs:elasticsearch-h3")
testImplementation(testArtifact(project(xpackModule('core'))))
testImplementation project(path: xpackModule('vector-tile'))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import org.elasticsearch.search.aggregations.metrics.GeoCentroidAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ValueCountAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ValueCountAggregator;
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
import org.elasticsearch.search.aggregations.support.ValuesSource;
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
import org.elasticsearch.xcontent.ContextParser;
import org.elasticsearch.xpack.core.XPackPlugin;
Expand All @@ -50,9 +52,13 @@
import org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid.BoundedGeoHashGridTiler;
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.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.UnboundedGeoTileGridTiler;
import org.elasticsearch.xpack.spatial.search.aggregations.metrics.GeoShapeBoundsAggregator;
Expand Down Expand Up @@ -87,6 +93,12 @@ public class SpatialPlugin extends Plugin implements ActionPlugin, MapperPlugin,
License.OperationMode.GOLD
);

private final LicensedFeature.Momentary GEO_HEX_AGG_FEATURE = LicensedFeature.momentary(
"spatial",
"geo-hex-agg",
License.OperationMode.GOLD
);

// to be overriden by tests
protected XPackLicenseState getLicenseState() {
return XPackPlugin.getSharedLicenseState();
Expand Down Expand Up @@ -139,7 +151,12 @@ public List<AggregationSpec> getAggregations() {
GeoLineAggregationBuilder.NAME,
GeoLineAggregationBuilder::new,
usage.track(SpatialStatsAction.Item.GEOLINE, checkLicense(GeoLineAggregationBuilder.PARSER, GEO_LINE_AGG_FEATURE))
).addResultReader(InternalGeoLine::new).setAggregatorRegistrar(GeoLineAggregationBuilder::registerUsage)
).addResultReader(InternalGeoLine::new).setAggregatorRegistrar(GeoLineAggregationBuilder::registerUsage),
new AggregationSpec(
GeoHexGridAggregationBuilder.NAME,
GeoHexGridAggregationBuilder::new,
usage.track(SpatialStatsAction.Item.GEOHEX, checkLicense(GeoHexGridAggregationBuilder.PARSER, GEO_HEX_AGG_FEATURE))
).addResultReader(InternalGeoHexGrid::new).setAggregatorRegistrar(this::registerGeoHexGridAggregator)
);
}

Expand Down Expand Up @@ -171,6 +188,47 @@ private void registerGeoShapeCentroidAggregator(ValuesSourceRegistry.Builder bui
);
}

private void registerGeoHexGridAggregator(ValuesSourceRegistry.Builder builder) {
builder.register(
GeoHexGridAggregationBuilder.REGISTRY_KEY,
CoreValuesSourceType.GEOPOINT,
(
name,
factories,
valuesSource,
precision,
geoBoundingBox,
requiredSize,
shardSize,
aggregationContext,
parent,
cardinality,
metadata) -> {
if (GEO_HEX_AGG_FEATURE.check(getLicenseState())) {
GeoHexCellIdSource cellIdSource = new GeoHexCellIdSource(
(ValuesSource.GeoPoint) valuesSource,
precision,
geoBoundingBox
);
return new GeoHexGridAggregator(
name,
factories,
cellIdSource,
requiredSize,
shardSize,
aggregationContext,
parent,
cardinality,
metadata
);
}

throw LicenseUtils.newComplianceException("geohex_grid aggregation on geo_point fields");
},
true
);
}

private void registerGeoShapeGridAggregators(ValuesSourceRegistry.Builder builder) {
builder.register(
GeoHashGridAggregationBuilder.REGISTRY_KEY,
Expand Down
Loading