如何使用C#驱动程序在MongoDB中更新和插入多个文档

16

我正在使用MongoDB 2,并且希望更新多个文档并将值 processed:true 插入到集合中。但是MongoDB的C# API只允许我们更新多个记录或插入单个记录。

如何使用C# API解决这个问题?


7个回答

20

Mongo 2.6版本后,您可以使用批量更新/插入操作。以下示例使用c#驱动程序执行批量更新。

MongoCollection<foo> collection = database.GetCollection<foo>(collectionName);
      var bulk = collection.InitializeUnorderedBulkOperation();
      foreach (FooDoc fooDoc in fooDocsList)
      {
        var update = new UpdateDocument { {fooDoc.ToBsonDocument() } };
        bulk.Find(Query.EQ("_id", fooDoc.Id)).Upsert().UpdateOne(update);
      }
      BulkWriteResult bwr =  bulk.Execute();

4
我们的开发团队使用-MongoDB 3.0.0和-MongoDB C#驱动程序版本1.7.0.4714。MongoDB C#驱动程序版本1.7.0.4714抱怨InitializeUnorderedBulkOperation()未定义。 - CS Lewis
1
虽然这是被接受的答案,但它已经过时了。请参见https://dev59.com/F1sV5IYBdhLWcg3w4iE_#35688613。 - Mugen
上面的评论是不正确的,它是针对插入或更新文档操作的 -- 这并不等同于更新多个文档中的各个字段。 - Anthony Nichols

16

无法仅使用一个语句完成。

您有两个选择:

1)循环所有对象并执行upserts操作

2)找出需要更新的对象和需要插入的对象,然后执行批量插入和多重更新操作


10

如果您使用的是版本2.0的MongoDB.Driver,您可以使用BulkWriteAsync方法。

<!-- language: c# -->
// our example list
List<Products> products = GetProductsFromSomewhere();  

var collection = YourDatabase.GetCollection<BsonDocument>("products"); 

// initialise write model to hold list of our upsert tasks
var models = new WriteModel<BsonDocument>[products.Count];

// use ReplaceOneModel with property IsUpsert set to true to upsert whole documents
for (var i = 0; i < products.Count; i++){
    var bsonDoc = products[i].ToBsonDocument();
    models[i] = new ReplaceOneModel<BsonDocument>(new BsonDocument("aw_product_id", products[i].aw_product_id), bsonDoc) { IsUpsert = true };
};

await collection.BulkWriteAsync(models); 

9
那有帮助。非常感谢。在我的情况下,我得到一个IEnumerable作为要upsert的项。BulkWriteAsync需要一个IEnumerable,所以我将其修改为类似于这样的东西:var models = items.Select(item => new ReplaceOneModel<T>(new ExpressionFilterDefinition<T>(doc => doc.Id == item.Id), item) { IsUpsert = true }); - F. Fix

2

首先从集合中移除所有要插入的项,然后调用insert方法:

        var search = [];
        arrayToInsert.forEach(function(v, k) {
            search.push(v.hash); // my unique key is hash. you could use _id or whatever
        })
        collection.remove({
            'hash' : {
                $in : search
            }
        }, function(e, docs) {

            collection.insert(arrayToInsert, function(e, docs) {
                if (e) {
                    console.log("data failed to update ", e);
                }
                else {
                    console.log("data updated ");
                }
            });
        })

3
这种方法的一个问题是,在操作期间,读取集合的客户端可能会看到缺失的对象。如果您的客户可以容忍这一点,那么这似乎是一个合理的方法。 - Tony K.

1

UpdateFlags 是 C# 驱动程序中的枚举,它可以让您同时指定两个选项。就像任何其他标志枚举一样,您可以通过按位“或”来实现。

var flags = UpdateFlags.Upsert | UpdateFlags.Multi;

您可以在此处阅读有关枚举的文档(http://msdn.microsoft.com/zh-cn/library/cc138362.aspx),特别注意枚举类型作为位标志的部分。


1
一个更加美观和健壮的方法:
public interface IProjection
{
    Guid Id { get; }
    bool IsDeleted { get; }
}

public async Task UpsertManyAsync<TProjection>(IEnumerable<TProjection> replacements, CancellationToken cancellationToken)
    where TProjection : IProjection
{
    var requests = replacements.Select(replacement => new ReplaceOneModel<TProjection>(
        filter: new ExpressionFilterDefinition<TProjection>(projection => projection.Id == replacement.Id),
        replacement: replacement) { IsUpsert = true });

    await _context
        .GetCollection<TProjection>()
        .BulkWriteAsync(
            requests: requests,
            options: new BulkWriteOptions { IsOrdered = false },
            cancellationToken: cancellationToken);
}

真的很酷。你是从哪里想到这个的?只是为了帮助其他人:这是用于插入或更新单个数据项的代码:await collection.ReplaceOneAsync(x => x.Id == data.Id, data, new ReplaceOptions { IsUpsert = true }).ConfigureAwait(false); - yBother

1

使用mongoose@5.9.9 - 尝试初始化UnorderedBulkOp():

export const InfoFunc = (Infos: Infos[]) => {

  const bulk = InfoResult.collection.initializeUnorderedBulkOp();
  Infos.forEach((info: Infos) => bulk.find({ "Id": info.Id}).upsert().updateOne(info));
  bulk.execute();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接