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

时间:2017-06-16 11:11:29

标签: 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
    }

查询:

      db.mytest1.aggregate( [          
      { $sort : { "name" : 1,"marks" : -1} },
             {$group:
             {
               _id: "$name",
               name: { $first: "$name" },
               marks: { $first: "$marks" }
         }}
])   
  1. 有更好的方法吗?
  2. 如果我的方案是根据标记对文档进行编号,我该如何实现呢?
  3. 结果:

         {"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} 
    

1 个答案:

答案 0 :(得分:5)

我认为这最简单的游标迭代是最实用的,但稍后会更多。

使用聚合框架的“小”分组的最快实用方法是使用MongoDB 3.2引入的includeArrayIndex中的$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} }
])

产生:

{ "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) }

或者再多走一点:

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} }
])

以你想要的方式。

{ "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 }

要小心,因为我们将所有内容放入数组中以进行“分组”,以便在提取时获得“索引”位置。这适用于小型列表,但您绝不会尝试使用数千个项目。

对于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);
})

结果相同:

{ "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 }

所以,你可以这样做,因为它非常简单快速。