【问题标题】:Continuing a Query (paginating) on a compound index在复合索引上继续查询(分页)
【发布时间】:2014-03-27 03:44:06
【问题描述】:

我有一个(希望很快)关于复合索引上的 MongoDB 查询的问题。

假设我有一个数据集(例如 cmets),我想按分数降序排序,然后按日期:

{ "score" : 10, "date" : ISODate("2014-02-24T00:00:00.000Z"), ...}
{ "score" : 10, "date" : ISODate("2014-02-18T00:00:00.000Z"), ...}
{ "score" : 10, "date" : ISODate("2014-02-12T00:00:00.000Z"), ...}
{ "score" : 9, "date" : ISODate("2014-02-22T00:00:00.000Z"), ...}
{ "score" : 9, "date" : ISODate("2014-02-16T00:00:00.000Z"), ...}
...

到目前为止,我的理解是我可以创建一个复合索引来支持这个查询,它看起来像{"score":-1,"date":-1}。 (为了清楚起见,我没有在索引中使用日期,而是使用 ObjectID 来表示唯一的、大致基于时间的顺序)

现在,假设我想支持通过 cmets 进行分页。第一页很简单,我可以在光标末尾粘贴一个.limit(n) 选项。我正在苦苦挣扎的是继续搜索。

我一直在参考 Kristina Chodorow 的 MongoDB:权威指南。在这本书中,Kristina 提到在大型数据集上使用 skip() 性能不是很好,并建议对上次看到的结果(例如上次看到的日期)的参数使用范围查询。

我想做的是执行一个作用于两个字段的范围查询,但将第二个字段视为第一个字段的次要字段(就像索引已排序一样。)因为我的复合索引已经按顺序排序我想要,似乎应该有某种方法可以通过指向索引中的特定元素并按排序顺序遍历它来跳入搜索。但是,根据我对 MongoDB 中查询的(不可否认的初步)理解,这似乎是不可能的。

据我所知,我有三个选择:

  1. 还是使用skip()
  2. 使用 $or 查询或两个不同的查询:{$or : [{"score" : lastScore, "date" : { $lt : lastDate}}, {'score' : {$lt : lastScore}]}
  3. 使用$max 特殊查询选项

数字 3 对我来说似乎最接近理想,但参考文本指出“您通常应该使用“$lt”而不是“$max”。

总结一下,我有几个问题:

  1. 是否有一些方法可以执行我所描述的操作,但我可能错过了? (跳入索引并按排序顺序遍历)
  2. 如果不是,在我描述的三个选项(或任何我忽略的选项)中,哪个(非常笼统地说)在复合指数下的表现最一致?
  3. 为什么在大多数情况下首选 $lt 而不是 $max?

提前感谢您的帮助!

【问题讨论】:

    标签: mongodb mongodb-query


    【解决方案1】:

    另一种选择是将scoredate 存储在子文档中,然后索引子文档。例如:

    {
      "a" : { "score" : 9,
              "date" : ISODate("2014-02-22T00:00:00Z") },
      ...
    }
    
    db.foo.ensureIndex( { a : 1 } )
    
    db.foo.find( { a : { $lt : { score : lastScore,
                                 date: lastDate } } } ).sort( { a : -1 } )
    

    使用这种方法,您需要确保 BSON 子文档中的字段始终以相同的顺序存储,否则查询将与您期望的不匹配,因为索引键比较是整个 BSON 子文档的二进制比较文件。

    我会使用$max 来指定上限,并结合$hint 来确保数据库使用您想要的索引。 $lt 通常优于 $max 的原因是因为 $max 使用指定的索引边界选择索引。这意味着:

    • 所选择的索引不一定是最佳选择。
    • 如果同一字段上存在多个排序顺序不同的索引,则索引的选择可能不明确。

    here 更详细地介绍了以上几点。

    最后一点:max 等效于 $lte,而不是 $lt,因此使用这种方法进行分页时,您需要跳过第一个返回的文档以避免输出相同的文档两次。

    【讨论】:

    • 感谢您的快速回复! $max 方法似乎对我最有效,因为查询是由 Web 应用程序执行的少数查询之一,因此无论如何都会专门为每个查询精心构建索引(索引选择无论如何都是固定的)。我很高兴听到 $max 没有重大问题。子文档方法非常有趣,我也一定会研究的。我不确定它在我们整个数据模型的上下文中是否有意义,但感谢您完全回答这个问题!
    猜你喜欢
    • 1970-01-01
    • 2021-08-17
    • 1970-01-01
    • 2018-02-27
    • 1970-01-01
    • 1970-01-01
    • 2022-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多