MongoDB .NET:使用ArrayFilter按子文档ID更新子文档失败排查
问题描述
目标是使用ArrayFilter通过子文档ID更新子文档,将指定玩家的PlayerName替换为"Josh"。已知该更新有多种实现方式,但出于学习目的选择使用ArrayFilter。
作为sanity check,若将BsonDocumentArrayFilterDefinition改为匹配PlayerName而非ID,更新可成功执行。
示例文档(精简版):
db.Teams.find() [ { _id: ObjectId('000000000000000000002000'), TeamName: 'GoldDiggers', Players: [ { _id: ObjectId('000000000000000000002001'), PlayerName: 'Gary', }, { _id: ObjectId('000000000000000000002002'), PlayerName: 'Greg', }, ] } ]
对应的MONGOSH命令可成功将Gary的PlayerName改为Josh:
db.Teams.updateOne( { _id: new ObjectId("000000000000000000002000") }, { $set: { "Players.$[myIdentifier1].PlayerName": "Josh" } }, { arrayFilters: [ {"myIdentifier1._id": { $eq: new ObjectId("000000000000000000002001") }} ] } )
但在C#中尝试多种ArrayFilter匹配子文档ID的写法后,均显示命令成功但未产生任何修改,相关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);
执行日志:
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"但未修改任何数据。
解决方案
问题核心是类型不匹配:MongoDB中的_id字段是ObjectId类型,而你在C#代码中直接传入字符串形式的ID,导致字符串与ObjectId无法匹配,因此找不到对应的子文档。
修改方式如下,将字符串ID转换为ObjectId类型后再传入ArrayFilter:
string myIdentifier1 = "firstIdentifier"; // 将字符串ID转换为ObjectId类型 var teamId = ObjectId.Parse("000000000000000000002000"); var filterTeam = Builders<Team>.Filter.Eq(t => t.Id, teamId); var updateUsingArrayFilter = Builders<Team>.Update.Set($"Players.$[{myIdentifier1}].PlayerName", "Josh"); // 子文档ID同样转换为ObjectId var playerId = ObjectId.Parse("000000000000000000002001"); var arrayFilter1 = new BsonDocumentArrayFilterDefinition<BsonDocument>( new BsonDocument($"{myIdentifier1}._id", new BsonDocument("$eq", playerId)) ); var arrayFilters = new List<ArrayFilterDefinition> { arrayFilter1 }; var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters }; var result = collection.UpdateOne(filterTeam, updateUsingArrayFilter, updateOptions);
关键说明:
- 使用
ObjectId.Parse()将字符串ID转换为MongoDB的ObjectId类型,确保与数据库中存储的字段类型一致。 - 团队ID过滤建议使用强类型写法
Eq(t => t.Id, teamId),避免字符串字段名可能出现的大小写或映射问题(比如实体类属性是Id,但Mongo中存储为_id,虽然驱动会自动映射,但强类型写法更可靠)。
修改后,ArrayFilter就能正确匹配到目标子文档,完成更新操作。
内容的提问来源于stack exchange,提问作者Skippy VonDrake
相关产品推荐
相关产品推荐

