Skip to content

Conversation

rstam
Copy link
Contributor

@rstam rstam commented Oct 22, 2025

SelectMany expects the selector to return an enumerable value.

Dictionary is a special case of an enumerable value, and we have to treat it specially.

@rstam rstam requested a review from a team as a code owner October 22, 2025 20:00
public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo)
{
if (_dictionaryRepresentation != DictionaryRepresentation.ArrayOfDocuments)
if (_dictionaryRepresentation is DictionaryRepresentation.ArrayOfArrays or DictionaryRepresentation.ArrayOfDocuments)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We were sort of working with ArrayOfDocuments representation, but this adds support for ArrayOfArrays.

var ienumerableSerializer = ArraySerializerHelper.CreateSerializer(keyValuePairSerializer);

return new TranslatedExpression(aggregateExpression.Expression, ast, enumerableSerializer);
aggregateExpression = new TranslatedExpression(expression, ast, ienumerableSerializer);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both of the above cases share this in common: they are both cases where an enumerable value has the values represented in some unusual way other than as a BsonArray.

The first case corresponds to IGrouping<TKey, TElement> where the elements are wrapped one level deeper in an _elements field.

The second is new in this PR, and corresponds to a Dictionary<TKey, TValue> with the Document representation. In order to use this dictionary as an enumerable of KeyValuePair<TKey, TValue> we have to insert a call to $objectToArray.

var selectorParameter = selectorLambda.Parameters.Single();
var selectorParameterSymbol = context.CreateSymbol(selectorParameter, sourceSerializer, isCurrent: true);
var selectorContext = context.WithSymbol(selectorParameterSymbol);
var selectorTranslation = ExpressionToAggregationExpressionTranslator.TranslateEnumerable(selectorContext, selectorLambda.Body);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Need to call TranslateEnumerable instead of Translate.

@rstam rstam changed the title CSHARP-1913: Support top-level SelectMany with Dictionary CSHARP-1913: Support using Dictionary fields as IEnumerable<KeyValuePair<TKey, TValue>> Oct 23, 2025
AssertStages(
stages,
"{ $match : { Name : 'TestName' } }",
"{ $project : { _v : { $reduce : { input : { $map : { input : '$DictionaryWithArrayOfArraysRepresentation', as : 'kvp', in : ['$$kvp'] } }, initialValue : [], in : { $concatArrays : ['$$value', '$$this'] } } }, _id : 0 } }");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would have liked to use kvp.Key or kvp.Value in the test test but that is not (yet) supported when the KeyValuePair is represented as an array. So I just used the entire KeyValuePair.

Copy link
Contributor

Choose a reason for hiding this comment

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

I have getting the Key or Value of a KeyValuePair represented as an array supported in my work for full support of dictionary representations. So I'll probably update this test when I rebase on this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed in our meeting, we can remove this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this test serves a useful purpose in proving that this PR also fixes 4251 but at your request I will remove it.

Comment on lines +181 to +182


Copy link
Contributor

Choose a reason for hiding this comment

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

remove extra spacing here.

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.

AssertStages(
stages,
"{ $match : { Name : 'TestName' } }",
"{ $project : { _v : { $reduce : { input : { $map : { input : '$DictionaryWithArrayOfArraysRepresentation', as : 'kvp', in : ['$$kvp'] } }, initialValue : [], in : { $concatArrays : ['$$value', '$$this'] } } }, _id : 0 } }");
Copy link
Contributor

Choose a reason for hiding this comment

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

I have getting the Key or Value of a KeyValuePair represented as an array supported in my work for full support of dictionary representations. So I'll probably update this test when I rebase on this PR.

@rstam rstam requested a review from adelinowona October 23, 2025 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants