MongoDB 使用排序查询结果中的位置来计算字段
MongoDB use position in sorted query result to compute field
我有一个供用户使用的 Mongoose 模型。每个用户都有一定数量的积分。我想创建一个用户排名字段:
rank = user position sorted by rank / total users
假设用户模型如下所示:
{
'name': 'bob',
'points': 15,
'rank': 9/15,
}
(我意识到分数在存储时实际上是小数)。
有什么方法可以更新所有这些用户:
1) 按分数排序
2) 获取用户在此排序列表中的索引
3) 将该索引除以列表中的项目总数
我不确定使用哪种 mongo 运算符来查找文档在查询结果中的位置以及查找查询结果的总大小。
您可以使用几个查询和一些 JavaScript 来完成此操作。扩展您概述的步骤,您需要做的是:
- 找到所有用户文档,按
points
降序排列并将结果分配给游标。您可能希望确保在此字段上有一个索引以使此查询 运行 更快。
- 获取返回的文档数。
- 使用索引跟踪文档在结果中的位置。
- 遍历文档,使用计数和索引计算
rank
,并使用计算结果更新相应用户的 rank
。
在 mongo
shell 中,代码如下所示。
var c = db.user.find().sort({ "points": -1 });
var count = c.count();
var i = 1;
while (c.hasNext()) {
var rank = i / count;
var user = c.next();
db.user.update(
{ "_id": user._id },
{ "$set": { "rank": rank } }
);
i++;
}
因此,如果您的 collection 中有以下三个用户:
{
"_id" : ObjectId("54f0af63cfb269d664de0b4e"),
"name" : "bob",
"points" : 15,
"rank" : 0
}
{
"_id" : ObjectId("54f0af7fcfb269d664de0b4f"),
"name" : "arnold",
"points" : 20,
"rank" : 0
}
{
"_id" : ObjectId("54f0af95cfb269d664de0b50"),
"name" : "claus",
"points" : 10,
"rank" : 0
}
更新后他们的文档将如下所示:
{
"_id" : ObjectId("54f0af63cfb269d664de0b4e"),
"name" : "bob",
"points" : 15,
"rank" : 0.6666666666666666
}
{
"_id" : ObjectId("54f0af7fcfb269d664de0b4f"),
"name" : "arnold",
"points" : 20,
"rank" : 0.3333333333333333
}
{
"_id" : ObjectId("54f0af95cfb269d664de0b50"),
"name" : "claus",
"points" : 10,
"rank" : 1
}
使用以前的答案不是一个好主意。每次更新 points
值后需要重新计算 rank
。
Mongo 版本 5.0+ 引入 $rank 聚合:
db.users.aggregate([
{
$setWindowFields: {
sortBy: { points: 1 },
output: {
rank: {
$rank: {}
}
}
}
}
])
会输出
{ "points": 140, "rank": 1 },
{ "points": 160, "rank": 2 },
{ "points": 170, "rank": 3 },
{ "points": 180, "rank": 4 },
{ "points": 220, "rank": 5 }
我有一个供用户使用的 Mongoose 模型。每个用户都有一定数量的积分。我想创建一个用户排名字段:
rank = user position sorted by rank / total users
假设用户模型如下所示:
{
'name': 'bob',
'points': 15,
'rank': 9/15,
}
(我意识到分数在存储时实际上是小数)。
有什么方法可以更新所有这些用户:
1) 按分数排序
2) 获取用户在此排序列表中的索引
3) 将该索引除以列表中的项目总数
我不确定使用哪种 mongo 运算符来查找文档在查询结果中的位置以及查找查询结果的总大小。
您可以使用几个查询和一些 JavaScript 来完成此操作。扩展您概述的步骤,您需要做的是:
- 找到所有用户文档,按
points
降序排列并将结果分配给游标。您可能希望确保在此字段上有一个索引以使此查询 运行 更快。 - 获取返回的文档数。
- 使用索引跟踪文档在结果中的位置。
- 遍历文档,使用计数和索引计算
rank
,并使用计算结果更新相应用户的rank
。
在 mongo
shell 中,代码如下所示。
var c = db.user.find().sort({ "points": -1 });
var count = c.count();
var i = 1;
while (c.hasNext()) {
var rank = i / count;
var user = c.next();
db.user.update(
{ "_id": user._id },
{ "$set": { "rank": rank } }
);
i++;
}
因此,如果您的 collection 中有以下三个用户:
{
"_id" : ObjectId("54f0af63cfb269d664de0b4e"),
"name" : "bob",
"points" : 15,
"rank" : 0
}
{
"_id" : ObjectId("54f0af7fcfb269d664de0b4f"),
"name" : "arnold",
"points" : 20,
"rank" : 0
}
{
"_id" : ObjectId("54f0af95cfb269d664de0b50"),
"name" : "claus",
"points" : 10,
"rank" : 0
}
更新后他们的文档将如下所示:
{
"_id" : ObjectId("54f0af63cfb269d664de0b4e"),
"name" : "bob",
"points" : 15,
"rank" : 0.6666666666666666
}
{
"_id" : ObjectId("54f0af7fcfb269d664de0b4f"),
"name" : "arnold",
"points" : 20,
"rank" : 0.3333333333333333
}
{
"_id" : ObjectId("54f0af95cfb269d664de0b50"),
"name" : "claus",
"points" : 10,
"rank" : 1
}
使用以前的答案不是一个好主意。每次更新 points
值后需要重新计算 rank
。
Mongo 版本 5.0+ 引入 $rank 聚合:
db.users.aggregate([
{
$setWindowFields: {
sortBy: { points: 1 },
output: {
rank: {
$rank: {}
}
}
}
}
])
会输出
{ "points": 140, "rank": 1 },
{ "points": 160, "rank": 2 },
{ "points": 170, "rank": 3 },
{ "points": 180, "rank": 4 },
{ "points": 220, "rank": 5 }