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 ответов
Оператор $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" : [ ]
}
Обновление: если все, что вам нужно сделать, это изменить структуру документа, не меняя значений, см . Ответ gipset для хорошего решения.
Согласно (теперь недоступному) комментарию на странице документации по обновлению, вы не можете ссылаться на свойства текущего документа изнутри 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);
}
)
Такая функция также может использоваться в задании по уменьшению карты или на стороне сервера. db.eval()
работа, в зависимости от ваших потребностей.
Нилс отвечает. Просто чтобы люди знали, что вы не можете запустить это на большой базе данных, если вы, скажем, делаете это удаленной оболочкой, как Robomongo. Вам нужно будет подключиться по ssh к монго-оболочке вашего фактического сервера. Также вы можете сделать это, если вы предпочитаете делать обновление.
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);
}
);
Мы можем использовать скрипт Mongo для управления данными на лету. Меня устраивает!
Я использую этот скрипт для исправления моих адресных данных.
Пример текущего адреса: "№12, ПЯТЫЙ ПРОСПЕКТ,".
Я хочу удалить последнюю лишнюю запятую, ожидаемый новый адрес "№ 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);
}
}
Надеюсь это поможет!
Пока вы в порядке с созданием копии данных, в качестве альтернативы можно использовать структуру агрегирования. У вас также есть возможность сделать больше с данными, если вы хотите использовать другие операторы, но вам нужен только один $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 не будет завершен, вы не сможете сделать эквивалент снимка, намекая на _id
Индекс, как предлагается в этом ответе, и вы можете в конечном итоге нажать документ более одного раза, если деятельность в другом месте заставляет его двигаться. Поскольку вы вставляете _id
В этом примере любое подобное вхождение может привести к нарушению уникального ключа, поэтому у вас не будет дубликатов, но у вас может быть "старая" версия документа. Как всегда, тщательно проверяйте свои данные перед тем, как выбросить их, и желательно сделать резервную копию.
Из CLI? Я думаю, что вы должны сначала извлечь значения и присвоить значение переменной. Затем запустите команду обновления.
Или (я не пробовал) удалить 'db' из строки. events.lat
а также events.lon
Если это сработает, у вас останется несколько значений: старые значения для "lat" и "lon" и новый созданный вами массив.