如何在Mongoose中按多个字段排序的列表中获得项目排名
How to get item ranking in list sorted by multiple fields in Mongoose
我在 MongoDB 集合中有许多用户记录(> 10000),可以按得分降序 + 时间升序 + 奖金降序排序。如何使用 Mongoose 根据此排序获得列表中一个用户的排名?假设索引已正确建立。
计算在您的排序顺序中排在该用户之前的用户数。我将从简单(非复合排序)的情况开始,因为复合情况下的查询更复杂,即使想法完全相同。
> db.test.drop()
> for (var i = 0; i < 10; i++) db.test.insert({ "x" : i })
> db.test.find({ }, { "_id" : 0 }).sort({ "x" : -1 }).limit(5)
{ "x" : 9 }
{ "x" : 8 }
{ "x" : 7 }
{ "x" : 6 }
{ "x" : 5 }
对于这个顺序,文档{ "x" : i }
的排名是文档{ "x" : j }
的数量i < j
> var rank = function(id) {
var i = db.test.findOne({ "_id" : id }).x
return db.test.count({ "x" : { "$gt" : i } })
}
> var id = db.test.findOne({ "x" : 5 }).id
> rank(id)
4
排名将基于 0。同样,如果要计算文档 { "x" : i }
在排序 { "x" : 1 }
中的排名,您将计算文档的数量 { "x" : j }
与 i > j
.
对于复合排序,同样的过程有效,但实现起来比较棘手,因为复合索引中的顺序是字典顺序的,即对于排序 { "a" : 1, "b" : 1}
,(a, b) < (c, d)
如果 a < c
或者 a = c
和 b < d
,所以我们需要一个更复杂的查询来表达这个条件。下面是复合索引的示例:
> db.test.drop()
> for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
db.test.insert({ "x" : i, "y" : j })
}
}
> db.test.find({}, { "_id" : 0 }).sort({ "x" : 1, "y" : -1 })
{ "x" : 0, "y" : 2 }
{ "x" : 0, "y" : 1 }
{ "x" : 0, "y" : 0 }
{ "x" : 1, "y" : 2 }
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 0 }
{ "x" : 2, "y" : 2 }
{ "x" : 2, "y" : 1 }
{ "x" : 2, "y" : 0 }
要找到文档 { "x" : i, "y" : j }
的排名,您需要找到顺序为 { "x" : 1, "y" : -1 }
的文档 { "x" : a, "y" : b }
的数量,使得 (i, j) < (a, b)
。给定排序规范,这等同于条件 i < a
或 i = a
和 j > b
:
> var rank = function(id) {
var doc = db.test.findOne(id)
var i = doc.x
var j = doc.y
return db.test.count({
"$or" : [
{ "x" : { "$lt" : i } },
{ "x" : i, "y" : { "$gt" : j } }
]
})
}
> id = db.test.findOne({ "x" : 1, "y" : 1 })._id
> rank(id)
4
最后,对于由三部分组成的复合索引
{ "score" : -1, "time" : 1, "bonus" : -1 }
rank
函数将是
> var rank = function(id) {
var doc = db.test.findOne(id)
var score = doc.score
var time = doc.time
var bonus = doc.bonus
return db.test.count({
"$or" : [
{ "score" : { "$gt" : score } },
{ "score" : score, "time" : { "$lt" : time } },
{ "score" : score, "time" : time, "bonus" : { "$gt" : bonus } }
]
})
}
我在 MongoDB 集合中有许多用户记录(> 10000),可以按得分降序 + 时间升序 + 奖金降序排序。如何使用 Mongoose 根据此排序获得列表中一个用户的排名?假设索引已正确建立。
计算在您的排序顺序中排在该用户之前的用户数。我将从简单(非复合排序)的情况开始,因为复合情况下的查询更复杂,即使想法完全相同。
> db.test.drop()
> for (var i = 0; i < 10; i++) db.test.insert({ "x" : i })
> db.test.find({ }, { "_id" : 0 }).sort({ "x" : -1 }).limit(5)
{ "x" : 9 }
{ "x" : 8 }
{ "x" : 7 }
{ "x" : 6 }
{ "x" : 5 }
对于这个顺序,文档{ "x" : i }
的排名是文档{ "x" : j }
的数量i < j
> var rank = function(id) {
var i = db.test.findOne({ "_id" : id }).x
return db.test.count({ "x" : { "$gt" : i } })
}
> var id = db.test.findOne({ "x" : 5 }).id
> rank(id)
4
排名将基于 0。同样,如果要计算文档 { "x" : i }
在排序 { "x" : 1 }
中的排名,您将计算文档的数量 { "x" : j }
与 i > j
.
对于复合排序,同样的过程有效,但实现起来比较棘手,因为复合索引中的顺序是字典顺序的,即对于排序 { "a" : 1, "b" : 1}
,(a, b) < (c, d)
如果 a < c
或者 a = c
和 b < d
,所以我们需要一个更复杂的查询来表达这个条件。下面是复合索引的示例:
> db.test.drop()
> for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
db.test.insert({ "x" : i, "y" : j })
}
}
> db.test.find({}, { "_id" : 0 }).sort({ "x" : 1, "y" : -1 })
{ "x" : 0, "y" : 2 }
{ "x" : 0, "y" : 1 }
{ "x" : 0, "y" : 0 }
{ "x" : 1, "y" : 2 }
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 0 }
{ "x" : 2, "y" : 2 }
{ "x" : 2, "y" : 1 }
{ "x" : 2, "y" : 0 }
要找到文档 { "x" : i, "y" : j }
的排名,您需要找到顺序为 { "x" : 1, "y" : -1 }
的文档 { "x" : a, "y" : b }
的数量,使得 (i, j) < (a, b)
。给定排序规范,这等同于条件 i < a
或 i = a
和 j > b
:
> var rank = function(id) {
var doc = db.test.findOne(id)
var i = doc.x
var j = doc.y
return db.test.count({
"$or" : [
{ "x" : { "$lt" : i } },
{ "x" : i, "y" : { "$gt" : j } }
]
})
}
> id = db.test.findOne({ "x" : 1, "y" : 1 })._id
> rank(id)
4
最后,对于由三部分组成的复合索引
{ "score" : -1, "time" : 1, "bonus" : -1 }
rank
函数将是
> var rank = function(id) {
var doc = db.test.findOne(id)
var score = doc.score
var time = doc.time
var bonus = doc.bonus
return db.test.count({
"$or" : [
{ "score" : { "$gt" : score } },
{ "score" : score, "time" : { "$lt" : time } },
{ "score" : score, "time" : time, "bonus" : { "$gt" : bonus } }
]
})
}