Skip to content

Commit 51cf2ff

Browse files
committed
CSHARP-4700: Fix issue with GroupBy followed by OrderBy.
1 parent a9bad55 commit 51cf2ff

File tree

3 files changed

+101
-3
lines changed

3 files changed

+101
-3
lines changed

src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupingPipelineOptimizer.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using System;
1717
using System.Collections;
1818
using System.Collections.Generic;
19+
using System.Linq;
1920
using MongoDB.Bson;
2021
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
2122
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters;
@@ -169,11 +170,20 @@ static bool StageCanBeOptimized(AstStage stage)
169170

170171
static bool IsLastStageThatCanBeOptimized(AstStage stage)
171172
{
172-
return stage.NodeType switch
173+
return stage switch
173174
{
174-
AstNodeType.ProjectStage => true,
175+
AstProjectStage projectStage => !ProjectsRoot(projectStage),
175176
_ => false
176177
};
178+
179+
static bool ProjectsRoot(AstProjectStage projectStage)
180+
{
181+
return projectStage.Specifications.Any(
182+
specification =>
183+
specification is AstProjectStageSetFieldSpecification setFieldSpecification &&
184+
setFieldSpecification.Value is AstVarExpression varExpression &&
185+
varExpression.Name == "ROOT");
186+
}
177187
}
178188
}
179189
}

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/OrderByMethodToPipelineTranslator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ private static AstStage[] CreateSortStages(string methodName, AggregationExpress
223223
{
224224
var projectStage = AstStage.Project(
225225
AstProject.Exclude("_id"),
226-
AstProject.Set("_document", AstExpression.FieldPath("$$ROOT")),
226+
AstProject.Set("_document", AstExpression.Var("ROOT")),
227227
AstProject.Set("_key1", keySelectorTranslation.Ast));
228228
var sortStage = AstStage.Sort(AstSort.Field("_key1", sortOrder));
229229
var replaceRootStage = AstStage.ReplaceRoot(AstExpression.FieldPath("$_document"));
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/* Copyright 2010-present 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.Linq;
17+
using FluentAssertions;
18+
using MongoDB.Driver.Linq;
19+
using MongoDB.TestHelpers.XunitExtensions;
20+
using Xunit;
21+
22+
namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira
23+
{
24+
public class CSharp4700Tests : Linq3IntegrationTest
25+
{
26+
[Theory]
27+
[ParameterAttributeData]
28+
public void OrderBy_Count_should_work(
29+
[Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider)
30+
{
31+
var collection = GetCollection(linqProvider);
32+
33+
var queryable = collection.AsQueryable()
34+
.GroupBy(x => x.Name)
35+
.OrderBy(x => x.Count());
36+
37+
var stages = Translate(collection, queryable);
38+
var results = queryable.ToList();
39+
40+
if (linqProvider == LinqProvider.V2)
41+
{
42+
AssertStages(
43+
stages,
44+
"{ $group : { _id : '$Name', __agg0 : { $sum : 1 } } }",
45+
"{ $sort : { __agg0 : 1 } }");
46+
47+
results.Should().HaveCount(2);
48+
results[0].Key.Should().Be("Jane");
49+
results[0].Count().Should().Be(0); // this result is incorrect in LINQ2
50+
results[1].Key.Should().Be("John");
51+
results[1].Count().Should().Be(0); // this result is incorrect in LINQ2
52+
}
53+
else
54+
{
55+
AssertStages(
56+
stages,
57+
"{ $group : { _id : '$Name', _elements : { $push : '$$ROOT' } } }",
58+
"{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $size : '$_elements' } } }",
59+
"{ $sort : { _key1 : 1 } }",
60+
"{ $replaceRoot : { newRoot : '$_document' } }");
61+
62+
results.Should().HaveCount(2);
63+
results[0].Key.Should().Be("Jane");
64+
results[0].Count().Should().Be(1);
65+
results[1].Key.Should().Be("John");
66+
results[1].Count().Should().Be(2);
67+
}
68+
}
69+
70+
private IMongoCollection<C> GetCollection(LinqProvider linqProvider)
71+
{
72+
var collection = GetCollection<C>("test", linqProvider);
73+
CreateCollection(
74+
collection,
75+
new C { Id = 1, Name = "John" },
76+
new C { Id = 2, Name = "John" },
77+
new C { Id = 6, Name = "Jane" });
78+
return collection;
79+
}
80+
81+
private class C
82+
{
83+
public int Id { get; set; }
84+
public string Name { get; set; }
85+
}
86+
87+
}
88+
}

0 commit comments

Comments
 (0)