Skip to content

add support for missing_bucket on composite aggregation as per https:… #3420

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ public interface ICompositeAggregationSource
/// </summary>
[JsonProperty("order")]
SortOrder? Order { get; set; }

/// <summary>
/// By default documents without a value for a given source are ignored. It is possible to include
/// them in the response as null by setting this to true
/// </summary>
[JsonProperty("missing_bucket")]
bool? MissingBucket { get; set; }
}

/// <inheritdoc />
Expand All @@ -59,6 +66,9 @@ protected CompositeAggregationSourceBase(string name) =>

/// <inheritdoc />
public SortOrder? Order { get; set; }

/// <inheritdoc />
public bool? MissingBucket { get; set; }
}

/// <inheritdoc cref="ICompositeAggregationSource"/>
Expand Down Expand Up @@ -93,6 +103,7 @@ public abstract class CompositeAggregationSourceDescriptorBase<TDescriptor, TInt
string ICompositeAggregationSource.SourceType => _sourceType;
Field ICompositeAggregationSource.Field { get; set; }
SortOrder? ICompositeAggregationSource.Order { get; set; }
bool? ICompositeAggregationSource.MissingBucket { get; set; }

protected CompositeAggregationSourceDescriptorBase(string name, string sourceType)
{
Expand All @@ -108,6 +119,9 @@ protected CompositeAggregationSourceDescriptorBase(string name, string sourceTyp

/// <inheritdoc cref="ICompositeAggregationSource.Order"/>
public TDescriptor Order(SortOrder? order) => Assign(a => a.Order = order);

/// <inheritdoc cref="ICompositeAggregationSource.MissingBucket"/>
public TDescriptor MissingBucket(bool? includeMissing = true) => Assign(a => a.MissingBucket = includeMissing);
}

internal class CompositeAggregationSourceConverter : ReserializeJsonConverter<CompositeAggregationSourceBase, ICompositeAggregationSource>
Expand Down
2 changes: 1 addition & 1 deletion src/Tests/Tests.Domain/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public class Project
.RuleFor(p => p.NumberOfCommits, f => Gimme.Random.Number(1, 1000))
.RuleFor(p => p.NumberOfContributors, f => Gimme.Random.Number(1, 200))
.RuleFor(p => p.Ranges, f => Ranges.Generator.Generate())
.RuleFor(p => p.Branches, f => Gimme.Random.ListItems(new List<string> { "master", "dev", "release", "qa", "test" }, 2))
.RuleFor(p => p.Branches, f => Gimme.Random.ListItems(new List<string> { "master", "dev", "release", "qa", "test" }))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did this have any knock on for other tests (I believe terms_set used this field, IIRC)?

.RuleFor(p => p.SourceOnly, f =>
TestConfiguration.Instance.Random.SourceSerializer ? new SourceOnlyObject() : null
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public CompositeAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) :
};

/**==== Handling Responses
* Each Composite aggregation bucket key is an `CompositeKey`, a specialized
* Each Composite aggregation bucket key is a `CompositeKey` type, a specialized
* `IReadOnlyDictionary<string, object>` type with methods to convert values to supported types
*/
protected override void ExpectResponse(ISearchResponse<Project> response)
Expand Down Expand Up @@ -196,6 +196,130 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
}
}

/**[float]
* == Missing buckets
* By default documents without a value for a given source are ignored.
* It is possible to include them in the response by setting missing_bucket to `true` (defaults to `false`):
*
* NOTE: Only available in Elasticsearch 6.4.0+
*/
[SkipVersion("<6.4.0", "Missing buckets added to Composite Aggregation Elasticsearch 6.4.0+")]
public class CompositeAggregationMissingBucketUsageTests : ProjectsOnlyAggregationUsageTestBase
{
public CompositeAggregationMissingBucketUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }

protected override object AggregationJson => new
{
my_buckets = new
{
composite = new
{
sources = new object[]
{
new
{
branches = new
{
terms = new
{
field = "branches.keyword",
order = "asc",
missing_bucket = true
}
}
},
}
},
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
.Terms("branches", t => t
.Field(f => f.Branches.Suffix("keyword"))
.MissingBucket()
.Order(SortOrder.Ascending)
)
)
.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 TermsCompositeAggregationSource("branches")
{
Field = Infer.Field<Project>(f => f.Branches.Suffix("keyword")),
MissingBucket = true,
Order = SortOrder.Ascending
}
},
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();

if (TestConfiguration.Instance.InRange(">=6.3.0"))
composite.AfterKey.Should().HaveCount(1).And.ContainKeys("branches");

var i = 0;
foreach (var item in composite.Buckets)
{
var key = item.Key;
key.Should().NotBeNull();

key.TryGetValue("branches", out string branches).Should().BeTrue("expected to find 'branches' in composite bucket");
if (i == 0) branches.Should().BeNull("First key should be null as we expect to have some projects with no branches");
else branches.Should().NotBeNullOrEmpty();

var nested = item.Nested("project_tags");
nested.Should().NotBeNull();

var nestedTerms = nested.Terms("tags");
nestedTerms.Buckets.Count.Should().BeGreaterThan(0);
i++;
}
}
}

//hide
[SkipVersion("<6.3.0", "Date histogram source only supports format starting from Elasticsearch 6.3.0+")]
Expand Down Expand Up @@ -286,10 +410,6 @@ public DateFormatCompositeAggregationUsageTests(ReadOnlyCluster i, EndpointUsage
}
};

/**==== 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();
Expand Down