【问题标题】:Does MongoDB find() query return documents sorted by creation time?MongoDB find() 查询是否返回按创建时间排序的文档?
【发布时间】:2015-09-02 09:52:22
【问题描述】:

我需要按创建时间(从最旧到最新)排序的文档。

由于ObjectID默认保存时间戳,我们可以使用CollectionName.find().sort({_id: 1})获取按创建时间排序的文档。

另外,我注意到常规的CollectionName.find() 查询总是以与CollectionName.find().sort({_id: 1})相同顺序 返回文档。

我的问题是:

CollectionName.find() 是否保证以与CollectionName.find().sort({_id: 1}) 相同的顺序返回文档,这样我就可以不用整理了?

【问题讨论】:

  • “总是以相同的顺序返回文档” - 不,并非总是如此。看我的剧本。 :)

标签: mongodb sorting database


【解决方案1】:

没有。嗯,不完全是。 大多数情况下,db.collection.find() 将按照文档在数据文件中出现的顺序为您提供文档,但这并不能保证。

结果排序

除非你指定 sort() 方法或使用 $near 运算符,否则 MongoDB 不保证查询结果的顺序。

只要您的数据文件相对较新并且很少发生更新,文档可能(并且大多数情况下)会以看起来按_id 排序的方式返回,因为 ObjectId 是单调递增的。

在生命周期的后期,旧文档可能已从其旧位置移动(因为它们的大小增加且文档从未分区),而新文档将写入之前由另一个文档占用的位置。在这种情况下,可能会在两个旧文档之间的位置返回一个较新的文档。

_id 对文档进行排序并没有错,因为索引将用于此目的,只会增加文档检索的一些延迟。

但是,出于以下几个原因,我强烈建议不要将 ObjectId 用于日期操作:

  1. ObjectIds 不能用于日期比较查询。因此,您无法查询日期 x 和日期 y 之间创建的所有文档。要归档它,您必须加载所有文档,从 ObjectId 中提取日期并进行比较——这是非常低效的。
  2. 如果创建日期很重要,则应在文档中明确说明
  3. 我认为 ObjectIds 是 _id 字段的最后选择,并且倾向于使用其他值(有时是复合值)作为 _ids,因为该字段默认为索引,并且很可能可以通过使用更有意义的值作为 id 来节省宝贵的 RAM。

您可以使用以下示例,它利用DBRefs

{
  _id: {
    creationDate: new ISODate(),
    user: { 
      "$ref" : "creators",
      "$id" : "mwmahlberg",
      "$db" : "users"
    }
  }
}

并通过使用进行非常便宜的排序

db.collection.find().sort({_id.creationDate:1})

【讨论】:

  • 原来如此。
  • 顺便说一句,你建议的这个技巧是能够使用 _id 上的免费索引对其子字段进行排序 - 它不起作用。子文档的索引非常有限。我从一开始就对此持怀疑态度,但今天早上我阅读了文档并进行了测试:gist.github.com/stulentsev/ad9525bfa2a1d620541e
  • @SergioTulentsev 哇!必须纠正我的答案和几行代码。从来没有检查过,真的,它应该适用于小型数据库,但这是一个缺陷。
  • @MarkusWMahlberg,那么,你是要更新答案还是什么? :)
  • @SergioTulentsev 该死的,这超出了我的范围。在我不断增长的待办事项中......顺便说一句:要点已被删除,可能对某些人有所帮助。
【解决方案2】:

是否保证 CollectionName.find() 以与 CollectionName.find().sort({_id: 1}) 相同的顺序返回文档

不,不是!如果您没有指定任何顺序,则使用所谓的“自然”顺序。这意味着文档将按照它们在数据文件中实际出现的顺序返回。

现在,如果您只插入文档而从不修改它们,这个自然顺序将与升序_id 重合。然而,想象一下,您更新文档的方式使其大小增加,并且必须将其移动到数据文件内的空闲位置(通常这意味着在文件末尾的某个位置)。如果您现在要查询文档,它们将不会遵循任何明智的(对于外部观察者而言)命令。

所以,如果您关心订单,请明确说明。

来源:http://docs.mongodb.org/manual/reference/glossary/#term-natural-order

自然秩序

数据库引用磁盘上文档的顺序。这是默认的排序顺序。请参阅 $natural 和按自然顺序返回。

测试脚本(困惑者)

> db.foo.insert({name: 'Joe'})
WriteResult({ "nInserted" : 1 })

> db.foo.insert({name: 'Bob'})
WriteResult({ "nInserted" : 1 })

> db.foo.find()
{ "_id" : ObjectId("55814b944e019172b7d358a0"), "name" : "Joe" }
{ "_id" : ObjectId("55814ba44e019172b7d358a1"), "name" : "Bob" }

> db.foo.update({_id: ObjectId("55814b944e019172b7d358a0")}, {$set: {answer: "On a sharded collection the $natural operator returns a collection scan sorted in natural order, the order the database inserts and stores documents on disk. Queries that include a sort by $natural order do not use indexes to fulfill the query predicate with the following exception: If the query predicate is an equality condition on the _id field { _id: <value> }, then the query with the sort by $natural order can use the _id index. You cannot specify $natural sort order if the query includes a $text expression."}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.foo.find()
{ "_id" : ObjectId("55814ba44e019172b7d358a1"), "name" : "Bob" }
{ "_id" : ObjectId("55814b944e019172b7d358a0"), "name" : "Joe", "answer" : "On a sharded collection the $natural operator returns a collection scan sorted in natural order, the order the database inserts and stores documents on disk. Queries that include a sort by $natural order do not use indexes to fulfill the query predicate with the following exception: If the query predicate is an equality condition on the _id field { _id: <value> }, then the query with the sort by $natural order can use the _id index. You cannot specify $natural sort order if the query includes a $text expression." }

【讨论】:

  • “自然”顺序只能通过使用.sort({ "$natural': 1 }) 来实现,这是“按存储在磁盘上的顺序”请求数据的唯一方法。此外,由于“磁盘上的数据”可以“移动”(并且经常这样做),这不是“插入顺序”。
  • 仔细阅读,这个答案并没有声称自然顺序与插入顺序相同。事实上,这整个答案是关于它是如何不一样的。
  • 引用:“如果您没有指定任何顺序,则使用所谓的“自然”顺序”。你的话不是我的。那不是真的
  • 是的,自然顺序,不是插入顺序。
  • 此测试无效。分片集合使用“分片键”进行选择和可能的“分散聚集”。所以这里不使用主键。这些都不支持$natural,因为如果您实际上显示文档在不同查询之间增长,那么$natural 显示的顺序将在结果之间更改,因为磁盘上的文档已经移动。
猜你喜欢
  • 1970-01-01
  • 2021-11-07
  • 1970-01-01
  • 2019-08-18
  • 1970-01-01
  • 2015-07-05
  • 2019-08-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多