[CDRIVER-4388] Aggregate projection with LINQ expression ignores serializers Created: 11/May/22  Updated: 02/Jun/22  Resolved: 11/May/22

Status: Closed
Project: C Driver
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Adam Gilmore Assignee: Unassigned
Resolution: Duplicate Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
duplicates CSHARP-4172 Enum constant not serialized using th... Closed

 Description   

Summary

With the latest C# driver (2.15.1), when running an aggregate query that includes a projection step with a LINQ predicate, the predicate is compiled ignoring any of the custom serializers, resulting in an incorrect query.

Environment

Visual Studio 2019

.NET Core 3.1

MongoDB.Driver 2.15.1

How to Reproduce

Consider the following program:

 

    class Program
    {
        static void Main(string[] args)
        {
            var mongo = new MongoClient(new MongoUrl("mongodb://localhost:27017/local"));
            var database = mongo.GetDatabase("local");
 
            var findQuery = database.GetCollection<Order>("orders")
                .Find(o => o.Items.Any(i => i.Type == ItemType.Refund));
            Console.WriteLine(findQuery.ToString());
 
            var query = database.GetCollection<Order>("orders")
                .Aggregate()
                .Project((o) => new
                {
                    o.Id,
                    HasAnyRefund = o.Items.Any(i => i.Type == ItemType.Refund)
                });
            Console.WriteLine(query.ToString());
        }
    }
 
    public class Order
    {
        public int Id { get; set; }
        public List<Item> Items { get; set; }
    }
 
    public class Item
    {
        [BsonSerializer(typeof(CamelCaseEnumSerializer<ItemType>))]
        public ItemType Type { get; set; }
    }
 
    public enum ItemType
    {
        SaleItem,
        Refund
    }
 
    public class CamelCaseEnumSerializer<T> : EnumSerializer<T>
        where T : struct, Enum
    {
        private static string ToCamelCase(string s)
        {
            return char.ToLowerInvariant(s[0]) + s.Substring(1);
        }
 
        public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, T value)
        {
            context.Writer.WriteString(ToCamelCase(value.ToString()));
        }
 
        public override T Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
        {
            return (T)Enum.Parse(typeof(T), context.Reader.ReadString(), true);
        }
    } 

In the above example, a custom serializer is registered for the Enum type, which should be respected.  When the standard Find query is run, the output is:

 

find({ "Items" : { "$elemMatch" : { "Type" : "refund" } } })

however, when the Aggregate query is run, the output is:

 

 

aggregate([{ "$project" : { "Id" : "$_id", "HasAnyRefund" : { "$anyElementTrue" : { "$map" : { "input" : "$Items", "as" : "i", "in" : { "$eq" : ["$$i.Type", 1] } } } }, "_id" : 0 } }])

Clearly, the predicate in the projection is not being compiled with serializers and it's serializing ItemType.Refund as 1 (its Enum index) instead of "refund" (as per the serializer).

 

 



 Comments   
Comment by Adam Gilmore [ 11/May/22 ]

Apologies - created in the wrong project - this is for the C# driver.  Please close this one (I've recreated as CSHARP-4172).

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