2016-02-27 101 views
0

鑑於這種MongoDB的集合:

[ 
    { client: 'client1', type: 'Defect', time: 5 }, 
    { client: 'client1', type: 'Test', time: 5 }, 
    { client: 'client2', type: 'Management', time: 3 }, 
    { client: 'client2', type: 'Defect',  time: 3 }, 
    { client: 'client3', type: 'Test',  time: 4 } 
] 

我想從每個ISSUE_TYPE這樣得到的時間的總和:

{ 
    client1: { 'Defect': 5, 'Test': 5 }, 
    client2: { 'Management': 3, 'Defect': 3 }, 
    client3: { 'Test': 4 } 
} 

,我一直在試圖使用聚合框架來做到這一點(取代現有的地圖/縮小),但只能得到像這樣的組合計數:

{ '_id': { client: 'Client1', class: 'Defect' }, sum: 5 } 
{ '_id': { client: 'Client1', class: 'Test' } count: 5 } 
{ '_id': { client: 'Client2', class: 'Management' }, count: 3 } 
{ '_id': { client: 'Client2',  class: 'Defect' }, count: 3 } 
{ '_id': { client: 'Client3',  class: 'Test' }, count: 4 } 

哪一個足夠簡單,以編程方式減少到期望的結果,但我希望能夠將它留給MongoDB。

對於任何可能提前致謝的幫助!

編輯

我加入這個聚合組

db.getCollection('issues').aggregate(
    [ 
     { 
      $group: 
      { 
       _id: {component:"$client"}, 
       totalTime:{$sum: "$time" } 
      } 
     } 
    ] 
) 
+0

你的值是字符串不是數字。如果您有兩個具有相同鍵/值對的文檔,例如'{client:'client1',type:'Defect',time:'5'}'?你想在這裏做什麼? – styvane

+0

您運行的是哪個版本的MongoDB? – Saleem

+0

請發佈您的聚合代碼,以便我們可以幫助您基於迄今爲止的內容。 – metame

回答

2

我不喜歡你的建議的輸出格式。你基本上要求的 正在把你的「數據」,並將其轉化爲所產生結果的「關鍵」。對我來說,這是清潔面向對象設計的對立面,因爲每個「對象」都是完全不同的,你基本上需要循環鍵來確定它是什麼類型的東西。

更好的方法是,保持鍵,因爲它們是,捲起的「客戶端」的組合與$group和「類型」,然後$group再次$push每「類型」的數據轉換成用於每一個陣列分組「客戶端」:

db.getCollection('issues').aggregate([ 
    { "$group": { 
     "_id": { 
      "client": "$client", 
      "type": "$type" 
     }, 
     "totalTime": { "$sum": "$time" } 
    }}, 
    { "$group": { 
     "_id": "$_id.client", 
     "data": { 
      "$push": { 
       "type": "$_id.type", 
       "totalTime": "$totalTime" 
      } 
     } 
    }} 
]) 

這給了你這樣一個結果:

{ 
     "_id" : "client1", 
     "data" : [ 
       { 
         "type" : "Test", 
         "totalTime" : 5 
       }, 
       { 
         "type" : "Defect", 
         "totalTime" : 5 
       } 
     ] 
} 
{ 
     "_id" : "client2", 
     "data" : [ 
       { 
         "type" : "Defect", 
         "totalTime" : 3 
       }, 
       { 
         "type" : "Management", 
         "totalTime" : 3 
       } 
     ] 
} 
{ 
     "_id" : "client3", 
     "data" : [ 
       { 
         "type" : "Test", 
         "totalTime" : 4 
       } 
     ] 
} 

這對我來說是每個「客戶」結果的完全自然的和結構化的形式作爲文件和自然可迭代列表,因爲它的內容。

如果你真的堅持使用命名鍵的單一對象輸出格式,那麼這個源代碼很容易轉換。而在我看來簡單的代碼再次演示瞭如何更好的一個結果是:

var output = {}; 

db.getCollection('issues').aggregate([ 
    { "$group": { 
     "_id": { 
      "client": "$client", 
      "type": "$type" 
     }, 
     "totalTime": { "$sum": "$time" } 
    }}, 
    { "$group": { 
     "_id": "$_id.client", 
     "data": { 
      "$push": { 
       "type": "$_id.type", 
       "totalTime": "$totalTime" 
      } 
     } 
    }} 
]).forEach(function(doc) { 
    output[doc._id] = {}; 

    doc.data.forEach(function(data) { 
     output[doc._id][data.type] = data.totalTime; 
    }); 
}); 

printjson(output); 

然後你得到你的對象,只要你喜歡:

{ 
     "client1" : { 
       "Test" : 5, 
       "Defect" : 5 
     }, 
     "client2" : { 
       "Defect" : 3, 
       "Management" : 3 
     }, 
     "client3" : { 
       "Test" : 4 
     } 
} 

但如果你是在服務器上真正堅持搗弄所有的工作,甚至沒有過載的結果的重塑,那麼你可以隨時解僱這是MapReduce的:

db.getCollection('issues').mapReduce(
    function() { 
     var output = { }, 
      data = {}; 

     data[this.type] = this.time; 
     output[this.client] = data; 

     emit(null,output) 
    }, 
    function(key,values) { 
     var result = {}; 

     values.forEach(function(value) { 
      Object.keys(value).forEach(function(key) { 
       if (!result.hasOwnProperty(key)) 
        result[key] = {}; 
       Object.keys(value[key]).forEach(function(dkey) { 
        if (!result[key].hasOwnProperty(dkey)) 
         result[key][dkey] = 0; 
        result[key][dkey] += value[key][dkey]; 
       }) 
      }) 
     }); 
     return result; 
    }, 
    { "out": { "inline": 1 } } 
) 

具有相同類型的輸出:

  { 
        "_id" : null, 
        "value" : { 
          "client1" : { 
            "Defect" : 5, 
            "Test" : 5 
          }, 
          "client2" : { 
            "Management" : 3, 
            "Defect" : 3 
          }, 
          "client3" : { 
            "Test" : 4 
          } 
        } 
      } 

不過既然是MapReduce的,該interpeted的JavaScript會比聚合管道的本地代碼運行 慢得多,當然絕不會擴展到生產超過16MB BSON限制的文檔的結果,因爲所有的結果都被嵌入到一個文檔中。

另外,只需查看遍歷對象鍵,檢查鍵,創建和添加的複雜性。這實際上只是一團糟,並且是任何進一步的代碼與這樣的結構一起工作的指示器。


因此,爲了我的錢,遠離將完美結構良好的數據轉換成實際「值」被表示爲「鍵」的東西。從簡潔的設計角度來看,它確實沒有任何意義,也沒有使用遍歷對象的鍵來取代自然的「數組」列表。

+0

Wooooww,奇妙的解釋和完美解決我的問題。我不知道$ push操作符。 – jomarmen