重命名Array中的子文档字段

fpe*_*a06 9 mongoose mongodb mongodb-query

考虑下面的文档,我如何将'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")
    }
Run Code Online (Sandbox Code Playgroud)

在mongo控制台我试过,这给了我好,但没有任何实际更新.

collection.update({"checkouts._id":ObjectId("55840f49e0b")},{ $rename: { "techId1": "techId" } });
Run Code Online (Sandbox Code Playgroud)

我也试过这个给我一个错误."不能使用该部分(checkouts.techId1的检查)来遍历元素"

collection.update({"checkouts._id":ObjectId("55856609e0b")},{ $rename: { "checkouts.techId1": "checkouts.techId" } })
Run Code Online (Sandbox Code Playgroud)

在猫鼬中,我尝试了以下内容.

collection.findByIdAndUpdate(id, { $rename: { "checkouts.techId1": "checkouts.techId" } }, function (err, data) {});
Run Code Online (Sandbox Code Playgroud)

collection.update({'checkouts._id': n1._id}, { $rename: { "checkouts.$.techId1": "checkouts.$.techId" } }, function (err, data) {});
Run Code Online (Sandbox Code Playgroud)

提前致谢.

Bla*_*ven 12

你最后很接近,但有一些东西丢失了.$rename在使用位置运算符时,您不能使用$set新名称和$unset旧名称.但是这里有另一个限制,因为它们都属于"结账"作为父路径,因为你不能同时做两件事.

你问题中的另一个核心是"遍历元素",这是你不能一次更新"全部"数组元素的一件事.好吧,不安全,无论如何都不会覆盖新数据.

您需要做的是"迭代"每个文档,并类似地迭代每个数组成员以"安全"更新.你不能真正迭代文档,并通过改动"保存"整个数组.当然不是在其他任何东西正在积极使用数据的情况下.

我个人会在MongoDB shell中运行这种操作,因为它是"一次性"(希望)的东西,这节省了编写其他API代码的开销.此外,我们在此处使用批量操作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();
Run Code Online (Sandbox Code Playgroud)

由于$set$unset操作是成对发生的,因此我们将每个执行的总批量大小保持为1000次操作,以保持客户端的内存使用率下降.

循环只查找要重命名的字段"存在"的文档,然后迭代每个文档的每个数组元素并提交两个更改.作为批量操作,在.execute()调用之前不会将这些内容发送到服务器,其中每次调用都会返回单个响应.这节省了大量的流量.

如果你坚持用猫鼬编码.请注意,.collection从核心驱动程序获取批量API方法需要一个访问者,如下所示:

var bulk = Model.collection.inititializeOrderedBulkOp();
Run Code Online (Sandbox Code Playgroud)

发送到服务器的唯一.execute()方法是方法,所以这是你唯一的执行回调:

bulk.exectute(function(err,response) {
    // code body and async iterator callback here
});
Run Code Online (Sandbox Code Playgroud)

并使用异步流量控制,而不是.forEach()async.each.

此外,如果您这样做,那么请注意,作为不受mongoose控制的原始驱动程序方法,您不会获得与使用mongoose方法相同的数据库连接感知.除非您确定已经建立了数据库连接,否则将此代码放在服务器连接的事件回调中:

mongoose.connection.on("connect",function(err) {
    // body of code
});
Run Code Online (Sandbox Code Playgroud)

但除此之外,这些是您真正需要的唯一真实(除了调用语法)更改.