@@ -155,7 +155,7 @@ public CompositeAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) :
155155 } ;
156156
157157 /**==== Handling Responses
158- * Each Composite aggregation bucket key is an `CompositeKey`, a specialized
158+ * Each Composite aggregation bucket key is a `CompositeKey` type , a specialized
159159 * `IReadOnlyDictionary<string, object>` type with methods to convert values to supported types
160160 */
161161 protected override void ExpectResponse ( ISearchResponse < Project > response )
@@ -196,6 +196,130 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
196196 }
197197 }
198198
199+ /**[float]
200+ * == Missing buckets
201+ * By default documents without a value for a given source are ignored.
202+ * It is possible to include them in the response by setting missing_bucket to `true` (defaults to `false`):
203+ *
204+ * NOTE: Only available in Elasticsearch 6.4.0+
205+ */
206+ [ SkipVersion ( "<6.4.0" , "Missing buckets added to Composite Aggregation Elasticsearch 6.4.0+" ) ]
207+ public class CompositeAggregationMissingBucketUsageTests : ProjectsOnlyAggregationUsageTestBase
208+ {
209+ public CompositeAggregationMissingBucketUsageTests ( ReadOnlyCluster i , EndpointUsage usage ) : base ( i , usage ) { }
210+
211+ protected override object AggregationJson => new
212+ {
213+ my_buckets = new
214+ {
215+ composite = new
216+ {
217+ sources = new object [ ]
218+ {
219+ new
220+ {
221+ branches = new
222+ {
223+ terms = new
224+ {
225+ field = "branches.keyword" ,
226+ order = "asc" ,
227+ missing_bucket = true
228+ }
229+ }
230+ } ,
231+ }
232+ } ,
233+ aggs = new
234+ {
235+ project_tags = new
236+ {
237+ nested = new { path = "tags" } ,
238+ aggs = new
239+ {
240+ tags = new { terms = new { field = "tags.name" } }
241+ }
242+ }
243+ }
244+ }
245+ } ;
246+
247+ protected override Func < AggregationContainerDescriptor < Project > , IAggregationContainer > FluentAggs => a => a
248+ . Composite ( "my_buckets" , date => date
249+ . Sources ( s => s
250+ . Terms ( "branches" , t => t
251+ . Field ( f => f . Branches . Suffix ( "keyword" ) )
252+ . MissingBucket ( )
253+ . Order ( SortOrder . Ascending )
254+ )
255+ )
256+ . Aggregations ( childAggs => childAggs
257+ . Nested ( "project_tags" , n => n
258+ . Path ( p => p . Tags )
259+ . Aggregations ( nestedAggs => nestedAggs
260+ . Terms ( "tags" , avg => avg . Field ( p => p . Tags . First ( ) . Name ) )
261+ )
262+ )
263+ )
264+ ) ;
265+
266+ protected override AggregationDictionary InitializerAggs =>
267+ new CompositeAggregation ( "my_buckets" )
268+ {
269+ Sources = new List < ICompositeAggregationSource >
270+ {
271+ new TermsCompositeAggregationSource ( "branches" )
272+ {
273+ Field = Infer . Field < Project > ( f => f . Branches . Suffix ( "keyword" ) ) ,
274+ MissingBucket = true ,
275+ Order = SortOrder . Ascending
276+ }
277+ } ,
278+ Aggregations = new NestedAggregation ( "project_tags" )
279+ {
280+ Path = Field < Project > ( p => p . Tags ) ,
281+ Aggregations = new TermsAggregation ( "tags" )
282+ {
283+ Field = Field < Project > ( p => p . Tags . First ( ) . Name )
284+ }
285+ }
286+ } ;
287+
288+ /**==== Handling Responses
289+ * Each Composite aggregation bucket key is an `CompositeKey`, a specialized
290+ * `IReadOnlyDictionary<string, object>` type with methods to convert values to supported types
291+ */
292+ protected override void ExpectResponse ( ISearchResponse < Project > response )
293+ {
294+ response . ShouldBeValid ( ) ;
295+
296+ var composite = response . Aggregations . Composite ( "my_buckets" ) ;
297+ composite . Should ( ) . NotBeNull ( ) ;
298+ composite . Buckets . Should ( ) . NotBeNullOrEmpty ( ) ;
299+ composite . AfterKey . Should ( ) . NotBeNull ( ) ;
300+
301+ if ( TestConfiguration . Instance . InRange ( ">=6.3.0" ) )
302+ composite . AfterKey . Should ( ) . HaveCount ( 1 ) . And . ContainKeys ( "branches" ) ;
303+
304+ var i = 0 ;
305+ foreach ( var item in composite . Buckets )
306+ {
307+ var key = item . Key ;
308+ key . Should ( ) . NotBeNull ( ) ;
309+
310+ key . TryGetValue ( "branches" , out string branches ) . Should ( ) . BeTrue ( "expected to find 'branches' in composite bucket" ) ;
311+ if ( i == 0 ) branches . Should ( ) . BeNull ( "First key should be null as we expect to have some projects with no branches" ) ;
312+ else branches . Should ( ) . NotBeNullOrEmpty ( ) ;
313+
314+ var nested = item . Nested ( "project_tags" ) ;
315+ nested . Should ( ) . NotBeNull ( ) ;
316+
317+ var nestedTerms = nested . Terms ( "tags" ) ;
318+ nestedTerms . Buckets . Count . Should ( ) . BeGreaterThan ( 0 ) ;
319+ i ++ ;
320+ }
321+ }
322+ }
199323
200324 //hide
201325 [ SkipVersion ( "<6.3.0" , "Date histogram source only supports format starting from Elasticsearch 6.3.0+" ) ]
@@ -286,10 +410,6 @@ public DateFormatCompositeAggregationUsageTests(ReadOnlyCluster i, EndpointUsage
286410 }
287411 } ;
288412
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- */
293413 protected override void ExpectResponse ( ISearchResponse < Project > response )
294414 {
295415 response . ShouldBeValid ( ) ;
0 commit comments