Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-3449

custom serializers sometimes not used in aggregation

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 2.14.0
    • Affects Version/s: 2.11.3
    • Component/s: Linq, LINQ3
    • None

      I am encountering a problem where custom serializers I have previously registered are not being used to convert types I use in an aggregation pipeline.

      Assume I have these types:

       

      public enum TestEnum { Foo, Bar, Baz }
      public record TestEntity(string Id, string Category, TestEnum EnumValue);
      

      where TestEnum will be my custom type. I am registering the type as follows:

       

      BsonClassMap.RegisterClassMap<TestEntity>(cm =>
      {
          cm.MapIdProperty(e => e.Id);
          cm.MapProperty(e => e.Category)
              .SetElementName("category");
          cm.MapProperty(e => e.EnumValue)
              .SetElementName("enum_value")
              .SetSerializer(new TestEnumSerializer());
      });
      

      TestEnumSerializer is a SerializerBase<TestEnum> that represents the enum values as the strings "my_foo", "my_bar" and "my_baz".

      I am inserting some entities as follows:

       

      IMongoCollection<TestEntity> collection = db.GetCollection<TestEntity>("test_collection");
      collection.InsertOne(new TestEntity("1", "cat1", TestEnum.Foo));
      collection.InsertOne(new TestEntity("2", "cat1", TestEnum.Bar));
      collection.InsertOne(new TestEntity("3", "cat2", TestEnum.Bar));
      

      and I can confirm that the entities get persisted as I expected:

       

       

      /* 1 */
      {
          "_id" : "1",
          "category" : "cat1",
          "enum_value" : "my_foo"
      }/* 2 */
      {
          "_id" : "2",
          "category" : "cat1",
          "enum_value" : "my_bar"
      }/* 3 */
      {
          "_id" : "3",
          "category" : "cat2",
          "enum_value" : "my_bar"
      }
      

      If I now perform the following aggregation:

       

       

      var bars = collection.Aggregate()
          .Group(
              e => e.Category,
              group => new { NumBars = group.Count(e2 => e2.EnumValue == TestEnum.Bar) })
          .ToList();
      

      I can see that the produced query looks as follows:

       

       

      "pipeline": [
        {
          "$group": {
            "_id": "$category",
            "NumBars": {
              "$sum": {
                "$cond": [
                  {
                    "$eq": [
                      "$enum_value",
                      1
                    ]
                  },
                  1,
                  0
                ]
              }
            }
          }
        }
      ]
      

      Note that it compares the $enum_value to 1 instead of "my_bar", which makes the match fail and return a wrong result.

       

      The 1 is the enum's ordinal value, which probably gets used because of the implicit conversion added in https://jira.mongodb.org/browse/CSHARP-131

      Since it appears to be using BsonType mappers instead of registered serializers, it is possible to add custom type mappers for types that do not have an implicit conversion using BsonTypeMapper.RegisterCustomTypeMapper. However, since the enum gets implicitly converted to an ordinal value before any custom type mappers are checked, I was unable to find a workaround.

      I have added the full source code of the test class I used as an attachment.

            Assignee:
            dmitry.lukyanov@mongodb.com Dmitry Lukyanov (Inactive)
            Reporter:
            de.felix.koenig@gmail.com Felix König
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: