CSHARP-5750: Add support for new QE prefix/substring/suffix aggregation expression operators#2003
CSHARP-5750: Add support for new QE prefix/substring/suffix aggregation expression operators#2003papafe wants to merge 8 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds LINQ3 + filter builder support for Queryable Encryption (QE) encrypted-string aggregation expression operators ($encStrStartsWith, $encStrContains, $encStrEndsWith, $encStrNormalizedEq) so they can be emitted from driver APIs and rendered into correct aggregation AST / $expr filters.
Changes:
- Introduces a new LINQ3 AST expression (
AstEncStrExpression) and operator enum (AstEncStrOperator) with rendering support. - Adds LINQ3 translation + serializer deduction for the new
Mql.EncStr*methods. - Adds
FilterDefinitionBuilderhelpers (EncStr*) that render$exprfilters, plus unit tests for both LINQ and filter builder output.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/EncStrMethodToAggregationExpressionTranslatorTests.cs | New unit tests asserting correct AST for Mql.EncStr* translations. |
| tests/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs | Adds tests for new FilterDefinitionBuilder.EncStr* helpers rendering $expr + $encStr*. |
| src/MongoDB.Driver/Mql.cs | Adds public Mql.EncStr* extension-style methods for LINQ translation. |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/EncStrMethodToAggregationExpressionTranslator.cs | New translator mapping Mql.EncStr* calls to an AstEncStrExpression. |
| src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs | Routes EncStr* method calls to the new translator. |
| src/MongoDB.Driver/Linq/Linq3Implementation/SerializerFinders/SerializerFinderVisitMethodCall.cs | Deduces serializers for EncStr* method calls (inputs and boolean return). |
| src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs | Registers Mql.EncStr* method infos and overload set for translation/deduction. |
| src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs | Adds visitor support for traversing AstEncStrExpression. |
| src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs | Adds factory method EncStrExpression(...). |
| src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstEncStrOperator.cs | New enum + rendering helpers for $encStr* operator names and value-arg key. |
| src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstEncStrExpression.cs | New AST node that renders { $encStrX: { input, <substring/prefix/...>: value } }. |
| src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstNodeType.cs | Adds EncStrExpression node type. |
| src/MongoDB.Driver/FilterDefinitionBuilder.cs | Adds FilterDefinitionBuilder.EncStr* helpers and internal $expr-rendering filter definition. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (method.IsOneOf(MqlMethod.EncStrMethodOverloads)) | ||
| { | ||
| var inputTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]); | ||
| var valueTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[1]); | ||
|
|
||
| var @operator = method.Name switch | ||
| { | ||
| "EncStrContains" => AstEncStrOperator.Contains, | ||
| "EncStrEndsWith" => AstEncStrOperator.EndsWith, | ||
| "EncStrNormalizedEq" => AstEncStrOperator.NormalizedEq, | ||
| "EncStrStartsWith" => AstEncStrOperator.StartsWith, | ||
| _ => throw new InvalidOperationException($"Unexpected method: {method.Name}") | ||
| }; | ||
|
|
||
| var ast = AstExpression.EncStrExpression(@operator, inputTranslation.Ast, valueTranslation.Ast); | ||
| return new TranslatedExpression(expression, ast, BooleanSerializer.Instance); |
|
|
||
| void DeduceMethodCallSerializers() | ||
| { | ||
| switch (node.Method.Name) |
There was a problem hiding this comment.
This switch seems dubious to me since it only matches by method name, and hence might be operating on some arbitrary method which has the same name as the one we expect, but is not the method we expect.
There was a problem hiding this comment.
It's true that here we just check by name, but then inside the various Deduce... methods we check if it's the correct one with method.IsOneOf(MqlMethod.EncStrMethodOverloads) for instance.
There was a problem hiding this comment.
Yep, we have PR to switch on MethodInfo instead, which is blocked for now, but I hope will be unblocked and merged soon.
|
|
||
| namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; | ||
|
|
||
| internal sealed class AstEncStrExpression : AstExpression |
There was a problem hiding this comment.
Consider using expression bodies for methods.
|
|
||
| namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators; | ||
|
|
||
| public class EncStrMethodToAggregationExpressionTranslatorTests |
There was a problem hiding this comment.
Looks like we have no end-to-end testing for these operators.
There was a problem hiding this comment.
We do not. Actually in general the only end-to-end tests for QE are the ones coming from specs, both prose and unified tests.
There was a problem hiding this comment.
I think we should have some, at least for happy-path.
There was a problem hiding this comment.
I feel we need to add end-to-end testing here. This is something I feel we need to do more of in general.
| /// <param name="representaion">The representation.</param> | ||
| /// <typeparam name="TValue">The type of the value.</typeparam> | ||
| /// <returns>The value</returns> | ||
| public static TValue Constant<TValue>(TValue value, BsonType representaion) |
There was a problem hiding this comment.
Unfortunately this is in the public API, so we should fix it for 4.0
| var valueExpression = arguments[1]; | ||
| if (IsNotKnown(inputExpression)) | ||
| { | ||
| AddNodeSerializer(inputExpression, StringSerializer.Instance); |
There was a problem hiding this comment.
Is the string serializer correct here? Could these be encrypted fields that require something else? Do we have tests for this?
There was a problem hiding this comment.
Yes it should be, QE-encrypted values must be strings.
Regarding tests... It depends what you mean?
| var inputTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]); | ||
| var valueTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[1]); | ||
| if (valueTranslation.Serializer is IRepresentationConfigurable representationConfigurable && | ||
| representationConfigurable.Representation != BsonType.String) |
There was a problem hiding this comment.
As far as I understood (but do not take it as a true without validating, I need to learn more about encryption in MongoDB) encrypted data could be stored either as base64 string or as BinaryData. Should we support BinaryData representation here as well?
|
|
||
| if (method.IsOneOf(MqlMethod.EncStrMethodOverloads)) | ||
| { | ||
| var inputTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]); |
There was a problem hiding this comment.
Do we need to validate representation for the input parameter too?
…on expression operators
…odies, serializer tests - Reject non-string value representation in EncStr translator (with negative test) - Use expression bodies in AstEncStrExpression - Add EncStr cases to SerializerFinder MqlTests
No description provided.