Порядок полей MongoDB и изменение положения документа после обновления
Я изучаю MongoDB, и я заметил, что всякий раз, когда я делаю обновление в документе, обновляемое поле помещается в конец заказа, поэтому, если у меня было что-то вроде:
db.collection.save({field1: value1, field2: value2, ..., field 10: value10});
db.collection.update({field1: value1}, {$set: {field2: new_value}});
тогда, если вы делаете:
db.collection.find();
будет отображаться:
{ "field1":"value1", ..., "field10":"value10", "field2":"new_value"}
Вы можете увидеть, как меняется порядок полей, когда обновленное поле помещается в конец документа. Кроме того, сам документ выдвигается до конца сбора. Я знаю, что это БД "без схемы", и это может не быть большой проблемой, но это не выглядит "красиво":). Есть ли способ сделать обновление на месте, не меняя порядок?
8 ответов
MongoDB выделяет место для нового документа на основе определенного коэффициента заполнения. Если ваше обновление увеличивает размер документа сверх первоначально выделенного размера, документ будет перемещен в конец коллекции. Та же концепция применяется к полям в документе.
К вашему сведению, в MongoDB 2.6 обновления будут сохранять порядок полей со следующими исключениями:
- Поле _id всегда является первым полем в документе.
- Обновления, которые включают переименование имен полей, могут привести к переупорядочению полей в документе.
Структура документа и структура коллекции в MongoDB основаны на принципах JSON. JSON - это набор пар ключ / значение (в частности fieldName/fieldValue для документа и index/document для коллекции). С этой точки зрения не похоже, что вы можете положиться на порядок вообще.
В случае документов, если размер поля изменяется, выписывается новый документ с полями, отсортированными по имени поля. Такое поведение можно увидеть с помощью следующих утверждений
Случай 1: без изменения размера поля, поэтому без изменения порядка полей
> db.testcol.find()
> db.testcol.save({a:1,c:3,b:2})
> db.testcol.find()
{ "_id" : ObjectId("4d5efc3bec5855af36834f5a"), "a" : 1, "c" : 3, "b" : 2 }
> db.testcol.update({a:1},{$set:{c:22}})
> db.testcol.find()
{ "_id" : ObjectId("4d5efc3bec5855af36834f5a"), "a" : 1, "c" : 22, "b" : 2 }
Случай 2: размер поля изменяется и поля переупорядочиваются
> db.testcol.find()
> db.testcol.save({a:1,c:"foo",b:2,d:4})
> db.testcol.find()
{ "_id" : ObjectId("4d5efdceec5855af36834f5e"), "a" : 1, "c" : "foo", "b" : 2, "d" : 4 }
> db.testcol.update({a:1},{$set:{c:"foobar"}})
> db.testcol.find()
{ "_id" : ObjectId("4d5efdceec5855af36834f5e"), "a" : 1, "b" : 2, "c" : "foobar", "d" : 4 }
Есть ли конкретная причина, почему вы не хотите, чтобы поля были переупорядочены? Выше было использование 1.8.0_rc0 на OS X
Я создал проект, который создает пользовательский mongorc.js, который сортирует ключи документов по умолчанию для вас. Это называется Монго Хакер
Чтобы поля были в нужном мне порядке, я вставил все документы со свойствами в правильном порядке в другую коллекцию. Затем я удалил старую коллекцию и переименовал новую коллекцию в первоначальное имя. Очевидно, сначала сделайте резервную копию ваших данных.
Раман, верно, мы не можем сортировать словари, но мы можем сортировать визуализацию диктонары, поэтому мы не можем сортировать поданный порядок документации, но я вижу, что он упорядочен.
Например в perl to_json есть опция canonical
print to_json( $data, { utf8 => 1, pretty => 1, convert_blessed => 1, canonical => 1 } );
(каноническая опция) будет выводить объекты JSON путем сортировки их ключей. Это добавляет сравнительно высокие накладные расходы. (конечно, мы делаем больше операцию сортировки...)
У меня была проблема, аналогичная вашей, когда при обновлении поля оно перемещалось в конец документа. Единственное достойное решение, которое я смог найти, - это выполнить:
- агрегация, объединяющая стадии $replaceWith и $out. Этап $replaceWith обрабатывает перестановку полей во всех документах в коллекции, а этап $out записывает выходные данные этапа $replaceWith в новую коллекцию.
- Тогда вам нужно будет отбросить старую коллекцию и ...
- (необязательно) переименуйте новую коллекцию.
Я не уверен, можно ли перезаписать коллекцию на этапе $out, используя то же имя входной коллекции, и пропустить шаги 2 и 3.
В противном случае команды выглядели бы примерно так:
> db.oldCollection.aggregate([{$replaceWith: {field1: "$field1", field2: "$field2"... fieldN: "$fieldN"}}, {$out: "newCollection"}])
> db.oldCollection.drop()
> db.newCollection.renameCollection("oldCollection")
Порядок, в котором вы определяете поля на этапе $replaceWith, - это порядок, в котором они будут выводиться. И да, значения полей должны быть самими именами полей с добавлением $.