Skip to content

Commit 3a9e837

Browse files
Mpdreamzrusscam
authored andcommitted
Fix #3317 and #3322 add after_key support to composite aggregation re… (#3367)
Fix #3317 and #3322 add after_key support to composite aggregation result and allow format to be set for date histogram composite source Introduce CompositeBucketAggregate derived from MultiBucketAggregate<CompositeBucket> to introduce AfterKey property. Bump dotnet-xunit .NET tool to official release
1 parent 3ea1dc3 commit 3a9e837

File tree

7 files changed

+190
-5
lines changed

7 files changed

+190
-5
lines changed

src/Nest/Aggregations/Aggregate.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
namespace Nest
44
{
55
/// <summary>
6-
/// Represents the result of an aggregation on the response
6+
/// Aggregation response for an aggregation request
77
/// </summary>
88
[ExactContractJsonConverter(typeof(AggregateJsonConverter))]
99
public interface IAggregate
1010
{
1111
//TODO this public set is problematic
12+
/// <summary>
13+
/// Metadata for the aggregation
14+
/// </summary>
1215
IReadOnlyDictionary<string, object> Meta { get; set; }
1316
}
1417
}

src/Nest/Aggregations/AggregateDictionary.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,17 @@ public TermsAggregate<TKey> Terms<TKey>(string key)
175175

176176
public MultiBucketAggregate<DateHistogramBucket> DateHistogram(string key) => GetMultiBucketAggregate<DateHistogramBucket>(key);
177177

178-
public MultiBucketAggregate<CompositeBucket> Composite(string key) => GetMultiBucketAggregate<CompositeBucket>(key);
178+
public CompositeBucketAggregate Composite(string key)
179+
{
180+
var bucket = this.TryGet<BucketAggregate>(key);
181+
if (bucket == null) return null;
182+
return new CompositeBucketAggregate
183+
{
184+
Buckets = bucket.Items.OfType<CompositeBucket>().ToList(),
185+
Meta = bucket.Meta,
186+
AfterKey = new CompositeKey(bucket.AfterKey)
187+
};
188+
}
179189

180190
public MatrixStatsAggregate MatrixStats(string key) => this.TryGet<MatrixStatsAggregate>(key);
181191

src/Nest/Aggregations/AggregateJsonConverter.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ private static class Parser
3636
public const string Hits = "hits";
3737
public const string Location = "location";
3838
public const string Fields = "fields";
39+
public const string AfterKey = "after_key";
3940

4041
public const string Key = "key";
4142
public const string From = "from";
@@ -126,6 +127,16 @@ private IAggregate ReadAggregate(JsonReader reader, JsonSerializer serializer)
126127
case Parser.Value:
127128
aggregate = GetValueAggregate(reader, serializer);
128129
break;
130+
case Parser.AfterKey:
131+
reader.Read();
132+
var afterKeys = serializer.Deserialize<Dictionary<string, object>>(reader);
133+
reader.Read();
134+
var bucketAggregate = reader.Value.ToString() == Parser.Buckets
135+
? this.GetMultiBucketAggregate(reader, serializer) as BucketAggregate ?? new BucketAggregate()
136+
: new BucketAggregate();
137+
bucketAggregate.AfterKey = afterKeys;
138+
aggregate = bucketAggregate;
139+
break;
129140
case Parser.Buckets:
130141
case Parser.DocCountErrorUpperBound:
131142
aggregate = GetMultiBucketAggregate(reader, serializer);

src/Nest/Aggregations/Bucket/BucketAggregate.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,42 @@ public SingleBucketAggregate(IReadOnlyDictionary<string, IAggregate> aggregation
2323
[Obsolete("Use methods on this instance to access sub aggregations. Will be removed in NEST 7.x")]
2424
public AggregateDictionary Aggregations { get; protected internal set; }
2525

26+
/// <summary>
27+
/// Count of documents in the bucket
28+
/// </summary>
2629
public long DocCount { get; internal set; }
2730
}
2831

32+
/// <summary>
33+
/// Aggregation response for a bucket aggregation
34+
/// </summary>
35+
/// <typeparam name="TBucket"></typeparam>
2936
public class MultiBucketAggregate<TBucket> : IAggregate
3037
where TBucket : IBucket
3138
{
39+
/// <inheritdoc />
3240
public IReadOnlyDictionary<string, object> Meta { get; set; }
3341

42+
/// <summary>
43+
/// The buckets into which results are grouped
44+
/// </summary>
3445
public IReadOnlyCollection<TBucket> Buckets { get; set; } = EmptyReadOnly<TBucket>.Collection;
3546
}
3647

48+
/// <summary>
49+
/// Aggregation response of <see cref="CompositeAggregation"/>
50+
/// </summary>
51+
public class CompositeBucketAggregate : MultiBucketAggregate<CompositeBucket>
52+
{
53+
/// <summary>
54+
/// The composite key of the last bucket returned
55+
/// in the response before any filtering by pipeline aggregations.
56+
/// If all buckets are filtered/removed by pipeline aggregations,
57+
/// <see cref="AfterKey"/> will contain the composite key of the last bucket before filtering.
58+
/// </summary>
59+
/// <remarks> Valid for Elasticsearch 6.3.0+ </remarks>
60+
public CompositeKey AfterKey { get; set; }
61+
}
3762

3863
// Intermediate object used for deserialization
3964
public class BucketAggregate : IAggregate
@@ -44,5 +69,6 @@ public class BucketAggregate : IAggregate
4469
public IReadOnlyDictionary<string, object> Meta { get; set; } = EmptyReadOnly<string, object>.Dictionary;
4570
public long DocCount { get; set; }
4671
public long BgCount { get; set; }
72+
public IReadOnlyDictionary<string, object> AfterKey { get; set; } = EmptyReadOnly<string, object>.Dictionary;
4773
}
4874
}

src/Nest/Aggregations/Bucket/Composite/DateHistogramCompositeAggregationSource.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ public interface IDateHistogramCompositeAggregationSource : ICompositeAggregatio
2222
/// </summary>
2323
[JsonProperty("time_zone")]
2424
string Timezone { get; set; }
25+
26+
/// <summary>
27+
/// Return a formatted date string as the key instead an epoch long
28+
/// </summary>
29+
/// <remarks> Valid for Elasticsearch 6.3.0+ </remarks>
30+
[JsonProperty("format")]
31+
string Format { get; set; }
2532
}
2633

2734
/// <inheritdoc cref="IDateHistogramCompositeAggregationSource"/>
@@ -35,6 +42,9 @@ public DateHistogramCompositeAggregationSource(string name) : base(name) {}
3542
/// <inheritdoc />
3643
public string Timezone { get; set; }
3744

45+
/// <inheritdoc />
46+
public string Format { get; set; }
47+
3848
/// <inheritdoc />
3949
protected override string SourceType => "date_histogram";
4050
}
@@ -46,6 +56,7 @@ public class DateHistogramCompositeAggregationSourceDescriptor<T>
4656
{
4757
Union<DateInterval?,Time> IDateHistogramCompositeAggregationSource.Interval { get; set; }
4858
string IDateHistogramCompositeAggregationSource.Timezone { get; set; }
59+
string IDateHistogramCompositeAggregationSource.Format { get; set; }
4960

5061
public DateHistogramCompositeAggregationSourceDescriptor(string name) : base(name, "date_histogram") {}
5162

@@ -58,7 +69,9 @@ public DateHistogramCompositeAggregationSourceDescriptor<T> Interval(Time interv
5869
Assign(a => a.Interval = interval);
5970

6071
/// <inheritdoc cref="IDateHistogramCompositeAggregationSource.Timezone"/>
61-
public DateHistogramCompositeAggregationSourceDescriptor<T> Timezone(string timezone) =>
62-
Assign(a => a.Timezone = timezone);
72+
public DateHistogramCompositeAggregationSourceDescriptor<T> Timezone(string timezone) => Assign(a => a.Timezone = timezone);
73+
74+
/// <inheritdoc cref="IDateHistogramCompositeAggregationSource.Timezone"/>
75+
public DateHistogramCompositeAggregationSourceDescriptor<T> Format(string format) => Assign(a => a.Format = format);
6376
}
6477
}

src/Tests/Tests/Aggregations/Bucket/Composite/CompositeAggregationUsageTests.cs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Threading.Tasks;
45
using Elastic.Xunit.XunitPlumbing;
56
using FluentAssertions;
67
using Nest;
78
using Newtonsoft.Json;
9+
using Tests.Configuration;
810
using Tests.Core.Extensions;
911
using Tests.Core.ManagedElasticsearch.Clusters;
1012
using Tests.Domain;
@@ -163,6 +165,12 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
163165
var composite = response.Aggregations.Composite("my_buckets");
164166
composite.Should().NotBeNull();
165167
composite.Buckets.Should().NotBeNullOrEmpty();
168+
composite.AfterKey.Should().NotBeNull();
169+
if (TestConfiguration.Instance.InRange(">=6.3.0"))
170+
{
171+
composite.AfterKey.Should().HaveCount(3)
172+
.And.ContainKeys("branches", "started", "branch_count");
173+
}
166174
foreach (var item in composite.Buckets)
167175
{
168176
var key = item.Key;
@@ -187,4 +195,118 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
187195
}
188196
}
189197
}
198+
199+
200+
//hide
201+
[SkipVersion("<6.3.0", "Date histogram source only supports format starting from Elasticsearch 6.3.0+")]
202+
public class DateFormatCompositeAggregationUsageTests : ProjectsOnlyAggregationUsageTestBase
203+
{
204+
public DateFormatCompositeAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }
205+
206+
protected override object AggregationJson => new
207+
{
208+
my_buckets = new
209+
{
210+
composite = new
211+
{
212+
sources = new object[]
213+
{
214+
new
215+
{
216+
started = new
217+
{
218+
date_histogram = new
219+
{
220+
field = "startedOn",
221+
interval = "month",
222+
format = "yyyy-MM-dd"
223+
}
224+
}
225+
},
226+
}
227+
},
228+
aggs = new
229+
{
230+
project_tags = new
231+
{
232+
nested = new
233+
{
234+
path = "tags"
235+
},
236+
aggs = new
237+
{
238+
tags = new
239+
{
240+
terms = new {field = "tags.name"}
241+
}
242+
}
243+
}
244+
}
245+
}
246+
};
247+
248+
protected override Func<AggregationContainerDescriptor<Project>, IAggregationContainer> FluentAggs => a => a
249+
.Composite("my_buckets", date => date
250+
.Sources(s => s
251+
.DateHistogram("started", d => d
252+
.Field(f => f.StartedOn)
253+
.Interval(DateInterval.Month)
254+
.Format("yyyy-MM-dd")
255+
)
256+
)
257+
.Aggregations(childAggs => childAggs
258+
.Nested("project_tags", n => n
259+
.Path(p => p.Tags)
260+
.Aggregations(nestedAggs => nestedAggs
261+
.Terms("tags", avg => avg.Field(p => p.Tags.First().Name))
262+
)
263+
)
264+
)
265+
);
266+
267+
protected override AggregationDictionary InitializerAggs =>
268+
new CompositeAggregation("my_buckets")
269+
{
270+
Sources = new List<ICompositeAggregationSource>
271+
{
272+
new DateHistogramCompositeAggregationSource("started")
273+
{
274+
Field = Infer.Field<Project>(f => f.StartedOn),
275+
Interval = DateInterval.Month,
276+
Format = "yyyy-MM-dd"
277+
},
278+
},
279+
Aggregations = new NestedAggregation("project_tags")
280+
{
281+
Path = Field<Project>(p => p.Tags),
282+
Aggregations = new TermsAggregation("tags")
283+
{
284+
Field = Field<Project>(p => p.Tags.First().Name)
285+
}
286+
}
287+
};
288+
289+
/**==== Handling Responses
290+
* Each Composite aggregation bucket key is an `CompositeKey`, a specialized
291+
* `IReadOnlyDictionary<string, object>` type with methods to convert values to supported types
292+
*/
293+
protected override void ExpectResponse(ISearchResponse<Project> response)
294+
{
295+
response.ShouldBeValid();
296+
297+
var composite = response.Aggregations.Composite("my_buckets");
298+
composite.Should().NotBeNull();
299+
composite.Buckets.Should().NotBeNullOrEmpty();
300+
composite.AfterKey.Should().NotBeNull();
301+
composite.AfterKey.Should().HaveCount(1).And.ContainKeys("started");
302+
foreach (var item in composite.Buckets)
303+
{
304+
var key = item.Key;
305+
key.Should().NotBeNull();
306+
307+
key.TryGetValue("started", out string startedString).Should().BeTrue();
308+
startedString.Should().NotBeNullOrWhiteSpace();
309+
}
310+
}
311+
}
190312
}

src/Tests/Tests/Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<NoWarn>$(NoWarn);xUnit1013</NoWarn>
88
</PropertyGroup>
99
<ItemGroup>
10-
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.0-beta1-build3642" />
10+
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
1111
</ItemGroup>
1212
<ItemGroup>
1313
<ProjectReference Include="..\Tests.Core\Tests.Core.csproj" />

0 commit comments

Comments
 (0)