MongoDB聚合(一) 聚合框架 mapreduce 命令:count,distinct,group 1. 聚合框架 使用聚合框架對集合中的文檔進行變換和組合,能夠用多個構件建立一個管道(pipeline),用於對一連串的文檔進行處理。這些構件包括篩選(filtering),投射(projecting),分組(grouping),排序(sorting),限制(limiting),跳過(skipping)。 例如一個保存着動物類型的集合,但願找出最多的那種動物,假設每種動物被保存爲一個mongodb文檔,能夠按照如下步驟建立管道。 1)將每一個文檔的動物名稱映射出來。 2)安裝名稱排序,統計每一個名稱出現的次數。 3)將文檔按照名稱出現的次數降序排列。 4)將返回結果限制爲前五個。 具體操做符: 1){"$porject", {"name" : 1}} 相似於查詢階段的字段選擇器,指定"fieldname" : 1選定須要的字段,"fieldname" : 0排除不須要的字段,"_id"字段自動顯示。結果保存在內存中,不會寫入磁盤。 db.test_collection.aggregate({"$project" : {"name" : 1}}); => { "_id" : ObjectId("535a2d3c169097010b92fdf6"), "name" : "snake" } 2){"$group", {"_id" : "$name", "count" : {"$sum" : 1}}} 首先指定了分組的字段"name",該操做執行完後,每一個name只對應一個結果,全部能夠將name指定爲惟一標識符"_id"。 第二個字段代表分組內的每一個文檔"count"字段加1。新加入的文檔中不會有count字段。 db.test_collection.aggregate({"$project" : {"name" : 1}}, {"$group" : {"_id" : "$name", "count" : {"$sum" : 1}}}); => { "_id" : "bird", "count" : 8344 } { "_id" : "snake", "count" : 8443 } { "_id" : "cat", "count" : 8183 } { "_id" : "rabbit", "count" : 8206 } { "_id" : "tiger", "count" : 8329 } { "_id" : "cow", "count" : 8309 } { "_id" : "horse", "count" : 8379 } { "_id" : "dog", "count" : 8406 } { "_id" : "dragon", "count" : 8372 } { "_id" : "elephant", "count" : 8264 } { "_id" : "pig", "count" : 8403 } { "_id" : "lion", "count" : 8362 } 3){"$sort" : {"count" : -1}} 對結果集中的文檔根據count字段作降序排列。 4){"$limit" : 5} 將返回結果限制爲5個文檔。 將上述結果綜合起來: db.test_collection.aggregate( { "$project" : {"name" : 1}}, {"$group" : {"_id" : "$name", "count" : {"$sum" : 1}}}, {"$sort" : {"count" : -1}}, {"$limit" : 5} ); aggregate會返回一個文檔數組,內容爲出現次數最多的5個動物: { "_id" : "snake", "count" : 8443 } { "_id" : "dog", "count" : 8406 } { "_id" : "pig", "count" : 8403 } { "_id" : "horse", "count" : 8379 } { "_id" : "dragon", "count" : 8372 } 調試過程當中。能夠逐一對管道符進行排查。 聚合框架不能對集合進行寫入操做,全部結果返回給客戶端,聚合結果必須限制在16M之內。 2. 管道操做符 每一個操做符都會接受一連串的文檔,對這些文檔進行類型轉換,最後獲得的文檔做爲結果傳遞給下一操做符。 不一樣的管道操做符能夠將任意順序組合在一塊兒使用,並且能夠被重複任意屢次。 2.1 $match $match用於對文檔集合進行篩選,以後獲得的文檔子集作聚合。 "$match"支持全部的常規查詢操做符("$gt","$lt","$ne")等,不能使用地理空間操做符。 實際操做中儘可能將"$match"放在管道的前面部分,一方面能夠提快速將不須要的文檔過濾掉,另外在映射和分組前篩選,查詢可使用索引。 2.2 $project 使用"$project"能夠提取字段,能夠重命名字段, db.foo.aggregate({"$project" : {"city" : 1, "_id" : 0}}) => { "city" : "NEW WORK" } 能夠將投射過的字段重命名: db.foo.aggregate({"$project" : {"newcity" : "$city", "_id" : 0}}) => { "newcity" : "NEW WORK" } 使用"$fieldname"語法爲了在聚合框架中引用fieldname字段,例如上面"$city"會被替換爲"NEW WORK"。 對字段重命名後,Mongdb不會記錄其記錄字段的歷史名稱,因此應該在修改字段名稱前使用索引。 2.2.1 管道表達式 可使用表達式將多個字面量和變量組合爲一個值。 可使用組合或者任意深度的嵌套,建立複雜的表達式。 2.2.2 數學表達式 數學表示式用來操做數據運算。 db.foo.aggregate( {"$project" : {"total" : {"$add" : ["$age", "$year"]}, "_id" : 0 } } ) {"total" : 15} 能夠將多個表達式組合爲更爲複雜的表達式: db.foo.aggregate( {"$project" : {"sub" : {"$subtract" : [{"$add" : ["$age", "$year"]}, 7]}, "_id" : 0 } } ) { "sub" : 8 } 操做符語法: 1)"$add" : [expr1, [, expr2, ..., exprN]] 將表達式相加 2)"$subtract" : [expr1, expr2] 表達式1減去表達式2 3)"$multiply" : [expr1, [, expr2, ..., exprN]] 將表達式相乘 4)"$divide" : [expr1, expr2] 表達式1除以表達式2獲得商 5)"$mod" : [expr1, expr2] 表達式1除以表達式2獲得餘數 2.2.3 日期表達式 用於提取日期信息的表達式:"$year","$month","$week","$dayOfMonth","$dayOfweek","$hour","$minute","$second"。只能對日期類型的字段進行日期操做,不能對數值類型進行日期操做。 db.bar.insert({"name" : "pipi", "date" : new Date()}) db.bar.aggregate( {"$project" : {"birth-month" : {"$month" : "$date"}, "_id" : 0 } } ) { "birth-month" : 4 } 也可使用字面量日期。 db.bar.aggregate( {"$project" : {"up-to-now" : {"$subtract" : [{"$minute" : new Date()}, {"$minute" : "$date"}]}, "_id" : 0 } } ) { "up-to-now" : 18 } 2.2.3 字符串表達式 操做符語法: 1)"$substr" : [expr, startOffset, numoReturn] 接受字符串,起始位置之後偏移N個字節,截取字符串。 2)"$concat" : [expr1[, expr2, ..., exprN]] 將給定的表達式鏈接在一塊兒做爲返回結果。 3)"$toLower" : expr 返回參數的小寫形式 4)"$toUpper" : expr 返回參數的大寫形式 例如: db.foo.insert({"firstname" : "caoqing", "lastname" : "lucifer"}) db.foo.aggregate( { "$project" : { "email" : { "$concat" : [ {"$substr" : ["$firstname", 0, 1]}, ".", "$lastname", "@gmail.com" ] }, "_id" : 0 } } ) { "email" : "c.lucifer@gmail.com" } 2.2.3 邏輯表達式 操做符語法: 1)"$cmp" : [expr1, expr2] 比較兩個參數,相等返回0,大於返回整數,小於返回負數。 2)"$strcasecmp" : [string1, string2] 比較字符串,區分大小寫 3)"$eq"/"$ne"/"$gt"/"$gte"/"lt"/"lte" : [expr1, expr2] 比較字符串,返回結果(true or false) 4)"$and" : [expr1[, expr2, ..., exprN]] 全部值爲true返回true,不然返回false。 5)"$or" : [expr1[, expr2, ..., exprN]] 任意表達式爲true返回true,不然返回false 6)"$not" : expr 對錶示式取反 還有兩個控制語句。 "$crond" : [booleanExpr, trueExpr, falseExpr] 若是爲true,返回trueExpr,不然,返回falseExpr。 "$ifFull" : [expr, replacementExpr] 若是expr爲null,返回replacementExpr,不然返回expr。 算術操做符必須接受數值,日期操做符必須接受日期,字符串操做符必須接受字符串。 例如,根據學生出勤率(10%),平時做業(30%)和考試成績(60%)得出最終成績,若是是老師寵愛的學生,直接得100分: 插入數據: db.bar.insert( { "name" : "xiaobao", "teachersPet" : 1, "attendance" : 90, "quizz" : 80, "test" : 85 } ) db.bar.insert( { "name" : "caoqing", "teachersPet" : 0, "attendance" : 20, "quizz" : 50, "test" : 90 } ) db.bar.insert( { "name" : "pipi", "teachersPet" : 0, "attendance" : 100, "quizz" : 50, "test" : 10 } ) 聚合: db.bar.aggregate( { "$project" : { "grade" : { "$cond" : [ "$teachersPet", 100, { "$add" : [ {"$multiply" : [0.1, "$attendance"]}, {"$multiply" : [0.3, "$quizz"]}, {"$multiply" : [0.6, "$test"]}, ] } ] }, "_id" : 0 } } ) 返回結果: { "grade" : 100 } { "grade" : 71 } { "grade" : 31 }