Skip to content

Conversation

@rstam
Copy link
Contributor

@rstam rstam commented Oct 28, 2025

No description provided.

@rstam rstam requested a review from a team as a code owner October 28, 2025 12:34
@rstam rstam added the improvement Optimizations or refactoring (no new features or fixes). label Oct 28, 2025
{
var leftExpression = Visit(node.Left);
if (leftExpression is ConstantExpression constantLeftExpression )
if (node.NodeType == ExpressionType.AndAlso)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally we like to do simplifications in the AstSimplifier but the existing simplifications for && and || were already being handled here.

VisitBinary is essentially the same as before but modified to use the new IsConstant helper method.

{
// true ? A : B => A
// false ? A : B => B
return testValue ? Visit(node.IfTrue) : Visit(node.IfFalse);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was an existing simplification.

(d => new R<bool> { N = d.Id, V = d.I1 == E.E1 ? true : false }, "{ $project : { N : '$_id', V : { $eq : ['$I1', 1] }, _id : 0 } }", new[] { true, false }),
(d => new R<bool> { N = d.Id, V = d.S1 == E.E1 ? true : false }, "{ $project : { N : '$_id', V : { $eq : ['$S1', 'E1'] }, _id : 0 } }", new[] { true, false }),
(d => new R<bool> { N = d.Id, V = E.E1 == d.I1 ? true : false }, "{ $project : { N : '$_id', V : { $eq : [1, '$I1'] }, _id : 0 } }", new[] { true, false }),
(d => new R<bool> { N = d.Id, V = E.E1 == d.S1 ? true : false }, "{ $project : { N : '$_id', V : { $eq : ['E1', '$S1'] }, _id : 0 } }", new[] { true, false })
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only existing test affected by these new simplifications.

@BorisDog BorisDog requested a review from Copilot October 28, 2025 20:22
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements boolean expression simplifications for MongoDB LINQ queries to generate more efficient aggregation pipelines. The changes optimize boolean logic patterns (e.g., x ? true : falsex, !!xx) in both Select and Where clauses.

Key changes:

  • Enhanced PartialEvaluator to simplify boolean expressions including conditional expressions, logical operators, and double negations
  • Updated expected MongoDB aggregation stages in existing tests to reflect simplified outputs
  • Added comprehensive test coverage for 23 different boolean simplification patterns

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 56 comments.

File Description
src/MongoDB.Driver/Linq/Linq3Implementation/Misc/PartialEvaluator.cs Expanded expression visitor with boolean simplification logic for conditionals, binary operations (AndAlso/OrElse), and unary operations (double negation)
tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5628Tests.cs New test file with comprehensive test cases validating boolean simplifications in Select and Where clauses
tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4337Tests.cs Updated test expectations to match simplified pipeline stages (removed redundant $cond expressions)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

var collection = Fixture.Collection;

// see: https://codeql.github.com/codeql-query-help/csharp/cs-simplifiable-boolean-expression/#recommendation
// not all simplifciations listed there are safe for a database (because of possibly missing fields or tri-valued logic)
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'simplifciations' to 'simplifications'.

Suggested change
// not all simplifciations listed there are safe for a database (because of possibly missing fields or tri-valued logic)
// not all simplifications listed there are safe for a database (because of possibly missing fields or tri-valued logic)

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

{
if (node.NodeType == ExpressionType.AndAlso)
var leftExpression = node.Left;
var rightExpression = node.Right;
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra space between '=' and 'node.Right'. Should be single space for consistency.

Suggested change
var rightExpression = node.Right;
var rightExpression = node.Right;

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


if (node.NodeType == ExpressionType.OrElse)
var ifTrue = Visit(node.IfTrue);
var ifFalse = Visit(node.IfFalse);
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra space between '=' and 'Visit'. Should be single space for consistency.

Suggested change
var ifFalse = Visit(node.IfFalse);
var ifFalse = Visit(node.IfFalse);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 158 to 167
? Visit(Expression.Or(test, ifFalse))
: Visit(Expression.And(Expression.Not(test), ifFalse));
}
else if (IsConstant<bool>(ifFalse, out ifFalseValue))
{
// T ? P : true => !T || P
// T ? P : false => T && P
return ifFalseValue
? Visit(Expression.Or(Expression.Not(test), ifTrue))
: Visit(Expression.And(test, ifTrue));
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Expression.Or and Expression.And instead of Expression.OrElse and Expression.AndAlso may cause issues. These methods create bitwise operations for integral types, not short-circuit logical operations. Use Expression.OrElse and Expression.AndAlso for boolean logic to ensure proper short-circuit evaluation.

Suggested change
? Visit(Expression.Or(test, ifFalse))
: Visit(Expression.And(Expression.Not(test), ifFalse));
}
else if (IsConstant<bool>(ifFalse, out ifFalseValue))
{
// T ? P : true => !T || P
// T ? P : false => T && P
return ifFalseValue
? Visit(Expression.Or(Expression.Not(test), ifTrue))
: Visit(Expression.And(test, ifTrue));
? Visit(Expression.OrElse(test, ifFalse))
: Visit(Expression.AndAlso(Expression.Not(test), ifFalse));
}
else if (IsConstant<bool>(ifFalse, out ifFalseValue))
{
// T ? P : true => !T || P
// T ? P : false => T && P
return ifFalseValue
? Visit(Expression.OrElse(Expression.Not(test), ifTrue))
: Visit(Expression.AndAlso(test, ifTrue));

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 158 to 167
? Visit(Expression.Or(test, ifFalse))
: Visit(Expression.And(Expression.Not(test), ifFalse));
}
else if (IsConstant<bool>(ifFalse, out ifFalseValue))
{
// T ? P : true => !T || P
// T ? P : false => T && P
return ifFalseValue
? Visit(Expression.Or(Expression.Not(test), ifTrue))
: Visit(Expression.And(test, ifTrue));
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Expression.Or and Expression.And instead of Expression.OrElse and Expression.AndAlso may cause issues. These methods create bitwise operations for integral types, not short-circuit logical operations. Use Expression.OrElse and Expression.AndAlso for boolean logic to ensure proper short-circuit evaluation.

Suggested change
? Visit(Expression.Or(test, ifFalse))
: Visit(Expression.And(Expression.Not(test), ifFalse));
}
else if (IsConstant<bool>(ifFalse, out ifFalseValue))
{
// T ? P : true => !T || P
// T ? P : false => T && P
return ifFalseValue
? Visit(Expression.Or(Expression.Not(test), ifTrue))
: Visit(Expression.And(test, ifTrue));
? Visit(Expression.OrElse(test, ifFalse))
: Visit(Expression.AndAlso(Expression.Not(test), ifFalse));
}
else if (IsConstant<bool>(ifFalse, out ifFalseValue))
{
// T ? P : true => !T || P
// T ? P : false => T && P
return ifFalseValue
? Visit(Expression.OrElse(Expression.Not(test), ifTrue))
: Visit(Expression.AndAlso(test, ifTrue));

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

17 => collection.AsQueryable().Where(x => x.P ? false : false),

18 => collection.AsQueryable().Where(x => !(x.X == x.Y)),
19 => collection.AsQueryable().Where(x => !(x.X != x.Y)),
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression '!(A != B)' can be simplified to 'A == B'.

Suggested change
19 => collection.AsQueryable().Where(x => !(x.X != x.Y)),
19 => collection.AsQueryable().Where(x => x.X == x.Y),

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't do because the point is to test that the LINQ translator does this simplification.


18 => collection.AsQueryable().Where(x => !(x.X == x.Y)),
19 => collection.AsQueryable().Where(x => !(x.X != x.Y)),
20 => collection.AsQueryable().Where(x => !(x.X < x.Y)), // not safe
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression '!(A < B)' can be simplified to 'A >= B'.

Suggested change
20 => collection.AsQueryable().Where(x => !(x.X < x.Y)), // not safe
20 => collection.AsQueryable().Where(x => x.X >= x.Y), // not safe

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't do because the point is to test that the LINQ translator does NOT do this simplification.

18 => collection.AsQueryable().Where(x => !(x.X == x.Y)),
19 => collection.AsQueryable().Where(x => !(x.X != x.Y)),
20 => collection.AsQueryable().Where(x => !(x.X < x.Y)), // not safe
21 => collection.AsQueryable().Where(x => !(x.X > x.Y)), // not safe
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression '!(A > B)' can be simplified to 'A <= B'.

Suggested change
21 => collection.AsQueryable().Where(x => !(x.X > x.Y)), // not safe
21 => collection.AsQueryable().Where(x => x.X <= x.Y), // not safe

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't do because the point is to test that the LINQ translator does NOT do this simplification.

19 => collection.AsQueryable().Where(x => !(x.X != x.Y)),
20 => collection.AsQueryable().Where(x => !(x.X < x.Y)), // not safe
21 => collection.AsQueryable().Where(x => !(x.X > x.Y)), // not safe
22 => collection.AsQueryable().Where(x => !(x.X <= x.Y)), // not safe
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression '!(A <= B)' can be simplified to 'A > B'.

Suggested change
22 => collection.AsQueryable().Where(x => !(x.X <= x.Y)), // not safe
22 => collection.AsQueryable().Where(x => x.X > x.Y), // not safe

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't do because the point is to test that the LINQ translator does NOT do this simplification.

20 => collection.AsQueryable().Where(x => !(x.X < x.Y)), // not safe
21 => collection.AsQueryable().Where(x => !(x.X > x.Y)), // not safe
22 => collection.AsQueryable().Where(x => !(x.X <= x.Y)), // not safe
23 => collection.AsQueryable().Where(x => !(x.X >= x.Y)), // not safe
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression '!(A >= B)' can be simplified to 'A < B'.

Suggested change
23 => collection.AsQueryable().Where(x => !(x.X >= x.Y)), // not safe
23 => collection.AsQueryable().Where(x => x.X < x.Y), // not safe

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't do because the point is to test that the LINQ translator does NOT do this simplification.

Copy link
Contributor

@adelinowona adelinowona left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Optimizations or refactoring (no new features or fixes).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants