From 72f590afcb01d38e1bb2cff1efd12e55973d79c7 Mon Sep 17 00:00:00 2001 From: Russ Cam Date: Fri, 21 Feb 2020 20:27:20 +1100 Subject: [PATCH] Add support for bounds in geohash_grid aggregation (#4403) Relates: #4341, elastic/elasticsearch#50002 This commit adds support for supplying bounds on a geohash_grid aggregation, similar to how bounds are supplied on geo_bounding_box query. --- .../GeoHashGrid/GeoHashGridAggregation.cs | 54 ++++++++++++++++- .../GeoHashGridAggregationUsageTests.cs | 59 +++++++++++++++++++ 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/src/Nest/Aggregations/Bucket/GeoHashGrid/GeoHashGridAggregation.cs b/src/Nest/Aggregations/Bucket/GeoHashGrid/GeoHashGridAggregation.cs index 65fda9856bd..d0422750959 100644 --- a/src/Nest/Aggregations/Bucket/GeoHashGrid/GeoHashGridAggregation.cs +++ b/src/Nest/Aggregations/Bucket/GeoHashGrid/GeoHashGridAggregation.cs @@ -5,34 +5,74 @@ namespace Nest { + /// + /// A multi-bucket aggregation that works on geo_point fields and groups points into buckets that represent cells in a grid. + /// The resulting grid can be sparse and only contains cells that have matching data. + /// Each cell is labeled using a geohash which is of user-definable precision. + /// [InterfaceDataContract] [ReadAs(typeof(GeoHashGridAggregation))] public interface IGeoHashGridAggregation : IBucketAggregation { + /// + /// The name of the field indexed with geopoints. + /// [DataMember(Name ="field")] Field Field { get; set; } + /// + /// The string length of the geohashes used to define cells/buckets in the results. + /// Defaults to . + /// [DataMember(Name ="precision")] GeoHashPrecision? Precision { get; set; } + /// + /// 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. + /// [DataMember(Name ="shard_size")] int? ShardSize { get; set; } + /// + /// The maximum number of geohash buckets to return. Defaults to 10,000. When results are trimmed, + /// buckets are prioritised based on the volumes of documents they contain. + /// [DataMember(Name ="size")] int? Size { get; set; } + + /// + /// Restricts the points considered to those that fall within the bounds provided. + /// + /// Available in Elasticsearch 7.6.0+. + /// + [DataMember(Name = "bounds")] + IBoundingBox Bounds { get; set; } } + /// public class GeoHashGridAggregation : BucketAggregationBase, IGeoHashGridAggregation { internal GeoHashGridAggregation() { } public GeoHashGridAggregation(string name) : base(name) { } + /// public Field Field { get; set; } + + /// public GeoHashPrecision? Precision { get; set; } + + /// public int? ShardSize { get; set; } + + /// public int? Size { get; set; } + /// + public IBoundingBox Bounds { get; set; } + internal override void WrapInContainer(AggregationContainer c) => c.GeoHash = this; } @@ -42,22 +82,30 @@ public class GeoHashGridAggregationDescriptor where T : class { Field IGeoHashGridAggregation.Field { get; set; } - GeoHashPrecision? IGeoHashGridAggregation.Precision { get; set; } - int? IGeoHashGridAggregation.ShardSize { get; set; } - int? IGeoHashGridAggregation.Size { get; set; } + IBoundingBox IGeoHashGridAggregation.Bounds { get; set; } + /// public GeoHashGridAggregationDescriptor Field(Field field) => Assign(field, (a, v) => a.Field = v); + /// public GeoHashGridAggregationDescriptor Field(Expression> field) => Assign(field, (a, v) => a.Field = v); + /// public GeoHashGridAggregationDescriptor Size(int? size) => Assign(size, (a, v) => a.Size = v); + /// public GeoHashGridAggregationDescriptor ShardSize(int? shardSize) => Assign(shardSize, (a, v) => a.ShardSize = v); + /// + // TODO: Rename to precision in next major. public GeoHashGridAggregationDescriptor GeoHashPrecision(GeoHashPrecision? precision) => Assign(precision, (a, v) => a.Precision = v); + + /// + public GeoHashGridAggregationDescriptor Bounds(Func selector) => + Assign(selector, (a, v) => a.Bounds = v?.Invoke(new BoundingBoxDescriptor())); } } diff --git a/tests/Tests/Aggregations/Bucket/GeoHashGrid/GeoHashGridAggregationUsageTests.cs b/tests/Tests/Aggregations/Bucket/GeoHashGrid/GeoHashGridAggregationUsageTests.cs index 29c0ca2056b..22be70ff9bf 100644 --- a/tests/Tests/Aggregations/Bucket/GeoHashGrid/GeoHashGridAggregationUsageTests.cs +++ b/tests/Tests/Aggregations/Bucket/GeoHashGrid/GeoHashGridAggregationUsageTests.cs @@ -1,4 +1,5 @@ using System; +using Elastic.Xunit.XunitPlumbing; using FluentAssertions; using Nest; using Tests.Core.Extensions; @@ -51,4 +52,62 @@ protected override void ExpectResponse(ISearchResponse response) myGeoHashGrid.Should().NotBeNull(); } } + + [SkipVersion("<7.6.0", "bounds introduced in 7.6.0")] + // hide + public class GeoHashGridAggregationWithBoundsUsageTests : AggregationUsageTestBase + { + public GeoHashGridAggregationWithBoundsUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { } + + protected override object AggregationJson => new + { + my_geohash_grid = new + { + geohash_grid = new + { + field = "locationPoint", + bounds = new + { + top_left = new + { + lat = 90.0, + lon = -180.0 + }, + bottom_right = new + { + lat = -90.0, + lon = 180.0 + } + } + } + } + }; + + protected override Func, IAggregationContainer> FluentAggs => a => a + .GeoHash("my_geohash_grid", g => g + .Field(p => p.LocationPoint) + .Bounds(b => b + .TopLeft(90,-180) + .BottomRight(-90, 180) + ) + ); + + protected override AggregationDictionary InitializerAggs => + new GeoHashGridAggregation("my_geohash_grid") + { + Field = Field(p => p.LocationPoint), + Bounds = new BoundingBox + { + TopLeft = new GeoLocation(90, -180), + BottomRight = new GeoLocation(-90, 180) + } + }; + + protected override void ExpectResponse(ISearchResponse response) + { + response.ShouldBeValid(); + var myGeoHashGrid = response.Aggregations.GeoHash("my_geohash_grid"); + myGeoHashGrid.Should().NotBeNull(); + } + } }