Skip to content

Commit 0c26639

Browse files
committed
Fixing negation implementation
Negating "AndAlso" isn't the same as negating an "OrElse". The "OrElse" can use a "$nor" whereas the "AndAlso" needs to have the individual operations use "$not" around their operator expression.
1 parent a66343c commit 0c26639

File tree

2 files changed

+88
-15
lines changed

2 files changed

+88
-15
lines changed

src/MongoFramework/Infrastructure/Querying/ExpressionTranslation.cs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -215,30 +215,30 @@ public static Expression GetMemberSource(Expression expression)
215215
return currentExpression;
216216
}
217217

218-
public static BsonDocument TranslateConditional(Expression expression)
218+
public static BsonDocument TranslateConditional(Expression expression, bool negated = false)
219219
{
220220
var localExpression = UnwrapLambda(expression);
221221

222-
static void UnwrapBinaryQuery(BsonArray target, ExpressionType expressionType, BinaryExpression expression)
222+
static void UnwrapBinaryQuery(BsonArray target, ExpressionType expressionType, BinaryExpression expression, bool negated)
223223
{
224224
if (expression.Left.NodeType == expressionType)
225225
{
226-
UnwrapBinaryQuery(target, expressionType, expression.Left as BinaryExpression);
226+
UnwrapBinaryQuery(target, expressionType, expression.Left as BinaryExpression, negated);
227227
}
228228
else
229229
{
230-
target.Add(TranslateSubExpression(expression.Left));
230+
target.Add(TranslateConditional(expression.Left, negated));
231231
}
232232

233-
target.Add(TranslateSubExpression(expression.Right));
233+
target.Add(TranslateConditional(expression.Right, negated));
234234
}
235235

236236
if (localExpression is BinaryExpression binaryExpression)
237237
{
238238
if (localExpression.NodeType == ExpressionType.AndAlso)
239239
{
240240
var unwrappedQuery = new BsonArray();
241-
UnwrapBinaryQuery(unwrappedQuery, ExpressionType.AndAlso, binaryExpression);
241+
UnwrapBinaryQuery(unwrappedQuery, ExpressionType.AndAlso, binaryExpression, negated);
242242

243243
var elements = new BsonElement[unwrappedQuery.Count];
244244

@@ -252,7 +252,7 @@ static void UnwrapBinaryQuery(BsonArray target, ExpressionType expressionType, B
252252
else if (localExpression.NodeType == ExpressionType.OrElse)
253253
{
254254
var unwrappedQuery = new BsonArray();
255-
UnwrapBinaryQuery(unwrappedQuery, ExpressionType.OrElse, binaryExpression);
255+
UnwrapBinaryQuery(unwrappedQuery, ExpressionType.OrElse, binaryExpression, negated);
256256
return new BsonDocument
257257
{
258258
{ "$or", unwrappedQuery }
@@ -289,25 +289,34 @@ static void UnwrapBinaryQuery(BsonArray target, ExpressionType expressionType, B
289289

290290
var expressionOperator = ComparatorToStringMap[expressionType];
291291
var valueComparison = new BsonDocument { { expressionOperator, value } };
292+
293+
if (negated)
294+
{
295+
valueComparison = new BsonDocument
296+
{
297+
{ "$not", valueComparison }
298+
};
299+
}
300+
292301
return new BsonDocument { { fieldName, valueComparison } };
293302
}
294303
}
295304
else if (localExpression is UnaryExpression unaryExpression && unaryExpression.NodeType == ExpressionType.Not)
296305
{
297-
string operatorName;
298306
if (unaryExpression.Operand.NodeType == ExpressionType.OrElse)
299307
{
300-
operatorName = "$nor";
308+
var translatedInnerExpression = TranslateConditional(unaryExpression.Operand, false);
309+
var valueItems = translatedInnerExpression.GetElement("$or").Value;
310+
311+
return new BsonDocument
312+
{
313+
{ "$nor", valueItems }
314+
};
301315
}
302316
else
303317
{
304-
operatorName = "$not";
318+
return TranslateConditional(unaryExpression.Operand, !negated);
305319
}
306-
307-
return new BsonDocument
308-
{
309-
{ operatorName, TranslateConditional(unaryExpression.Operand) }
310-
};
311320
}
312321

313322
throw new ArgumentException($"Unexpected node type {expression.NodeType} for a conditional statement");

tests/MongoFramework.Tests/Infrastructure/Querying/ExpressionTranslationTests.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,69 @@ public void TranslateConditional_GreaterThanOrEqual()
8585
};
8686
Assert.AreEqual(expected, result);
8787
}
88+
89+
[TestMethod]
90+
public void TranslateConditional_AndAlso()
91+
{
92+
var expression = GetConditional(e => e.Id == "" && e.SingleNumber >= 5);
93+
var result = ExpressionTranslation.TranslateConditional(expression);
94+
var expected = new BsonDocument
95+
{
96+
{ "Id", new BsonDocument { { "$eq", "" } } },
97+
{ "SingleNumber", new BsonDocument { { "$gte", 5 } } }
98+
};
99+
Assert.AreEqual(expected, result);
100+
}
101+
102+
[TestMethod]
103+
public void TranslateConditional_OrElse()
104+
{
105+
var expression = GetConditional(e => e.Id == "" || e.SingleNumber >= 5);
106+
var result = ExpressionTranslation.TranslateConditional(expression);
107+
var expected = new BsonDocument
108+
{
109+
{
110+
"$or",
111+
new BsonArray
112+
{
113+
new BsonDocument { { "Id", new BsonDocument { { "$eq", "" } } } },
114+
new BsonDocument { { "SingleNumber", new BsonDocument { { "$gte", 5 } } } }
115+
}
116+
}
117+
};
118+
Assert.AreEqual(expected, result);
119+
}
120+
121+
[TestMethod]
122+
public void TranslateConditional_Not_AndAlso()
123+
{
124+
var expression = GetConditional(e => !(e.Id == "" && e.SingleNumber >= 5));
125+
var result = ExpressionTranslation.TranslateConditional(expression);
126+
var expected = new BsonDocument
127+
{
128+
{ "Id", new BsonDocument { { "$not", new BsonDocument { { "$eq", "" } } } } },
129+
{ "SingleNumber", new BsonDocument { { "$not", new BsonDocument { { "$gte", 5 } } } } }
130+
};
131+
Assert.AreEqual(expected, result);
132+
}
133+
134+
[TestMethod]
135+
public void TranslateConditional_Not_OrElse()
136+
{
137+
var expression = GetConditional(e => !(e.Id == "" || e.SingleNumber >= 5));
138+
var result = ExpressionTranslation.TranslateConditional(expression);
139+
var expected = new BsonDocument
140+
{
141+
{
142+
"$nor",
143+
new BsonArray
144+
{
145+
new BsonDocument { { "Id", new BsonDocument { { "$eq", "" } } } },
146+
new BsonDocument { { "SingleNumber", new BsonDocument { { "$gte", 5 } } } }
147+
}
148+
}
149+
};
150+
Assert.AreEqual(expected, result);
151+
}
88152
}
89153
}

0 commit comments

Comments
 (0)