MongoDB - 將簡單字段更改為對象 (MongoDB - change simple field into an object)


問題描述

MongoDB ‑ 將簡單字段更改為對象 (MongoDB ‑ change simple field into an object)

在 MongoDB 中,我想將文檔的結構從:

{
    discount: 10,
    discountType: "AMOUNT"
}

更改為:

{
    discount: {
        value: 10,
        type: "AMOUNT"
    }
}

所以我嘗試在 mongo shell 中進行以下查詢:

db.discounts.update({},
    {
        $rename: {
             discount: "discount.value",
             discountType: "discount.type"
        }
    },
    {multi: true}
)

但它會拋出一個錯誤:

"writeError" : {
    "code" : 2,
    "errmsg" : "The source and target field for $rename must not be on the same path: discount: \"discount.value\""
}

我想到的解決方法是分兩步完成:首先將新結構分配給新字段(比如說 discount2),然後將其重命名為 discount。但也許有一種方法可以一步到位?


參考解法

方法 1:

The simplest way is to do it in two steps as you allude to in your question; initially renaming discount to a temporary field name so that it can be reused in the second step:

db.discounts.update({}, {$rename: {discount: 'temp'}}, {multi: true})
db.discounts.update({}, 
    {$rename: {temp: 'discount.value', discountType: 'discount.type'}},
    {multi: true})

方法 2:

The reason you are getting this error is because as mentioned in the documentation:

The $rename operator logically performs an $unset of both the old name and the new name, and then performs a $set operation with the new name. As such, the operation may not preserve the order of the fields in the document; i.e. the renamed field may move within the document.

And the problem with this is that you can't $set and $unset same field at the same time in MongoDB.

The solution will be to use bulk operations to update your documents in order to change their structure, and even in that case you need to use a field's name that doesn't exist in your collection. Of course the best way to do all this is using "Bulk" operations for maximum efficiency

MongoDB 3.2 or newer

MongoDB 3.2 deprecates Bulk() and its associated methods. You need to use the .bulkWrite() method.

var operations = [];
db.discounts.find().forEach(function(doc) {
    var discount = doc.discount; 
    var discountType = doc.discountType; 
    var operation = { 'updateOne': { 
        'filter': { '_id': doc._id }, 
        'update': { 
            '$unset': { 'discount': '', 'discountType': '' }, 
            '$set': { 'discounts.value': discount, 'discounts.type': discountType }
        }
    }};
    operations.push(operation); 
});

operations.push( {
    ordered: true,      
    writeConcern: { w: "majority", wtimeout: 5000 } 
});

db.discounts.bulkWrite(operations);

Which yields:

{
        "_id" : ObjectId("56682a02e6a2321d88f6d078"),
        "discounts" : {
                "value" : 10,
                "type" : "AMOUNT"
        }
}

MongoDB 2.6

Prior to MongoDB 3.2 and using MongoDB version 2.6 or newer you can use the "Bulk" API.

var  bulk = db.discounts.initializeOrderedBulkOp();
var count = 0;
db.discounts.find().forEach(function(doc) { 
    var discount = doc.discount; 
    var discountType = doc.discountType; 
    bulk.find( { '_id': doc._id } ).updateOne( {
        '$unset': { 'discount': '', 'discountType': '' }, 
        '$set': { 'discounts.value': discount, 'discounts.type': discountType }  }); 
   count++; 
   if (count % 500 === 0) {
       bulk.execute();
       bulk = db.discounts.initializeOrderedBulkOp(); 
    } 
})

if (count > 0)   
    bulk.execute();

This query yields same result as previous one.

方法 3:

Thanks to answers from Update MongoDB field using value of another field I figured out following solution:

db.discounts.find().snapshot().forEach(
    function(elem) {
        elem.discount = {
            value: elem.discount,
            type: elem.discountType
        }
        delete elem.discountType;
        db.discounts.save(elem);
    }
)

Which I quite like because the source code reads nicely but performance sucks for large amount of documents.

(by Lukasz WiktorJohnnyHKstyvaneLukasz Wiktor)

參考文件

  1. MongoDB ‑ change simple field into an object (CC BY‑SA 2.5/3.0/4.0)

#mongo-shell #mongoDB #mongodb-query






相關問題

Windows 是否有任何 mongo shell 擴展? (Is there any mongo shell extensions for windows?)

獲取查詢中所有文檔的大小 (Get the size of all the documents in a query)

如何從 java 程序運行 mongo 查詢? (How to run a mongo query from java program?)

MongoDB 更新 ObjectId 字段 (MongoDB update ObjectId field)

MongoDB - 將簡單字段更改為對象 (MongoDB - change simple field into an object)

mongoDB aggregate() 在電子郵件對象集合中查找電子郵件時間 (mongoDB aggregate() finding email times in a collection of email objects)

指定在 mongodb .js 腳本中使用哪個數據庫 (Specify which database to use in mongodb .js script)

命令失敗:MongoError:CMD_NOT_ALLOWED:配置文件 (command failed: MongoError: CMD_NOT_ALLOWED: profile)

有沒有辦法在 MongoDB 的一個語句中添加遞增的 id? (Is there a way to add an incrementing id in one statement in MongoDB?)

運行 rs.initiate() 後 mongodb 副本集錯誤“...replSetHeartbeat 需要身份驗證...” (mongodb replica set error "...replSetHeartbeat requires authentication..." after running rs.initiate())

將 mongo shell 連接到受限的 MongoDB Atlas 數據庫? (Connect mongo shell to restricted MongoDB Atlas Database?)

[MongoDB]:更改所有數組字段中的值類型 ([MongoDB]: Changing the type of values in all array fields)







留言討論