diff --git a/src/Nest/XPack/Transform/Pivot/GeoTileGridGroupSource.cs b/src/Nest/XPack/Transform/Pivot/GeoTileGridGroupSource.cs new file mode 100644 index 00000000000..f3b262ef2d0 --- /dev/null +++ b/src/Nest/XPack/Transform/Pivot/GeoTileGridGroupSource.cs @@ -0,0 +1,59 @@ +using System; +using System.Runtime.Serialization; +using Elasticsearch.Net.Utf8Json; + +namespace Nest +{ + /// + /// The geotile grid value source 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. + /// + /// Available in Elasticsearch 7.9.0+ + /// + [InterfaceDataContract] + public interface IGeoTileGridGroupSource : ISingleGroupSource + { + /// + /// The highest-precision geotile of length 29 produces cells that cover less than 10cm by 10cm of land. + /// This precision is uniquely suited for composite aggregations as each tile does not have to be + /// generated and loaded in memory. + /// + [DataMember(Name = "precision")] + GeoTilePrecision? Precision { get; set; } + + /// + /// Constrained to a specific geo bounding box, which reduces the range of tiles used. + /// These bounds are useful when only a specific part of a geographical area needs high precision tiling. + /// + /// Available in Elasticsearch 7.6.0+. + /// + [DataMember(Name = "bounds")] + IBoundingBox Bounds { get; set; } + } + + /// + public class GeoTileGridGroupSource : SingleGroupSourceBase, IGeoTileGridGroupSource + { + /// + public GeoTilePrecision? Precision { get; set; } + /// + public IBoundingBox Bounds { get; set; } + } + + /// + public class GeoTileGridGroupSourceDescriptor + : SingleGroupSourceDescriptorBase, IGeoTileGridGroupSource, T>, + IGeoTileGridGroupSource + { + GeoTilePrecision? IGeoTileGridGroupSource.Precision { get; set; } + IBoundingBox IGeoTileGridGroupSource.Bounds { get; set; } + + /// + public GeoTileGridGroupSourceDescriptor Precision(GeoTilePrecision? precision) => + Assign(precision, (a, v) => a.Precision = v); + + /// + public GeoTileGridGroupSourceDescriptor Bounds(Func selector) => + Assign(selector, (a, v) => a.Bounds = v?.Invoke(new BoundingBoxDescriptor())); + } +} diff --git a/src/Nest/XPack/Transform/Pivot/SingleGroupSource.cs b/src/Nest/XPack/Transform/Pivot/SingleGroupSource.cs index b9973b48862..25f0a6b53a8 100644 --- a/src/Nest/XPack/Transform/Pivot/SingleGroupSource.cs +++ b/src/Nest/XPack/Transform/Pivot/SingleGroupSource.cs @@ -86,6 +86,12 @@ public SingleGroupSourcesDescriptor DateHistogram(string name, Func, IDateHistogramGroupSource> selector ) => Assign(new Tuple(name, selector?.Invoke(new DateHistogramGroupSourceDescriptor())), (a, v) => a.Add(v.Item1, v.Item2)); + + /// + public SingleGroupSourcesDescriptor GeoTileGrid(string name, + Func, IGeoTileGridGroupSource> selector + ) => + Assign(new Tuple(name, selector?.Invoke(new GeoTileGridGroupSourceDescriptor())), (a, v) => a.Add(v.Item1, v.Item2)); } internal class SingleGroupSourceFormatter : IJsonFormatter @@ -95,6 +101,7 @@ internal class SingleGroupSourceFormatter : IJsonFormatter { "terms", 0 }, { "date_histogram", 1 }, { "histogram", 2 }, + { "geotile_grid", 3 }, }; public void Serialize(ref JsonWriter writer, ISingleGroupSource value, IJsonFormatterResolver formatterResolver) @@ -121,6 +128,10 @@ public void Serialize(ref JsonWriter writer, ISingleGroupSource value, IJsonForm writer.WritePropertyName("histogram"); Serialize(ref writer, histogramGroupSource, formatterResolver); break; + case IGeoTileGridGroupSource geoTileGridGroupSource: + writer.WritePropertyName("geotile_grid"); + Serialize(ref writer, geoTileGridGroupSource, formatterResolver); + break; default: throw new JsonParsingException($"Unknown {nameof(ISingleGroupSource)}: {value.GetType().Name}"); } @@ -164,6 +175,10 @@ public ISingleGroupSource Deserialize(ref JsonReader reader, IJsonFormatterResol groupSource = formatterResolver.GetFormatter() .Deserialize(ref reader, formatterResolver); break; + case 3: + groupSource = formatterResolver.GetFormatter() + .Deserialize(ref reader, formatterResolver); + break; } } else diff --git a/tests/Tests/XPack/Transform/TransformApiTests.cs b/tests/Tests/XPack/Transform/TransformApiTests.cs index 10f9b13704e..7f1ccd5fe05 100644 --- a/tests/Tests/XPack/Transform/TransformApiTests.cs +++ b/tests/Tests/XPack/Transform/TransformApiTests.cs @@ -13,7 +13,7 @@ namespace Tests.XPack.Transform { - [SkipVersion("<7.7.0", "Introduced in 7.7.0")] + [SkipVersion("<7.9.0", "Geotile grid group by introduced in 7.9.0")] public class TransformApiTests : CoordinatedIntegrationTestBase { private const string PutTransformStep = nameof(PutTransformStep); @@ -61,6 +61,18 @@ public TransformApiTests(WritableCluster cluster, EndpointUsage usage) : base(ne Field = Field(f => f.StartedOn), CalendarInterval = DateInterval.Week } + }, + { + "geotile", new GeoTileGridGroupSource + { + Field = Field(f => f.LocationPoint), + Precision = GeoTilePrecision.Precision6, + Bounds = new BoundingBox + { + TopLeft = new GeoLocation(-90, 180), + BottomRight = new GeoLocation(90, -180) + } + } } } } @@ -92,6 +104,14 @@ public TransformApiTests(WritableCluster cluster, EndpointUsage usage) : base(ne .Field(f => f.StartedOn) .CalendarInterval(DateInterval.Week) ) + .GeoTileGrid("geotile", gtg => gtg + .Field(f => f.LocationPoint) + .Precision(GeoTilePrecision.Precision6) + .Bounds(b => b + .TopLeft(-90, 180) + .BottomRight(90, -180) + ) + ) ) ), (v, c, f) => c.Transform.Put(v, f),