MongoDB聚合(一)

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 }
相關文章
相關標籤/搜索