MongoDB及其驱动程序可以保留文档元素的顺序

时间:2011-12-20 14:17:41

标签: php javascript json mongodb bson

我正在考虑使用MongoDB来存储包含键/值对列表的文档。存储它的安全但丑陋和臃肿的方式是

[ ['k1' : 'v1'] , ['k2' : 'v2'],  ...]

但是文档元素本质上是在底层BSON数据结构中排序的,所以原则上是:

{k1 : 'v1', 
 k2 : 'v2',  ...}

应该足够了。但是我希望大多数语言绑定会将这些解释为关联数组,因此可能会对排序进行加扰。所以我需要知道的是:

  • MongoDB本身承诺是否保留第二个表单的项目排序。
  • 语言绑定是否有一些API可以提取它的有序形式 - 即使通常的“方便”API返回一个关联数组。

我最感兴趣的是Javascript和PHP,但我也想了解其他语言。感谢任何帮助,或只是链接到一些文档,我可以去RTM。

4 个答案:

答案 0 :(得分:11)

从版本2.6开始,MongoDB preserves the order of fields where possible.但是,_id字段始终是第一个重命名字段可能导致重新排序。但是,我一般不会依赖这样的细节。正如最初的问题所提到的那样,还需要考虑其他层次,每个层面必须为订单的稳定性提供某种保证......

原始答案:

不,MongoDB does not make guarantees about the ordering of fields

  

“无法保证更新后字段顺序一致或相同。”

特别是,更改文档大小的就地更新通常会更改字段的顺序。例如,如果您$set字段的旧值类型为number且新值为NumberLong,则字段通常会重新排序。

但是,数组保持正确排序:

[ {'key1' : 'value1'}, {'key2' : 'value2'}, ... ]

我不明白为什么这个“丑陋”和“臃肿”。存储复杂对象列表并不容易。然而,滥用对象作为列表肯定是丑陋的:对象具有关联数组语义(即,只能有一个给定名称的字段),而列表/数组不具有:

// not ok:
db.foo2.insert({"foo" : "bar", "foo" : "lala" });
db.foo2.find();
{ "_id" : ObjectId("4ef09cd9b37bc3cdb0e7fb26"), "foo" : "lala" }

// a list can do that
db.foo2.insert({ 'array' : [ {'foo' : 'bar'}, { 'foo' : 'lala' } ]});
db.foo2.find();
{ "_id" : ObjectId("4ef09e01b37bc3cdb0e7fb27"), "array" : 
      [ { "foo" : "bar" }, { "foo" : "lala" } ] }

请记住,MongoDB是一个对象数据库,而不是键/值存储。

答案 1 :(得分:7)

从Mongo 2.6.1开始,它会保持你的字段顺序:

  

MongoDB在写操作之后保留文档字段的顺序,但以下情况除外:

     
      
  • _id字段始终是文档中的第一个字段。
  •   
  • 更新   包括重命名字段名称可能导致重新排序   文件中的字段。
  •   

http://docs.mongodb.org/manual/release-notes/2.6/#insert-and-update-improvements

答案 2 :(得分:0)

其中一个难点是在shell中将文档相互比较。

我创建了一个创建自定义mongorc.js的项目,默认情况下会为您打印文档键,因为至少可以看到shell中的内容。如果你想给它一个旋转,它被称为Mongo Hacker

答案 3 :(得分:0)

虽然从Mongo 2.6.1开始,它确实保留了顺序,但仍应小心更新操作。

mattwad指出更新可以重新排序,但至少还有一个我能想到的其他问题。

例如$ addToSet:

https://docs.mongodb.com/manual/reference/operator/update/addToSet/

这里讨论/举例说明了在数组中的嵌入式文档上使用时的$ addToSet: https://stackoverflow.com/a/21578556/3643190

在帖子中,mnemosyn解释了当通过值比较匹配深度值中的元素时,$ addToSet 忽略 的顺序。

($ addToSet仅在它们唯一时添加记录)

如果决定构建这样的数据,这是相关的:

[{key1: v1, key2: v2}, {key1: v3, key2: v4}]

使用这样的更新(注意嵌入式文档中的不同顺序):

db.collection.update({_id: "id"},{$addToSet: {field:
{key2: v2, key1: v1}
}});

Mongo会将此视为副本而不是数组的此对象。