嵌入式文档中的批量更新

时间:2015-11-14 07:30:21

标签: mongodb mongodb-query

在每日密钥中添加条目时,有两种情况需要处理

a)增加新的一天 - :{“day”:6,“sessions”:300}

b)更新特定日期的字段,例如会话重新计算并在第2天更改为105

示例架构

{
    _id: "201010/site-1/apache_pb.gif",
    metadata: {
        date: ISODate("2000-10-00T00:00:00Z"),
        site: "site-1",
        page: "/apache_pb.gif" },
    daily: [
        { "day": 1, "sessions": 300, "bounces": 10},
        { "day": 2, "sessions": 100, "bounces": 5},
        { "day": 3, "sessions": 10},
        { "day": 4, "sessions": 100, "bounces": 4}
    ]
}

我最初尝试过这个

db.monthly.update(
    { "metadata.page": "/apache_pb.gif", "daily.day": "6" },
    { "$set": { "daily.$.sessions": 300 } },
    { "upsert": true }
)

但是如果那天不存在,我会得到“位置操作员没有找到查询所需的匹配”

我能看到正在发生的操作的唯一方法是

db.monthly.update(
    { "metadata.page": "/apache_pb.gif", "daily.day": "6" },
    { "$set": { "daily.$.sessions": 300 } }
)

并且在响应中,如果我被修改为0,那么我这样做

db.monthly.update(
    { "metadata.page": "/apache_pb.gif" },
    { "$push": { "daily": {
        "day": "6",
        "sessions": 300
    }}
)

这看起来并不优雅。我们是否可以使用单个查询来做到这一点? (也许是BulkOperations)

1 个答案:

答案 0 :(得分:3)

正如您的标题所示,解决方案是使用Bulk Operations API,它可用于通过单个响应在单个请求中发送多个操作:

var bulk = db.monthly.initializeOrderedBulkOp();

// Attempt to match and modify
bulk.find({ "metadata.page": "/apache_pb.gif", "daily.day": 6 }).updateOne({
    "$inc": { "daily.$.sessions": 600 }
});

// Attempt to push where matched and array element does not exist
bulk.find({ "metadata.page": "/apache_pb.gif", "daily.day": { "$ne": 6 } }).updateOne({
    "$push": { "daily": { "day": 6, "sessions": 600 } }
});

// upsert and only modify on actual creation
bulk.find({ "metadata.page": "/apache_pb.gif" }).upsert().updateOne({
    "$setOnInsert": {
        "_id": "201010/site-1/apache_pb.gif",
        "metadata": {
            "date": ISODate("2000-10-00T00:00:00Z"),
            "site": "site-1",
            "page": "/apache_pb.gif" 
        },
        "daily": [
            { "day": 6, "sessions": 600 }
        ]
    }
});

// The only time the server is actually touched.
bulk.execute();

因此,虽然有三个操作来处​​理批处理中的所有情况,但只有每个请求都发送到服务器和一个响应。另请注意,只有一个的操作集才能实际修改或创建一些数据。

基本情况是匹配和修改所需元素的存在位置。如果满足条件,则更新数组元素。 “增量”,在这个例子中,因为之前提到过预先聚合的数据,并且通常递增已经存在的值在这种情况下是有意义的。

以下步骤是测试数组成员不存在的位置,然后当然$push将新元素放入数组中。

最后存在“upsert”条件,这在以前的陈述中是有目的的。这故意不是查看数组元素,而是查看文档的唯一属性。只有这样才能安全地在不满足查询条件时考虑“upsert”,因为在意图添加到数组时,在检查中添加数组元素可能会创建一个新文档。

这里的$setOnInsert修饰符确保不会对实际找到的任何文档进行任何更改,并且发生的唯一操作是实际发生“upsert”时。

因此,控制对数组的添加并消除“upsert”行为需要这些基本步骤。但是,“批量操作”的使用使得它成为服务器的单个请求/响应以及确保只有一个操作会产生任何影响的一般逻辑。

它非常高效,并消除了在请求和响应中与服务器来回通信时需要执行每个操作的开销,直到其中一个操作成功。

此控件无法进行一次查询/更新。但是有一个请求是可能的,这就是它的作用。