我有一个(希望很快)关于复合索引的MongoDB查询的问题。
假设我有一个数据集(例如,评论),我想按分数降序排序,然后是日期:
{ "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来获取唯一的,大致基于时间的顺序)
现在,我想支持通过评论进行分页。第一页很简单,我可以在光标的末尾添加.limit(n)
选项。我正在努力的是继续搜索。
我一直在提到Kristina Chodorow的 MongoDB:The Definitive Guide 。在本书中,Kristina提到在大型数据集上使用skip()不是很有效,并建议对上次看到的结果(例如,最后看到的日期)使用范围查询。
我想要做的是执行一个作用于两个字段的范围查询,但将第二个字段视为第二个字段的第二个字段(就像索引已排序一样。)因为我的复合索引已经按顺序排序我想,似乎应该通过指向索引中的特定元素并以排序顺序遍历它来跳转到搜索中。但是,从我对MongoDB中的查询的理解(基本上是初步的)来看,这似乎是不可能的。
据我所知,我有三个选择:
skip()
{$or : [{"score" : lastScore, "date" : { $lt : lastDate}}, {'score' : {$lt : lastScore}]}
$max
特殊查询选项3号对我来说似乎最接近理想,但参考文本指出“你通常应该使用”$ lt“而不是”$ max“'。
总结一下,我有几个问题:
提前感谢您的帮助!
答案 0 :(得分:2)
另一种选择是将score
和date
存储在子文档中,然后将子文档编入索引。例如:
{
"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
,因此使用此方法进行分页时,您需要跳过第一个返回的文档以避免输出相同内容文件两次。