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.