CSFLE - Problem when try update "array field" filtering field by the same “field name” present in “CSFLE Schema Map”

Hi,

We have the following C# model:

public class Foo
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string MotherName { get; set; }
    public IReadOnlyList<Bar> Bars { get; init; }
}

public class Bar
{
    public string Name { get; set; }
    public DateTime CreatedAt { get; set; }
}

And we using the following methods to create the CSFLE Schema Map:

public class JsonSchemaHelper
{
	private static readonly string RANDOM_ENCRYPTION_TYPE = "AEAD_AES_256_CBC_HMAC_SHA_512-Random";
    private static readonly string DETERMINISTIC_ENCRYPTION_TYPE = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic";
		
	private static BsonDocument CreateEncryptedMetadata(string dataEncriptionKeyBase64)
    {
        var keyId = new BsonBinaryData(Convert.FromBase64String(dataEncriptionKeyBase64), BsonBinarySubType.UuidStandard);
        
        return new BsonDocument(nameof(keyId), new BsonArray(new[] { keyId }));
    }
	
	private static BsonDocument CreateEncryptedField(string bsonType, bool isDeterministic)
    {
        return new BsonDocument
        {
            {
                "encrypt",
                new BsonDocument
                {
                    { "bsonType", bsonType },
                    { "algorithm", isDeterministic ? DETERMINISTIC_ENCRYPTION_TYPE : RANDOM_ENCRYPTION_TYPE }
                }
            }
        };
    }
	
	public static BsonDocument CreateJsonSchemaFoo(string dataEncriptionKeyBase64)
	{
		return new BsonDocument
		{
			{ "bsonType", "object" },
			{ "encryptMetadata", CreateEncryptedMetadata(dataEncriptionKeyBase64) },
			{
				"properties",
				new BsonDocument
				{
					{  JsonNamingPolicy.CamelCase.ConvertName(nameof(Foo.Name)), CreateEncryptedField(bsonType: "string", isDeterministic: true) },
					{  JsonNamingPolicy.CamelCase.ConvertName(nameof(Foo.MotherName)), CreateEncryptedField(bsonType: "string", isDeterministic: true) },
				}
			}
		};
	}
}

Resulting in this “document format” stored in the database:

{
   "_id": {
      "$oid" : "62f16d12b118b4057859d709"
   },
   "name" : "*******",  // <- Encrypted
   "motherName": "*******", // <- Encrypted
   "bars" : [
      {
         "name" : "bar1",
         "createdAt" : "2022-08-08T20:07:49.368+00:00"
      },
      {
         "name" : "bar2",
         "createdAt" : "2022-08-08T20:07:53.368+00:00"
      }
   ]
}

The problem is when we try to update an item of bars[] array with following approach:

public async Task<bool> UpsertBar(string fooId, Bar bar, CancellationToken cancellationToken)
{
    var filterBuilder = Builders<Foo>.Filter;

    var filter = filterBuilder.Eq(_ => _.Id, fooId) & filterBuilder.ElemMatch(foo => foo.Bars, b => b.Name == bar.Name);

    var update = Builders<Foo>.Update.Set(foo => foo.Bars[-1], bar);

    var updateResult = await _propostasCollection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken);
	
	Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(updateResult));
}

Output:

{
   "IsAcknowledged" : true,
   "IsModifiedCountAvailable" : true,
   "MatchedCount" : 0,
   "ModifiedCount" : 0,
   "UpsertedId" : null
}

The UpdateOneAsync method does not affect any item on array.

But when I rename the field Name in Bar class “ALL WORKS”, like:

public class Bar
{
    public string FullName { get; set; }
    public DateTime CreatedAt { get; set; }
}

Filter:

var filter = filterBuilder.Eq(_ => _.Id, fooId) & filterBuilder.ElemMatch(foo => foo.Bars, b => *b.FullName == bar.FullName*);

Output:

{
   "IsAcknowledged":true,
   "IsModifiedCountAvailable":true,
   "MatchedCount":1,
   "ModifiedCount":1,
   "UpsertedId":null
}

I suppose it is related to the fields “with the same name” between classes, in this case Foo.Name and Bar.Name and for some reason our Schema Map or MongoDB Driver cannot differentiate what is an Name field from the main class Foo to another of Bar class, and this way the filter does not works.

Our Schema Map can be translated to:

 var schemaMap = $@"{{
                properties: {{
                    name: {{
                        encrypt: {{
                            keyId: [{{
                                '$binary' : {{
                                    'base64' : '{base64DataKeyId}',
                                    'subType' : '04'
                                }}
                            }}],
                        bsonType: 'string',
                        algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'
                        }}
                    }},
					motherName: {{
                        encrypt: {{
                            keyId: [{{
                                '$binary' : {{
                                    'base64' : '{base64DataKeyId}',
                                    'subType' : '04'
                                }}
                            }}],
                        bsonType: 'string',
                        algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'
                        }}
                    }},
                }},
                'bsonType': 'object'
            }}";

The field is name: {{...}} in Schema Map and this “looks generic” to the driver(I think).

Does my assumption make sense? Why the method UpdateOneAsync can’t update the “array items” with my initial configuration?(Foo.Name & Bar.Name)

Thanks in advance.

Hey @Igor_N_A , I can’t reproduce your issue.
Can you please provide a full repo with a small console application? Also, can you check the following cases:

  1. whether updating works without csfle enabled
  2. If you simplify a filter to a simple empty document: { }

Hy @Dmitry_Lukyanov, sorry for the late reply…

I made this detailed repo with all configurations to simulate the situation, when you run it you will be able to see the problem.

To make it “work as desired”(without reported problem), you only need to comment the line 45 of JsonSchemaHelper class.

  1. whether updating works without csfle enabled

Yes.

  1. If you simplify a filter to a simple empty document: { }

Same reported result.

Cheers. :wink:

Hey @Igor_N_A , thanks for your reports, it looks like we have a server issue that I reported here that is a reason of behavior you’re seeing. I will recheck this issue as soon as this ticket will be resolved.

Thank’s @Dmitry_Lukyanov!