根据键值对文档进行排序和排序?

Awk*_*bie 3 mongodb aggregation-framework

我正在尝试接触mongodb,并尝试为mytest1集合中的学生拉出最高分的文档,并在下面形成查询。

{
        "_id" : ObjectId("5943b63496459374d40da429"),
        "name" : "Person1",
        "marks" : 20
    },
    {
        "_id" : ObjectId("5943b65196459374d40da43a"),
        "name" : "Person2",
        "marks" : 20
    },
    {
        "_id" : ObjectId("5943b65196459374d40da43c"),
        "name" : "Person1",
        "marks" : 30
    },
    {
        "_id" : ObjectId("5943b6d696459374d40da47a"),
        "name" : "Person1",
        "marks" : 25
    },
    {
        "_id" : ObjectId("5943b6d696459374d40da47c"),
        "name" : "Person2",
        "marks" : 50
    },
    {
        "_id" : ObjectId("5943b6d696459374d40da47e"),
        "name" : "Person1",
        "marks" : 90
    },
    {
        "_id" : ObjectId("5943b6d696459374d40da480"),
        "name" : "Person3",
        "marks" : 990
    }
Run Code Online (Sandbox Code Playgroud)

查询:

      db.mytest1.aggregate( [          
      { $sort : { "name" : 1,"marks" : -1} },
             {$group:
             {
               _id: "$name",
               name: { $first: "$name" },
               marks: { $first: "$marks" }
         }}
])   
Run Code Online (Sandbox Code Playgroud)
  1. 有一个更好的方法吗?
  2. 如果我的情况是根据标记对文档进行编号,我该如何实现?

结果:

     {"name" : "Person1","marks" : 30,"rank" : 1}
     {"name" : "Person1","marks" : 25,"rank" : 2}
     {"name" : "Person1","marks" : 20,"rank" : 3} 
     {"name" : "Person2","marks" : 50,"rank" : 1}
     {"name" : "Person2","marks" : 20,"rank" : 2}
     {"name" : "Person3","marks" : 990,"rank" : 3} 
Run Code Online (Sandbox Code Playgroud)

Nei*_*unn 6

我确实认为这是最简单的游标迭代方法,但稍后会介绍更多。

使用聚合框架进行“小型”分组的最快实用方法是使用MongoDB 3.2引入的includeArrayIndexfrom $unwind

db.mytest1.aggregate([
  { "$sort": { "name" : 1,"marks" : -1} },
  { "$group": {
    "_id": "$name",
    "items": { "$push": "$$ROOT" }
  }},
  { "$unwind": { "path": "$items", "includeArrayIndex": "items.rank" } },
  { "$replaceRoot": { "newRoot": "$items" } },
  { "$sort": { "name" : 1,"marks" : -1} }
])
Run Code Online (Sandbox Code Playgroud)

产生:

{ "name" : "Person1", "marks" : 90, "rank" : NumberLong(0) }
{ "name" : "Person1", "marks" : 30, "rank" : NumberLong(1) }
{ "name" : "Person1", "marks" : 25, "rank" : NumberLong(2) }
{ "name" : "Person1", "marks" : 20, "rank" : NumberLong(3) }
{ "name" : "Person2", "marks" : 50, "rank" : NumberLong(0) }
{ "name" : "Person2", "marks" : 20, "rank" : NumberLong(1) }
{ "name" : "Person3", "marks" : 990, "rank" : NumberLong(0) }
Run Code Online (Sandbox Code Playgroud)

或者再走一点:

db.mytest1.aggregate([
  { "$sort": { "name" : 1,"marks" : -1} },
  { "$group": {
    "_id": "$name",
    "items": { "$push": "$$ROOT" }
  }},
  { "$unwind": { "path": "$items", "includeArrayIndex": "items.rank" } },
  { "$project": { 
    "_id": 0, 
    "name": "$items.name", 
    "marks": "$items.marks",
    "rank": { "$add": [ "$items.rank", 1 ] } 
  }},
  { "$sort": { "name" : 1,"marks" : -1} }
])
Run Code Online (Sandbox Code Playgroud)

以您想要的方式。

{ "name" : "Person1", "marks" : 90, "rank" : 1 }
{ "name" : "Person1", "marks" : 30, "rank" : 2 }
{ "name" : "Person1", "marks" : 25, "rank" : 3 }
{ "name" : "Person1", "marks" : 20, "rank" : 4 }
{ "name" : "Person2", "marks" : 50, "rank" : 1 }
{ "name" : "Person2", "marks" : 20, "rank" : 2 }
{ "name" : "Person3", "marks" : 990, "rank" : 1 }
Run Code Online (Sandbox Code Playgroud)

但是要小心,因为我们将所有内容放入“分组”数组中,以便在提取时获得“索引”位置。这对于较小的列表很好,但是您永远不会尝试使用数千个项目。

对于1000个项目,然后迭代游标并在中断处排名:

var current = null,
    rank = 0;

db.mytest1.find().sort({ "name": 1, "marks": -1 }).forEach(doc => {
  if ( doc.name != current || current == null ) {
    rank = 0;
    current = doc.name;
  }
  rank++;
  doc.rank = rank;
  delete doc._id;
  printjson(doc);
})
Run Code Online (Sandbox Code Playgroud)

结果是一样的:

{ "name" : "Person1", "marks" : 90, "rank" : 1 }
{ "name" : "Person1", "marks" : 30, "rank" : 2 }
{ "name" : "Person1", "marks" : 25, "rank" : 3 }
{ "name" : "Person1", "marks" : 20, "rank" : 4 }
{ "name" : "Person2", "marks" : 50, "rank" : 1 }
{ "name" : "Person2", "marks" : 20, "rank" : 2 }
{ "name" : "Person3", "marks" : 990, "rank" : 1 }
Run Code Online (Sandbox Code Playgroud)

因此,实际上,您可以这样做,因为它非常简单且快速。