用文档中另一个字段的值更新字段[重复]

Posted

技术标签:

【中文标题】用文档中另一个字段的值更新字段[重复]【英文标题】: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 中做到这一点?

【问题讨论】:

非常相似的问题:***.com/questions/3974985/… 【参考方案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
                
            
        );
    
);

【讨论】:

您复制粘贴 ***.com/a/14423151/2054072 失败 是的,@Deejay 如果您在 *** 的其他地方找到答案,请参考链接,这将有助于我们与老师本人/她自己进行交叉提问。

以上是关于用文档中另一个字段的值更新字段[重复]的主要内容,如果未能解决你的问题,请参考以下文章

使用表中另一个字段的值更新一个字段,其中字段值本身就是键

更新猫鼬文档中另一个数组内的数组中的字段

使用 T-SQL 中另一个(非 xml)列的值更新 XML

添加新字段,它是文档另一个字段的大小[重复]

查找一个字段与数组中另一个字段进行比较的文档

根据 Access DB 中另一个表中的多条记录计算字段的值