在Mongodb中将字段乘以值

31

我一直在寻找一种创建更新语句的方法,可以使用表达式修改现有数值字段。例如,如果我有一个名为Price的字段,是否可以进行更新操作以将价格设置为原来的50%?

所以,对于给定的 { Price : 19.99 }

我想执行 db.collection.update({tag : "refurb"}, {$set {Price : Price * 0.50 }}, false, true);

这个能否实现,或者我必须将价值读回客户端进行修改,然后再进行更新?我想问的问题是,更新操作中是否可以使用表达式,并且它们是否可以引用正在被更新的文档。


3
请注意,在即将发布的MongoDB版本中,您几乎可以不花费任何代价地执行此操作,而无需进行所有这些eval和DB锁定。请检查并可能接受新答案,以便新的开发人员不使用过时的信息。 - Salvador Dali
4个回答

37

你可以使用db.eval()来运行服务器端代码。

db.eval(function() { 
    db.collection.find({tag : "refurb"}).forEach(function(e) {
        e.Price = e.Price * 0.5;
        db.collection.save(e);
    });
});

请注意这会阻塞数据库,因此最好进行查找更新操作对。

参见https://docs.mongodb.com/manual/core/server-side-javascript/


谢谢。你是说我应该像你展示的那样使用eval(),还是应该在客户端使用find/update来避免阻塞? - Brad
1
@Brad,你应该评估这个特定更新操作的总成本。如果操作将会很长(集合中有许多文档需要更新),你永远不应该使用eval。首先查询文档然后更新它而不是原地更新没有太大的开销。特别是对于大型文档查询限制字段并使用覆盖索引功能(http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-CoveredIndexes)。 - pingw33n
1
很酷,谢谢。我也看到eval()不能与分片一起使用,所以长期来看这不适合我。Nolock是一个有趣的补充。 - Brad
这不是原子操作。请参考Salvador的答案,使用$mul来实现原子操作解决方案。 - JohnnyHK

23

在新的Mongo 2.6.x中,有一个$mul运算符。 它可以通过以下语法将字段的值乘以数字。

{
  $mul: { field: <number> }
}

因此,在您的情况下,您需要执行以下操作:

db.collection.update(
  { tag : "refurb"},
  { $mul: { Price : 0.5 } }
);

5

Mongo 4.2 开始,db.collection.update() 可以接受聚合管道,最终允许根据其他字段更新字段的内容:

// { price: 19.99 }
// { price: 2.04  }
db.collection.update(
  {},
  [{ $set: { price: { $multiply: [ 0.5, "$price" ] } } }],
  { multi: true }
)
// { price: 9.995 }
// { price: 1.02  }
  • 第一部分 {} 是匹配查询,过滤要更新的文档(在本例中是所有文档)。

  • 第二部分[{ $set: { price: ... } }] 是更新聚合管道(注意使用聚合管道的方括号符号)。$set 是一个新的聚合操作符,是$addFields 的别名。请注意,price 直接根据其自身值 ($price) 进行修改。

  • 不要忘记 { multi: true },否则只会更新第一个匹配的文档。


-1

嗯,这可以通过原子操作 $set 实现。

你有几个选项:

  • 使用 pingw33n 提出的 eval() 解决方案
  • 检索要修改的文档以获取当前值,并使用 set 进行修改
  • 如果您的操作频率很高,您可能希望确保在获取其值时文档未更改(使用上一个解决方案),因此您可能希望使用 findAndModify(请参见 this page 以获得灵感)操作。

这真的取决于您的上下文:对于数据库压力非常低的情况,我会选择 pingw33n 的解决方案。对于操作频率非常高的情况,我会使用第三种解决方案。


谢谢。我不认为findAndModify会允许使用表达式,这正是我的主要目标。 - Brad
@Brad,findAndModify 允许使用表达式,但它允许执行两个阶段的操作:首先获取项目,然后仅当自检索以来未更改时(使用您的 50% 减少)才更新它。 - kamaradclimber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接