Skip to content

Commit 1b22e17

Browse files
committed
Support for other_bucket and other_bucket_key on Filters Aggregation
Closes #1686
1 parent 9319f42 commit 1b22e17

File tree

2 files changed

+69
-3
lines changed

2 files changed

+69
-3
lines changed

Diff for: src/Nest/Aggregations/Bucket/Filters/FiltersAggregation.cs

+50
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,35 @@ public interface IFiltersAggregation : IBucketAggregation
1111
{
1212
[JsonProperty("filters")]
1313
Union<INamedFiltersContainer, List<QueryContainer>> Filters { get; set; }
14+
15+
[JsonProperty("other_bucket")]
16+
bool? OtherBucket { get; set; }
17+
18+
[JsonProperty("other_bucket_key")]
19+
string OtherBucketKey { get; set; }
1420
}
1521

1622
public class FiltersAggregation : BucketAggregationBase, IFiltersAggregation
1723
{
1824
public Union<INamedFiltersContainer, List<QueryContainer>> Filters { get; set; }
1925

26+
/// <summary>
27+
/// Gets or sets whether to add a bucket to the response which will contain all documents
28+
/// that do not match any of the given filters.
29+
/// When set to <c>true</c>, the other bucket will be returned either in a bucket
30+
/// (named "_other_" by default) if named filters are being used,
31+
/// or as the last bucket if anonymous filters are being used
32+
/// When set to <c>false</c>, does not compute
33+
/// the other bucket.
34+
/// </summary>
35+
public bool? OtherBucket { get; set; }
36+
37+
/// <summary>
38+
/// Gets or sets the key for the other bucket to a value other than the default "_other_".
39+
/// Setting this parameter will implicitly set the <see cref="OtherBucket"/> parameter to true
40+
/// </summary>
41+
public string OtherBucketKey { get; set; }
42+
2043
internal FiltersAggregation() { }
2144

2245
public FiltersAggregation(string name) : base(name) { }
@@ -31,6 +54,33 @@ public class FiltersAggregationDescriptor<T>
3154
{
3255
Union<INamedFiltersContainer, List<QueryContainer>> IFiltersAggregation.Filters { get; set; }
3356

57+
bool? IFiltersAggregation.OtherBucket{ get; set; }
58+
59+
string IFiltersAggregation.OtherBucketKey{ get; set; }
60+
61+
/// <summary>
62+
/// Adds a bucket to the response which will contain all documents
63+
/// that do not match any of the given filters.
64+
/// When set to <c>true</c>, the other bucket will be returned either in a bucket
65+
/// (named "_other_" by default) if named filters are being used,
66+
/// or as the last bucket if anonymous filters are being used
67+
/// When set to <c>false</c>, does not compute
68+
/// the other bucket.
69+
/// </summary>
70+
/// <param name="otherBucket">whether to set the other bucket</param>
71+
/// <returns>the <see cref="FiltersAggregationDescriptor{T}"/></returns>
72+
public FiltersAggregationDescriptor<T> OtherBucket(bool otherBucket = true) =>
73+
Assign(a => a.OtherBucket = otherBucket);
74+
75+
/// <summary>
76+
/// Sets the key for the other bucket to a value other than the default "_other_".
77+
/// Setting this parameter will implicitly set the <see cref="OtherBucket"/> parameter to true
78+
/// </summary>
79+
/// <param name="otherBucketKey">the name for the other bucket</param>
80+
/// <returns>the <see cref="FiltersAggregationDescriptor{T}"/></returns>
81+
public FiltersAggregationDescriptor<T> OtherBucketKey(string otherBucketKey) =>
82+
Assign(a => a.OtherBucketKey = otherBucketKey);
83+
3484
public FiltersAggregationDescriptor<T> NamedFilters(Func<NamedFiltersContainerDescriptor<T>, IPromise<INamedFiltersContainer>> selector) =>
3585
Assign(a => a.Filters = new Union<INamedFiltersContainer, List<QueryContainer>>(selector?.Invoke(new NamedFiltersContainerDescriptor<T>())?.Value));
3686

Diff for: src/Tests/Aggregations/Bucket/Filters/FiltersAggregationUsageTests.cs

+19-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ namespace Tests.Aggregations.Bucket.Filters
1111
{
1212
/**
1313
* Defines a multi bucket aggregations where each bucket is associated with a filter.
14-
* Each bucket will collect all documents that match its associated filter.
14+
* Each bucket will collect all documents that match its associated filter. For documents
15+
* that do not match any filter, these will be collected in the other bucket.
1516
*
1617
* Be sure to read the elasticsearch documentation {ref}/search-aggregations-bucket-filters-aggregation.html[on this subject here]
1718
*/
@@ -29,6 +30,8 @@ public FiltersAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : ba
2930
{
3031
filters = new
3132
{
33+
other_bucket = true,
34+
other_bucket_key = "other_states_of_being",
3235
filters = new
3336
{
3437
belly_up = new { term = new { state = new { value = "BellyUp" } } },
@@ -47,6 +50,8 @@ public FiltersAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : ba
4750
protected override Func<SearchDescriptor<Project>, ISearchRequest> Fluent => s => s
4851
.Aggregations(aggs => aggs
4952
.Filters("projects_by_state", agg => agg
53+
.OtherBucket()
54+
.OtherBucketKey("other_states_of_being")
5055
.NamedFilters(filters => filters
5156
.Filter("belly_up", f => f.Term(p => p.State, StateOfBeing.BellyUp))
5257
.Filter("stable", f => f.Term(p => p.State, StateOfBeing.Stable))
@@ -63,6 +68,8 @@ public FiltersAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : ba
6368
{
6469
Aggregations = new FiltersAggregation("projects_by_state")
6570
{
71+
OtherBucket = true,
72+
OtherBucketKey = "other_states_of_being",
6673
Filters = new NamedFiltersContainer
6774
{
6875
{ "belly_up", Query<Project>.Term(p=>p.State, StateOfBeing.BellyUp) },
@@ -97,6 +104,9 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
97104
namedResult.Should().NotBeNull();
98105
namedResult.DocCount.Should().BeGreaterThan(0);
99106

107+
namedResult = filterAgg.NamedBucket("other_states_of_being");
108+
namedResult.Should().NotBeNull();
109+
namedResult.DocCount.Should().Be(0);
100110
}
101111
}
102112

@@ -114,6 +124,7 @@ public AnonymousUsage(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) {
114124
{
115125
filters = new
116126
{
127+
other_bucket = true,
117128
filters = new[] {
118129
new { term = new { state = new { value = "BellyUp" } }},
119130
new { term = new { state = new { value = "Stable" } }},
@@ -131,6 +142,7 @@ public AnonymousUsage(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) {
131142
protected override Func<SearchDescriptor<Project>, ISearchRequest> Fluent => s => s
132143
.Aggregations(aggs => aggs
133144
.Filters("projects_by_state", agg => agg
145+
.OtherBucket()
134146
.AnonymousFilters(
135147
f => f.Term(p => p.State, StateOfBeing.BellyUp),
136148
f => f.Term(p => p.State, StateOfBeing.Stable),
@@ -147,6 +159,7 @@ public AnonymousUsage(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) {
147159
{
148160
Aggregations = new FiltersAggregation("projects_by_state")
149161
{
162+
OtherBucket = true,
150163
Filters = new List<QueryContainer>
151164
{
152165
Query<Project>.Term(p=>p.State, StateOfBeing.BellyUp) ,
@@ -169,11 +182,14 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
169182
var filterAgg = response.Aggs.Filters("projects_by_state");
170183
filterAgg.Should().NotBeNull();
171184
var results = filterAgg.AnonymousBuckets();
172-
results.Count.Should().Be(3);
173-
foreach (var singleBucket in results)
185+
results.Count.Should().Be(4);
186+
187+
foreach (var singleBucket in results.Take(3))
174188
{
175189
singleBucket.DocCount.Should().BeGreaterThan(0);
176190
}
191+
192+
results.Last().DocCount.Should().Be(0);
177193
}
178194
}
179195
}

0 commit comments

Comments
 (0)