【问题标题】:How to get ranking position of a mongoDB collection?如何获取 mongoDB 集合的排名位置?
【发布时间】:2017-08-08 02:38:33
【问题描述】:

我有一个如下所示的 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,因为他的比赛次数比任何人都多每日星球用户。

但是,即使经过几天的研究,我也找不到方法。 (我什至不知道如何做全球排名或公司排名)。

关于如何解决此问题或如何以不同方式解决问题的任何想法?

【问题讨论】:

    标签: mongodb ranking


    【解决方案1】:

    基本思想是首先按照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。

    【讨论】:

    • 非常感谢! “includeArrayIndex”是我要找的!!
    • @hyades,内存安全吗?如果有数千个文档怎么办?
    【解决方案2】:

    你期待这个结果吗? . 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
            }
    ]
    

    【讨论】:

      【解决方案3】:

      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: {} } })
        • 这是基于排序字段pointsglobRnk: { $rank: {} }的所有文档中文档的排名。

      第二个$setWindowFields 阶段与第一个阶段非常相似,除了现在在firm 定义的每个分区内计算的排名:partitionBy: "$firm"

      【讨论】:

        【解决方案4】:

        你使用 $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)
        }
        

        【讨论】:

          猜你喜欢
          • 2013-08-29
          • 1970-01-01
          • 2020-02-28
          • 2014-05-26
          • 2012-06-04
          • 1970-01-01
          • 2021-07-05
          • 2020-08-06
          • 1970-01-01
          相关资源
          最近更新 更多