MongoDB:使用同一文档中的数据更新文档

时间:2010-09-24 14:52:44

标签: mongodb

我有一个文档列表,每个文档都有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.lat db.events.lon 视为字符串。如何引用文档的属性?

干杯。

6 个答案:

答案 0 :(得分:204)

更新:如果您只需更改文档的结构而不更改值,请参阅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()作业,具体取决于您的需求。

答案 1 :(得分:53)

$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" : [ ]
}

答案 2 :(得分:3)

只要您可以创建数据副本,就可以在此处使用聚合框架作为替代方案。如果您希望使用其他运算符,您还可以选择对数据执行更多操作,但您唯一需要的是$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 :(得分:3)

我们可以使用Mongo脚本动态处理数据。它对我有用!

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

当前地址示例:&#34; No.12,FIFTH AVENUE,&#34;。

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

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 :(得分:3)

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);
  }
);

答案 5 :(得分:0)

从CLI?我认为你必须首先拉出值并将值分配给变量。然后运行更新命令。

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