Mongo C# Driver fails BsonSerialization for Compound Must $search query

I have an atlas search index, and using mongoDb compass to write a $search aggregation, I was able to get the following syntax working and successfully returning results:

{
  index: "ShipmentProjections",
  compound: {
    must: {
      equals: {
        value: 208414,
        path: "MerchantId",
      },
    },
    must: {
      in: {
        path: "ShipmentId",
        value: [100405234]
      }
    }
  },
}

My problem is that I can’t convert this query into C#. I am using the latest MongoDb C# driver, but when I try to pass this query into BsonDocument.Parse(projectQuery), I get the following error:

System.InvalidOperationException: Duplicate element name 'must'.
   at MongoDB.Bson.BsonDocument.Add(BsonElement element)
   at MongoDB.Bson.BsonDocument.Add(String name, BsonValue value)
   at MongoDB.Bson.Serialization.Serializers.BsonDocumentSerializer.DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.Serializers.BsonValueSerializerBase`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
   at MongoDB.Bson.Serialization.Serializers.BsonValueSerializer.DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.Serializers.BsonValueSerializerBase`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
   at MongoDB.Bson.Serialization.Serializers.BsonDocumentSerializer.DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.Serializers.BsonValueSerializerBase`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
   at MongoDB.Bson.BsonDocument.Parse(String json)

However, it seems like having duplicate must elements within the compound statement is the required format of the query.

It seems as if there is a conflict between the syntax $search is requiring, and the uniqueness constraints on the BsonDocument in the C# driver.

Is there anyway to get the BsonDocument in the C# driver to accept this search query with it’s duplicate must keys? Is there an alternative way to write the same query that avoids the duplicate key issues?

Thanks for your help!

Hi, @Michael_Evans,

Welcome to the MongoDB Community Forums. I understand that you’re trying to parse a JSON string into BSON related to an Atlas Search query. By default, the .NET/C# Driver does not allow duplicate element names in BsonDocument, but you can opt into it. Unfortunately this functionality is not exposed in BsonDocument.Parse or BsonDocument.TryParse. You can however copy our BsonDocument.Parse method and enable duplicate element name support:

using (var jsonReader = new JsonReader(json))
{
    var context = BsonDeserializationContext.CreateRoot(jsonReader, cfg => cfg.AllowDuplicateElementNames = true);
    var document = BsonDocumentSerializer.Instance.Deserialize(context);
    if (!jsonReader.IsAtEndOfFile())
    {
        throw new FormatException("String contains extra non-whitespace characters beyond the end of the document.");
    }
    Console.WriteLine(document);
}

I have created CSHARP-5039 to implement these new overloads in our BSON library.

Sincerely,
James

Hi, @Michael_Evans,

Upon further investigation, I realize that the Atlas Search query syntax for the compound operator that you’re using is slightly incorrect. If you want to include multiple must clauses, you supply an array to the single must element rather than including duplicate must elements. Re-writing your example:

{
  index: "ShipmentProjections",
  compound: {
    must: [
      {
        equals: {
          value: 208414,
          path: "MerchantId",
        },
      },
      {
        in: {
          path: "ShipmentId",
          value: [100405234]
        }
      }
  ]
}

This removes the need for BsonDocument.Parse allowing duplicate element names. While BSON with duplicate element names is possible, we discourage it because semantics are not consistent across language implementations. For that reason, I’m going to close CSHARP-5039 as Won't Fix.

Please let me know if you have any additional questions.

Sincerely,
James