【问题标题】:Mongo bulk find and update matched documents field in single query?Mongodb在单个查询中批量查找和更新匹配的文档字段?
【发布时间】:2016-10-19 07:08:55
【问题描述】:

我有一个图片库,其中元数据存储在 mongo 中。每次网站查询匹配一些存储的图像时,我希望文档shown 计数器字段增加。使用findAndModify 对于单个文档可以正常工作,但我看不到匹配多个文档并全部更新它们的好方法。

最新版本的 mongo 可以做到这一点吗?或者有什么推荐的最佳实践来实现这一点?

谢谢 fLo

文档格式很简单

{
"name"         : "img name",
"description"  : "some more info",
"size"         : "img size in bytes",
"shown"        : "count of times the image was selected by query",
"viewed"       : "count of times the image was clicked"
}

查询是一个简单的查找,然后使用光标循环结果并使用文档 ID 增加显示的计数..即

db.images.update(
   { _id: "xxxx" },
   { $inc: { shown: 1 } }
)

但我不希望获得 100 个文档,然后必须遍历每个文档以单独更新。希望在单个查询中执行查找和更新。

【问题讨论】:

  • 您能否向我们展示一些示例文档和您当前的更新查询?
  • 好的,更多信息添加到问题中。谢谢。

标签: mongodb mongodb-query


【解决方案1】:

为了提高性能,请利用 Bulk() API 来有效地批量更新集合,因为您将批量发送操作到服务器(例如,批量大小500)。这为您提供了更好的性能,因为您不会向服务器发送每个请求,而是每 500 个请求中只发送一次,从而使您的更新更加高效和快捷。

下面演示了这种方法,第一个示例使用了 Bulk() API 在 MongoDB 版本 >= 2.6 和

var images = [
    { "_id": 1, "name": "img_1.png" },
    { "_id": 2, "name": "img_2.png" }
    { "_id": 3, "name": "img_3.png" },
    ...
    { "_id": n, "name": "img_n.png" }
]

MongoDB 版本 >= 2.6 和 :

var bulk = db.images.initializeUnorderedBulkOp(),
    counter = 0;

images.forEach(function (doc) {    
    bulk.find({ "_id": doc._id }).updateOne({ 
        "$inc": { "shown": 1 }
    });

    counter++;
    if (counter % 500 === 0) {
        // Execute per 500 operations
        bulk.execute(); 
        // re-initialize every 500 update statements
        bulk = db.images.initializeUnorderedBulkOp();
    }
})
// Clean up remaining queue
if (counter % 500 !== 0) { bulk.execute(); }

下一个示例适用于新的 MongoDB 版本 3.2,它从 deprecated 开始具有 Bulk() API,并使用 bulkWrite() 提供了一组更新的 API。

MongoDB 3.2 及更高版本

var ops = [];
images.forEach(function(doc) {
    ops.push({
        "updateOne": {
            "filter": { "_id": doc._id },
            "update": {
                "$inc": { "shown": 1 }
            }
        }
    });

    if (ops.length === 500 ) {
        db.images.bulkWrite(ops);
        ops = [];
    }
})

if (ops.length > 0)  
    db.images.bulkWrite(ops);

【讨论】:

  • 太好了,非常感谢。现在将进行实验。我想我正在寻找类似批量写入的东西,但没有返回一些元数据..它返回了一个带有更新文档的游标,所以我可以像使用 find() 一样迭代它们。所以我可以在一个查询中实现更新和查找。但这似乎是不可能的。
  • 除了上面示例中使用的images 数组,您实际上可以使用find() 光标来迭代匹配的文档,因为它还有一个您可以使用的forEach() 方法。
  • 嗨@chridam 你能详细说明find()forEach() 方法吗?我在这里遇到同样的情况stackoverflow.com/questions/65342270/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-20
  • 2019-05-06
  • 1970-01-01
  • 2021-08-03
  • 1970-01-01
  • 2021-08-07
相关资源
最近更新 更多