如何获得 mongoDB 集合的排名位置?
How to get ranking position of a mongoDB collection?
我有一个 mongoDB 集合,如下所示:
{
"_id": 1,
"name": "John Doe",
"company": "Acme",
"email": "john.doe@acme.com",
"matches": [171844, 169729, 173168, 174310, 168752, 174972, 172959, 169546]
}
{
"_id": 2,
"name": "Bruce Wayne",
"company": "Wayne Enterprises",
"email": "bruce@wayne.com",
"matches": [171844, 232333, 233312, 123456]
}
{
"_id": 3,
"name": "Tony Stark",
"company": "Stark Industries",
"email": "tony@stark.com",
"matches": [173844, 155729, 133168, 199310, 132752, 139972]
}
{
"_id": 4,
"name": "Clark Kent",
"company": "Daily Planet",
"email": "clark.kent@planet.com",
"matches": [169729, 174310, 168752]
}
{
"_id": 5,
"name": "Lois Lane",
"company": "Daily Planet",
"email": "lois.lane@planet.com",
"matches": [172959, 169546]
}
我需要获得经过过滤的用户列表,但需要使用一个键来显示用户的“排名”位置,该位置基于它拥有的“匹配”记录的数量。
应该有一个global ranking
的位置和一个company ranking
的位置。
期望的结果应该是这样的(例如过滤 company='Daily Planet'):
{
_id: 4,
name: 'Clark Kent',
company: 'Daily Planet',
email: 'clark.kent@planet.com',
points: 3, // <=
globalRank: 4, // <=
companyRank: 1 // <=
},
{
_id: 5,
name: 'Lois Lane',
company: 'Daily Planet',
email: 'lois.lane@planet.com',
points: 2, // <=
globalRank: 4, // <=
companyRank: 2 // <=
}
请注意,Clark Kent 在全球排名中排名第 4,因为他有 3 场比赛(John Doe、Bruce Wayne 和 Tony Stark 的比赛比他多)并且在公司排名中排名第 1,因为他有更多的比赛比任何 Daily Planet 用户都多。
然而,我研究了几天,还是找不到办法。 (我什至不知道如何只做全球排名或公司排名)。
关于如何解决这个问题或如何以不同的方式解决问题有什么想法吗?
基本思路是先按照 points
对点进行排序,然后按 $push
将它们放入数组中。这确保元素按排序顺序插入。然后我们 $unwind 使用 includeArrayIndex
属性 生成排序数组中对应于排名的元素的索引。
使用上述逻辑的管道如下(尝试逐步进行以更好地理解):-
aggregate([
{
$project: {
_id: 1,
name: "$name",
company: "$company",
email: "$email",
points: {
$size: "$matches"
}
}
}, {
$sort: {
points: -1
}
},
{
$group: {
_id: {},
arr: {
$push: {
name: '$name',
company: '$company',
email: '$email',
points: '$points'
}
}
}
}, {
$unwind: {
path: '$arr',
includeArrayIndex: 'globalRank',
}
}, {
$sort: {
'arr.company': 1,
'arr.points': -1
}
}, {
$group: {
_id: '$arr.company',
arr: {
$push: {
name: '$arr.name',
company: '$arr.company',
email: '$arr.email',
points: '$arr.points',
globalRank: '$globalRank'
}
}
}
}, {
$unwind: {
path: '$arr',
includeArrayIndex: 'companyRank',
}
}, {
$project: {
_id: 0,
name: '$arr.name',
company: '$arr.company',
email: '$arr.email',
points: '$arr.points',
globalRank: '$arr.globalRank',
companyRank: '$companyRank'
}
}
]);
查询的输出是
/* 1 */
{
"companyRank" : NumberLong(0),
"name" : "Bruce Wayne",
"company" : "Wayne Enterprises",
"email" : "bruce@wayne.com",
"points" : 4,
"globalRank" : NumberLong(2)
}
/* 2 */
{
"companyRank" : NumberLong(0),
"name" : "Tony Stark",
"company" : "Stark Industries",
"email" : "tony@stark.com",
"points" : 6,
"globalRank" : NumberLong(1)
}
/* 3 */
{
"companyRank" : NumberLong(0),
"name" : "Clark Kent",
"company" : "Daily Planet",
"email" : "clark.kent@planet.com",
"points" : 3,
"globalRank" : NumberLong(3)
}
/* 4 */
{
"companyRank" : NumberLong(1),
"name" : "Lois Lane",
"company" : "Daily Planet",
"email" : "lois.lane@planet.com",
"points" : 2,
"globalRank" : NumberLong(4)
}
/* 5 */
{
"companyRank" : NumberLong(0),
"name" : "John Doe",
"company" : "Acme",
"email" : "john.doe@acme.com",
"points" : 8,
"globalRank" : NumberLong(0)
}
此处排名为 0。
您使用了 $match 条件。所以,你也试试这个..
db.rank.aggregate([{
$match: {
"company": "Daily Planet"
}
}, {
$project: {
_id: 1,
name: "$name",
company: "$company",
email: "$email",
points: {
$size: "$matches"
}
}
}, {
$sort: {
points: -1
}
}, {
$group: {
_id: {},
list: {
$push: {
name: '$name',
company: '$company',
email: '$email',
points: '$points'
}
}
}
}, {
$unwind: {
path: '$list',
includeArrayIndex: 'globalRank',
}
}, {
$sort: {
'list.company': 1,
'list.points': -1
}
}, {
$group: {
_id: '$list.company',
list: {
$push: {
name: '$list.name',
company: '$list.company',
email: '$list.email',
points: '$list.points',
globalRank: '$globalRank'
}
}
}
}, {
$unwind: {
path: '$list',
includeArrayIndex: 'companyRank',
}
}, {
$project: {
_id: 0,
name: '$list.name',
company: '$list.company',
email: '$list.email',
points: '$list.points',
globalRank: '$list.globalRank',
companyRank: '$companyRank'
}
}]).pretty()
这样输出,
{
"companyRank" : NumberLong(0),
"name" : "Clark Kent",
"company" : "Daily Planet",
"email" : "clark.kent@planet.com",
"points" : 3,
"globalRank" : NumberLong(0)
}
{
"companyRank" : NumberLong(1),
"name" : "Lois Lane",
"company" : "Daily Planet",
"email" : "lois.lane@planet.com",
"points" : 2,
"globalRank" : NumberLong(1)
}
你期待这样的结果吗? . result_array
将保留最终结果。
var my_array = db.testCol.aggregate([{ $project: { _id:1, name:1, company:1, email:1, "points" : {$size: "$matches"}, "globalRank":{$literal: 0}, companyRank:{$literal: 0} } },
{$sort: {points : -1 } },
]).toArray()
var result_array = [];
var companyCount = {};
for (i = 0; i < my_array.length; i++) {
var company_name = my_array[i].company
if (companyCount[company_name] == null ){
companyCount[company_name] = 1;
}
else{
companyCount[company_name] = companyCount[company_name] + 1
}
result_array.push({ "_id" : my_array[i]._id, "name": my_array[i].name, "company" : my_array[i].company, "email" : my_array[i].email, "points" : my_array[i].points, "globalRank":i+1 , "companyRank" : companyCount[company_name]})
}
result_array
输出为:
[
{
"_id" : 1,
"name" : "John Doe",
"company" : "Acme",
"email" : "john.doe@acme.com",
"points" : 8,
"globalRank" : 1,
"companyRank" : 1
},
{
"_id" : 3,
"name" : "Tony Stark",
"company" : "Stark Industries",
"email" : "tony@stark.com",
"points" : 6,
"globalRank" : 2,
"companyRank" : 1
},
{
"_id" : 2,
"name" : "Bruce Wayne",
"company" : "Wayne Enterprises",
"email" : "bruce@wayne.com",
"points" : 4,
"globalRank" : 3,
"companyRank" : 1
},
{
"_id" : 4,
"name" : "Clark Kent",
"company" : "Daily Planet",
"email" : "clark.kent@planet.com",
"points" : 3,
"globalRank" : 4,
"companyRank" : 1
},
{
"_id" : 5,
"name" : "Lois Lane",
"company" : "Daily Planet",
"email" : "lois.lane@planet.com",
"points" : 2,
"globalRank" : 5,
"companyRank" : 2
}
]
从 Mongo 5
开始,这是新 $setWindowFields
聚合运算符的完美用例:
// { name: "John Doe", firm: "Acme", matches: [171844, 169729, 173168, 174310, 168752, 174972, 172959, 169546] }
// { name: "Bruce Wayne", firm: "Wayne Enterprises", matches: [171844, 232333, 233312, 123456] }
// { name: "Tony Stark", firm: "Stark Industries", matches: [173844, 155729, 133168, 199310, 132752, 139972] }
// { name: "Clark Kent", firm: "Daily Planet", matches: [169729, 174310, 168752] }
// { name: "Lois Lane", firm: "Daily Planet", matches: [172959, 169546] }
db.collection.aggregate([
{ $set: { pts: { $size: "$matches" } } },
{ $setWindowFields: {
sortBy: { pts: -1 },
output: { globRnk: { $rank: {} } }
}},
{ $setWindowFields: {
partitionBy: "$firm",
sortBy: { pts: -1 },
output: { firmRnk: { $rank: {} } }
}}
])
// { name: "John Doe", firm: "Acme", pts: 8, globRnk: 1, firmRnk: 1, matches: [171844, 169729, 173168, 174310, 168752, 174972, 172959, 169546] }
// { name: "Tony Stark", firm: "Stark Industries", pts: 6, globRnk: 2, firmRnk: 1, matches: [173844, 155729, 133168, 199310, 132752, 139972] }
// { name: "Bruce Wayne",firm: "Wayne Enterprises",pts: 4, globRnk: 3, firmRnk: 1, matches: [171844, 232333, 233312, 123456] }
// { name: "Clark Kent", firm: "Daily Planet", pts: 3, globRnk: 4, firmRnk: 1, matches: [169729, 174310, 168752] }
// { name: "Lois Lane", firm: "Daily Planet", pts: 2, globRnk: 5, firmRnk: 2, matches: [172959, 169546 ] }
第一个 $setWindowFields
阶段增加全球排名:
- 按
points
的降序排列文档:sortBy: { points: -1 }
- 并在每个文档中添加
globRnk
字段 (output: { globRnk: { $rank: {} } }
)
- 根据排序字段
points
,该文档在所有文档中的排名:globRnk: { $rank: {} }
.
第二个 $setWindowFields
阶段与第一个阶段非常相似,除了现在在 firm
定义的每个分区内计算的排名:partitionBy: "$firm"
.
我有一个 mongoDB 集合,如下所示:
{
"_id": 1,
"name": "John Doe",
"company": "Acme",
"email": "john.doe@acme.com",
"matches": [171844, 169729, 173168, 174310, 168752, 174972, 172959, 169546]
}
{
"_id": 2,
"name": "Bruce Wayne",
"company": "Wayne Enterprises",
"email": "bruce@wayne.com",
"matches": [171844, 232333, 233312, 123456]
}
{
"_id": 3,
"name": "Tony Stark",
"company": "Stark Industries",
"email": "tony@stark.com",
"matches": [173844, 155729, 133168, 199310, 132752, 139972]
}
{
"_id": 4,
"name": "Clark Kent",
"company": "Daily Planet",
"email": "clark.kent@planet.com",
"matches": [169729, 174310, 168752]
}
{
"_id": 5,
"name": "Lois Lane",
"company": "Daily Planet",
"email": "lois.lane@planet.com",
"matches": [172959, 169546]
}
我需要获得经过过滤的用户列表,但需要使用一个键来显示用户的“排名”位置,该位置基于它拥有的“匹配”记录的数量。
应该有一个global ranking
的位置和一个company ranking
的位置。
期望的结果应该是这样的(例如过滤 company='Daily Planet'):
{
_id: 4,
name: 'Clark Kent',
company: 'Daily Planet',
email: 'clark.kent@planet.com',
points: 3, // <=
globalRank: 4, // <=
companyRank: 1 // <=
},
{
_id: 5,
name: 'Lois Lane',
company: 'Daily Planet',
email: 'lois.lane@planet.com',
points: 2, // <=
globalRank: 4, // <=
companyRank: 2 // <=
}
请注意,Clark Kent 在全球排名中排名第 4,因为他有 3 场比赛(John Doe、Bruce Wayne 和 Tony Stark 的比赛比他多)并且在公司排名中排名第 1,因为他有更多的比赛比任何 Daily Planet 用户都多。
然而,我研究了几天,还是找不到办法。 (我什至不知道如何只做全球排名或公司排名)。
关于如何解决这个问题或如何以不同的方式解决问题有什么想法吗?
基本思路是先按照 points
对点进行排序,然后按 $push
将它们放入数组中。这确保元素按排序顺序插入。然后我们 $unwind 使用 includeArrayIndex
属性 生成排序数组中对应于排名的元素的索引。
使用上述逻辑的管道如下(尝试逐步进行以更好地理解):-
aggregate([
{
$project: {
_id: 1,
name: "$name",
company: "$company",
email: "$email",
points: {
$size: "$matches"
}
}
}, {
$sort: {
points: -1
}
},
{
$group: {
_id: {},
arr: {
$push: {
name: '$name',
company: '$company',
email: '$email',
points: '$points'
}
}
}
}, {
$unwind: {
path: '$arr',
includeArrayIndex: 'globalRank',
}
}, {
$sort: {
'arr.company': 1,
'arr.points': -1
}
}, {
$group: {
_id: '$arr.company',
arr: {
$push: {
name: '$arr.name',
company: '$arr.company',
email: '$arr.email',
points: '$arr.points',
globalRank: '$globalRank'
}
}
}
}, {
$unwind: {
path: '$arr',
includeArrayIndex: 'companyRank',
}
}, {
$project: {
_id: 0,
name: '$arr.name',
company: '$arr.company',
email: '$arr.email',
points: '$arr.points',
globalRank: '$arr.globalRank',
companyRank: '$companyRank'
}
}
]);
查询的输出是
/* 1 */
{
"companyRank" : NumberLong(0),
"name" : "Bruce Wayne",
"company" : "Wayne Enterprises",
"email" : "bruce@wayne.com",
"points" : 4,
"globalRank" : NumberLong(2)
}
/* 2 */
{
"companyRank" : NumberLong(0),
"name" : "Tony Stark",
"company" : "Stark Industries",
"email" : "tony@stark.com",
"points" : 6,
"globalRank" : NumberLong(1)
}
/* 3 */
{
"companyRank" : NumberLong(0),
"name" : "Clark Kent",
"company" : "Daily Planet",
"email" : "clark.kent@planet.com",
"points" : 3,
"globalRank" : NumberLong(3)
}
/* 4 */
{
"companyRank" : NumberLong(1),
"name" : "Lois Lane",
"company" : "Daily Planet",
"email" : "lois.lane@planet.com",
"points" : 2,
"globalRank" : NumberLong(4)
}
/* 5 */
{
"companyRank" : NumberLong(0),
"name" : "John Doe",
"company" : "Acme",
"email" : "john.doe@acme.com",
"points" : 8,
"globalRank" : NumberLong(0)
}
此处排名为 0。
您使用了 $match 条件。所以,你也试试这个..
db.rank.aggregate([{
$match: {
"company": "Daily Planet"
}
}, {
$project: {
_id: 1,
name: "$name",
company: "$company",
email: "$email",
points: {
$size: "$matches"
}
}
}, {
$sort: {
points: -1
}
}, {
$group: {
_id: {},
list: {
$push: {
name: '$name',
company: '$company',
email: '$email',
points: '$points'
}
}
}
}, {
$unwind: {
path: '$list',
includeArrayIndex: 'globalRank',
}
}, {
$sort: {
'list.company': 1,
'list.points': -1
}
}, {
$group: {
_id: '$list.company',
list: {
$push: {
name: '$list.name',
company: '$list.company',
email: '$list.email',
points: '$list.points',
globalRank: '$globalRank'
}
}
}
}, {
$unwind: {
path: '$list',
includeArrayIndex: 'companyRank',
}
}, {
$project: {
_id: 0,
name: '$list.name',
company: '$list.company',
email: '$list.email',
points: '$list.points',
globalRank: '$list.globalRank',
companyRank: '$companyRank'
}
}]).pretty()
这样输出,
{
"companyRank" : NumberLong(0),
"name" : "Clark Kent",
"company" : "Daily Planet",
"email" : "clark.kent@planet.com",
"points" : 3,
"globalRank" : NumberLong(0)
}
{
"companyRank" : NumberLong(1),
"name" : "Lois Lane",
"company" : "Daily Planet",
"email" : "lois.lane@planet.com",
"points" : 2,
"globalRank" : NumberLong(1)
}
你期待这样的结果吗? . result_array
将保留最终结果。
var my_array = db.testCol.aggregate([{ $project: { _id:1, name:1, company:1, email:1, "points" : {$size: "$matches"}, "globalRank":{$literal: 0}, companyRank:{$literal: 0} } },
{$sort: {points : -1 } },
]).toArray()
var result_array = [];
var companyCount = {};
for (i = 0; i < my_array.length; i++) {
var company_name = my_array[i].company
if (companyCount[company_name] == null ){
companyCount[company_name] = 1;
}
else{
companyCount[company_name] = companyCount[company_name] + 1
}
result_array.push({ "_id" : my_array[i]._id, "name": my_array[i].name, "company" : my_array[i].company, "email" : my_array[i].email, "points" : my_array[i].points, "globalRank":i+1 , "companyRank" : companyCount[company_name]})
}
result_array
输出为:
[
{
"_id" : 1,
"name" : "John Doe",
"company" : "Acme",
"email" : "john.doe@acme.com",
"points" : 8,
"globalRank" : 1,
"companyRank" : 1
},
{
"_id" : 3,
"name" : "Tony Stark",
"company" : "Stark Industries",
"email" : "tony@stark.com",
"points" : 6,
"globalRank" : 2,
"companyRank" : 1
},
{
"_id" : 2,
"name" : "Bruce Wayne",
"company" : "Wayne Enterprises",
"email" : "bruce@wayne.com",
"points" : 4,
"globalRank" : 3,
"companyRank" : 1
},
{
"_id" : 4,
"name" : "Clark Kent",
"company" : "Daily Planet",
"email" : "clark.kent@planet.com",
"points" : 3,
"globalRank" : 4,
"companyRank" : 1
},
{
"_id" : 5,
"name" : "Lois Lane",
"company" : "Daily Planet",
"email" : "lois.lane@planet.com",
"points" : 2,
"globalRank" : 5,
"companyRank" : 2
}
]
从 Mongo 5
开始,这是新 $setWindowFields
聚合运算符的完美用例:
// { name: "John Doe", firm: "Acme", matches: [171844, 169729, 173168, 174310, 168752, 174972, 172959, 169546] }
// { name: "Bruce Wayne", firm: "Wayne Enterprises", matches: [171844, 232333, 233312, 123456] }
// { name: "Tony Stark", firm: "Stark Industries", matches: [173844, 155729, 133168, 199310, 132752, 139972] }
// { name: "Clark Kent", firm: "Daily Planet", matches: [169729, 174310, 168752] }
// { name: "Lois Lane", firm: "Daily Planet", matches: [172959, 169546] }
db.collection.aggregate([
{ $set: { pts: { $size: "$matches" } } },
{ $setWindowFields: {
sortBy: { pts: -1 },
output: { globRnk: { $rank: {} } }
}},
{ $setWindowFields: {
partitionBy: "$firm",
sortBy: { pts: -1 },
output: { firmRnk: { $rank: {} } }
}}
])
// { name: "John Doe", firm: "Acme", pts: 8, globRnk: 1, firmRnk: 1, matches: [171844, 169729, 173168, 174310, 168752, 174972, 172959, 169546] }
// { name: "Tony Stark", firm: "Stark Industries", pts: 6, globRnk: 2, firmRnk: 1, matches: [173844, 155729, 133168, 199310, 132752, 139972] }
// { name: "Bruce Wayne",firm: "Wayne Enterprises",pts: 4, globRnk: 3, firmRnk: 1, matches: [171844, 232333, 233312, 123456] }
// { name: "Clark Kent", firm: "Daily Planet", pts: 3, globRnk: 4, firmRnk: 1, matches: [169729, 174310, 168752] }
// { name: "Lois Lane", firm: "Daily Planet", pts: 2, globRnk: 5, firmRnk: 2, matches: [172959, 169546 ] }
第一个 $setWindowFields
阶段增加全球排名:
- 按
points
的降序排列文档:sortBy: { points: -1 }
- 并在每个文档中添加
globRnk
字段 (output: { globRnk: { $rank: {} } }
)- 根据排序字段
points
,该文档在所有文档中的排名:globRnk: { $rank: {} }
.
- 根据排序字段
第二个 $setWindowFields
阶段与第一个阶段非常相似,除了现在在 firm
定义的每个分区内计算的排名:partitionBy: "$firm"
.