14
14
*/
15
15
16
16
using System ;
17
+ using System . Collections . Generic ;
17
18
using System . Linq ;
18
19
using System . Linq . Expressions ;
19
20
using System . Threading ;
@@ -31,6 +32,58 @@ namespace MongoDB.Driver
31
32
/// </summary>
32
33
public static class IAggregateFluentExtensions
33
34
{
35
+ /// <summary>
36
+ /// Appends a $bucket stage to the pipeline.
37
+ /// </summary>
38
+ /// <typeparam name="TResult">The type of the result.</typeparam>
39
+ /// <typeparam name="TValue">The type of the value.</typeparam>
40
+ /// <param name="aggregate">The aggregate.</param>
41
+ /// <param name="groupBy">The expression providing the value to group by.</param>
42
+ /// <param name="boundaries">The bucket boundaries.</param>
43
+ /// <param name="defaultBucket">The default bucket (optional).</param>
44
+ /// <returns>The fluent aggregate interface.</returns>
45
+ public static IAggregateFluent < AggregateBucketResult < TValue > > Bucket < TResult , TValue > (
46
+ this IAggregateFluent < TResult > aggregate ,
47
+ Expression < Func < TResult , TValue > > groupBy ,
48
+ IEnumerable < TValue > boundaries ,
49
+ Optional < TValue > defaultBucket = default ( Optional < TValue > ) )
50
+ {
51
+ Ensure . IsNotNull ( aggregate , nameof ( aggregate ) ) ;
52
+ Ensure . IsNotNull ( groupBy , nameof ( groupBy ) ) ;
53
+ Ensure . IsNotNull ( boundaries , nameof ( boundaries ) ) ;
54
+
55
+ var groupByDefinition = new ExpressionAggregateExpressionDefinition < TResult , TValue > ( groupBy , aggregate . Options . TranslationOptions ) ;
56
+ return aggregate . Bucket ( groupByDefinition , boundaries , defaultBucket ) ;
57
+ }
58
+
59
+ /// <summary>
60
+ /// Appends a $bucket stage to the pipeline.
61
+ /// </summary>
62
+ /// <typeparam name="TResult">The type of the result.</typeparam>
63
+ /// <typeparam name="TValue">The type of the value.</typeparam>
64
+ /// <typeparam name="TNewResult">The type of the new result.</typeparam>
65
+ /// <param name="aggregate">The aggregate.</param>
66
+ /// <param name="groupBy">The expression providing the value to group by.</param>
67
+ /// <param name="boundaries">The bucket boundaries.</param>
68
+ /// <param name="output">The output projection.</param>
69
+ /// <param name="defaultBucket">The default bucket (optional).</param>
70
+ /// <returns>The fluent aggregate interface.</returns>
71
+ public static IAggregateFluent < TNewResult > Bucket < TResult , TValue , TNewResult > (
72
+ this IAggregateFluent < TResult > aggregate ,
73
+ Expression < Func < TResult , TValue > > groupBy ,
74
+ IEnumerable < TValue > boundaries ,
75
+ Expression < Func < IGrouping < TValue , TResult > , TNewResult > > output ,
76
+ Optional < TValue > defaultBucket = default ( Optional < TValue > ) )
77
+ {
78
+ Ensure . IsNotNull ( aggregate , nameof ( aggregate ) ) ;
79
+ Ensure . IsNotNull ( groupBy , nameof ( groupBy ) ) ;
80
+ Ensure . IsNotNull ( boundaries , nameof ( boundaries ) ) ;
81
+
82
+ var groupByDefinition = new ExpressionAggregateExpressionDefinition < TResult , TValue > ( groupBy , aggregate . Options . TranslationOptions ) ;
83
+ var outputDefinition = new ExpressionBucketOutputProjection < TResult , TValue , TNewResult > ( x => default ( TValue ) , output , aggregate . Options . TranslationOptions ) ;
84
+ return aggregate . Bucket ( groupByDefinition , boundaries , outputDefinition , defaultBucket ) ;
85
+ }
86
+
34
87
/// <summary>
35
88
/// Appends a group stage to the pipeline.
36
89
/// </summary>
@@ -588,5 +641,36 @@ public override RenderedProjectionDefinition<TNewResult> Render(IBsonSerializer<
588
641
return AggregateGroupTranslator . Translate < TKey , TResult , TNewResult > ( _idExpression , _groupExpression , documentSerializer , serializerRegistry , _translationOptions ) ;
589
642
}
590
643
}
644
+
645
+ private sealed class ExpressionBucketOutputProjection < TResult , TValue , TNewResult > : ProjectionDefinition < TResult , TNewResult >
646
+ {
647
+ private readonly Expression < Func < IGrouping < TValue , TResult > , TNewResult > > _outputExpression ;
648
+ private readonly ExpressionTranslationOptions _translationOptions ;
649
+ private readonly Expression < Func < TResult , TValue > > _valueExpression ;
650
+
651
+ public ExpressionBucketOutputProjection (
652
+ Expression < Func < TResult , TValue > > valueExpression ,
653
+ Expression < Func < IGrouping < TValue , TResult > , TNewResult > > outputExpression ,
654
+ ExpressionTranslationOptions translationOptions )
655
+ {
656
+ _valueExpression = Ensure . IsNotNull ( valueExpression , nameof ( valueExpression ) ) ;
657
+ _outputExpression = Ensure . IsNotNull ( outputExpression , nameof ( outputExpression ) ) ;
658
+ _translationOptions = translationOptions ; // can be null
659
+
660
+ }
661
+
662
+ public Expression < Func < IGrouping < TValue , TResult > , TNewResult > > OutputExpression
663
+ {
664
+ get { return _outputExpression ; }
665
+ }
666
+
667
+ public override RenderedProjectionDefinition < TNewResult > Render ( IBsonSerializer < TResult > documentSerializer , IBsonSerializerRegistry serializerRegistry )
668
+ {
669
+ var renderedOutput = AggregateGroupTranslator . Translate < TValue , TResult , TNewResult > ( _valueExpression , _outputExpression , documentSerializer , serializerRegistry , _translationOptions ) ;
670
+ var document = renderedOutput . Document ;
671
+ document . Remove ( "_id" ) ;
672
+ return new RenderedProjectionDefinition < TNewResult > ( document , renderedOutput . ProjectionSerializer ) ;
673
+ }
674
+ }
591
675
}
592
676
}
0 commit comments