Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,12 @@ obj is DictionarySerializerBase<TDictionary, TKey, TValue> other &&
/// <inheritdoc/>
public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo)
{
if (_dictionaryRepresentation == DictionaryRepresentation.Document && !IsNumericType(typeof(TKey)))
{
serializationInfo = null;
return false;
}

var representation = _dictionaryRepresentation == DictionaryRepresentation.ArrayOfArrays
? BsonType.Array
: BsonType.Document;
Expand All @@ -510,6 +516,14 @@ public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationI
return true;
}

private static bool IsNumericType(Type type)
Comment thread
damieng marked this conversation as resolved.
{
return type == typeof(int) || type == typeof(long) ||
type == typeof(short) || type == typeof(byte) ||
type == typeof(uint) || type == typeof(ulong) ||
type == typeof(ushort) || type == typeof(sbyte);
}

/// <inheritdoc/>
public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Options;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
using MongoDB.Driver.Linq.Linq3Implementation.Serializers;
Expand Down Expand Up @@ -81,7 +83,21 @@ IBsonSerializer CreateCollectionSerializerFromCollectionSerializer(Type collecti
return UnknowableSerializer.Create(collectionType);
}

var itemSerializer = collectionSerializer.GetItemSerializer();
IBsonSerializer itemSerializer;
if (collectionSerializer is IBsonDictionarySerializer dictionarySerializer &&
collectionSerializer is IBsonArraySerializer arraySerializer &&
!arraySerializer.TryGetItemSerializationInfo(out _))
{
var representation = dictionarySerializer.DictionaryRepresentation == DictionaryRepresentation.ArrayOfArrays
? BsonType.Array
: BsonType.Document;
itemSerializer = KeyValuePairSerializer.Create(representation, dictionarySerializer.KeySerializer, dictionarySerializer.ValueSerializer);
}
else
{
itemSerializer = collectionSerializer.GetItemSerializer();
}

return CreateCollectionSerializerFromItemSerializer(collectionType, itemSerializer);
}

Expand Down Expand Up @@ -235,11 +251,22 @@ private void DeduceUnknowableSerializer(Expression node)
private bool IsItemSerializerKnown(Expression node, out IBsonSerializer itemSerializer)
{
if (IsKnown(node, out var nodeSerializer) &&
nodeSerializer is IBsonArraySerializer arraySerializer &&
arraySerializer.TryGetItemSerializationInfo(out var itemSerializationInfo))
nodeSerializer is IBsonArraySerializer arraySerializer)
{
itemSerializer = itemSerializationInfo.Serializer;
return true;
if (arraySerializer.TryGetItemSerializationInfo(out var itemSerializationInfo))
{
itemSerializer = itemSerializationInfo.Serializer;
return true;
}

if (nodeSerializer is IBsonDictionarySerializer dictionarySerializer)
{
var representation = dictionarySerializer.DictionaryRepresentation == DictionaryRepresentation.ArrayOfArrays
? BsonType.Array
: BsonType.Document;
itemSerializer = KeyValuePairSerializer.Create(representation, dictionarySerializer.KeySerializer, dictionarySerializer.ValueSerializer);
return true;
}
}

itemSerializer = null;
Expand Down
162 changes: 162 additions & 0 deletions tests/MongoDB.Driver.Tests/UpdateDefinitionBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Options;
using MongoDB.Bson.TestHelpers;
using MongoDB.Driver.Linq;
using MongoDB.TestHelpers.XunitExtensions;
Expand Down Expand Up @@ -66,6 +67,102 @@ public void AddToSetEach_Typed()
Assert(subject.AddToSetEach("FavoriteColors", new[] { "green", "violet" }), "{$addToSet: {colors: {$each: ['green', 'violet']}}}");
}

[Fact]
public void AddToSet_with_string_path_through_dictionary_to_list_value()
{
var subject = CreateSubject<DocumentWithDictionaryOfLists>();

Assert(subject.AddToSet("Buckets.myKey", "newValue"), "{$addToSet: {'buckets.myKey': 'newValue'}}");
}

[Fact]
public void AddToSet_with_expression_path_through_dictionary_to_list_value()
{
var subject = CreateSubject<DocumentWithDictionaryOfLists>();

Assert(subject.AddToSet(x => x.Buckets["myKey"], "newValue"), "{$addToSet: {'buckets.myKey': 'newValue'}}");
}

[Fact]
public void AddToSetEach_with_string_path_through_dictionary_to_list_value()
{
var subject = CreateSubject<DocumentWithDictionaryOfLists>();

Assert(subject.AddToSetEach("Buckets.myKey", new[] { "a", "b" }), "{$addToSet: {'buckets.myKey': {$each: ['a', 'b']}}}");
}

[Fact]
public void AddToSetEach_with_expression_path_through_dictionary_to_list_value()
{
var subject = CreateSubject<DocumentWithDictionaryOfLists>();

Assert(subject.AddToSetEach(x => x.Buckets["myKey"], new[] { "a", "b" }), "{$addToSet: {'buckets.myKey': {$each: ['a', 'b']}}}");
}

[Fact]
public void AddToSet_with_string_path_through_dictionary_to_nested_list()
{
var subject = CreateSubject<DocumentWithDictionaryOfObjects>();

Assert(subject.AddToSet("Buckets.myKey.Items", "newValue"), "{$addToSet: {'buckets.myKey.items': 'newValue'}}");
}

[Fact]
public void AddToSet_with_expression_path_through_dictionary_to_nested_list()
{
var subject = CreateSubject<DocumentWithDictionaryOfObjects>();

Assert(subject.AddToSet(x => x.Buckets["myKey"].Items, "newValue"), "{$addToSet: {'buckets.myKey.items': 'newValue'}}");
}

[Fact]
public void AddToSet_with_string_path_through_dictionary_to_hashset_value()
{
var subject = CreateSubject<DocumentWithDictionaryOfHashSets>();

Assert(subject.AddToSet("Buckets.myKey", "newValue"), "{$addToSet: {'buckets.myKey': 'newValue'}}");
}

[Fact]
public void AddToSet_with_expression_path_through_dictionary_to_hashset_value()
{
var subject = CreateSubject<DocumentWithDictionaryOfHashSets>();

Assert(subject.AddToSet(x => x.Buckets["myKey"], "newValue"), "{$addToSet: {'buckets.myKey': 'newValue'}}");
}

[Fact]
public void AddToSet_with_string_path_through_array_rep_dictionary()
{
var subject = CreateSubject<DocumentWithArrayRepDictionary>();

Assert(subject.AddToSet("Buckets.myKey", "newValue"), "{$addToSet: {'buckets.myKey': 'newValue'}}");
}

[Fact]
public void AddToSet_with_string_path_through_dictionary_where_value_is_IEnumerable_interface()
{
var subject = CreateSubject<DocumentWithDictionaryOfIEnumerable>();

Assert(subject.AddToSet("Buckets.myKey", "newValue"), "{$addToSet: {'buckets.myKey': 'newValue'}}");
}

[Fact]
public void AddToSet_with_numeric_string_key_through_dictionary()
{
var subject = CreateSubject<DataContainer>();

Assert(subject.AddToSet("Buckets.10", new DataRecord { Label = "test" }), "{$addToSet: {'buckets.10': { CreatedAt: ISODate('0001-01-01T00:00:00Z'), Label: 'test' }}}");
}

[Fact]
public void AddToSet_with_numeric_string_key_through_dictionary_of_List()
{
var subject = CreateSubject<DocumentWithDictionaryOfLists>();

Assert(subject.AddToSet("Buckets.10", "newValue"), "{$addToSet: {'buckets.10': 'newValue'}}");
}

[Fact]
public void BitwiseAnd()
{
Expand Down Expand Up @@ -742,5 +839,70 @@ public class F
{
public string Z { get; set; }
}

private class DocumentWithDictionaryOfLists
{
public ObjectId Id { get; set; }

[BsonElement("buckets")]
public Dictionary<string, List<string>> Buckets { get; set; }
}

private class DocumentWithDictionaryOfObjects
{
public ObjectId Id { get; set; }

[BsonElement("buckets")]
public Dictionary<string, Bucket> Buckets { get; set; }
}

private class DocumentWithDictionaryOfHashSets
{
public ObjectId Id { get; set; }

[BsonElement("buckets")]
public Dictionary<string, HashSet<string>> Buckets { get; set; }
}

private class DocumentWithArrayRepDictionary
{
public ObjectId Id { get; set; }

[BsonElement("buckets")]
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
public Dictionary<string, List<string>> Buckets { get; set; }
}

private class DocumentWithDictionaryOfIEnumerable
{
public ObjectId Id { get; set; }

[BsonElement("buckets")]
public Dictionary<string, IEnumerable<string>> Buckets { get; set; }
}

[BsonIgnoreExtraElements]
private class DataContainer
{
public string Id { get; set; }

[BsonElement("buckets")]
[BsonDictionaryOptions(DictionaryRepresentation.Document)]
public IDictionary<string, IEnumerable<DataRecord>> Buckets { get; set; }
= new Dictionary<string, IEnumerable<DataRecord>>();
}

[BsonIgnoreExtraElements]
private class DataRecord
{
public DateTime CreatedAt { get; set; }
public string Label { get; set; }
}

private class Bucket
{
[BsonElement("items")]
public List<string> Items { get; set; }
}
}
}