Skip to content

CSHARP-5750: Add support for new QE prefix/substring/suffix aggregation expression operators#2003

Open
papafe wants to merge 8 commits into
mongodb:mainfrom
papafe:csharp5750
Open

CSHARP-5750: Add support for new QE prefix/substring/suffix aggregation expression operators#2003
papafe wants to merge 8 commits into
mongodb:mainfrom
papafe:csharp5750

Conversation

@papafe

@papafe papafe commented May 19, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@papafe papafe added the feature Adds new user-facing functionality. label May 19, 2026
@papafe papafe marked this pull request as ready for review May 27, 2026 11:13
@papafe papafe requested a review from a team as a code owner May 27, 2026 11:13
@papafe papafe requested review from ajcvickers and Copilot May 27, 2026 11:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 FilterDefinitionBuilder helpers (EncStr*) that render $expr filters, 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.

Comment on lines +32 to +47
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consider using expression bodies for methods.


namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators;

public class EncStrMethodToAggregationExpressionTranslatorTests

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks like we have no end-to-end testing for these operators.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should have some, at least for happy-path.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I feel we need to add end-to-end testing here. This is something I feel we need to do more of in general.

Comment thread src/MongoDB.Driver/Mql.cs
/// <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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Typo

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is the string serializer correct here? Could these be encrypted fields that require something else? Do we have tests for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes it should be, QE-encrypted values must be strings.
Regarding tests... It depends what you mean?

@papafe papafe requested a review from ajcvickers June 2, 2026 16:33
var inputTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]);
var valueTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[1]);
if (valueTranslation.Serializer is IRepresentationConfigurable representationConfigurable &&
representationConfigurable.Representation != BsonType.String)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need to validate representation for the input parameter too?

papafe added 7 commits June 8, 2026 18:17
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Adds new user-facing functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants