我有一个文档列表,每个文档都有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 视为字符串。如何引用文档的属性?
干杯。
答案 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
修改lat
和lon
字段,然后将其发送到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.lat
和events.lon
如果有效,您仍会有多个值,即“lat”和“lon”的旧值以及您创建的新数组。