MongoDB .NET:使用ElemMatch() 替换数组中的字符串

编程语言 2026-07-11

我在使用MongoDb .NET驱动程序(v3.7)来在数组中替换字符串,使用 ElemMatch 来匹配数组中的字符串元素。

我看到的关于 ElemMatch() 的所有示例都涉及在对象数组(或对象列表)中匹配一个对象。

我不能用同样的方法来定位数组中的一个字符串吗?

例如,我想把 round 替换为 triangle,并尝试了下面的代码,认为位置运算符会在需要修改的数组元素中找到/引用该字符串。但也许对原始类型的数组来说,这样做不可行?

var idFilter = Builders<Sample>.Filter.Eq(d => d.Id, "000000000000000000001002");
var elemMatchFilter = Builders<Sample>.Filter.ElemMatch(x => x.MyStringArray, "round");
var filtersAnded = Builders<Sample>.Filter.And(idFilter, elemMatchFilter);
var update = Builders<Sample>.Update.Set("MyStringArray.[$]", "triangle");
//var update = Builders<Sample>.Update.Set("MyStringArray.$", "triangle"); // Tried this but gave same exception
UpdateResult result = collection.UpdateOne(filtersAnded, update);

这会导致一个异常:

JSON reader was expecting a value but found 'round'.

数据模型:

internal class Sample
{
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; } = ObjectId.GenerateNewId().ToString();

    public int[] MyIntArray { get; set; } = [];
    public string[] MyStringArray { get; set; } = [];
}

用于生成两个文档的代码:

public const string Samples = "Samples";
IMongoDatabase? iMongoDatabase = mongoClient.GetDatabase("UpdateArraysTesting");
iMongoDatabase.CreateCollection(Samples);
SamplesCollection = iMongoDatabase!.GetCollection<Sample>(Samples);

Sample sample = new() {
    Id = "000000000000000000001001",
    MyIntArray = new int[] { 10, 15 }, 
    MyStringArray = new string[] {"square","box" } 
};
SamplesCollection.InsertOne(sample);

sample = new()
{
    Id = "000000000000000000001002",
    MyIntArray = new int[] { 22, 27 },
    MyStringArray = new string[] { "round", "ball" }
};
SamplesCollection.InsertOne(sample);

更新:为了教我如何使用 ElemMatch()Aneroid 提供了下面的代码。他为 ElemMatch() 过滤器提供了两个版本,但第一版未能找到匹配项,第二版则成功了。

var idFilter = Builders<Sample>.Filter.Eq(d => d.Id, "000000000000000000001002");

// Version 1 does not find a match
var elemMatchFilter_v1 = Builders<Sample>.Filter.ElemMatch(
    x => x.MyStringArray, 
    Builders<string>.Filter.Eq(s => s, "triangle"));

// Version 2 succeeds in finding a match    
var elemMatchFilter_v2 = Builders<Sample>.Filter.ElemMatch(
    x => x.MyStringArray, 
    s => s == "triangle")); 

var filtersAnded = Builders<Sample>.Filter.And(idFilter, elemMatchFilter_v2); // elemMatchFilter_v1 does NOT match
var update = Builders<Sample>.Update.Set("MyStringArray.$", "rectangle");
var result = collection.UpdateOne(filtersAnded, update);

以下是从上面代码的 UpdateOne() 调用中捕获的日志输出。

// Version 1 Serilog output
Command started update UpdateArraysTesting { "update" : "Samples", "ordered" : true, "$db" : "UpdateArraysTesting", "lsid" : { "id" : { "$binary" : { "base64" : "i6evdcuCQ+WC3dq0TBO2qw==", "subType" : "04" } } }, "updates" : [{ "q" : { "_id" : { "$oid" : "000000000000000000001002" }, "MyStringArray" : { "$elemMatch" : { "@<current>" : "triangle" } } }, "u" : { "$set" : { "MyStringArray.$" : "rectangle" } } }] }
Command succeeded update UpdateArraysTesting 0.8399 { "n" : 0, "nModified" : 0, "ok" : 1.0 }

// Version 2 Serilog output
Command started update UpdateArraysTesting { "update" : "Samples", "ordered" : true, "$db" : "UpdateArraysTesting", "lsid" : { "id" : { "$binary" : { "base64" : "p9PIap2yRKqvqtBjRKFa+Q==", "subType" : "04" } } }, "updates" : [{ "q" : { "_id" : { "$oid" : "000000000000000000001002" }, "MyStringArray" : { "$elemMatch" : { "$eq" : "triangle" } } }, "u" : { "$set" : { "MyStringArray.$" : "rectangle" } } }] }
Command succeeded update UpdateArraysTesting 0.7627 { "n" : 1, "nModified" : 1, "ok" : 1.0 }

GitHub仓库

解决方案

elemMatch 在这里并不需要。你可以使用 $ 的位置更新运算符 来更新该数组的值。

位置更新运算符 $ 在更新查询文档的第一个匹配项处充当占位符。

db.collection.update(
  {
    _id: "000000000000000000001002",
    MyStringArray: "round"
  },
  {
    $set: { "MyStringArray.$": "triangle" }
  }
)

Mongo Playground

在.NET代码中,可能是:

var idFilter = Builders<Sample>.Filter.Eq(d => d.Id, "000000000000000000001002");
var elemFilter = Builders<Sample>.Filter.Eq(x => x.MyStringArray, "round");
// may need AnyEq instead of Eq above
var filtersAnded = Builders<Sample>.Filter.And(idFilter, elemFilter);
var update = Builders<Sample>.Update.Set("MyStringArray.$", "triangle");
UpdateResult result = collection.UpdateOne(filtersAnded, update);
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章