MongoDB查询,排序然后为组取第n个文档

时间:2016-08-22 12:24:17

标签: mongodb mongodb-query

我正在尝试撰写MongoDB查询...

我的目标是按countryrating排序,然后返回 每个国家/地区的第n个评级玩家列出的玩家记录列表(所以评分最高,或者评分最高的第三名)。

我已经实现了第一部分:

db.getCollection('players').find( { event: 'open' }).sort({ country: 1, rating: -1 });

更新

以下是两个国家的样本,每个国家有三名玩家,按评级排序:

第一队:

{
    "id" : 400041,
    "name" : "Adams Michael",
    "rating" : 2727,
    "country" : "England",
    "event" : "open"
},
{
    "id" : 404853,
    "name" : "McShane Luke J",
    "rating" : 2671,
    "country" : "England",
    "event" : "open",
},
{
    "id" : 400025,
    "name" : "Short Nigel D",
    "rating" : 2666,
    "country" : "England",
    "event" : "open"
}

第二组:

 {
    "id": 4101588,
    "name": "Kramnik Vladimir",
    "rating": 2808,
    "country": "Russia",
    "event": "open"
},
{
    "id": 4126025,
    "name": "Grischuk Alexander",
    "rating": 2784,
    "country": "Russia",
    "event": "open"
},
{
    "id": 14109603,
    "name": "Karjakin Sergey",
    "rating": 2769,
    "country": "Russia",
    "event": "open"
}

我希望我的查询返回以下对象:

  • 亚当斯迈克尔
  • Kramnik Vladimir

(因为这是评价最高的两个)

我希望我的查询也允许我查询第二高的评分:

  • McShane Luke J
  • Grischuk Alexander

3 个答案:

答案 0 :(得分:8)

这是使用4阶段聚合管道

的方法
  1. 按照已经完成的国家/地区和评级排序
  2. 按国家/地区分组并将所有玩家详细信息推送到数组
  3. 这个是硬道理。使用project$arrayElemAt一起获得各个国家/地区ith评分的玩家
  4. 再次使用项目为您提供所需格式的对象

    db.getCollection('players').aggregate(
    {
        $sort: {country: 1, rating: -1}
    },
    {
        $group:   {
            _id: "$country", 
            players: {$push: {name: "$name", rating: "$rating", event: "$event"}}
        }
    },
    {
        $project: {
            player: {$arrayElemAt: ["$players", iTH_RATING]}
        } 
    },
    {
        $project: {
            name: "$player.name",
            rating: "$player.rating",
            event: "$player.event"
            }
    })
    
  5. 请参阅下面的屏幕抓取2nd Highest Rated Players。由于这些是0-indexed,因此查询中的iTH_RATING变量将替换为1.要获得评分最高的玩家,请将其替换为0,依此类推。 2nd Highest Rated Players

答案 1 :(得分:1)

您可以通过聚合执行此操作。如果您在 $ group 阶段之前 $ sort ,则可以使用 $ first 从每个组(国家/地区)的第一个相关文档中检索数据:

db.getCollection('players').aggregate([
    {
        $sort: { country: 1, rating: -1 }
    },
    {
        $group:{
            _id: "$country",
            "highest_rated": { "$first": "$name" }
        }
    }
]);

如果您想要匹配文档中的所有归属(不仅仅是玩家名称),您可以使用$$ ROOT:

db.getCollection('players').aggregate([
    {
        $sort: { country: 1, rating: -1 }
    },
    {
        $group:{
            _id: "$country",
            "highest_rated": { "$first": "$$ROOT" }
        }
    }
]);

答案 2 :(得分:1)

您可以使用$skip$limit使用var map = function map(){ emit(this.country, this); }; var reduce = function(key, values){ var position_to_return = 1; // change here to 0 to return the first rated player for certain country var sorted_values = values.sort(function(first, second) { return second.rating - first.rating; }) return key, sorted_values[position_to_return]; }; db.runCommand({"mapReduce":"players", map:map, reduce:reduce, out:{replace:"players2"}, query:{ "event": "open" }}) { "_id" : "England", "value" : { "_id" : ObjectId("57bb093c6124c1a9d8be905d"), "id" : 404853, "name" : "McShane Luke J", "rating" : 2671, "country" : "England", "event" : "open" } } { "_id" : "Russia", "value" : { "_id" : ObjectId("57bb093c6124c1a9d8be9060"), "id" : 4126025, "name" : "Grischuk Alexander", "rating" : 2784, "country" : "Russia", "event" : "open" } } ,在他的评论中使用多个查询,每个国家/地区一个查询。

您可以使用map-reduce,而不是在1'查询'中获得预期结果(但请记住,map-reduce不适合实时查询,可能是非常慢的操作,但这很大程度上取决于你的数据大小)。

在地图功能中,使用键作为国家/地区来触发文档。在reduce功能中,根据评级从同一国家/地区对玩家进行排序,并仅从排名中的所需位置返回文档(玩家):

Option Explicit

Function CAGR2(rng As Range) As Double

Dim cell As Variant
Dim n As Integer

CAGR2 = 1
n = 0
For Each cell In rng
    CAGR2 = CAGR2 * (cell.Value + 1)
    n = n + 1
Next cell

CAGR2 = CAGR2 ^ (1 / (n / 12))

End Function

输出,当返回每个国家的第二名球员时:

n