简体   繁体   English

MongoDB-将简单字段更改为对象

[英]MongoDB - change simple field into an object

In MongoDB, I want to change the structure of my documents from: 在MongoDB中,我想从以下位置更改文档的结构:

{
    discount: 10,
    discountType: "AMOUNT"
}

to: 至:

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

so I tried following query in mongo shell: 所以我尝试在mongo shell中执行以下查询:

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

but it throws an error: 但是它抛出一个错误:

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

A workaround that comes to my mind is to do it in 2 steps: first assign the new structure to a new field (let's say discount2 ) and then rename it to discount . 我想到的一种解决方法是分两个步骤进行操作:首先将新结构分配给新字段(比如discount2 ),然后将其重命名为discount But maybe there is a way to do it one step? 但是也许有一种方法可以一步一步完成?

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: 最初将discount重命名为临时字段名称,以便可以在第二步中重新使用它:

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

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. $rename运算符在逻辑上执行旧名称和新名称的$unset ,然后使用新名称执行$set操作。 As such, the operation may not preserve the order of the fields in the document; 因此,该操作可能不会保留文档中字段的顺序; ie 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 . 这样做的问题是,您不能在MongoDB同时$set$unset同一字段。

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或更高版本

MongoDB 3.2 deprecates Bulk() and its associated methods. MongoDB 3.2不赞成Bulk()及其关联的方法。 You need to use the .bulkWrite() method. 您需要使用.bulkWrite()方法。

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 MongoDB 2.6

Prior to MongoDB 3.2 and using MongoDB version 2.6 or newer you can use the "Bulk" API. 在MongoDB 3.2之前且使用MongoDB 2.6或更高版本的您可以使用“批量” 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. 此查询产生与上一个相同的结果。

Thanks to answers from Update MongoDB field using value of another field I figured out following solution: 感谢来自使用另一个字段的值的Update MongoDB字段的答案,我找出了以下解决方案:

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. 我非常喜欢它,因为源代码读起来很不错,但是对于大量文档而言性能却很差。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM