-
Notifications
You must be signed in to change notification settings - Fork 1.3k
CSHARP-5785: Optimize LINQ translation for First() and FirstOrDefault() methods with predicates #1817
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CSHARP-5785: Optimize LINQ translation for First() and FirstOrDefault() methods with predicates #1817
Changes from all commits
984874b
d5f7846
e13fd75
8cc0d37
fed12d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using FluentAssertions; | ||
| using MongoDB.Driver.Core.Misc; | ||
| using MongoDB.Driver.Linq; | ||
| using MongoDB.Driver.TestHelpers; | ||
| using Xunit; | ||
|
|
@@ -24,6 +25,8 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira | |
| { | ||
| public class CSharp4048Tests : LinqIntegrationTest<CSharp4048Tests.ClassFixture> | ||
| { | ||
| private static readonly bool FilterLimitIsSupported = Feature.FilterLimit.IsSupported(CoreTestConfiguration.MaxWireVersion); | ||
|
|
||
| public CSharp4048Tests(ClassFixture fixture) | ||
| : base(fixture) | ||
| { | ||
|
|
@@ -118,7 +121,7 @@ public void List_get_Item_of_scalar_should_work() | |
| var expectedStages = new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push: '$X' } } }", | ||
| "{ $project : { _id : '$_id' Result : { $arrayElemAt : ['$_elements', 0] } } }", | ||
| "{ $project : { _id : '$_id', Result : { $arrayElemAt : ['$_elements', 0] } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| }; | ||
| AssertStages(stages, expectedStages); | ||
|
|
@@ -1040,12 +1043,21 @@ public void IGrouping_First_with_predicate_of_root_should_work() | |
| .OrderBy(x => x.Id); | ||
|
|
||
| var stages = Translate(collection, queryable); | ||
| var expectedStages = new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$$ROOT' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $arrayElemAt : [{ $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e.X', 1] } } }, 0] } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| }; | ||
|
|
||
| var expectedStages = FilterLimitIsSupported | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I almost find the longer version more readable: because I don't have to chase down how In some other files you still use the long form.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rather keep the In some other files. there is only one use case, which is why I just used the longer form directly. |
||
| ? new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$$ROOT' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $arrayElemAt : [{ $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e.X', 1] }, limit : 1 } }, 0] } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| } | ||
| : new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$$ROOT' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $arrayElemAt : [{ $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e.X', 1] } } }, 0] } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| }; | ||
|
|
||
| AssertStages(stages, expectedStages); | ||
|
|
||
| var results = queryable.ToList(); | ||
|
|
@@ -1065,12 +1077,21 @@ public void IGrouping_First_with_predicate_of_scalar_should_work() | |
| .OrderBy(x => x.Id); | ||
|
|
||
| var stages = Translate(collection, queryable); | ||
| var expectedStages = new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $arrayElemAt : [{ $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e', 1] } } }, 0] } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| }; | ||
|
|
||
| var expectedStages = FilterLimitIsSupported | ||
| ? new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $arrayElemAt : [{ $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e', 1] }, limit : 1 } }, 0] } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| } | ||
| : new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $arrayElemAt : [{ $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e', 1] } } }, 0] } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| }; | ||
|
|
||
| AssertStages(stages, expectedStages); | ||
|
|
||
| var results = queryable.ToList(); | ||
|
|
@@ -1140,12 +1161,21 @@ public void IGrouping_FirstOrDefault_with_predicate_of_root_should_work() | |
| .OrderBy(x => x.Id); | ||
|
|
||
| var stages = Translate(collection, queryable); | ||
| var expectedStages = new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$$ROOT' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $let : { vars : { values : { $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e.X', 1] } } } }, in : { $cond : { if : { $eq : [{ $size : '$$values' }, 0] }, then : null, else : { $arrayElemAt : ['$$values', 0] } } } } } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| }; | ||
|
|
||
| var expectedStages = FilterLimitIsSupported | ||
| ? new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$$ROOT' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $let : { vars : { values : { $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e.X', 1] }, limit : 1 } } }, in : { $cond : { if : { $eq : [{ $size : '$$values' }, 0] }, then : null, else : { $arrayElemAt : ['$$values', 0] } } } } } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| } | ||
| : new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$$ROOT' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $let : { vars : { values : { $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e.X', 1] } } } }, in : { $cond : { if : { $eq : [{ $size : '$$values' }, 0] }, then : null, else : { $arrayElemAt : ['$$values', 0] } } } } } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| }; | ||
|
|
||
| AssertStages(stages, expectedStages); | ||
|
|
||
| var results = queryable.ToList(); | ||
|
|
@@ -1166,12 +1196,21 @@ public void IGrouping_FirstOrDefault_with_predicate_of_scalar_should_work() | |
| .OrderBy(x => x.Id); | ||
|
|
||
| var stages = Translate(collection, queryable); | ||
| var expectedStages = new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $let : { vars : { values : { $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e', 1] } } } }, in : { $cond : { if : { $eq : [{ $size : '$$values' }, 0] }, then : 0, else : { $arrayElemAt : ['$$values', 0] } } } } } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| }; | ||
|
|
||
| var expectedStages = FilterLimitIsSupported | ||
| ? new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $let : { vars : { values : { $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e', 1] }, limit : 1 } } }, in : { $cond : { if : { $eq : [{ $size : '$$values' }, 0] }, then : 0, else : { $arrayElemAt : ['$$values', 0] } } } } } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| } | ||
| : new[] | ||
| { | ||
| "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", | ||
| "{ $project : { _id : '$_id', Result : { $let : { vars : { values : { $filter : { input : '$_elements', as : 'e', cond : { $ne : ['$$e', 1] } } } }, in : { $cond : { if : { $eq : [{ $size : '$$values' }, 0] }, then : 0, else : { $arrayElemAt : ['$$values', 0] } } } } } } }", | ||
| "{ $sort : { _id : 1 } }" | ||
| }; | ||
|
|
||
| AssertStages(stages, expectedStages); | ||
|
|
||
| var results = queryable.ToList(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How was this test not failing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea. I just happened to catch it.