使用$toLower更新MongoDB集合

46

我有一个已存在的MongoDB集合包含用户名称,其中用户名包含小写字母和大写字母。

我想更新所有用户名,使它们只包含小写字母。

我尝试了这个脚本,但它没有起作用:

db.myCollection.find().forEach(
 function(e) {
 e.UserName = $toLower(e.UserName);
 db.myCollection.save(e);
 }
)

"Didn't work" == "Nothing changed"? "没有起作用" == "没有改变"? - Phil
6个回答

75

MongoDB没有$toLower作为一个命令的概念。解决方案是运行一个大的for循环来遍历数据并逐个发出更新命令。

您可以在任何驱动程序或shell中执行此操作:

db.myCollection.find().forEach(
  function(e) {
    e.UserName = e.UserName.toLowerCase();
    db.myCollection.save(e);
  }
)

你也可以使用原子更新来替换保存操作:

db.myCollection.update({_id: e._id}, {$set: {UserName: e.UserName.toLowerCase() } })

同样地,您也可以从任何一个驱动程序中执行此操作,代码将非常相似。


编辑:Remon指出了一个很好的观点。$toLower 命令确实作为聚合框架的一部分存在,但这与更新无关。更新文档在这里


4
实际上,截至MongoDB 2.1版本,它确实有$toLower函数,但它只能在聚合框架中使用。;) - Remon van Vliet
谢谢提供的信息。最终我使用C#编写了一个控制台应用程序,而不是尝试为shell编写脚本。 - sveatch42
1
你测试过第二个表单(使用 $set 的原子更新)了吗?我无法使其工作(没有显示错误,但实际上没有更新任何文档)。我是 JavaScript 新手,所以可能只是犯了一个初学者的错误。 - Adam Monsen
1
虽然我来晚了,但是请问更新操作与保存操作有什么区别呢?因为 e.UserName 和 e._id 的值已经在内存中了,所以这两个操作都可以执行。 只是说,更新操作是针对 MongoDB 内部的原子性操作吗? - Drew R
1
@DrewR 我猜他的意思是对于任何其他字段的更改不会被覆盖,因为它只更新了“用户名”。但是,如果“用户名”已更改,我认为更改将被覆盖! - Neal Gokli
显示剩余2条评论

67

Mongo 4.2 开始,db.collection.update() 可以接受聚合管道,从而最终允许根据其自身值更新字段:

// { username: "Hello World" }
db.collection.updateMany(
  {},
  [{ $set: { username: { $toLower: "$username" } } }]
)
// { username: "hello world" }
  • 第一部分{}是匹配查询,用于过滤需要更新的文档(在此例中为所有文档)。

  • 第二部分[{ $set: { username: { $toLower: "$username" } } }]是更新聚合管道(注意方括号表示使用聚合管道):

    • $set是一个新的聚合操作符,此处修改了"username"字段的值。
    • 使用$toLower"username"字段的值转换为小写字母。

1
谢谢,这是唯一适用于大型数据集的解决方案。 - Kise
这是唯一有效的解决方案,请确保使用数组,这样MongoDB就知道要使用聚合操作。 - undefined

2
非常相似的解决方案,但这个在新版的Mongo 3.2上对我起作用了。 在Mongo Shell或类似的DB工具(如MongoChef)中执行以下操作!
db.tag.find({hashtag :{ $exists:true}}).forEach(
 function(e) {
   e.hashtag = e.hashtag.toLowerCase();
   db.tag.save(e);
});

1

使用已接受的解决方案,我知道对于一组元素做相同的操作非常简单,以防万一。

db.myCollection.find().forEach(
   function(e) {
      for(var i = 0; i < e.articles.length; i++) { 
          e.articles[i] = e.articles[i].toLowerCase(); 
      }
      db.myCollection.save(e); 
   }
)

1
有点晚了,但以下答案非常适用于mongo 3.4及以上版本。首先只获取那些大小写不同的记录,并批量更新这些记录。这个查询的性能提高了很多。
var bulk = db.myCollection.initializeUnorderedBulkOp();
var count = 0
db.myCollection.find({userId:{$regex:'.*[A-Z]'}}).forEach(function(e) {
 var newId = e.userId.toLowerCase();   
    bulk.find({_id:e._id}).updateOne({$set:{userId: newId}})
    count++
    if (count % 500 === 0) {
        bulk.execute();
        bulk = db.myCollection.initializeUnorderedBulkOp();
        count = 0;
    }
})
if (count > 0)  bulk.execute();

0

请注意确保集合中所有条目都存在该字段。如果不存在,则需要使用if语句,如下所示:

if (e.UserName) e.UserName = e.UserName.toLowerCase();

你是不是想说 if (e.UserName) 而不是 if (e.username) - pravin

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