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

13

我正在考虑使用MongoDB存储包含键/值对列表的文档。安全但不够优美和臃肿的存储方式是

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

但是文档元素在底层 BSON 数据结构中具有固有的顺序,因此从原理上讲:

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

应该足够了。然而,我预计大多数语言绑定将会将它们解释为关联数组,从而可能会打乱顺序。因此,我需要知道:

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

我主要对JavaScript和PHP感兴趣,但我也想了解其他语言。任何帮助都将不胜感激,或者只是一个链接,让我可以去RTM。


1
MongoDB用户论坛是提问这些问题的最佳场所;MongoDB开发人员非常乐于回答此类问题,并能够给出权威的答案。https://groups.google.com/forum/#!forum/mongodb-user - Jason S
1
@JasonS 谢谢你的回复。我之所以在这里问,是因为这个问题涉及到很多不同的语言/标准,我不知道该去哪里寻找答案。但你说得对,Mongo位于它们所有的中心。 - Adrian Ratnapala
1
这实际上是更合适的地方来询问。 - Remon van Vliet
2
这就是我得到答案的地方。 - Adrian Ratnapala
4个回答

12

从2.6版开始,MongoDB会在可能的情况下保留字段的顺序。但是_id字段总是排在第一位,重命名字段可能会导致重新排序。不过,通常我会尽量不依赖这样的细节。正如原问题提到的那样,还有其他需要考虑的层面,每个层面都必须提供某种保证以确保顺序的稳定性...

原回答:

不,MongoDB没有关于字段顺序的保证

“更新后字段顺序不一定会保持一致或相同。”

特别是更改文档大小的原地更新通常会改变字段的顺序。例如,如果你用$set设置一个原值为数字类型但新值是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
干净...没有其他要添加的了.. +1 - RameshVel
2
好的,我会按照那种方式去做。我仍然认为它很丑陋,但MongoDB以那种方式工作是相当合理的。“对象具有关联数组语义”是一种JavaScript主义(通常我不会使用“对象”这样的术语来描述这样的数据结构)。从我(肤浅的)对MongoDB的阅读中,我认为它是一个BSON文档数据库——而BSON有一个非常清晰的规范,其中元素是有序的。但实际上MongoDB是一种类似于JavaScript对象(或Python字典)的存储方式,只是在底层使用了BSON。也就是说,这样也可以。 - Adrian Ratnapala
"MongDB 不保证字段的顺序" 是错误的。MongoDB 对字段的顺序确实有一定严格的保证。它不保证每个操作之后(例如“更新后”)的顺序,但它绝对有保证。话虽如此,依赖于保留顺序的字典是一个噩梦,几乎总是最好避免使用,因为大多数语言的本地字典都不会保留顺序。 - Glenn Maynard
我可以看出这个句子并不完全正确,因为每个确定性系统都会提供保证。但我认为这并不值得被踩。能否详细说明一下这些保证是什么,它们在哪里记录(除了源代码,因为我们不想依赖于实现细节),以及它们如何在实际环境中使用,在这种环境中,单个代码点很难知道对数据执行了哪些操作? - mnemosyn
@mnemosyn 我认为文档确实做出了保证,但是以一种非常间接的方式,这并不令人信服。他们说MongoDB存储BSON文档,而在BSON中,kv-pairs列表是有序的(因为它们是列表)。但是,如果语言绑定不以保证的方式公开列表,则这是无用的。 - Adrian Ratnapala
1
这个答案似乎已经过时,截至2017年。 - noahlz

9

0

其中一个痛点是在 shell 中比较文档之间的差异。

我创建了一个项目,它会创建一个自定义的 mongorc.js ,默认情况下对打印出来的文档键进行排序,这样,至少您可以清楚地看到正在 shell 中发生的事情。 如果您想要尝试,它叫做 Mongo Hacker


0

虽然Mongo 2.6.1以后确实保留了顺序,但更新操作仍需小心。

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

例如$addToSet:

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

在数组中使用$addToSet操作嵌套文档的情况在这里得到了讨论和举例: https://dev59.com/aWEi5IYBdhLWcg3wJpcl#21578556

在帖子中,mnemosyn解释了$addToSet如何忽略顺序,并通过深度逐个比较来匹配元素。 ($addToSet仅在记录唯一时添加它们)

如果一个人决定按以下方式结构化数据,则此信息是相关的:

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

有了这样的更新(请注意嵌入文档中不同的顺序):

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

Mongo会将此视为重复项,并不会将此对象添加到数组中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接