dra11767 2014-11-30 22:21
浏览 164

Mongodb聚合排序关联数组

As $ positional operator doesn't work 2 level deep nested array, I am using alternative schema to enable update function for nested array.

I have a nested documents as follows

{
  '_id' : 1234,
  'bio' : {
     'achievements' : {
        'Yhg87Hghg65' : {
           'title' : 'Achievement 1',
           'score' : 95,
           'year' : 2004
        },
        '67gjfygt8Hd' : {
           'title' : 'Achievement 2',
           'score' : 89,
           'year' : 2003
        },
        'Lkoh8hHggf7' : {
           'title' : 'Achievement 1',
           'score' : 90,
           'year' : 2005
        }
     }
  }
}

Now, using mongodb aggregation pipeline, I can fetch this as follow in PHP

$doc = $collection -> aggregate(
  array(
    '$match' => array(
        '_id' => 1234
     )
  ),
  array(
    '$project' => array(
       'bio.achievements' => 1
    )
  )
);

Until now, everything works fine. But I need to sort achievements by year. Using normal '$sort' before '$project' wont work because because achievements aren't arrays, they are fields whose values are array. If anyone know how to achieve it, please give me some hints.

  • 写回答

1条回答 默认 最新

  • dqkyz02602 2014-12-01 05:15
    关注

    When you have your objects in a map, in javascript, you can never guarantee the order of retrieval. Nor has MongoDb any in-built methods to sort the properties of a map. So you need to do the ordering in the client side. Your code may look like:

    var result = db.collection.find({"_id":1234},{"bio.achievements":1}).
           map(function(doc){
                var achievements = (Object.keys(doc.bio.achievements)).
                map(function(key){
                doc.bio.achievements[key]["key"] = key;
                return doc.bio.achievements[key];
               });
               achievements.sort(function(a,b){
               return a.year-b.year;
             })
          doc.bio.achievements = achievements;
          return doc;
      })
    

    Having said this, the best way to store ordered objects is to store them in an array. If you could change your schema as below:

    {
      '_id' : 1234,
      'bio' : {
         'achievements' : [
            {  'id':'Yhg87Hghg65',
               'title' : 'Achievement 1',
               'score' : 95,
               'year' : 2004
            },
            {
               'id':'67gjfygt8Hd',
               'title' : 'Achievement 2',
               'score' : 89,
               'year' : 2003
            },
            {  'id':'Lkoh8hHggf7',
               'title' : 'Achievement 1',
               'score' : 90,
               'year' : 2005
            }
         ]
      }
    }
    

    The you could easily aggregate the desired result:

    db.collection.aggregate([
    {$match:{"_id":1234}},
    {$unwind:"$bio.achievements"},
    {$sort:{"bio.achievements.year":1}},
    {$group:{"_id":"$_id","achievements":{$push:"$bio.achievements"}}}
    ])
    

    Sample o/p:

    {
            "_id" : 1234,
            "achievements" : [
                    {
                            "id" : "67gjfygt8Hd",
                            "title" : "Achievement 2",
                            "score" : 89,
                            "year" : 2003
                    },
                    {
                            "id" : "Yhg87Hghg65",
                            "title" : "Achievement 1",
                            "score" : 95,
                            "year" : 2004
                    },
                    {
                            "id" : "Lkoh8hHggf7",
                            "title" : "Achievement 1",
                            "score" : 90,
                            "year" : 2005
                    }
            ]
    }
    

    As $ positional operator doesn't work 2 level deep nested array, I am using alternative schema to enable update function for nested array.

    You need to design your schema in such a way that the update can be made using the $ operator, as far as the posted example is in question, an update can be made with the positional operator, similar to an operation below:

        db.collection.update({"bio.achievements.id":"Yhg87Hghg65"},
                             {$set:{"bio.achievements.$.year":2006}});
    
    评论

报告相同问题?

悬赏问题

  • ¥15 安卓adb backup备份应用数据失败
  • ¥15 eclipse运行项目时遇到的问题
  • ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
  • ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
  • ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站
  • ¥50 成都蓉城足球俱乐部小程序抢票
  • ¥15 yolov7训练自己的数据集
  • ¥15 esp8266与51单片机连接问题(标签-单片机|关键词-串口)(相关搜索:51单片机|单片机|测试代码)
  • ¥15 电力市场出清matlab yalmip kkt 双层优化问题
  • ¥30 ros小车路径规划实现不了,如何解决?(操作系统-ubuntu)