巧用 db.system.js 提高20% 開發效率

開門見山,20%是我造的,哈哈,爲的就是讓各位mongoer可以對db.system.js collection 引發注意。javascript

這個也是在我最近瀏覽InfoQ 的時候,看到一篇關於MongoDB 文章1的時候意識到的問題,隨後和開發們溝通了下,結果是對這個collection 不是很瞭解,遂生此文。java

system.js

先來看下官文給出的解釋:linux

<database>.system.js
The <database>.system.js collection holds special JavaScript code for use in server side JavaScript. See Store a JavaScript Function on the Server for more information.mongodb

解釋很簡單,立刻就進入實操環節shell

db.system.js.save(
   {
     _id: "echoFunction",
     value : function(x) { return x; }
   }
)

可是並無任何效果,shell裏表示,echoFunction undefined. json

在查看 db.system.js 確實有一條記錄segmentfault

> db.system.js.find({_id: 'echoFunction'}).pretty()
{
        "_id" : "echoFunction",
        "value" : {
                "code" : "function (x) { return x; }"
        }
}

繼續查看doc,原來還須要經過 loadServerScripts 函數 load 進數據字典,這個操做就有點像咱們在linux 環境中 source ~/.bash_profile 同樣了。bash

執行一次,db.loadServerScripts() , 果真就可使用咱們自定義的函數了。ide

那問題來了,如何提高咱們的工做效率呢?函數

在MongoDB 中,雖然有 $sum, $avg 等一系列的pipeline,可是,對於DBA也好,Developer 也罷,許多的報表、統計aggregation 並不能徹底代勞,mapReduce 就是爲了這個時候而上的,那每次都要去寫一個function 去 sum,去 avg 總顯得在反覆造輪子,所以咱們徹底能夠在這種狀況下,在 db.system.js 中加入咱們經常使用的統計函數,好比 sum, avg, max, min 等等。

這裏我就給出本身經常使用的函數供你們參考:

  • SUM
db.system.js.save( { _id : "Sum" ,
value : function(key,values)
{
    var total = 0;
    for(var i = 0; i < values.length; i++)
        total += values[i];
    return total;
}});
  • AVERAGE
db.system.js.save( { _id : "Avg" ,
value : function(key,values)
{
    var total = Sum(key,values);
    var mean = total/values.length;
    return mean;
}});
  • MAX
db.system.js.save( { _id : "Max" ,
value : function(key,values)
{
    var maxValue=values[0];
    for(var i=1;i<values.length;i++)
    {
        if(values[i]>maxValue)
        {
            maxValue=values[i];
        }
    }
    returnmaxValue;
}});
  • MIN
db.system.js.save( { _id : "Min" ,
value : function(key,values)
{
    var minValue=values[0];
    for(var i=1;i<values.length;i++)
    {
        if(values[i]<minValue)
        {
            minValue=values[i];
        }
    }
    return minValue;
}});
  • VARIANCE
db.system.js.save( { _id : "Variance" ,
value : function(key,values)
{
    var squared_Diff = 0;
    var mean = Avg(key,values);
    for(var i = 0; i < values.length; i++)
    {
        var deviation = values[i] - mean;
        squared_Diff += deviation * deviation;
    }
    var variance = squared_Diff/(values.length);
    return variance;
}});
  • STD DEVIATION
db.system.js.save( { _id : "Standard_Deviation"
, value : function(key,values)
{
    var variance = Variance(key,values);
    return Math.sqrt(variance);
}});

MapReduce

那麼接下來咱們就用Map-Reduce來結合以前的自定義聚合函數來作詳解。(這裏權當各位大佬熟悉Map-Reduce了)

  • 引入demo data
{ "_id" : ObjectId("4f7be0d3e37b457077c4b13e"), "_class" : "com.infosys.mongo.Sales", "orderId" : 1, "orderDate" : "26/03/2011",
"quantity" : 20, "salesAmt" : 200, "profit" : 150, "customerName" : "CUST1", "productCategory" : "IT", "productSubCategory" : "software", 
"productName" : "Grad", "productId" : 1 }
{ "_id" : ObjectId("4f7be0d3e37b457077c4b13f"), "_class" : "com.infosys.mongo.Sales", "orderId" : 2, "orderDate" : "23/05/2011", 
"quantity" : 30, "salesAmt" : 200, "profit" : 40, "customerName" : "CUST2", "productCategory" : "IT", "productSubCategory" : "hardware",
 "productName" : "HIM", "productId" : 1 }
{ "_id" : ObjectId("4f7be0d3e37b457077c4b140"), "_class" : "com.infosys.mongo.Sales", "orderId" : 3, "orderDate" : "22/09/2011",
 "quantity" : 40, "salesAmt" : 200, "profit" : 80, "customerName" : "CUST1", "productCategory" : "BT", "productSubCategory" : "services",
 "productName" : "VOCI", "productId" : 2 }
{ "_id" : ObjectId("4f7be0d3e37b457077c4b141"), "_class" : "com.infosys.mongo.Sales", "orderId" : 4, "orderDate" : "21/10/2011", 
"quantity" : 30, "salesAmt" : 200, "profit" : 20, "customerName" : "CUST3", "productCategory" : "BT", "productSubCategory" : "hardware", 
"productName" : "CRUD", "productId" : 2 }
{ "_id" : ObjectId("4f7be0d3e37b457077c4b142"), "_class" : "com.infosys.mongo.Sales", "orderId" : 5, "orderDate" : "21/06/2011", 
"quantity" : 50, "salesAmt" : 200, "profit" : 20, "customerName" : "CUST3", "productCategory" : "BT", "productSubCategory" : "hardware", 
"productName" : "CRUD", "productId" : 1 }
  • 建立聚合函數Sum
db.system.js.save({ 
    _id : "Sum" ,
    value: function(key,values) {
                    var total = 0;
                    for(var i = 0; i < values.length; i++)
                        total += values[i];
                    return total;
            }
    });
  • 結合Sum 聚合函數執行Map-Reduce
db.runCommand(
    {
        mapreduce: "sales" ,
        map: function() {
            emit({
                key0:this.productCategory,
                key1:this.productSubCategory,
                key2:this.productName
            },
            this.salesAmt
            );
        },
        reduce: function(key, values) {
            var result = Sum(key, values);
            return result;
        },
        out: {inline: 1}
    }
)

這裏,就直接把結果輸出的stdout 了,若是須要能夠指定collection,將咱們的Map-Reduce結果存儲下來。

來看一下結果

{
        "results" : [
                {
                        "_id" : {
                                "key0" : "BT",
                                "key1" : "hardware",
                                "key2" : "CRUD"
                        },
                        "value" : 400
                },
                {
                        "_id" : {
                                "key0" : "BT",
                                "key1" : "services",
                                "key2" : "VOCI"
                        },
                        "value" : 200
                },
                {
                        "_id" : {
                                "key0" : "IT",
                                "key1" : "hardware",
                                "key2" : "HIM"
                        },
                        "value" : 200
                },
                {
                        "_id" : {
                                "key0" : "IT",
                                "key1" : "software",
                                "key2" : "Grad"
                        },
                        "value" : 200
                }
        ],
        "timeMillis" : 14,
        "counts" : {
                "input" : 5,
                "emit" : 5,
                "reduce" : 1,
                "output" : 4
        },
        "ok" : 1
}

這裏能夠看到,咱們的Sum 函數已經將emit 事後的 "productCategory" : "BT", "productSubCategory" : "hardware", "productName" : "CRUD" 這組數據的 salesAmt 累加了。

到這裏,咱們基本就能夠實現一個自定義的Function + Map-Reduce 的強大組合了!

上海小胖[MiracleYoung] 原創地址: https://segmentfault.com/u/shanghaixiaopang/articles

歡迎各位大神前來評論。

每週五,敬請期待,上海小胖[MiracleYoung] 獨更。

若是夏雨荷還在大明湖畔等着個人話,我就不更了。


  1. Implementing Aggregation Functions in MongoDB
相關文章
相關標籤/搜索