在日常 key 中添加条目时,有两种情况需要处理
a) 添加新的一天 -: { "day": 6, "sessions": 300}
b) 更新特定日期的字段,例如重新计算 session 并将第二天更改为 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
}}
)
这看起来一点也不优雅。知道我们是否可以使用单个查询来完成此操作吗? (也许是批量操作)
最佳答案
正如您的标题所示,解决方案是使用 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();
因此,虽然有 3 个操作可以处理批处理中的所有情况,但只有向服务器发出的每个请求和一个响应。另请注意,只有一组操作中的一个才能实际修改或创建某些数据。
基本情况是匹配并修改所需元素所在的位置。如果满足条件,则更新数组元素。 “增量”,在此示例中,因为之前提到了预聚合数据,并且通常增量已经存在的值在这种情况下是有意义的。
接下来的步骤是测试数组成员不存在的位置,然后当然将新元素$push
到数组中。
最后是“upsert”条件,之前的语句中故意没有使用该条件。这故意不查看数组元素,而只是查看文档的唯一属性。只有这样,当不满足查询条件时,才可以安全地考虑“更新插入”,因为在意图添加到数组时,在检查中添加数组元素可能会创建一个新文档。
$setOnInsert
这里的修饰符确保不会对实际找到的任何文档进行任何更改,并且唯一发生的操作是在实际发生“upsert”时。
因此,控制对数组的添加并考虑“更新插入”行为需要这些基本步骤。然而,“批量操作”的使用使得这成为对服务器的单个请求/响应以及确保只有一个操作会产生任何效果的一般逻辑。
它非常高效,并且消除了在请求和响应中与服务器来回通信时需要执行每个操作的开销,直到其中一个操作成功为止。
此控件无法进行一次查询/更新。但一个请求是可能的,这就是它的作用。
关于mongodb - 嵌入文档中的批量更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33706185/