在MongoDB中按多个字段排序

39

我在MongoDB中有一个查询,我想优先考虑第一个字段,然后再考虑第二个字段。

比如说,我需要进行以下查询:

db.col.find({category: A}).sort({updated: -1, rating: -1}).limit(10).explain()

所以我创建了以下索引

db.col.ensureIndex({category: 1, rating: -1, updated: -1})

当我需要扫描尽可能多的对象时,即10个对象,它可以完美地工作。

但是现在我需要查询

db.col.find({category: { $ne: A}}).sort({updated: -1, rating: -1}).limit(10)

于是我创建了以下索引:

 db.col.ensureIndex({rating: -1, updated: -1})

但这会导致整个文档被扫描,而当我创建时

 db.col.ensureIndex({ updated: -1 ,rating: -1})

扫描的文档数量更少:

我只是想问清楚在多个字段上排序时应该保留什么顺序。通过阅读MongoDB文档,很明显需要对进行排序的字段应该是最后一个字段。因此,在我的$ne查询中,我假设了这种情况。我有做错什么吗?

4个回答

47

MongoDB的查询优化器通过尝试不同的计划来确定哪种方法最适合给定的查询。然后,该查询模式的获胜计划将被缓存,供下一个约1,000个查询或直到您执行explain()为止。

要了解考虑了哪些查询计划,您应该使用explain(1),例如:

db.col.find({category:'A'}).sort({updated: -1}).explain(1)
allPlans详细信息将显示所有比较过的计划。
如果您运行的查询不是非常选择性的(例如,如果许多记录与您的{category: {$ne:'A'}}条件匹配),那么使用BasicCursor(表扫描)查找结果可能比匹配索引更快。
查询中字段的顺序通常不会影响索引选择(有一些范围查询的例外)。排序中字段的顺序会影响索引选择。如果您的sort()条件与索引顺序不匹配,则在使用索引后必须重新对结果数据进行排序(如果发生这种情况,您应该在解释输出中看到scanAndOrder:true)。
还值得注意的是,MongoDB每次查询只使用一个索引(除了$or)。
因此,如果您正在尝试优化查询:
db.col.find({category:'A'}).sort({updated: -1, rating: -1})

你需要在索引中包含所有三个字段:
db.col.ensureIndex({category: 1, updated: -1, rating: -1})

如果您想强制使用特定索引来查询(通常不需要或不建议这样做),可以尝试使用hint()选项。


这如何回答问题?仍然无法确保您可以按“评分降序,更新时间降序”的顺序进行排序,而不是“更新时间降序,评分降序”。 - jobermark
@jobermark 在原始问题中,查询条件是 category,复合排序顺序为 {updated: -1, rating: -1}。索引中键的顺序(和方向)很重要;建议的索引将无法有效地支持按 {rating: -1, updated: -1} 排序的 content 搜索。如果您正在使用驱动程序操作复合排序值,请使用有序哈希/字典来确保保留排序。有关更多信息,请参见 MongoDB 文档中的 Sort on Multiple Fields - Stennie

1

这是正确的,但由于您正在对复合索引进行排序,因此您有两个排序层级。

正如您所注意到的,当索引的第一个字段与排序的第一个字段匹配时,它可以工作并且索引被看到。然而,当以相反的方式工作时,它就不行了。

因此,根据您自己的观察,需要保留的顺序是从第一个到最后一个字段的查询顺序。Mongo分析器有时会移动字段以匹配索引,但通常它只会尝试匹配第一个字段,如果无法匹配,则会跳过它。


0

排序和索引使用

MongoDB可以从包含排序字段的索引中获取排序操作的结果。如果排序使用与查询谓词相同的索引,MongoDB可能会使用多个索引来支持排序操作。使用索引进行排序的操作通常比阻塞排序具有更好的性能。

db.restaurants.find().sort( { "borough": 1, "_id": 1 } )

更多信息: https://docs.mongodb.com/manual/reference/method/cursor.sort/


0

尝试这段代码,它将首先根据名称对数据进行排序,然后将“名称”保存在键持有者中,它将对“过滤器”进行排序

 var cursor = db.collection('vc').find({   "name" :   { $in: [ /cpu/, /memo/ ]   }     }, { _id: 0, }).sort( { "name":1  ,  "filter": 1 } );

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