Skip to content

Commit c6b1f2c

Browse files
committed
CSHARP-1693: Added support for $sortByCount aggregation stage.
1 parent 38db928 commit c6b1f2c

15 files changed

+451
-4
lines changed

src/MongoDB.Driver/AggregateCountResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
namespace MongoDB.Driver
2020
{
2121
/// <summary>
22-
/// Default result type for the aggregate $count stage.
22+
/// Result type for the aggregate $count stage.
2323
/// </summary>
2424
public sealed class AggregateCountResult
2525
{
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/* Copyright 2016 MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Linq.Expressions;
18+
using MongoDB.Bson;
19+
using MongoDB.Bson.Serialization;
20+
using MongoDB.Driver.Core.Misc;
21+
using MongoDB.Driver.Linq.Translators;
22+
23+
namespace MongoDB.Driver
24+
{
25+
/// <summary>
26+
/// An aggregation expression.
27+
/// </summary>
28+
/// <typeparam name="TSource">The type of the source.</typeparam>
29+
/// <typeparam name="TResult">The type of the result.</typeparam>
30+
public abstract class AggregateExpressionDefinition<TSource, TResult>
31+
{
32+
#region static
33+
// public implicit conversions
34+
/// <summary>
35+
/// Performs an implicit conversion from <see cref="BsonValue"/> to <see cref="AggregateExpressionDefinition{TSource, TResult}"/>.
36+
/// </summary>
37+
/// <param name="expression">The expression.</param>
38+
/// <returns>
39+
/// The result of the conversion.
40+
/// </returns>
41+
public static implicit operator AggregateExpressionDefinition<TSource, TResult>(BsonValue expression)
42+
{
43+
Ensure.IsNotNull(expression, nameof(expression));
44+
return new BsonValueAggregateExpressionDefinition<TSource, TResult>(expression);
45+
}
46+
47+
/// <summary>
48+
/// Performs an implicit conversion from <see cref="System.String"/> to <see cref="AggregateExpressionDefinition{TSource, TResult}"/>.
49+
/// </summary>
50+
/// <param name="expression">The expression.</param>
51+
/// <returns>
52+
/// The result of the conversion.
53+
/// </returns>
54+
public static implicit operator AggregateExpressionDefinition<TSource, TResult>(string expression)
55+
{
56+
Ensure.IsNotNullOrEmpty(expression, nameof(expression));
57+
if (expression[0] == '{')
58+
{
59+
return new BsonValueAggregateExpressionDefinition<TSource, TResult>(BsonDocument.Parse(expression));
60+
}
61+
else
62+
{
63+
return new BsonValueAggregateExpressionDefinition<TSource, TResult>(new BsonString(expression));
64+
}
65+
}
66+
#endregion
67+
68+
/// <summary>
69+
/// Renders the aggregation expression.
70+
/// </summary>
71+
/// <param name="sourceSerializer">The source serializer.</param>
72+
/// <param name="serializerRegistry">The serializer registry.</param>
73+
/// <returns>The rendered aggregation expression.</returns>
74+
public abstract BsonValue Render(IBsonSerializer<TSource> sourceSerializer, IBsonSerializerRegistry serializerRegistry);
75+
}
76+
77+
/// <summary>
78+
/// A <see cref="BsonValue"/> based aggregate expression.
79+
/// </summary>
80+
/// <typeparam name="TSource">The type of the source.</typeparam>
81+
/// <typeparam name="TResult">The type of the result.</typeparam>
82+
/// <seealso cref="MongoDB.Driver.AggregateExpressionDefinition{TSource, TResult}" />
83+
public sealed class BsonValueAggregateExpressionDefinition<TSource, TResult> : AggregateExpressionDefinition<TSource, TResult>
84+
{
85+
// private fields
86+
private readonly BsonValue _expression;
87+
88+
// constructors
89+
/// <summary>
90+
/// Initializes a new instance of the <see cref="BsonValueAggregateExpressionDefinition{TSource, TResult}"/> class.
91+
/// </summary>
92+
/// <param name="expression">The expression.</param>
93+
public BsonValueAggregateExpressionDefinition(BsonValue expression)
94+
{
95+
_expression = Ensure.IsNotNull(expression, nameof(expression));
96+
}
97+
98+
// public methods
99+
/// <inheritdoc/>
100+
public override BsonValue Render(IBsonSerializer<TSource> sourceSerializer, IBsonSerializerRegistry serializerRegistry)
101+
{
102+
return _expression;
103+
}
104+
}
105+
106+
/// <summary>
107+
/// A <see cref="BsonValue"/> based aggregate expression.
108+
/// </summary>
109+
/// <typeparam name="TSource">The type of the source.</typeparam>
110+
/// <typeparam name="TResult">The type of the result.</typeparam>
111+
/// <seealso cref="MongoDB.Driver.AggregateExpressionDefinition{TSource, TResult}" />
112+
public sealed class ExpressionAggregateExpressionDefinition<TSource, TResult> : AggregateExpressionDefinition<TSource, TResult>
113+
{
114+
// private fields
115+
private readonly Expression<Func<TSource, TResult>> _expression;
116+
private readonly ExpressionTranslationOptions _translationOptions;
117+
118+
// constructors
119+
/// <summary>
120+
/// Initializes a new instance of the <see cref="ExpressionAggregateExpressionDefinition{TSource, TResult}" /> class.
121+
/// </summary>
122+
/// <param name="expression">The expression.</param>
123+
/// <param name="translationOptions">The translation options.</param>
124+
public ExpressionAggregateExpressionDefinition(Expression<Func<TSource, TResult>> expression, ExpressionTranslationOptions translationOptions)
125+
{
126+
_expression = Ensure.IsNotNull(expression, nameof(expression));
127+
_translationOptions = translationOptions; // can be null
128+
}
129+
130+
// public methods
131+
/// <inheritdoc/>
132+
public override BsonValue Render(IBsonSerializer<TSource> sourceSerializer, IBsonSerializerRegistry serializerRegistry)
133+
{
134+
return AggregateExpressionTranslator.Translate(_expression, sourceSerializer, serializerRegistry, _translationOptions);
135+
}
136+
}
137+
}

src/MongoDB.Driver/AggregateFluent.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,19 @@ public override IAggregateFluent<TResult> Sort(SortDefinition<TResult> sort)
209209
operatorName,
210210
(s, sr) => new RenderedPipelineStageDefinition<TResult>(operatorName, new BsonDocument(operatorName, sort.Render(s, sr)), s));
211211

212+
return AppendStage(stage);
213+
}
214+
215+
public override IAggregateFluent<AggregateSortByCountResult<TId>> SortByCount<TId>(AggregateExpressionDefinition<TResult, TId> id)
216+
{
217+
const string operatorName = "$sortByCount";
218+
var stage = new DelegatedPipelineStageDefinition<TResult, AggregateSortByCountResult<TId>>(
219+
operatorName,
220+
(s, sr) =>
221+
{
222+
var outputSerializer = sr.GetSerializer<AggregateSortByCountResult<TId>>();
223+
return new RenderedPipelineStageDefinition<AggregateSortByCountResult<TId>>(operatorName, new BsonDocument(operatorName, id.Render(s, sr)), outputSerializer);
224+
});
212225

213226
return AppendStage(stage);
214227
}

src/MongoDB.Driver/AggregateFluentBase.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ public virtual IAsyncCursor<TResult> Out(string collectionName, CancellationToke
8282
/// <inheritdoc />
8383
public abstract IAggregateFluent<TResult> Sort(SortDefinition<TResult> sort);
8484

85+
/// <inheritdoc />
86+
public virtual IAggregateFluent<AggregateSortByCountResult<TId>> SortByCount<TId>(AggregateExpressionDefinition<TResult, TId> id)
87+
{
88+
throw new NotImplementedException();
89+
}
90+
8591
/// <inheritdoc />
8692
public abstract IAggregateFluent<TNewResult> Unwind<TNewResult>(FieldDefinition<TResult> field, IBsonSerializer<TNewResult> newResultSerializer);
8793

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/* Copyright 2016 MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using MongoDB.Bson.Serialization.Attributes;
17+
18+
namespace MongoDB.Driver
19+
{
20+
/// <summary>
21+
/// Result type for the aggregate $sortByCount stage.
22+
/// </summary>
23+
public sealed class AggregateSortByCountResult<TId>
24+
{
25+
private long _count;
26+
private TId _id;
27+
28+
/// <summary>
29+
/// Initializes a new instance of the <see cref="AggregateCountResult" /> class.
30+
/// </summary>
31+
/// <param name="id">The identifier.</param>
32+
/// <param name="count">The count.</param>
33+
public AggregateSortByCountResult(TId id, long count)
34+
{
35+
_id = id;
36+
_count = count;
37+
}
38+
39+
/// <summary>
40+
/// Gets the count.
41+
/// </summary>
42+
/// <value>
43+
/// The count.
44+
/// </value>
45+
[BsonElement("count")]
46+
public long Count
47+
{
48+
get { return _count; }
49+
}
50+
51+
/// <summary>
52+
/// Gets the identifier.
53+
/// </summary>
54+
/// <value>
55+
/// The identifier.
56+
/// </value>
57+
[BsonElement("_id")]
58+
public TId Id
59+
{
60+
get { return _id; }
61+
}
62+
}
63+
}

src/MongoDB.Driver/IAggregateFluent.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ public interface IAggregateFluent<TResult> : IAsyncCursorSource<TResult>
147147
/// <returns>The fluent aggregate interface.</returns>
148148
IAggregateFluent<TResult> Sort(SortDefinition<TResult> sort);
149149

150+
/// <summary>
151+
/// Appends a sortByCount stage to the pipeline.
152+
/// </summary>
153+
/// <typeparam name="TId">The type of the identifier.</typeparam>
154+
/// <param name="id">The identifier.</param>
155+
/// <returns>The fluent aggregate interface.</returns>
156+
IAggregateFluent<AggregateSortByCountResult<TId>> SortByCount<TId>(AggregateExpressionDefinition<TResult, TId> id);
157+
150158
/// <summary>
151159
/// Appends an unwind stage to the pipeline.
152160
/// </summary>

src/MongoDB.Driver/IAggregateFluentExtensions.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2015 MongoDB Inc.
1+
/* Copyright 2010-2016 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -209,6 +209,26 @@ public static IOrderedAggregateFluent<TResult> SortBy<TResult>(this IAggregateFl
209209
new DirectionalSortDefinition<TResult>(new ExpressionFieldDefinition<TResult>(field), SortDirection.Ascending));
210210
}
211211

212+
/// <summary>
213+
/// Appends a sortByCount stage to the pipeline.
214+
/// </summary>
215+
/// <typeparam name="TResult">The type of the result.</typeparam>
216+
/// <typeparam name="TKey">The type of the key.</typeparam>
217+
/// <param name="aggregate">The aggregate.</param>
218+
/// <param name="id">The id.</param>
219+
/// <returns>
220+
/// The fluent aggregate interface.
221+
/// </returns>
222+
public static IAggregateFluent<AggregateSortByCountResult<TKey>> SortByCount<TResult, TKey>(
223+
this IAggregateFluent<TResult> aggregate,
224+
Expression<Func<TResult, TKey>> id)
225+
{
226+
Ensure.IsNotNull(aggregate, nameof(aggregate));
227+
Ensure.IsNotNull(id, nameof(id));
228+
229+
return aggregate.SortByCount<TKey>(new ExpressionAggregateExpressionDefinition<TResult, TKey>(id, aggregate.Options.TranslationOptions));
230+
}
231+
212232
/// <summary>
213233
/// Appends a descending sort stage to the pipeline.
214234
/// </summary>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/* Copyright 2016 MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Linq.Expressions;
18+
using MongoDB.Bson.Serialization;
19+
using MongoDB.Driver.Core.Misc;
20+
21+
namespace MongoDB.Driver.Linq.Expressions
22+
{
23+
internal sealed class AggregateExpressionExpression : SerializationExpression
24+
{
25+
private readonly Expression _expression;
26+
private readonly IBsonSerializer _serializer;
27+
28+
public AggregateExpressionExpression(Expression expression, IBsonSerializer serializer)
29+
{
30+
_expression = Ensure.IsNotNull(expression, nameof(expression));
31+
_serializer = Ensure.IsNotNull(serializer, nameof(serializer));
32+
}
33+
34+
public Expression Expression
35+
{
36+
get { return _expression; }
37+
}
38+
39+
public override ExtensionExpressionType ExtensionType
40+
{
41+
get { return ExtensionExpressionType.AggregateExpression; }
42+
}
43+
44+
public override IBsonSerializer Serializer
45+
{
46+
get { return _serializer; }
47+
}
48+
49+
public override Type Type
50+
{
51+
get { return _expression.Type; }
52+
}
53+
54+
public override string ToString()
55+
{
56+
return _expression.ToString();
57+
}
58+
59+
public AggregateExpressionExpression Update(Expression expression)
60+
{
61+
if (expression != _expression)
62+
{
63+
return new AggregateExpressionExpression(expression, _serializer);
64+
}
65+
66+
return this;
67+
}
68+
69+
protected internal override Expression Accept(ExtensionExpressionVisitor visitor)
70+
{
71+
return visitor.VisitAggregateExpression(this);
72+
}
73+
}
74+
}

src/MongoDB.Driver/Linq/Expressions/ExtensionExpressionType.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2015 MongoDB Inc.
1+
/* Copyright 2015-2016 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -42,6 +42,7 @@ internal enum ExtensionExpressionType
4242

4343
// Bindings
4444
Accumulator,
45+
AggregateExpression,
4546
ArrayIndex,
4647
Collection,
4748
Document,

src/MongoDB.Driver/Linq/Expressions/ExtensionExpressionVisitor.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2015 MongoDB Inc.
1+
/* Copyright 2015-2016 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -30,6 +30,11 @@ protected internal virtual Expression VisitAccumulator(AccumulatorExpression nod
3030
return node.Update(Visit(node.Argument));
3131
}
3232

33+
protected internal virtual Expression VisitAggregateExpression(AggregateExpressionExpression node)
34+
{
35+
return node.Update(Visit(node.Expression));
36+
}
37+
3338
protected internal virtual Expression VisitArrayIndex(ArrayIndexExpression node)
3439
{
3540
return node.Update(

0 commit comments

Comments
 (0)