MongoDB .NET:在ArrayFilter中通过ID更新子文档
目标是使用目标子文档的ID来更新一个子文档——使用一个 ArrayFilter。
我理解有多种方式可以完成这次数据更新。
但出于学习目的,想使用 ArrayFilter。
此更新使用玩家文档的ID,将 PlayerName 替换为 "Josh"。
注:作为一个合理性检查,如果把 BsonDocumentArrayFilterDefinition 改为基于 PlayerName 进行匹配,而不是按ID,则更新就会成功。
样本文档(已裁剪):
db.Teams.find()
[
{
_id: ObjectId('000000000000000000002000'),
TeamName: 'GoldDiggers',
Players: [
{
_id: ObjectId('000000000000000000002001'),
PlayerName: 'Gary',
},
{
_id: ObjectId('000000000000000000002002'),
PlayerName: 'Greg',
},
]
}
]
下面的MONGOSH命令执行成功,将 PlayerName 的值从 "Gary" 改为 "Josh"。
db.Teams.updateOne(
{ _id: new ObjectId("000000000000000000002000") },
{ $set: { "Players.$[myIdentifier1].PlayerName": "Josh" } },
{
arrayFilters:
[
{"myIdentifier1._id": { $eq: new ObjectId("000000000000000000002001") }}
]
}
)
尝试使用一个 ArrayFilter 做同样更新的C#代码:
string myIdentifier1 = "firstIdentifier";
var filterTeam = Builders<Team>.Filter.Eq("Id", "000000000000000000002000");
var updateUsingArrayFilter = Builders<Team>.Update.Set($"Players.$[{myIdentifier1}].PlayerName", "Josh");
var arrayFilter1 = new BsonDocumentArrayFilterDefinition<BsonDocument>
(
// new BsonDocument($"{myIdentifier1}.PlayerName", new BsonDocument("$eq", "Gary")) // Sanity check - using PlayerName instead of ID => successful update
// new BsonDocument($"{myIdentifier1}.Id", "000000000000000000002001")
// new BsonDocument($"{myIdentifier1}._id", "000000000000000000002001")
// new BsonDocument($"{myIdentifier1}.Id", new BsonDocument("$eq", "000000000000000000002001"))
new BsonDocument($"{myIdentifier1}._id", new BsonDocument("$eq", "000000000000000000002001"))
);
arrayFilters = new List<ArrayFilterDefinition> { arrayFilter1 };
updateOptions = new UpdateOptions
{
ArrayFilters = arrayFilters
};
var result = collection.UpdateOne(filterTeam, updateUsingArrayFilter, updateOptions);
上方被注释掉的几行是不同的用法尝试,匹配子文档的ID。以下是这四次尝试所记录的日志。
Command started update UpdateSubDocumentTesting { "update" : "Teams", "ordered" : true, "$db" : "UpdateSubDocumentTesting", "lsid" : { "id" : { "$binary" : { "base64" : "tFsUYfi2SLyLv1vi3JF6eg==", "subType" : "04" } } }, "updates" : [{ "q" : { "_id" : { "$oid" : "000000000000000000002000" } },
"u" : { "$set" : { "Players.$[firstIdentifier].PlayerName" : "Josh" } },
"arrayFilters" : [{ "firstIdentifier.Id" : "000000000000000000002001" }] }] }
Command started update UpdateSubDocumentTesting { "update" : "Teams", "ordered" : true, "$db" : "UpdateSubDocumentTesting", "lsid" : { "id" : { "$binary" : { "base64" : "siFCYB15RiOsqa3zB3FFSg==", "subType" : "04" } } }, "updates" : [{ "q" : { "_id" : { "$oid" : "000000000000000000002000" } },
"u" : { "$set" : { "Players.$[firstIdentifier].PlayerName" : "Josh" } },
"arrayFilters" : [{ "firstIdentifier._id" : "000000000000000000002001" }] }] }
Command started update UpdateSubDocumentTesting { "update" : "Teams", "ordered" : true, "$db" : "UpdateSubDocumentTesting", "lsid" : { "id" : { "$binary" : { "base64" : "1MjYpIlTTJGzBLQD0oa/bQ==", "subType" : "04" } } }, "updates" : [{ "q" : { "_id" : { "$oid" : "000000000000000000002000" } },
"u" : { "$set" : { "Players.$[firstIdentifier].PlayerName" : "Josh" } },
"arrayFilters" : [{ "firstIdentifier.Id" : { "$eq" : "000000000000000000002001" } }] }] }
Command started update UpdateSubDocumentTesting { "update" : "Teams", "ordered" : true, "$db" : "UpdateSubDocumentTesting", "lsid" : { "id" : { "$binary" : { "base64" : "9/5IuiFsTP6bgcLQaF5lqw==", "subType" : "04" } } }, "updates" : [{ "q" : { "_id" : { "$oid" : "000000000000000000002000" } },
"u" : { "$set" : { "Players.$[firstIdentifier].PlayerName" : "Josh" } },
"arrayFilters" : [{ "firstIdentifier._id" : { "$eq" : "000000000000000000002001" } }] }] }
所有更新尝试都返回了 "Command succeeded",但实际没有进行修改。
解决方案
确保在执行任何筛选时,所提供的筛选值的类型必须与文档中定义的属性类型相匹配。
所提供的文档显示,_id 在 Players 数组中属于 ObjectId 类型。因此,你应该把值以 ObjectId 类型提供,而不是字符串类型,用于 arrayFilter1。
var arrayFilter1 = new BsonDocumentArrayFilterDefinition<BsonDocument>(
new BsonDocument($"{myIdentifier1}._id",
new BsonDocument("$eq",
ObjectId.Parse("000000000000000000002001")
)
)
);
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。