Skip to content

Commit b216b95

Browse files
[Backport master] Add multi terms aggregation (#5389)
* Add multi terms aggregation (#5372) * Implement multi terms aggregation * Add skip version * Fixup namespaces Co-authored-by: Steve Gordon <sgordon@hotmail.co.uk>
1 parent d1630a2 commit b216b95

File tree

13 files changed

+519
-4
lines changed

13 files changed

+519
-4
lines changed

docs/aggregations.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ In addition to the buckets themselves, the bucket aggregations also compute and
168168

169169
* <<missing-aggregation-usage,Missing Aggregation Usage>>
170170

171+
* <<multi-terms-aggregation-usage,Multi Terms Aggregation Usage>>
172+
171173
* <<nested-aggregation-usage,Nested Aggregation Usage>>
172174

173175
* <<parent-aggregation-usage,Parent Aggregation Usage>>
@@ -231,6 +233,8 @@ include::aggregations/bucket/ip-range/ip-range-aggregation-usage.asciidoc[]
231233

232234
include::aggregations/bucket/missing/missing-aggregation-usage.asciidoc[]
233235

236+
include::aggregations/bucket/multi-terms/multi-terms-aggregation-usage.asciidoc[]
237+
234238
include::aggregations/bucket/nested/nested-aggregation-usage.asciidoc[]
235239

236240
include::aggregations/bucket/parent/parent-aggregation-usage.asciidoc[]
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
:ref_current: https://www.elastic.co/guide/en/elasticsearch/reference/7.x
2+
3+
:github: https://github.com/elastic/elasticsearch-net
4+
5+
:nuget: https://www.nuget.org/packages
6+
7+
////
8+
IMPORTANT NOTE
9+
==============
10+
This file has been generated from https://github.com/elastic/elasticsearch-net/tree/7.x/src/Tests/Tests/Aggregations/Bucket/MultiTerms/MultiTermsAggregationUsageTests.cs.
11+
If you wish to submit a PR for any spelling mistakes, typos or grammatical errors for this file,
12+
please modify the original csharp file found at the link and submit the PR with that change. Thanks!
13+
////
14+
15+
[[multi-terms-aggregation-usage]]
16+
=== Multi Terms Aggregation Usage
17+
18+
A multi-bucket value source based aggregation where buckets are dynamically built - one per unique set of values.
19+
20+
See the Elasticsearch documentation on {ref_current}//search-aggregations-bucket-multi-terms-aggregation.html[multi terms aggregation] for more detail.
21+
22+
==== Fluent DSL example
23+
24+
[source,csharp]
25+
----
26+
a => a
27+
.MultiTerms("states", st => st
28+
.CollectMode(TermsAggregationCollectMode.BreadthFirst)
29+
.Terms(t => t.Field(f => f.Name), t => t.Field(f => f.NumberOfCommits).Missing(0))
30+
.MinimumDocumentCount(1)
31+
.Size(5)
32+
.ShardSize(100)
33+
.ShardMinimumDocumentCount(1)
34+
.ShowTermDocCountError(true)
35+
.Order(o => o
36+
.KeyAscending()
37+
.CountDescending()
38+
)
39+
.Meta(m => m
40+
.Add("foo", "bar")
41+
)
42+
)
43+
----
44+
45+
==== Object Initializer syntax example
46+
47+
[source,csharp]
48+
----
49+
new MultiTermsAggregation("states")
50+
{
51+
CollectMode = TermsAggregationCollectMode.BreadthFirst,
52+
Terms = new List<Term>
53+
{
54+
new() {Field = Field<Project>(f => f.Name) },
55+
new() {Field = Field<Project>(f => f.NumberOfCommits), Missing = 0 }
56+
},
57+
MinimumDocumentCount = 1,
58+
Size = 5,
59+
ShardSize = 100,
60+
ShardMinimumDocumentCount = 1,
61+
ShowTermDocCountError = true,
62+
Order = new List<TermsOrder>
63+
{
64+
TermsOrder.KeyAscending,
65+
TermsOrder.CountDescending
66+
},
67+
Meta = new Dictionary<string, object>
68+
{
69+
{ "foo", "bar" }
70+
}
71+
}
72+
----
73+
74+
[source,javascript]
75+
.Example json output
76+
----
77+
{
78+
"states": {
79+
"meta": {
80+
"foo": "bar"
81+
},
82+
"multi_terms": {
83+
"collect_mode": "breadth_first",
84+
"terms": [
85+
{
86+
"field": "name"
87+
},
88+
{
89+
"field": "numberOfCommits",
90+
"missing": 0
91+
}
92+
],
93+
"min_doc_count": 1,
94+
"shard_min_doc_count": 1,
95+
"size": 5,
96+
"shard_size": 100,
97+
"show_term_doc_count_error": true,
98+
"order": [
99+
{
100+
"_key": "asc"
101+
},
102+
{
103+
"_count": "desc"
104+
}
105+
]
106+
}
107+
}
108+
}
109+
----
110+
111+
==== Handling Responses
112+
113+
[source,csharp]
114+
----
115+
response.ShouldBeValid();
116+
var states = response.Aggregations.MultiTerms("states");
117+
states.Should().NotBeNull();
118+
states.DocCountErrorUpperBound.Should().HaveValue();
119+
states.SumOtherDocCount.Should().HaveValue();
120+
states.Buckets.Should().NotBeNull();
121+
states.Buckets.Count.Should().BeGreaterThan(0);
122+
foreach (var item in states.Buckets)
123+
{
124+
item.Key.Should().NotBeNullOrEmpty();
125+
item.DocCount.Should().BeGreaterOrEqualTo(1);
126+
item.KeyAsString.Should().NotBeNullOrEmpty();
127+
}
128+
states.Meta.Should().NotBeNull().And.HaveCount(1);
129+
states.Meta["foo"].Should().Be("bar");
130+
----
131+

src/Nest/Aggregations/AggregateDictionary.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,22 @@ public MultiBucketAggregate<RareTermsBucket<TKey>> RareTerms<TKey>(string key)
221221

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

224+
public MultiTermsAggregate<string> MultiTerms(string key) => MultiTerms<string>(key);
225+
226+
public MultiTermsAggregate<TKey> MultiTerms<TKey>(string key)
227+
{
228+
var bucket = TryGet<BucketAggregate>(key);
229+
return bucket == null
230+
? null
231+
: new MultiTermsAggregate<TKey>
232+
{
233+
DocCountErrorUpperBound = bucket.DocCountErrorUpperBound,
234+
SumOtherDocCount = bucket.SumOtherDocCount,
235+
Buckets = GetMultiTermsBuckets<TKey>(bucket.Items).ToList(),
236+
Meta = bucket.Meta
237+
};
238+
}
239+
224240
public AutoDateHistogramAggregate AutoDateHistogram(string key)
225241
{
226242
var bucket = TryGet<BucketAggregate>(key);
@@ -324,6 +340,28 @@ private IEnumerable<RareTermsBucket<TKey>> GetRareTermsBuckets<TKey>(IEnumerable
324340
};
325341
}
326342

343+
private static IEnumerable<MultiTermsBucket<TKey>> GetMultiTermsBuckets<TKey>(IEnumerable<IBucket> items)
344+
{
345+
var buckets = items.Cast<KeyedBucket<object>>();
346+
347+
foreach (var bucket in buckets)
348+
{
349+
var aggregates = new MultiTermsBucket<TKey>(bucket.BackingDictionary)
350+
{
351+
DocCount = bucket.DocCount,
352+
DocCountErrorUpperBound = bucket.DocCountErrorUpperBound,
353+
KeyAsString = bucket.KeyAsString
354+
};
355+
356+
if (bucket.Key is List<object> allKeys)
357+
aggregates.Key = allKeys.Select(GetKeyFromBucketKey<TKey>).ToList();
358+
else
359+
aggregates.Key = new[] { GetKeyFromBucketKey<TKey>(bucket.Key) };
360+
361+
yield return aggregates;
362+
}
363+
}
364+
327365
private static TKey GetKeyFromBucketKey<TKey>(object key) =>
328366
typeof(TKey).IsEnum
329367
? (TKey)Enum.Parse(typeof(TKey), key.ToString(), true)

src/Nest/Aggregations/AggregateFormatter.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -937,9 +937,7 @@ private IBucket GetDateHistogramBucket(ref JsonReader reader, IJsonFormatterReso
937937

938938
return dateHistogram;
939939
}
940-
941-
942-
940+
943941
private IBucket GetKeyedBucket(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
944942
{
945943
var token = reader.GetCurrentJsonToken();
@@ -949,6 +947,32 @@ private IBucket GetKeyedBucket(ref JsonReader reader, IJsonFormatterResolver for
949947
object key;
950948
if (token == JsonToken.String)
951949
key = reader.ReadString();
950+
else if (token == JsonToken.BeginArray) // we're in a multi-terms bucket
951+
{
952+
var keys = new List<object>();
953+
var count = 0;
954+
955+
while(reader.ReadIsInArray(ref count))
956+
{
957+
object keyItem;
958+
959+
var keyToken = reader.GetCurrentJsonToken();
960+
961+
if (keyToken == JsonToken.String)
962+
keyItem = reader.ReadString();
963+
else
964+
{
965+
var numberKey = reader.ReadNumberSegment();
966+
if (numberKey.IsLong())
967+
keyItem = NumberConverter.ReadInt64(numberKey.Array, numberKey.Offset, out _);
968+
else
969+
keyItem = NumberConverter.ReadDouble(numberKey.Array, numberKey.Offset, out _);
970+
}
971+
972+
keys.Add(keyItem);
973+
}
974+
key = keys;
975+
}
952976
else
953977
{
954978
var numberSegment = reader.ReadNumberSegment();

src/Nest/Aggregations/AggregationContainer.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,9 @@ public interface IAggregationContainer
294294
[DataMember(Name = "top_metrics")]
295295
ITopMetricsAggregation TopMetrics { get; set; }
296296

297+
[DataMember(Name = "multi_terms")]
298+
IMultiTermsAggregation MultiTerms { get; set; }
299+
297300
void Accept(IAggregationVisitor visitor);
298301
}
299302

@@ -442,6 +445,8 @@ public class AggregationContainer : IAggregationContainer
442445

443446
public ITopMetricsAggregation TopMetrics { get; set; }
444447

448+
public IMultiTermsAggregation MultiTerms { get; set; }
449+
445450
public void Accept(IAggregationVisitor visitor)
446451
{
447452
if (visitor.Scope == AggregationVisitorScope.Unknown) visitor.Scope = AggregationVisitorScope.Aggregation;
@@ -555,6 +560,8 @@ public class AggregationContainerDescriptor<T> : DescriptorBase<AggregationConta
555560

556561
IMovingPercentilesAggregation IAggregationContainer.MovingPercentiles { get; set; }
557562

563+
IMultiTermsAggregation IAggregationContainer.MultiTerms { get; set; }
564+
558565
INestedAggregation IAggregationContainer.Nested { get; set; }
559566

560567
INormalizeAggregation IAggregationContainer.Normalize { get; set; }
@@ -719,6 +726,11 @@ Func<MissingAggregationDescriptor<T>, IMissingAggregation> selector
719726
) =>
720727
_SetInnerAggregation(name, selector, (a, d) => a.Missing = d);
721728

729+
public AggregationContainerDescriptor<T> MultiTerms(string name,
730+
Func<MultiTermsAggregationDescriptor<T>, IMultiTermsAggregation> selector
731+
) =>
732+
_SetInnerAggregation(name, selector, (a, d) => a.MultiTerms = d);
733+
722734
public AggregationContainerDescriptor<T> Nested(string name,
723735
Func<NestedAggregationDescriptor<T>, INestedAggregation> selector
724736
) =>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
namespace Nest
6+
{
7+
public class MultiTermsAggregate<TKey> : MultiBucketAggregate<MultiTermsBucket<TKey>>
8+
{
9+
public long? DocCountErrorUpperBound { get; set; }
10+
public long? SumOtherDocCount { get; set; }
11+
}
12+
}

0 commit comments

Comments
 (0)