【问题标题】:MongoDB: Updating documents using data from the same document [duplicate]MongoDB:使用来自同一文档的数据更新文档[重复]
【发布时间】:2011-04-16 20:08:33
【问题描述】:

我有一个文档列表,每个文档都有 lat 和 lon 属性(等等)。

{ 'lat': 1, 'lon': 2, someotherdata [...] } 
{ 'lat': 4, 'lon': 1, someotherdata [...] }
[...]

我想修改它,使它看起来像这样:

{ 'coords': {'lat': 1, 'lon': 2}, someotherdata [...]} 
{ 'coords': {'lat': 4, 'lon': 1}, someotherdata [...]}
[...]

到目前为止,我得到了这个:

db.events.update({}, {$set : {'coords': {'lat': db.events.lat, 'lon': db.events.lon}}}, false, true)

但它将 db.events.latdb.events.lon 视为字符串。如何引用文档的属性?

干杯。

【问题讨论】:

标签: mongodb


【解决方案1】:

Neils answer。只是为了让人们知道,如果你可以说像 Robomongo 这样的远程 shell,你不能在大型数据库上运行它。您将需要 ssh 进入您实际服务器的 mongo shell。如果您愿意进行更新,也可以这样做。

db.Collection.find({***/ possible query /***}).toArray().forEach(
  function(obj){
    obj.item = obj.copiedItem;
    obj.otherItem = obj.copiedItem;
    obj.thirdItem = true;
    obj.fourthItem = "string";
    db.Collection.update({_id: obj._id}, obj);
  }
);

【讨论】:

  • 这只是对 Niels van der Rest 发布的答案的评论吗?
  • 这是一条评论,但也想发布一个比您在添加 cmets 部分中留下的更具描述性的答案。我还想根据他的回答发布我使用的代码,所以如果人们懒得去查找这些链接,那么这里就是一个工作模型。当然只适用于服务器。或者如果您只需要更新
【解决方案2】:

只要您可以创建数据副本,这里就可以使用聚合框架作为替代方案。如果您希望使用其他运算符,您还可以选择对数据执行更多操作,但您唯一需要的是$project。它在空间方面有点浪费,但可能更快,更适合某些用途。为了说明,我将首先将一些示例数据插入到foo 集合中:

db.foo.insert({ 'lat': 1, 'lon': 2, someotherdata : [1, 2, 3] })
db.foo.insert({ 'lat': 4, 'lon': 1, someotherdata : [4, 5, 6] })

现在,我们只需使用$project 重新处理latlon 字段,然后将它们发送到newfoo 集合:

db.foo.aggregate([
    {$project : {_id : "$_id", "coords.lat" : "$lat", "coords.lon" : "$lon", "someotherdata" : "$someotherdata" }},
    { $out : "newfoo" }
])

然后检查 newfoo 以获取我们更改的数据:

db.newfoo.find()
{ "_id" : ObjectId("544548a71b5cf91c4893eb9a"), "someotherdata" : [ 1, 2, 3 ], "coords" : { "lat" : 1, "lon" : 2 } }
{ "_id" : ObjectId("544548a81b5cf91c4893eb9b"), "someotherdata" : [ 4, 5, 6 ], "coords" : { "lat" : 4, "lon" : 1 } }

一旦您对新数据感到满意,您就可以使用renameCollection() 命令删除旧数据并使用旧名称下的新数据:

> db.newfoo.renameCollection("foo", true)
{ "ok" : 1 }
> db.foo.find()
{ "_id" : ObjectId("544548a71b5cf91c4893eb9a"), "someotherdata" : [ 1, 2, 3 ], "coords" : { "lat" : 1, "lon" : 2 } }
{ "_id" : ObjectId("544548a81b5cf91c4893eb9b"), "someotherdata" : [ 4, 5, 6 ], "coords" : { "lat" : 4, "lon" : 1 } }

最后一点 - 在完成SERVER-7944 之前,您无法按照this answer 中的建议通过提示_id 索引来执行相当于快照的操作,因此如果有活动,您最终可能会多次点击文档其他地方使它移动。由于您在此示例中插入了 _id 字段,因此任何此类事件都会导致唯一密钥冲突,因此您不会最终得到欺骗,但您可能有一个“旧”版本的文档。与往常一样,在删除数据之前彻底检查您的数据,并最好进行备份。

【讨论】:

    【解决方案3】:

    我们可以使用 Mongo 脚本来动态操作数据。它对我有用!

    我使用这个脚本来更正我的地址数据。

    当前地址示例:“第五大道 12 号”。

    我想删除最后一个多余的逗号,即预期的新地址“No.12, FIFTH AVENUE”。

    var cursor = db.myCollection.find().limit(100);
    
    while (cursor.hasNext()) {
      var currentDocument = cursor.next();
    
      var address = currentDocument['address'];
      var lastPosition = address.length - 1;
    
      var lastChar = address.charAt(lastPosition);
    
      if (lastChar == ",") {
    
        var newAddress = address.slice(0, lastPosition);
    
    
        currentDocument['address'] = newAddress;
    
        db.localbizs.update({_id: currentDocument._id}, currentDocument);
    
      }
    }
    

    希望这会有所帮助!

    【讨论】:

      【解决方案4】:

      更新:如果您只需更改文档的结构而不更改值,请参阅gipset's answer 以获得很好的解决方案。


      根据Update documentation page 上的一条(现在不可用)评论,您无法从update() 中引用当前文档的属性。

      您必须像这样遍历所有文档并更新它们:

      db.events.find().snapshot().forEach(
        function (e) {
          // update document, using its own properties
          e.coords = { lat: e.lat, lon: e.lon };
      
          // remove old properties
          delete e.lat;
          delete e.lon;
      
          // save the updated document
          db.events.save(e);
        }
      )
      

      这样的功能也可以用于 map-reduce 作业或服务器端 db.eval() 作业,具体取决于您的需要。

      【讨论】:

      • 根据对类似问题的评论,我发现在某些情况下无法对您的查找结果进行快照()会使光标陷入无限循环。 update field with another field's value试试db.events.find().snapshot().forEach(// ..function goes here)
      • @AshleyRaiteri 谢谢,我已经在答案中包含了对snapshot() 的调用。很好的收获:)
      • 为什么必须在forEach 块内做events.save()?为什么不把events.update()放在同一个地方呢?
      • 这很有帮助。我将它们保存在一个新集合db.event_new.save() 中,然后将它们重命名为db.event.renameCollection("event_old")db.event_new.renameCollection("event"),以便将其替换,但如果出现任何错误,它不会影响原始数据。
      • @AlbertRothman,对于 mongo 4.0,使用 .hint({_id: 1}) 而不是 .snapshot()
      【解决方案5】:

      $rename 运算符(在此问题发布一个月后推出)使您无需修改​​值即可轻松完成此类操作。

      插入一些测试文档

      db.events.insert({ 'lat': 1, 'lon': 2, someotherdata: [] })
      db.events.insert({ 'lat': 4, 'lon': 1, someotherdata: [] })
      

      使用$rename 运算符

      db.events.update({}, {$rename: {'lat': 'coords.lat', 'lon': 'coords.lon'}}, false, true)
      

      结果

      db.events.find()
      {
          "_id" : ObjectId("5113c82dd28c4e8b79971add"),
          "coords" : {
              "lat" : 1,
              "lon" : 2
          },
          "someotherdata" : [ ]
      }
      {
          "_id" : ObjectId("5113c82ed28c4e8b79971ade"),
          "coords" : {
              "lat" : 4,
              "lon" : 1
          },
          "someotherdata" : [ ]
      }
      

      【讨论】:

        【解决方案6】:

        来自 CLI?我认为您必须先将值提取出来并将值分配给变量。然后运行你的更新命令。

        或者(我没有尝试过)从字符串中删除 'db'。 events.latevents.lon 如果有效,您仍然会有多个值,“lat”和“lon”的旧值以及您创建的新数组。

        【讨论】:

          猜你喜欢
          • 2020-11-19
          • 1970-01-01
          • 2016-08-18
          • 2016-11-04
          • 2016-11-23
          • 2016-11-18
          • 2014-01-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多