【问题标题】:Update with expression instead of value用表达式而不是值更新
【发布时间】:2012-04-16 19:52:07
【问题描述】:

我对 MongoDB 完全陌生...我缺少一个“新手”标签,所以专家不必看到这个问题。

我正在尝试使用表达式更新集合中的所有文档。我期望解决的查询是:

db.QUESTIONS.update({}, { $set: { i_pp : i_up * 100 - i_down * 20 } }, false, true);

但是,这会导致以下错误消息:

ReferenceError: i_up is not defined (shell):1

同时数据库吃这个也没问题:

db.QUESTIONS.update({}, { $set: { i_pp : 0 } }, false, true);

我必须一次完成一份文件吗?这似乎过于复杂了。

更新 感谢 Sergio Tulentsev 告诉我它不起作用。现在,我真的很挣扎如何做到这一点。我向乐于助人的灵魂提供 500 Profit Points,他们可以以 MongoDB 理解的方式编写此代码。如果您在our forum 注册,我可以在那里将利润点添加到您的帐户。

【问题讨论】:

    标签: mongodb


    【解决方案1】:

    我在搜索 MongoDB 等价的 SQL 时遇到了这个问题:

    update t
    set c1 = c2
    where ...
    

    Sergio 是正确的,您不能在直接更新中引用另一个属性作为值。但是,db.c.find(...) 返回一个cursor and that cursor has a forEach method

    对 MongoDB 的查询返回一个游标,可以迭代检索 结果。查询的确切方式因语言驱动程序而异。 下面的详细信息侧重于来自 MongoDB shell 的查询(即 mongo 进程)。

    shell find() 方法返回一个游标对象,然后我们可以对其进行迭代以从结果中检索特定文档。我们用 用于此目的的 hasNext()next() 方法。

    for( var c = db.parts.find(); c.hasNext(); ) {
       print( c.next());
    }
    

    另外,在 shell 中,forEach() 可以与光标一起使用:

    db.users.find().forEach( function(u) { print("user: " + u.name); } );
    

    所以你可以这样说:

    db.QUESTIONS.find({}, {_id: true, i_up: true, i_down: true}).forEach(function(q) {
        db.QUESTIONS.update(
            { _id: q._id },
            { $set: { i_pp: q.i_up * 100 - q.i_down * 20 } }
        );
    });
    

    在不离开 MongoDB 的情况下一次更新它们。

    如果您使用驱动程序连接到 MongoDB,那么应该有某种方法可以将 JavaScript 字符串发送到 MongoDB;例如,对于 Ruby 驱动程序,您将使用 eval

    connection.eval(%q{
        db.QUESTIONS.find({}, {_id: true, i_up: true, i_down: true}).forEach(function(q) {
            db.QUESTIONS.update(
                { _id: q._id },
                { $set: { i_pp: q.i_up * 100 - q.i_down * 20 } }
            );
        });
    })
    

    其他语言应该类似。

    【讨论】:

    • eval 已弃用,有什么替代品或替代品?直接执行,不用eval?效果一样吗?
    • @CaptainObvious:我不再使用 MongoDB,所以我不确定。 this other answer 有帮助吗?
    • 不幸的是,没有。 docs.mongodb.com/v3.2/reference/command 声明 eval 也已被弃用。不过感谢您的努力,我会努力解决的。
    【解决方案2】:

    您不能在更新中使用表达式。或者,您不能使用依赖于文档字段的表达式。简单的自包含数学表达式就可以了(例如2 * 2)。

    如果您想为所有文档设置一个新字段,该字段是其他字段的函数,您必须遍历它们并手动更新。多次更新在这里无济于事。

    【讨论】:

    • 您不能向 MongoDB 发送一些 db.questions.find(...).forEach(function(q) { /* db.questions.update(...) to patch 'q' in-place */ }) JavaScript 以避免离开数据库吗?我已经做了类似的事情来避免从数据库中往返。
    • @muistooshort:理论上,你可以。我自己没有这样做。
    • 我刚刚做了一些测试,它似乎可以工作,the docs 甚至建议db.questions.find(...) 将是一个游标,因此它不会尝试为forEach 调用实例化整个数组.
    • 你在shell中尝试吗?我更想知道我必须在 (ruby) 驱动程序中使用什么命令。
    • 您可以使用 db.eval() 直接发送 JavaScript。我将在今晚晚些时候将其写成答案,可能对未来的搜索者有用。
    【解决方案3】:

    Rha7 给出了一个好主意,但是上面的代码如果不定义临时变量是行不通的。

    此示例代码基于“生日”字段生成年龄(闰年)的近似计算,并将该值插入到所有不包含此类的文档的合适字段中:

    db.employers.find({age: {$exists: false}}).forEach(function(doc){
        var new_age = parseInt((ISODate() - doc.birthday)/(3600*1000*24*365));
        db.employers.update({_id: doc._id}, {$set: {age: new_age}});
    });
    

    【讨论】:

      【解决方案4】:

      //the only differnce is to make it look like and aggregation pipeline
      db.table.updateMany({}, [{
            $set: {
              col3:{"$sum":["$col1","$col2"]}
            },
          }]
       )

      【讨论】:

        【解决方案5】:

        从呼叫者 id 开头删除“00”的示例:

        db.call_detail_records_201312.find(
            { destination: /^001/ }, 
            { "destination": true }
        ).forEach(function(row){
            db.call_detail_records_201312.update(
                { _id: row["_id"] },
                { $set: {
                        destination: row["destination"].replace(/^001/, '1')
                    }
                }
            )
        });
        

        【讨论】:

        • 只需更新row.destination 并使用row.save 代替db.call...update(
        • 非常抱歉:正确的 sintax 是 db.call.save(row) 而不是 row.save
        猜你喜欢
        • 1970-01-01
        • 2020-11-09
        • 1970-01-01
        • 1970-01-01
        • 2012-05-15
        • 2023-01-17
        • 2019-09-13
        • 2023-03-15
        • 2020-07-11
        相关资源
        最近更新 更多