mongodb聚合

hexel發表於2013-09-01

MongoDB的聚合框架實現sum()、avg()、group by等聚合操作。通過聚合框架,還可對返回的結果進行處理,實現一些特殊需求,例如資料過濾、別名顯示、增加欄位、提取子欄位等。
 

1 聚合框架元件 

聚合框架包含兩大元件:管道和表示式。 

1.1管道 

管道與unix管道類似,實質就是把掃描的資料輸入聚合程式,進行一些過濾、分組、求和等操作,這些操作是通過管道操作符完成的。 

例如,實現類似SQL:select b as ok from test111, 

聚合語句如下: 

db.test111.aggregate([{$project:{_id:0,ok:"$b"}}]); 

{ 

        "result" : [ 

                { 

                        "ok" : 2 

                }, 

              ……………………省略……………………………… 

                { 

                        "ok" : 10000 

                } 

        ], 

        "ok" : 1 

} 

$project管道操作符就是用來控制欄位輸出的,本例$project不讓_id欄位顯示,只顯示欄位b,並且用別名”ok”顯示。與unix類似,多個管道操作符是可以一起使用的。例如,上面的結果,只要求顯示前2行,可以用$limit管道操作符,語句如下: 

mongos> db.test111.aggregate([{$project:{_id:0,ok:"$b"}},{ $limit : 2 },{$match:{ok:{$ne:2}}}]) 

{ "result" : [ { "ok" : 3 } ], "ok" : 1 } 

還有其他的管道操作符:$group,$skip,$sort等 

1.2 表示式 

表示式實際就是對管道的結果進行一些計算,例如求平均值,最大值,記錄條數等。Sql語句:select * from (select avg(b),max(b),count(a) from test111 group by a) where rownum<=3,對應的mongo查詢語句如下: 

db.test111.aggregate([  

  { $group: {  

    _id: "$a",  

    avg_b:  { $avg: "$b" },  

    max_b:  { $max: "$b" },  

    nb_b: { $sum: 1 }  

  } },{ $limit : 3 }  

]); 

2 聚合運算效能優化 

db.test.aggregate({$group:{_id:{EN_T:"$EN_T",EN_N:"$EN_N"}, SE:{$sum:"$S_PT"}}},{$limit:20},{$sort:{TIME:-1}}) 

聚合操作會把集合內容輸入管道,然後計算輸出。優化的目標就是要儘量讓輸入管道的內容更少一些。 

2.1下面的管道操作符出現在管道開頭時,都可以良好地使用索引: 

$match,$sort,$limit,$skip 

2.2 儘早過濾 

如果聚合操作只需要部分文件,那麼就應該儘早用$match操作戶進行過濾,隨後用$sort操作符進行排序,這樣才能有效使用索引 

2.3 管道順序優化 

3 聚合框架例項 

考慮atricles集合: 

3.1 $unwind: 

Articles是文章集合,文章中有tags包含各種標籤,以及使用者評論。現在按照標籤分組,列出每個標籤下的文章作者名稱。 

mongos> db.articles.find(0 ).pretty() 

{ 

        "_id" : ObjectId("521d64e482f40a1927af1d4c"), 

        "title" : "this is my title", 

        "author" : "bob", 

        "posted" : ISODate("2013-08-28T02:48:04.561Z"), 

        "pageViews" : 5, 

        "tags" : [ 

                "fun", 

                "good", 

                "fun" 

        ], 

        "comments" : [ 

                { 

                        "author" : "joe", 

                        "text" : "this is cool" 

                }, 

                { 

                        "author" : "sam", 

                        "text" : "this is bad" 

                } 

        ], 

        "other" : { 

                "foo" : 5 

        } 

} 

mongos> a 

{ 

        "title" : "this is my title", 

        "author" : "huangxing", 

        "posted" : ISODate("2013-08-28T02:50:39.639Z"), 

        "pageViews" : 5, 

        "tags" : [ 

                "fun", 

                "good", 

                "fun" 

        ], 

        "comments" : [ 

                { 

                        "author" : "lihao", 

                        "text" : "this is cool" 

                }, 

                { 

                        "author" : "wangm", 

                        "text" : "this is bad" 

                } 

        ], 

        "other" : { 

                "foo" : 5 

        } 

} 

mongos> db.articles.insert(a) 

mongos> db.articles.aggregate( { $project : { author : 1, tags : 1, } }, { $unwind : "$tags" }, { $group : { _id : { tags : "$tags" }, authors : { $addToSet : "$author" } } } ); 

{ 

        "result" : [ 

                { 

                        "_id" : { 

                                "tags" : "good" 

                        }, 

                        "authors" : [ 

                                "huangxing", 

                                "bob" 

                        ] 

                }, 

                { 

                        "_id" : { 

                                "tags" : "fun" 

                        }, 

                        "authors" : [ 

                                "huangxing", 

                                "bob" 

                        ] 

                } 

        ], 

        "ok" : 1 

} 

構建新集合,匯入資料: 

[root@55 ~]# mongoimport -h 192.168.69.55 --port 30000 -d test -c zipcodes -u test -p test  --file ./zips.json 

> db.zipcodes.findOne() 

{ 

        "city" : "ACMAR", 

        "loc" : [ 

                -86.51557, 

                33.584132 

        ], 

        "pop" : 6055, 

        "state" : "AL", 

        "_id" : "35004" 

} 

3.2 查詢總人口超過1300萬的州 

> db.zipcodes.aggregate( { $group : 

{ _id : "$state", 

totalPop : { $sum : "$pop" } } }, 

{ $match : {totalPop : { $gte : 13 * 1000 * 1000 } } } ) 

{ 

        "result" : [ 

                { 

                        "_id" : "NY", 

                        "totalPop" : 17990455 

                }, 

                { 

                        "_id" : "TX", 

                        "totalPop" : 16986510 

                }, 

                { 

                        "_id" : "CA", 

                        "totalPop" : 29760021 

                } 

        ], 

        "ok" : 1 

} 

3.3 查詢每個州的城市的平均人口 

mongos> db.zipcodes.aggregate( { $group : 

{ _id : { state : "$state", city : "$city" }, 

pop : { $sum : "$pop" } } }, 

{ $group : 

{ _id : "$_id.state", 

{ 

        "result" : [ 

                { 

                        "_id" : "RI", 

                        "avgCityPop" : 18933.283018867925 

                }, 

                

        ], 

        "ok" : 1 

} 

3.4 查詢每個州中人口最多的城市和人口少的城市 

db.zipcodes.aggregate( { $group: 

{ _id: { state: "$state", city: "$city" }, 

pop: { $sum: "$pop" } } }, 

{ $sort: { pop: 1 } }, 

{ $group: 

{ _id : "$_id.state", 

biggestCity: { $last: "$_id.city" }, 

biggestPop: { $last: "$pop" }, 

smallestCity: { $first: "$_id.city" }, 

smallestPop: { $first: "$pop" } } }, 

{ $project: 

{ _id: 0, 

state: "$_id", 

biggestCity: { name: "$biggestCity", pop: "$biggestPop" }, 

smallestCity: { name: "$smallestCity", pop: "$smallestPop" } } } ,{$limit:3}) 

{ 

        "result" : [ 

                { 

                        "biggestCity" : { 

                                "name" : "CRANSTON", 

                                "pop" : 176404 

                        }, 

                        "smallestCity" : { 

                                "name" : "CLAYVILLE", 

                                "pop" : 45 

                        }, 

                        "state" : "RI" 

                }, 

                { 

                        "biggestCity" : { 

                                "name" : "CLEVELAND", 

                                "pop" : 536759 

                        }, 

                        "smallestCity" : { 

                                "name" : "ISLE SAINT GEORG", 

                                "pop" : 38 

                        }, 

                        "state" : "OH" 

                }, 

                { 

                        "biggestCity" : { 

                                "name" : "BALTIMORE", 

                                "pop" : 733081 

                        }, 

                        "smallestCity" : { 

                                "name" : "ANNAPOLIS JUNCTI", 

                                "pop" : 32 

                        }, 

                        "state" : "MD" 

                } 

        ], 

        "ok" : 1 

} 

3.5 查詢排序,對映欄位 

db.zipcodes.aggregate( { $group : 

{ _id : "$state", 

totalPop : { $sum : "$pop" } } }, 

{ $match : {totalPop : { $gte : 13 * 1000 * 1000 } } },{$project:{name:{$toUpper:"$_id"},_id:0,pop:"$totalPop"}},{$sort : { name : 1 } }) 

3.6 Sql與聚合對映: 

3.6.1 Select count(*) as count from zipcides 

mongos> db.zipcodes.aggregate(  

{ $group: { _id: null, 

count: { $sum: 1 } } } 

 ) 

{ "result" : [ { "_id" : null, "count" : 29467 } ], "ok" : 1 } 

3.6.2 Select sum(pop)  as totalpop from zipcodes 

db.zipcodes.aggregate( [ 

{ $group: { _id: null, 

totalpop: { $sum: "$pop" } } },{$project:{_id:0,totalpop:1}} 

 ]) 

{ "result" : [ { "totalpop" : 248706415 } ], "ok" : 1 } 

3.6.3 Select state,sum(pop) as state_pop from zipcodes goup by state_pop  having total_pop>=13000 order by state_pop  

db.zipcodes.aggregate([ 

 {$group:{_id:"$state",state_pop:{ $sum:"$pop"}}}, 

{$match:{state_pop:{$gt:13000000}}}, 

 {$sort:{state_pop:1}} 

 ]) 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28813259/viewspace-772000/,如需轉載,請註明出處,否則將追究法律責任。

相關文章