MongoDB:使用来自同一文档的数据更新文档
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3788256/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MongoDB: Updating documents using data from the same document
提问by Pawel Decowski
I have a list of documents, each with lat and lon properties (among others).
我有一个文件列表,每个文件都有 lat 和 lon 属性(等等)。
{ 'lat': 1, 'lon': 2, someotherdata [...] }
{ 'lat': 4, 'lon': 1, someotherdata [...] }
[...]
I want to modify it so that it looks like this:
我想修改它,使其看起来像这样:
{ 'coords': {'lat': 1, 'lon': 2}, someotherdata [...]}
{ 'coords': {'lat': 4, 'lon': 1}, someotherdata [...]}
[...]
So far I've got this:
到目前为止,我有这个:
db.events.update({}, {$set : {'coords': {'lat': db.events.lat, 'lon': db.events.lon}}}, false, true)
But it treats the db.events.latand db.events.lonas strings. How can I reference the document's properties?
但它将db.events.lat和db.events.lon视为字符串。如何引用文档的属性?
Cheers.
干杯。
采纳答案by gipset
The $renameoperator (introduced a month after this question was posted) makes it really easy to do these kinds of things where you don't need to modify the values.
在$重命名操作(介绍这个问题被张贴一个月后),使得它很容易做这些事情,你并不需要修改的值。
Insert some test documents
插入一些测试文件
db.events.insert({ 'lat': 1, 'lon': 2, someotherdata: [] })
db.events.insert({ 'lat': 4, 'lon': 1, someotherdata: [] })
use the $rename
operator
使用$rename
运算符
db.events.update({}, {$rename: {'lat': 'coords.lat', 'lon': 'coords.lon'}}, false, true)
Results
结果
db.events.find()
{
"_id" : ObjectId("5113c82dd28c4e8b79971add"),
"coords" : {
"lat" : 1,
"lon" : 2
},
"someotherdata" : [ ]
}
{
"_id" : ObjectId("5113c82ed28c4e8b79971ade"),
"coords" : {
"lat" : 4,
"lon" : 1
},
"someotherdata" : [ ]
}
回答by Niels van der Rest
Update:If all you have to do is change the structure of a document without changing the values, see gipset's answerfor a nice solution.
更新:如果您所要做的就是在不更改值的情况下更改文档的结构,请参阅gipset 的答案以获得一个不错的解决方案。
According to a (now unavailable) comment on the Update documentation page, you cannot reference the current document's properties from within an update()
.
根据更新文档页面上的(现在不可用)评论,您不能从update()
.
You'll have to iterate through all the documents and update them like this:
您必须遍历所有文档并像这样更新它们:
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);
}
)
Such a function can also be used in a map-reduce job or a server-side db.eval()
job, depending on your needs.
db.eval()
根据您的需要,此类功能也可用于 map-reduce 作业或服务器端作业。
回答by mjwrazor
Neils answer. Just to let people know you cannot run this on a large database if you are lets say doing it remote shell like Robomongo. You will need to ssh into your actual server's mongo shell. Also you could also do this if you would rather do an Update.
尼尔斯回答。只是为了让人们知道你不能在大型数据库上运行它,如果你可以说它像 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);
}
);
回答by Adam Comerford
As long as you are OK with creating a copy of the data, the aggregation framework can be used as an alternative here. You also have the option to do more to the data if you wish using other operators, but the only one you need is $project
. It's somewhat wasteful in terms of space, but may be faster and more appropriate for some uses. To illustrate, I'll first insert some sample data into the foo
collection:
只要您可以创建数据的副本,这里就可以使用聚合框架作为替代方案。如果您希望使用其他运算符,您还可以选择对数据执行更多操作,但您唯一需要的是$project
. 它在空间方面有点浪费,但可能更快,更适合某些用途。为了说明这一点,我首先将一些示例数据插入到foo
集合中:
db.foo.insert({ 'lat': 1, 'lon': 2, someotherdata : [1, 2, 3] })
db.foo.insert({ 'lat': 4, 'lon': 1, someotherdata : [4, 5, 6] })
Now, we just use $project
to rework the lat
and lon
fields, then send them to the newfoo
collection:
现在,我们只$project
需要重新处理lat
和lon
字段,然后将它们发送到newfoo
集合中:
db.foo.aggregate([
{$project : {_id : "$_id", "coords.lat" : "$lat", "coords.lon" : "$lon", "someotherdata" : "$someotherdata" }},
{ $out : "newfoo" }
])
Then check newfoo
for our altered data:
然后检查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 } }
Once you are happy with the new data, you can then use the renameCollection()
command to drop the old data and use the new data under the old name:
一旦您对新数据感到满意,您就可以使用该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 } }
One last note - until SERVER-7944is completed you can't do the equivalent of a snapshot by hinting the _id
index as suggested in this answerand so you can end up hitting a document more than once if activity elsewhere causes it to move. Since you are inserting the _id
field in this example, any such occurrence would cause a unique key violation, so you will not end up with dupes, but you might have an "old" version of a document. As always, check your data thoroughly before dropping it, and preferably take a backup.
最后一个注意事项 - 在完成SERVER-7944之前,您无法_id
按照本答案中的建议提示索引来执行相当于快照的操作,因此如果其他地方的活动导致文档移动,您最终可能会多次点击文档。由于您_id
在此示例中插入字段,因此任何此类事件都会导致唯一键冲突,因此您不会以欺骗结束,但您可能拥有文档的“旧”版本。与往常一样,在删除数据之前彻底检查您的数据,最好进行备份。
回答by Dac Nguyen
We can use Mongo script to manipulate data on the fly. It works for me!
我们可以使用 Mongo 脚本来动态操作数据。这个对我有用!
I use this script to correct my address data.
我使用这个脚本来更正我的地址数据。
Example of current address: "No.12, FIFTH AVENUE,".
当前地址示例:“第五大道 12 号”。
I want to remove the last redundant comma, the expected new address ""No.12, FIFTH AVENUE".
我想去掉最后一个多余的逗号,预期的新地址“第五大道12号”。
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);
}
}
Hope this helps!
希望这可以帮助!
回答by luckytaxi
From the CLI? I think you have to pull the values out first and assign the value into a variable. Then run your update command.
从命令行界面?我认为您必须先提取值并将值分配给变量。然后运行您的更新命令。
Or (I haven't tried) remove 'db' from the string. events.lat
and events.lon
If it works, you will still have multiple values, the old values for "lat" and "lon" and the new array you created.
或者(我没试过)从字符串中删除 'db'。events.lat
而events.lon
如果一切正常,你仍然有多个值,您创建了“纬度”和“经度”和新阵旧值。