[CSHARP-1891] Distinct should still work when TField is not exactly right (like it used to) Created: 12/Jan/17  Updated: 27/Jan/17  Resolved: 27/Jan/17

Status: Closed
Project: C# Driver
Component/s: API, Serialization
Affects Version/s: 2.4.1
Fix Version/s: 2.4.2

Type: Bug Priority: Major - P3
Reporter: Alexander Mezhov Assignee: Robert Stam
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: Zip Archive DistinctValuesForAnArrayField.zip    
Issue Links:
Related
related to CSHARP-1884 In should still work when TField is n... Closed
related to CSHARP-1890 Eq should still work when TField is n... Closed
related to CSHARP-1893 New DistinctMany method which is like... Closed

 Description   

I got an exception when I had updated MongoDB.Driver from 2.4.0 to 2.4.1.

System.NotSupportedException : Values of type 'String' cannot be deserialized using a serializer of type 'CastingSerializer<String, IEnumerable<String>>'.
   в MongoDB.Bson.Serialization.Serializers.SerializerBase`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
   в MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
   в MongoDB.Bson.Serialization.Serializers.EnumerableSerializerBase`2.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
   в MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
   в MongoDB.Driver.Core.Operations.ElementDeserializer`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
   в MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
   в MongoDB.Driver.Core.WireProtocol.CommandWireProtocol`1.ProcessReply(ConnectionId connectionId, ReplyMessage`1 reply)
   в MongoDB.Driver.Core.WireProtocol.CommandWireProtocol`1.Execute(IConnection connection, CancellationToken cancellationToken)
   в MongoDB.Driver.Core.Servers.Server.ServerChannel.ExecuteProtocol[TResult](IWireProtocol`1 protocol, CancellationToken cancellationToken)
   в MongoDB.Driver.Core.Servers.Server.ServerChannel.Command[TResult](DatabaseNamespace databaseNamespace, BsonDocument command, IElementNameValidator commandValidator, Func`1 responseHandling, Boolean slaveOk, IBsonSerializer`1 resultSerializer, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken)
   в MongoDB.Driver.Core.Operations.CommandOperationBase`1.ExecuteProtocol(IChannelHandle channel, ServerDescription serverDescription, ReadPreference readPreference, CancellationToken cancellationToken)
   в MongoDB.Driver.Core.Operations.CommandOperationBase`1.ExecuteProtocol(IChannelSource channelSource, ReadPreference readPreference, CancellationToken cancellationToken)
   в MongoDB.Driver.Core.Operations.ReadCommandOperation`1.Execute(IReadBinding binding, CancellationToken cancellationToken)
   в MongoDB.Driver.Core.Operations.DistinctOperation`1.Execute(IReadBinding binding, CancellationToken cancellationToken)
   в MongoDB.Driver.OperationExecutor.ExecuteReadOperation[TResult](IReadBinding binding, IReadOperation`1 operation, CancellationToken cancellationToken)
   в MongoDB.Driver.MongoCollectionImpl`1.ExecuteReadOperation[TResult](IReadOperation`1 operation, ReadPreference readPreference, CancellationToken cancellationToken)
   в MongoDB.Driver.MongoCollectionImpl`1.ExecuteReadOperation[TResult](IReadOperation`1 operation, CancellationToken cancellationToken)
   в MongoDB.Driver.MongoCollectionImpl`1.Distinct[TField](FieldDefinition`2 field, FilterDefinition`1 filter, DistinctOptions options, CancellationToken cancellationToken)

My use case is very simple and complies next example:
https://docs.mongodb.com/manual/reference/method/db.collection.distinct/#return-distinct-values-for-an-array-field

In my case I have a news collection. Each news can contain few tags. I want to select all unique tags from all news in collection. Next code block shows this situation (also see attachments).

[TestFixture]
public class DistinctValuesForAnArrayFieldTest
{
    [Test]
    public void ShouldDistinctValuesForAnArrayField()
    {
        // Given
 
        var mongoClientSettings = new MongoClientSettings { Server = new MongoServerAddress("localhost") };
        var mongoClient = new MongoClient(mongoClientSettings);
        var mongoDatabase = mongoClient.GetDatabase("test");
        var mongoCollection = mongoDatabase.GetCollection<News>("news");
 
        mongoCollection.InsertMany(new[]
                                   {
                                       new News { Title = "News 1", Tags = new [] { "A", "B" } },
                                       new News { Title = "News 2", Tags = new [] { "B", "C" } },
                                       new News { Title = "News 3", Tags = new [] { "C", "D" } }
                                   });
 
        // When
 
        // It works fine only for v2.4.0 and throws an exception for v2.4.1
        // System.NotSupportedException : Values of type 'String' cannot be deserialized using a serializer of type 'CastingSerializer<String, IEnumerable<String>>'.
        var uniqueTags = mongoCollection.Distinct<string>("Tags", Builders<News>.Filter.Empty).ToList();
 
 
        // Then
 
        CollectionAssert.AreEquivalent(new[] { "A", "B", "C", "D" }, uniqueTags);
    }
}
 
 
public class News
{
    public string Title { get; set; }
 
    public IEnumerable<string> Tags { get; set; }
}

Until 2.4.1 this code worked fine but not now. I noticed your changes in FieldValueSerializerHelper.cs and I think this bug relates with them. But anyway this bug relates with latest changes in the serialization mechanism.

If you know a correct way to get distinct items from array field which complies v2.4.1, please, let me know.



 Comments   
Comment by Githook User [ 27/Jan/17 ]

Author:

{u'username': u'rstam', u'name': u'rstam', u'email': u'robert@robertstam.org'}

Message: CSHARP-1891: Code review changes.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/9640b12c16396517a6c928733b11eef4bf6295e1

Comment by Githook User [ 27/Jan/17 ]

Author:

{u'username': u'rstam', u'name': u'rstam', u'email': u'robert@robertstam.org'}

Message: CSHARP-1891: Code review changes.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/2e5658eab4c2a1f6a9695d837cf2887bb25de546

Comment by Githook User [ 27/Jan/17 ]

Author:

{u'username': u'rstam', u'name': u'rstam', u'email': u'robert@robertstam.org'}

Message: CSHARP-1891: Further changes to Distinct for backward compatibility.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/c88d2fb41ecf72b60c6fe5fe5e89af85bfc800d9

Comment by Githook User [ 27/Jan/17 ]

Author:

{u'username': u'rstam', u'name': u'rstam', u'email': u'robert@robertstam.org'}

Message: CSHARP-1891: Fix backward breaking changes in Distinct method.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/3be268ec8b26c169fdfba83d91f6c3360814ca68

Comment by Robert Stam [ 13/Jan/17 ]

This is a result of fudging the type of the field in this line:

mongoCollection.Distinct<string>("Tags", Builders<News>.Filter.Empty)

The Tags field is actually of type IEnumerable<string>, not string.

However, due to the absence of an overload of Distinct for array members (see CSHARP-1893), in 2.4.0 and earlier the only way to use Distinct with an array member was to fudge the field type in this way.

For backward compatibility reasons we are going to make further changes in 2.4.1 to allow applications to fudge field types when they want to.

Generated at Wed Feb 07 21:40:57 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.