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" и новый созданный вами массив.

Другие вопросы по тегам