重命名数组中的子文档字段
Posted
技术标签:
【中文标题】重命名数组中的子文档字段【英文标题】:Rename a sub-document field within an Array 【发布时间】:2015-10-19 16:57:10 【问题描述】:考虑到下面的文档,我如何将“techId1”重命名为“techId”。我尝试了不同的方法,但无法让它发挥作用。
"_id" : ObjectId("55840f49e0b"),
"__v" : 0,
"accessCard" : "123456789",
"checkouts" : [
"user" : ObjectId("5571e7619f"),
"_id" : ObjectId("55840f49e0bf"),
"date" : ISODate("2015-06-19T12:45:52.339Z"),
"techId1" : ObjectId("553d9cbcaf")
,
"user" : ObjectId("5571e7619f15"),
"_id" : ObjectId("55880e8ee0bf"),
"date" : ISODate("2015-06-22T13:01:51.672Z"),
"techId1" : ObjectId("55b7db39989")
],
"created" : ISODate("2015-06-19T12:47:05.422Z"),
"date" : ISODate("2015-06-19T12:45:52.339Z"),
"location" : ObjectId("55743c8ddbda"),
"model" : "model1",
"order" : ObjectId("55840f49e0bf"),
"rid" : "987654321",
"serialNumber" : "AHSJSHSKSK",
"user" : ObjectId("5571e7619f1"),
"techId" : ObjectId("55b7db399")
在 mongo 控制台中,我试过了,但没有任何实际更新。
collection.update("checkouts._id":ObjectId("55840f49e0b"), $rename: "techId1": "techId" );
我也试过这个,这给了我一个错误。 “不能使用部分(checkouts.techId1的结账)来遍历元素”
collection.update("checkouts._id":ObjectId("55856609e0b"), $rename: "checkouts.techId1": "checkouts.techId" )
在猫鼬中,我尝试了以下方法。
collection.findByIdAndUpdate(id, $rename: "checkouts.techId1": "checkouts.techId" , function (err, data) );
和
collection.update('checkouts._id': n1._id, $rename: "checkouts.$.techId1": "checkouts.$.techId" , function (err, data) );
提前致谢。
【问题讨论】:
【参考方案1】:最后你很接近,但缺少一些东西。使用位置运算符时不能$rename
,而是需要$set
新名称和$unset
旧名称。但是这里还有另一个限制,因为它们都属于“结帐”作为父路径,因为您不能同时执行这两个操作。
您问题中的另一条核心线是“遍历元素”,这是您无法一次更新“所有”数组元素的一件事。好吧,这并不安全,而且无论如何都不会覆盖新数据。
您需要做的是“迭代”每个文档并类似地迭代每个数组成员以“安全地”更新。您不能真正迭代文档并通过更改“保存”整个数组。当然不是在其他任何东西都在积极使用数据的情况下。
如果可以的话,我个人会在 MongoDB shell 中运行这种操作,因为它是“一次性”(希望)的事情,这样可以节省编写其他 API 代码的开销。此外,我们在这里使用Bulk Operations API 以使其尽可能高效。使用 mongoose 需要更多的挖掘来实现,但仍然可以完成。但这里是 shell 列表:
var bulk = db.collection.initializeOrderedBulkOp(),
count = 0;
db.collection.find( "checkouts.techId1": "$exists": true ).forEach(function(doc)
doc.checkouts.forEach(function(checkout)
if ( checkout.hasOwnProperty("techId1") )
bulk.find( "_id": doc._id, "checkouts._id": checkout._id ).updateOne(
"$set": "checkouts.$.techId": checkout.techId1
);
bulk.find( "_id": doc._id, "checkouts._id": checkout._id ).updateOne(
"$unset": "checkouts.$.techId1": 1
);
count += 2;
if ( count % 500 == 0 )
bulk.execute();
bulk = db.collection.initializeOrderedBulkOp();
);
);
if ( count % 500 !== 0 )
bulk.execute();
由于 $set
和 $unset
操作成对发生,我们将每次执行的总批处理大小保持在 1000 次操作,以降低客户端的内存使用量。
循环只是查找要重命名的字段“存在”的文档,然后迭代每个文档的每个数组元素并提交两个更改。作为批量操作,这些操作在调用 .execute()
之前不会发送到服务器,每次调用也会返回一个响应。这样可以节省大量流量。
如果您坚持使用 mongoose 进行编码。请注意,需要 .collection
访问器才能从核心驱动程序访问 Bulk API 方法,如下所示:
var bulk = Model.collection.inititializeOrderedBulkOp();
唯一发送到服务器的是.execute()
方法,所以这是你唯一的执行回调:
bulk.exectute(function(err,response)
// code body and async iterator callback here
);
并使用异步流控制代替.forEach()
如async.each。
另外,如果您这样做,请注意,作为不受 mongoose 管理的原始驱动程序方法,您不会获得与使用 mongoose 方法相同的数据库连接意识。除非您确定数据库连接已经建立,否则最好将此代码放在服务器连接的事件回调中:
mongoose.connection.on("connect",function(err)
// body of code
);
但除此之外,这些是您真正需要的唯一真正的(除了调用语法)更改。
【讨论】:
这正是我想要的。非常感谢您的详细解释,阅读您的答案后绝对理解这个概念。 @fpena06 我注意到我错过的一件事是添加检查“techId1”元素是否确实存在于正在处理的数组元素上。假设它总是在那里可能是可以的,但以防万一我在包装数组迭代块的代码中添加了条件检查。【参考方案2】:这对我有用,我创建了这个查询来执行这个过程并分享它,(虽然我知道这不是最优化的方式):
首先,创建一个aggregate
,即(1)$match
具有checkouts
数组字段的文档,其中techId1
作为每个子文档的键之一。 (2)$unwind
checkouts
字段(从输入文档解构数组字段以输出每个元素的文档),(3)添加techId
字段(与$addFields
),(4)@ 987654325@旧的techId1
字段,(5)$group
_id
的文档再次将checkout
子文档按其_id
分组,以及(6)将这些聚合的结果写在一个temporal
集合(与$out
)。
const collection = 'yourCollection'
db[collection].aggregate([
$match:
'checkouts.techId1': '$exists': true
,
$unwind:
path: '$checkouts'
,
$addFields:
'checkouts.techId': '$checkouts.techId1'
,
$project:
'checkouts.techId1': 0
,
$group:
'_id': '$_id',
'checkouts': $push: 'techId': '$checkouts.techId'
,
$out: 'temporal'
])
然后,您可以从这个temporal
集合到$merge
将具有修改的checkouts
字段的文档再次聚合到您的原始集合。
db.temporal.aggregate([
$merge:
into: collection,
on: "_id",
whenMatched:"merge",
whenNotMatched: "insert"
])
【讨论】:
以上是关于重命名数组中的子文档字段的主要内容,如果未能解决你的问题,请参考以下文章