Skip to content

Commit

Permalink
Fix #3317 and #3322 add after_key support to composite aggregation re… (
Browse files Browse the repository at this point in the history
#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

(cherry picked from commit 69775d6)
  • Loading branch information
Mpdreamz committed Sep 3, 2018
1 parent 6d69e97 commit f3a5ef1
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 5 deletions.
5 changes: 4 additions & 1 deletion src/Nest/Aggregations/Aggregate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
namespace Nest
{
/// <summary>
/// Represents the result of an aggregation on the response
/// Aggregation response for an aggregation request
/// </summary>
[ExactContractJsonConverter(typeof(AggregateJsonConverter))]
public interface IAggregate
{
//TODO this public set is problematic
/// <summary>
/// Metadata for the aggregation
/// </summary>
IReadOnlyDictionary<string, object> Meta { get; set; }
}
}
12 changes: 11 additions & 1 deletion src/Nest/Aggregations/AggregateDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,17 @@ public TermsAggregate<TKey> Terms<TKey>(string key)

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

public MultiBucketAggregate<CompositeBucket> Composite(string key) => GetMultiBucketAggregate<CompositeBucket>(key);
public CompositeBucketAggregate Composite(string key)
{
var bucket = this.TryGet<BucketAggregate>(key);
if (bucket == null) return null;
return new CompositeBucketAggregate
{
Buckets = bucket.Items.OfType<CompositeBucket>().ToList(),
Meta = bucket.Meta,
AfterKey = new CompositeKey(bucket.AfterKey)
};
}

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

Expand Down
11 changes: 11 additions & 0 deletions src/Nest/Aggregations/AggregateJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ private static class Parser
public const string Hits = "hits";
public const string Location = "location";
public const string Fields = "fields";
public const string AfterKey = "after_key";

public const string Key = "key";
public const string From = "from";
Expand Down Expand Up @@ -122,6 +123,16 @@ private IAggregate ReadAggregate(JsonReader reader, JsonSerializer serializer)
case Parser.Value:
aggregate = GetValueAggregate(reader, serializer);
break;
case Parser.AfterKey:
reader.Read();
var afterKeys = serializer.Deserialize<Dictionary<string, object>>(reader);
reader.Read();
var bucketAggregate = reader.Value.ToString() == Parser.Buckets
? this.GetMultiBucketAggregate(reader, serializer) as BucketAggregate ?? new BucketAggregate()
: new BucketAggregate();
bucketAggregate.AfterKey = afterKeys;
aggregate = bucketAggregate;
break;
case Parser.Buckets:
case Parser.DocCountErrorUpperBound:
aggregate = GetMultiBucketAggregate(reader, serializer);
Expand Down
26 changes: 26 additions & 0 deletions src/Nest/Aggregations/Bucket/BucketAggregate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,42 @@ public class SingleBucketAggregate : BucketAggregateBase
{
public SingleBucketAggregate(IReadOnlyDictionary<string, IAggregate> aggregations) : base(aggregations) { }

/// <summary>
/// Count of documents in the bucket
/// </summary>
public long DocCount { get; internal set; }
}

/// <summary>
/// Aggregation response for a bucket aggregation
/// </summary>
/// <typeparam name="TBucket"></typeparam>
public class MultiBucketAggregate<TBucket> : IAggregate
where TBucket : IBucket
{
/// <inheritdoc />
public IReadOnlyDictionary<string, object> Meta { get; set; }

/// <summary>
/// The buckets into which results are grouped
/// </summary>
public IReadOnlyCollection<TBucket> Buckets { get; set; } = EmptyReadOnly<TBucket>.Collection;
}

/// <summary>
/// Aggregation response of <see cref="CompositeAggregation"/>
/// </summary>
public class CompositeBucketAggregate : MultiBucketAggregate<CompositeBucket>
{
/// <summary>
/// The composite key of the last bucket returned
/// in the response before any filtering by pipeline aggregations.
/// If all buckets are filtered/removed by pipeline aggregations,
/// <see cref="AfterKey"/> will contain the composite key of the last bucket before filtering.
/// </summary>
/// <remarks> Valid for Elasticsearch 6.3.0+ </remarks>
public CompositeKey AfterKey { get; set; }
}

// Intermediate object used for deserialization
public class BucketAggregate : IAggregate
Expand All @@ -35,5 +60,6 @@ public class BucketAggregate : IAggregate
public IReadOnlyDictionary<string, object> Meta { get; set; } = EmptyReadOnly<string, object>.Dictionary;
public long DocCount { get; set; }
public long BgCount { get; set; }
public IReadOnlyDictionary<string, object> AfterKey { get; set; } = EmptyReadOnly<string, object>.Dictionary;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public interface IDateHistogramCompositeAggregationSource : ICompositeAggregatio
/// </summary>
[JsonProperty("time_zone")]
string Timezone { get; set; }

/// <summary>
/// Return a formatted date string as the key instead an epoch long
/// </summary>
/// <remarks> Valid for Elasticsearch 6.3.0+ </remarks>
[JsonProperty("format")]
string Format { get; set; }
}

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

/// <inheritdoc />
public string Format { get; set; }

/// <inheritdoc />
protected override string SourceType => "date_histogram";
}
Expand All @@ -46,6 +56,7 @@ public class DateHistogramCompositeAggregationSourceDescriptor<T>
{
Union<DateInterval?,Time> IDateHistogramCompositeAggregationSource.Interval { get; set; }
string IDateHistogramCompositeAggregationSource.Timezone { get; set; }
string IDateHistogramCompositeAggregationSource.Format { get; set; }

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

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

/// <inheritdoc cref="IDateHistogramCompositeAggregationSource.Timezone"/>
public DateHistogramCompositeAggregationSourceDescriptor<T> Timezone(string timezone) =>
Assign(a => a.Timezone = timezone);
public DateHistogramCompositeAggregationSourceDescriptor<T> Timezone(string timezone) => Assign(a => a.Timezone = timezone);

/// <inheritdoc cref="IDateHistogramCompositeAggregationSource.Timezone"/>
public DateHistogramCompositeAggregationSourceDescriptor<T> Format(string format) => Assign(a => a.Format = format);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Elastic.Xunit.XunitPlumbing;
using FluentAssertions;
using Nest;
using Newtonsoft.Json;
using Tests.Configuration;
using Tests.Core.Extensions;
using Tests.Core.ManagedElasticsearch.Clusters;
using Tests.Domain;
Expand Down Expand Up @@ -163,6 +165,12 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
var composite = response.Aggregations.Composite("my_buckets");
composite.Should().NotBeNull();
composite.Buckets.Should().NotBeNullOrEmpty();
composite.AfterKey.Should().NotBeNull();
if (TestConfiguration.Instance.InRange(">=6.3.0"))
{
composite.AfterKey.Should().HaveCount(3)
.And.ContainKeys("branches", "started", "branch_count");
}
foreach (var item in composite.Buckets)
{
var key = item.Key;
Expand All @@ -187,4 +195,118 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
}
}
}


//hide
[SkipVersion("<6.3.0", "Date histogram source only supports format starting from Elasticsearch 6.3.0+")]
public class DateFormatCompositeAggregationUsageTests : ProjectsOnlyAggregationUsageTestBase
{
public DateFormatCompositeAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }

protected override object AggregationJson => new
{
my_buckets = new
{
composite = new
{
sources = new object[]
{
new
{
started = new
{
date_histogram = new
{
field = "startedOn",
interval = "month",
format = "yyyy-MM-dd"
}
}
},
}
},
aggs = new
{
project_tags = new
{
nested = new
{
path = "tags"
},
aggs = new
{
tags = new
{
terms = new {field = "tags.name"}
}
}
}
}
}
};

protected override Func<AggregationContainerDescriptor<Project>, IAggregationContainer> FluentAggs => a => a
.Composite("my_buckets", date => date
.Sources(s => s
.DateHistogram("started", d => d
.Field(f => f.StartedOn)
.Interval(DateInterval.Month)
.Format("yyyy-MM-dd")
)
)
.Aggregations(childAggs => childAggs
.Nested("project_tags", n => n
.Path(p => p.Tags)
.Aggregations(nestedAggs => nestedAggs
.Terms("tags", avg => avg.Field(p => p.Tags.First().Name))
)
)
)
);

protected override AggregationDictionary InitializerAggs =>
new CompositeAggregation("my_buckets")
{
Sources = new List<ICompositeAggregationSource>
{
new DateHistogramCompositeAggregationSource("started")
{
Field = Infer.Field<Project>(f => f.StartedOn),
Interval = DateInterval.Month,
Format = "yyyy-MM-dd"
},
},
Aggregations = new NestedAggregation("project_tags")
{
Path = Field<Project>(p => p.Tags),
Aggregations = new TermsAggregation("tags")
{
Field = Field<Project>(p => p.Tags.First().Name)
}
}
};

/**==== Handling Responses
* Each Composite aggregation bucket key is an `CompositeKey`, a specialized
* `IReadOnlyDictionary<string, object>` type with methods to convert values to supported types
*/
protected override void ExpectResponse(ISearchResponse<Project> response)
{
response.ShouldBeValid();

var composite = response.Aggregations.Composite("my_buckets");
composite.Should().NotBeNull();
composite.Buckets.Should().NotBeNullOrEmpty();
composite.AfterKey.Should().NotBeNull();
composite.AfterKey.Should().HaveCount(1).And.ContainKeys("started");
foreach (var item in composite.Buckets)
{
var key = item.Key;
key.Should().NotBeNull();

key.TryGetValue("started", out string startedString).Should().BeTrue();
startedString.Should().NotBeNullOrWhiteSpace();
}
}
}
}
2 changes: 1 addition & 1 deletion src/Tests/Tests/Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NoWarn>$(NoWarn);xUnit1013</NoWarn>
</PropertyGroup>
<ItemGroup>
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.0-beta1-build3642" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Tests.Core\Tests.Core.csproj" />
Expand Down

0 comments on commit f3a5ef1

Please sign in to comment.