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

BSON Representations not respected in nested objects in LINQ3

    • Type: Icon: Bug Bug
    • Resolution: Unresolved
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: None
    • Component/s: LINQ3
    • None
    • None
    • Dotnet Drivers
    • None
    • None
    • None
    • None
    • None
    • None

      In MongoDB C# Driver version 3.2.1, the LinqProvider V3 incorrectly generates MongoDB queries when performing projections on nested collections that contain ObjectId fields. This results in incorrect query results without throwing any exceptions. This behavior differs from the previous 2.30.0 version of the driver, which correctly preserves ObjectId representations in projections.

      Repro code:
      BsonClassMap.RegisterClassMap<Document>();
      BsonClassMap.RegisterClassMap<Chain>();
      BsonClassMap.RegisterClassMap<Unit>();var connectionString = "mongodb://localhost/db?directConnection=true";
      var settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString));settings.ClusterConfigurator = builder =>
      {
          builder.Subscribe<CommandStartedEvent>(e =>
          {
              Console.WriteLine($"Command

      {e.CommandName}

      -> {e.Command.ToJson()}");
          });
      };
      var client = new MongoClient(settings);// bootstrap
      var collection = client.GetDatabase("db").GetCollection<Chain>("test");var id1 = BsonObjectId.Create(ObjectId.GenerateNewId());
      var id2 = BsonObjectId.Create(ObjectId.GenerateNewId());
      var id3 = BsonObjectId.Create(ObjectId.GenerateNewId());var chain = new Chain()
      {
          Parts = new List<Unit>()
          {
              new()
              {
                  Refs = new List<Document>()
                  {
                      new()
                     

      {                     id = id1.ToString(),                 }

      ,
                      new()
                      {
                          id = id2.ToString(),
                      },
                      new()
                      {
                          id = id3.ToString(),
                      },
                      
                  }
              }
          }
      };await collection.InsertOneAsync(chain);
      // end bootstrap
      List<string> jobIds = [id2.ToString()];var findFluent = collection
          .Find(x => x.Parts.Any(a => a.Refs.Any(b => jobIds.Contains(b.id))))
          .Project(chain => new { chain.Parts
              .First(p => p.Refs.Any(j => jobIds.Contains(j.id)))
              .Refs.First(j => jobIds.Contains(j.id)).id });var result = await findFluent.ToListAsync();// Driver 2.30.0 result.First().id is not null
      // Driver 3.2.1 result.First().id is nullpublic class Document
      {
          [BsonId]
          [BsonRepresentation(BsonType.ObjectId)]
          public string id

      { get; set; }
      }public class Chain : Document
      {
          public ICollection<Unit> Parts { get; set; }

      = new List<Unit>();
      }public class Unit
      {
          public ICollection<Document> Refs

      { get; set; }

          public Unit()
          {
              Refs = new List<Document>();
          }
      }
      the resulting query when using driver 2.30.0 is:

      find({ "Parts" : { "$elemMatch" : { "Refs" : { "$elemMatch" : { "_id" : { "$in" : [ObjectId("67dc3b79a35f548871c29233")] } } } } } }, { "_id" : { "$let" : { "vars" : { "this" : { "$arrayElemAt" : [{ "$filter" : { "input" : { "$let" : { "vars" : { "this" : { "$arrayElemAt" : [{ "$filter" : { "input" : "$Parts", "as" : "p", "cond" : { "$anyElementTrue" : { "$map" : { "input" : "$$p.Refs", "as" : "j", "in" : { "$in" : ["$$j._id", [ObjectId("67dc3b79a35f548871c29233")]] } } } } } }, 0] } }, "in" : "$$this.Refs" } }, "as" : "j", "cond" : { "$in" : ["$$j._id", [ObjectId("67dc3b79a35f548871c29233")]] } } }, 0] } }, "in" : "$$this._id" } } }) 

      for the new driver 3.2.1 it’s:

      find({ "Parts" : { "$elemMatch" : { "Refs" : { "$elemMatch" : { "_id" : { "$in" : [{ "$oid" : "67dc3bc04c5e85b06180264f" }] } } } } } }, { "_id" : { "$let" : { "vars" : { "this" : { "$arrayElemAt" : [{ "$filter" : { "input" : { "$let" : { "vars" : { "this" : { "$arrayElemAt" : [{ "$filter" : { "input" : "$Parts", "as" : "p", "cond" : { "$anyElementTrue" : { "$map" : { "input" : "$$p.Refs", "as" : "j", "in" : { "$in" : ["$$j._id", ["67dc3bc04c5e85b06180264f"]] } } } } } }, 0] } }, "in" : "$$this.Refs" } }, "as" : "j", "cond" : { "$in" : ["$$j._id", ["67dc3bc04c5e85b06180264f"]] } } }, 0] } }, "in" : "$$this._id" } } }) 

      Clearly, the ObjectId or $oid is missing in the inner selections of _id. This is especially dangerous issue because it alters the final results without any errors or exceptions.

      Reference: Reported in forums here.

            Assignee:
            robert@mongodb.com Robert Stam
            Reporter:
            rishit.bhatia@mongodb.com Rishit Bhatia
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              None
              None
              None
              None