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
Contributor

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.

adelinowona
adelinowona previously approved these changes Oct 30, 2025
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

sanych-sun
sanych-sun previously approved these changes Nov 10, 2025
Copy link
Member

@sanych-sun sanych-sun left a comment

Choose a reason for hiding this comment

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

LGTM + Minor comments. Also should we unit-test the new translations done by the PartialEvaluator?

{
// true && Q => Q
// false && Q => false
return leftValue ? Visit(rightExpression) : Expression.Constant(false);
Copy link
Member

Choose a reason for hiding this comment

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

Minor: can save a little on allocation probably by returning leftExpression instead of Expression.Constant(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.

Using leftExpression is a bit subtle. It requires understanding why that works.

Instead I added __falseConstantExpression and __trueConstantExpression fields that can be used for both left and right sides.

{
// P && true => P
// P && false => false
return rightValue ? leftExpression : Expression.Constant(false);
Copy link
Member

Choose a reason for hiding this comment

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

Minor: same as above can return rightExpression instead of Expression.Constant(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.

Used __falseConstantExpression and __trueConstantExpression instead.

{
// true || Q => true
// false || Q => Q
return leftValue ? Expression.Constant(true) : Visit(rightExpression);
Copy link
Member

Choose a reason for hiding this comment

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

Same as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Used __falseConstantExpression and __trueConstantExpression instead.

{
// P || true => true
// P || false => P
return rightValue ? Expression.Constant(true) : leftExpression;
Copy link
Member

Choose a reason for hiding this comment

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

Same as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Used __falseConstantExpression and __trueConstantExpression instead.

@rstam rstam dismissed stale reviews from sanych-sun and adelinowona via e29abd4 November 10, 2025 21:27
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.

3 participants