【问题标题】:Update field with another field's value in the document [duplicate]用文档中另一个字段的值更新字段[重复]
【发布时间】:2011-02-06 02:12:39
【问题描述】:

我有一个集合 t1,其架构中有以下字段

_id, field1, field1

我想设置field2的值field1,比如sql:

update t1 set field1=field2;

我如何在 MongoDB 中做到这一点?

【问题讨论】:

标签: mongodb mongodb-query


【解决方案1】:

这里有好消息和坏消息。

坏消息是 AFAIK 你不能用一个 update() 调用来做到这一点 - mongo 不支持在更新中引用当前对象。

好消息是还有其他方法可以做到这一点,例如你可以运行一个 forEach 循环:

db.item.find(conditions...).snapshot().forEach( function (doc) {
  doc.field1 = doc.field2; 
  db.item.save(doc); 
});

您可以在管理 shell('mongo' 命令)中运行 forEach,或者通过特定驱动程序的某些方法(例如,在 PHP 中,我希望它可以与 mongodb.execute() 一起使用,如下所述: http://www.php.net/manual/en/mongodb.execute.php)

【讨论】:

  • 因为在 forEach 循环中调用了 save(),所以光标会被弄乱,函数可能会被每个文档多次调用。解决方法是在foreach之前调用snapshot():db.item.find(blah).snapshot().forEach()
  • 谢谢。我不知道要按预期工作的 snapshot() 要求。 TBH 一开始我很吃惊,一个名为“forEach”的函数并不能保证它的名字所暗示的含义(因为它类似于你在编程语言的集合库中可以找到的),但话又说回来,我知道可能有有很多理由这样做。
  • 没有原子方法可以做到这一点吗?这个解决方案似乎不是很健壮。
  • 对 mongodb 3.0 仍然如此吗?
  • @tacone 是的。如果您具有在停机时间运行的优势,那么两种解决方案都会为您提供相似的结果,但删除并重命名可能更容易知道您“完成”了。如果您有一个热同步,那么就地执行它可能是一个更简单的解决方案,因为您可能能够修复数据源,然后循环直到它运行干净。可能是。我之前没有在生产中进行过热同步 :) 只是想指出同步问题,真的。
【解决方案2】:

从 3.4 版本开始,我们可以使用$addFields 聚合管道操作符,无需客户端处理,这是最有效的方式。

db.collection.aggregate(
    [
        { "$addFields": { "field2": "$field1" }},
        { "$out": "collection" }
    ]
)

在 3.4 版之前,我们需要迭代 Cursor 对象并使用 $set 运算符添加具有现有“field1”值的新字段。您需要使用“批量”操作来执行此操作以获得最大效率。

MongoDB 3.2 弃用了 Bulk() 及其 associated methods,,因此从 3.2 开始,您需要使用 bulkWrite 方法。

var requests = [];
db.collection.find({}, { 'field1': 1 } ).snapshot().forEach(document => { 
    requests.push( { 
        'updateOne': {
            'filter': { '_id': document._id },
            'update': { '$set': { 'field2': document.field1 } }
        }
    });
    if (requests.length === 1000) {
        //Execute per 1000 operations and re-init
        db.collection.bulkWrite(requests);
        requests = [];
    }
});

if(requests.length > 0) {
    db.collection.bulkWrite(requests);
}

从版本 2.6 到 3.0,您可以使用 Bulk API。

var bulk = db.collection.initializeUnorderedBulOp();
var count = 0;

db.collection.find({}, { 'field1': 1 }).snapshot().forEach(function(document) { 
    bulk.find({ '_id': document._id }).updateOne( {
        '$set': { 'field2': document.field1 }
    });
    count++;
    if(count%1000 === 0) {
        // Excecute per 1000 operations and re-init
        bulk.execute();
        bulk = db.collection.initializeUnorderedBulkOp();
    }
})

// clean up queues
if(count > 0) {
    bulk.execute();
}

【讨论】:

  • 在我的测试中,我发现聚合方法要快得多。我的样本数据有 150K 文档,需要 5 秒才能重塑,而 .foreach 方法需要约 1.5 分钟。采用聚合方法时需要考虑哪些注意事项/陷阱?
  • aggregateout 不合适,如果需要 match:最终集合将仅包含匹配的文档。
【解决方案3】:

这可以通过:

db.nameOfCollection.find().forEach(
    function (elem) {
        db.nameOfCollection.update(
            {
                _id: elem._id
            },
            {
                $set: {
                    field2: elem.field1
                }
            }
        );
    }
);

【讨论】:

  • 您复制粘贴 stackoverflow.com/a/14423151/2054072 失败
  • 是的,@Deejay 如果您在 StackOverflow 的其他地方找到答案,请参考链接,这将有助于我们与老师本人/她自己进行交叉提问。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-10
  • 1970-01-01
  • 2018-04-19
  • 2017-01-29
  • 2020-12-29
  • 1970-01-01
  • 2011-04-27
相关资源
最近更新 更多