5.mongodb的group與sum操作


一、group與sum的概念

1.知識儲備:聚合與管道

1.1 MongoDB 聚合:

MongoDB 中聚合(aggregate)主要用於處理數據(諸如統計平均值,求和等),並返回計算后的數據結果。

有點類似 SQL 語句中的 count(*)。

介紹一下聚合的中的一些表達式方法:

表達式 描述 實例
$sum 計算總和。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])
$avg 計算平均值 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])
$min 獲取集合中所有文檔對應值得最小值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])
$max 獲取集合中所有文檔對應值得最大值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])
$push 在結果文檔中插入值到一個數組中。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])
$addToSet 在結果文檔中插入值到一個數組中,但不創建副本。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])
$first 根據資源文檔的排序獲取第一個文檔數據。 db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])
$last 根據資源文檔的排序獲取最后一個文檔數據 db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])

1.2 MongoDB 管道:

管道在Unix和Linux中一般用於將當前命令的輸出結果作為下一個命令的參數。

MongoDB的聚合管道將MongoDB文檔在一個管道處理完畢后將結果傳遞給下一個管道處理。管道操作是可以重復的。

表達式:處理輸入文檔並輸出。表達式是無狀態的,只能用於計算當前聚合管道的文檔,不能處理其它的文檔。

這里我們介紹一下聚合框架中常用的幾個操作:

  • $project:修改輸入文檔的結構。可以用來重命名、增加或刪除域,也可以用於創建計算結果以及嵌套文檔。
  • $match:用於過濾數據,只輸出符合條件的文檔。$match使用MongoDB的標准查詢操作。
  • $limit:用來限制MongoDB聚合管道返回的文檔數。
  • $skip:在聚合管道中跳過指定數量的文檔,並返回余下的文檔。
  • $unwind:將文檔中的某一個數組類型字段拆分成多條,每條包含數組中的一個值。
  • $group:將集合中的文檔分組,可用於統計結果。
  • $sort:將輸入文檔排序后輸出。
  • $geoNear:輸出接近某一地理位置的有序文檔。

1.3 聚合和管道的合作:

例子:這段將會把文章的分數在70到90之間的所有文章,然后將符合條件的記錄送到下一階段$group管道操作符進行處理。(所以理論上你可以定義一個聚合,在聚合下使用無數個管道)

group將會使用id進行分組,然后sum來計算每一個相同_id的總數並賦值給count。最后打印count的值就是所有符合條件的文章的數目了。

db.articles.aggregate( [
                        { $match : { score : { $gt : 70, $lte : 90 } } },
                        { $group: { _id: null, count: { $sum: 1 } } }
                       ] );

此時看到此處,有的人可能會有疑問:

1.group使用id進行分組,那么id怎么為null;

2.sum進行計算,為什么給sum后面賦值個1;

別急,往下看:

2.正文:

group上面已解釋是一種管道,將符合條件的進行分組,sum是聚合的計算方法。

sum可以在下面的管道或者方法中使用:

 

 sum后面通常跟得是一個field(翻譯為領域,我把它理解為變量領域,可能不太恰當,但是我找不到其它詞去形容它):

1.如果這個變量領域是數值類型的,那么它會根據變量的數值進行計算;

2.如果這個變量領域有數值也有非數值類型的,它會根據那個數值類型的進行計算;

3.如果這個變量領域(不管是不是數值類型),只要他在表中沒出現,那么它會返回0;

4.如果這個變量領域是非數值類型的,那么它會返回0;

 看了sum后面的field的介紹,有的人可能馬上反應過來,但是,有的人還可能一臉悶逼,那么我繼續解釋:

1.由於在group中使用_id進行分組,那么mogodb的group管道就會根據_id的具體值進行分組,但是當我們傳入_id為:null時,mongodb的group此時就不會根據_id的具體值進行分組,它會把所有符合條件的記錄看成一個整體,此時group取出的將是整個符合條件的所有記錄。

2.由於sum后面跟的是1的值,那么sum在遍歷每一個group組合的成員時,或者說sum在遍歷變量領域的每一個成員的時候,由於這個變量領域里的值都是1,它每遍歷到一個變量(表里的每條符合條件的記錄)都會進行加1操作,此時由於group本身只有一個,就是一整個表中符合條件的所有記錄,那么sum遍歷整個表的每一條符合條件的記錄時就會進行加1操作,隨后count保存的就是所有符合條件的值。

 

所以通常情況下,我可以使用group+sum的組合寫法,將數據庫每條記錄進行分類:

我有這樣一個數據庫:其中除了flag是布爾類型,agree是number類型,其它的都是字符串類型

 

 

 現在我想做的是,如何計算每一條記錄中的agree使他們相加起來,再返回給我:

例子:下面這段代碼的執行過程:

1.使用group將每一個_id進行分組。

2.計算每一個agree的總和,由於我的_id是不重復的,所以sum的作用其實沒有體現出來,不過當你的表里有多個記錄是同一個id值,那么sum將會把每一個相同_id下的agree值進行計算,然后打印。

3.把計算后的每一個_id的agree總和作為commentAgreesCount的值。

4.打印輸出。

db.article_comments.aggregate(      [{ $group :        {  _id:"$_id",  commentAgreesCount: { $sum: "$agree" }} }]   )
運行結果: {
"_id" : ObjectId("606a70ac298cbe2978047810"), "commentAgreesCount" : 1 } { "_id" : ObjectId("606a60ba3878e73b78a9f5f0"), "commentAgreesCount" : 1 } { "_id" : ObjectId("606a7694298cbe2978047814"), "commentAgreesCount" : 0 } { "_id" : ObjectId("606a5ce13878e73b78a9f5ec"), "commentAgreesCount" : 1 }

但是我的本意並不是想獲得每一個分組后的結果,我是要獲取所有的記錄的agree總和。

所以解決方法:

1.把id定義為null,mongodb內部就會不區分_id,即把所有的_id進行計算,然后輸出結果

db.article_comments.aggregate(      [{ $group :        {  _id:null, commentAgreesCount: { $sum: "$agree" }} }]   )
運行結果: {
"_id" : null, "commentAgreesCount" : 3 }

但是sum方法只能計算數值類型的,如果是字符串類型的,它就會默認把值設置為0。這個我上面也有貼官網的圖,也有進行相應的解釋。

例子:

db.article_comments.aggregate(
     [{$group : 
      {
       _id:null,
       commentAgreesCount: { $sum: "$topic_id" }
    
        }
     }]
  )

結果:

{ "_id" : null, "commentAgreesCount" : 0 }

 

3.后話:

如果你要查詢的是非數值類型的總和,打比方:我的agree設置成了String類型,但是我想獲得每一條記錄中agree加起來的總和。

又或者是我有一個數據庫,里面保存着這個月賣出去的所有商品,所有商品的id是字符類型的,我想統計所有商品的賣出情況,比如id:1的所有商品,因為id:1的商品一個月內可能賣出很多次。id:2的所有商品,因為id:2的商品一個月內也可能賣出很多次 ……以此類推,然后我想計算所有id相同的商品賣出去的情況,並使用sum進行計算所有相同id商品的價格*件數=每件商品的營業額

此時再假設你的商品價格也是字符串類型的,那么此時就無法用上面那個sum方法計算了,此時解決方法也有,就是根據數據庫查詢到的所有記錄,再進行foreach循環遍歷一下所有商品的價格字符,再把他們轉換為數值類型的進行計算。

所以數據庫的方法並不是萬能的,有時候你無法根據數據庫進行查詢,獲取想要的數據類型時,那么只能將數據庫獲得的冗余數據,使用其他語言再進行數據處理。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM