Skip to content

Conversation

@adelinowona
Copy link
Contributor

No description provided.

@adelinowona adelinowona requested a review from a team as a code owner October 31, 2025 00:48
@adelinowona adelinowona added the feature Adds new user-facing functionality. label Oct 31, 2025
Copy link
Contributor

@rstam rstam left a comment

Choose a reason for hiding this comment

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

Note: I've only reviewed the tests, where I have a lot of suggestions about possible MQL for things you didn't implement or "better" MQL in some cases for things you did implement.

Please take all suggestions with a grain of salt. You may have reasons for thinking that what I think is "better" MQL might not actually be better.

I am postponing reviewing the implementation because if you accept many of the MQL suggestions you will need to make many changes to the implementation.

I will review the implementation after you have accepted/rejected the MQL suggestions.


namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira
{
public class CSharp2509Tests : LinqIntegrationTest<CSharp2509Tests.ClassFixture>
Copy link
Contributor

Choose a reason for hiding this comment

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

I was surprised that these tests were deleted, but Adelin says they duplicate new tests he added in CSharp4443Tests.cs.

We don't usually remove tests... why bother?

Copy link
Contributor

Choose a reason for hiding this comment

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

Note to other reviewers: we have to manually verify that we haven't lost any test coverage as a result of deleting these tests.

Copilot AI review requested due to automatic review settings November 26, 2025 19:46
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 extends LINQ support for dictionary operations across all three BSON representations (Document, ArrayOfDocuments, and ArrayOfArrays). Previously, some operations like ContainsKey, ContainsValue, indexer access, and property access (Count, Keys, Values) only worked with the Document representation.

Key Changes:

  • Extended ContainsKey and ContainsValue to support ArrayOfDocuments and ArrayOfArrays representations
  • Added support for dictionary indexer access across all representations
  • Implemented dictionary property access (Count, Keys, Values) for all representations
  • Added support for KeyValuePair property access in array representation
  • Removed obsolete test files that tested the previous limited functionality

Reviewed changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
CSharp4813Tests.cs Removed dictionary-specific test methods that tested unsupported scenarios
CSharp4557Tests.cs Deleted entire test file for ContainsKey with Document representation only
CSharp2509Tests.cs Deleted entire test file for ContainsValue with limited representation support
MemberExpressionToFilterFieldTranslator.cs Added KeyValuePair member translation and validation for Dictionary collections
ContainsValueMethodToFilterTranslator.cs Extended to support ArrayOfArrays representation
ContainsMethodToFilterTranslator.cs Added dictionary Keys/Values Contains support
ContainsKeyMethodToFilterTranslator.cs Extended to support ArrayOfDocuments and ArrayOfArrays representations
AllOrAnyMethodToFilterTranslator.cs Added validation to reject All/Any on Document representation dictionaries
GetItemMethodToAggregationExpressionTranslator.cs Implemented indexer access for all dictionary representations
ContainsValueMethodToAggregationExpressionTranslator.cs Refactored to extract reusable translation method
ContainsMethodToAggregationExpressionTranslator.cs Added dictionary Keys/Values Contains support in aggregation expressions
ContainsKeyMethodToAggregationExpressionTranslator.cs Extended to support all dictionary representations
MemberExpressionToAggregationExpressionTranslator.cs Added comprehensive dictionary and KeyValuePair property translation
AstExpression.cs Added ArrayToObject helper method
KeyValuePairSerializer.cs Added IKeyValuePairSerializerV2 interface with key/value serializer accessors

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved the tests on Keys and Values properties of Dictionaries into this file since I think the ticket csharp5779 should have tested them.

@adelinowona adelinowona requested a review from rstam November 26, 2025 20:32
Copy link
Contributor

@rstam rstam left a comment

Choose a reason for hiding this comment

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

Overall looking great. I would characterize most of my comments or requested changes as minor.

I haven't had time to review the tests yet, but wanted to give you the feedback I have so far.

Good work!

else
{
ast = AstExpression.Avg(sourceTranslation.Ast);
var sourceItemSerializer = ArraySerializerHelper.GetItemSerializer(sourceTranslation.Serializer);
Copy link
Contributor

Choose a reason for hiding this comment

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

There is similar code in the translators for Max, Min and Sum.

Is there any way to share the code?

No problem if not.

Expression valueExpression,
out TranslatedExpression translation)
{
translation = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as above about assigning null to out parameter at top of method.


if (!declaringType.IsGenericType ||
(declaringType.GetGenericTypeDefinition() != typeof(Dictionary<,>) &&
declaringType.GetGenericTypeDefinition() != typeof(IDictionary<,>)))
Copy link
Contributor

Choose a reason for hiding this comment

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

110-112 might be simpler like this:

if (!declaringType.ImplementsDictionaryInterface(out var keyType, out var valueType))

also: test with IReadOnlyDictionary


private static bool TryTranslateKeyValuePairProperty(MemberExpression expression, TranslatedExpression container, MemberInfo memberInfo, out TranslatedExpression result)
{
result = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like assigning null to out variables at the beginning of the method.

I realize it makes it more convenient to return false, but it also disables the compiler warning you get if you try to return true without setting an out parameter.

Your call.


if (container.Expression.Type.IsGenericType &&
container.Expression.Type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>) &&
container.Serializer is IKeyValuePairSerializerV2 { Representation: BsonType.Array } kvpSerializer)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we ignoring Document representation here?


if (fieldTranslation.Serializer is IBsonDictionarySerializer { DictionaryRepresentation: DictionaryRepresentation.Document })
{
throw new ExpressionNotSupportedException(expression);
Copy link
Contributor

Choose a reason for hiding this comment

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

throw new ExpressionNotSupportedException(expression, because: "dictionary is represented as a document");

Add because?

case DictionaryRepresentation.ArrayOfDocuments:
case DictionaryRepresentation.ArrayOfArrays:
{
var key = GetKeyStringConstant(expression, keyExpression, dictionarySerializer.KeySerializer);
Copy link
Contributor

Choose a reason for hiding this comment

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

If you move setting of key variable before the switch the braces would not be needed.

{
case "Keys":
{
var dictionaryExpression = memberExpression.Expression;
Copy link
Contributor

Choose a reason for hiding this comment

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

If you move setting of dictionaryExpression variable before the switch the braces would not be needed.


if (!declaringType.IsGenericType ||
(declaringType.GetGenericTypeDefinition() != typeof(Dictionary<,>) &&
declaringType.GetGenericTypeDefinition() != typeof(IDictionary<,>)))
Copy link
Contributor

Choose a reason for hiding this comment

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

This would probably be simpler:

if (!declaringType.ImplementsDictionaryInterface(out _, out _))

and test with IReadOnlyDictionary also?

(memberExpression.Type.GetGenericTypeDefinition() == typeof(Dictionary<,>.KeyCollection) ||
memberExpression.Type.GetGenericTypeDefinition() == typeof(Dictionary<,>.ValueCollection)))
{
throw new ExpressionNotSupportedException(memberExpression);
Copy link
Contributor

Choose a reason for hiding this comment

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

Add because?

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.

2 participants