2015-12-04 78 views
4

我試着去拉的文檔的集合,它看起來像數據:計數陣列出現在所有文件與蒙戈

[ 
    { 
    name: 'john', 
    sex: 'male', 
    hobbies: ['football', 'tennis', 'swimming'] 
    }, 
    { 
    name: 'betty' 
    sex: 'female', 
    hobbies: ['football', 'tennis'] 
    }, 
    { 
    name: 'frank' 
    sex: 'male', 
    hobbies: ['football', 'tennis'] 
    } 
] 

我試圖使用聚合框架來呈現數據,按性別劃分,計數最常見的愛好。結果應該看起來像。

{ _id: 'male', 
    total: 2, 
    hobbies: { 
    football: 2, 
    tennis: 2, 
    swimming: 1 
    } 
}, 
{ _id: 'female', 
    total: 1, 
    hobbies: { 
     football: 1, 
     tennis: 1 
    } 
} 

到目前爲止,我可以得到總每個性別的,但我不知道如何我能用放鬆身心得到愛好陣列的總和。

我迄今爲止代碼:

collection.aggregate([ 
     { 
      $group: { 
       _id: '$sex', 
       total: { $sum: 1 } 
      } 
     } 
    ]) 

回答

7

個人我不轉化「數據」作爲結果的鍵名的大風扇。由於這種操作也不被支持,所以彙總框架原理趨於集中。

所以個人的偏好是保持「數據」爲「數據」,並接受處理後的輸出實際上是更好的,更合乎邏輯的一致目標設計:

db.people.aggregate([ 
    { "$group": { 
     "_id": "$sex", 
     "hobbies": { "$push": "$hobbies" }, 
     "total": { "$sum": 1 } 
    }}, 
    { "$unwind": "$hobbies" }, 
    { "$unwind": "$hobbies" }, 
    { "$group": { 
     "_id": { 
      "sex": "$_id", 
      "hobby": "$hobbies" 
     }, 
     "total": { "$first": "$total" }, 
     "hobbyCount": { "$sum": 1 } 
    }}, 
    { "$group": { 
     "_id": "$_id.sex", 
     "total": { "$first": "$total" }, 
     "hobbies": { 
      "$push": { "name": "$_id.hobby", "count": "$hobbyCount" } 
     } 
    }} 
]) 

其產生的結果是這樣的:

[ 
    { 
      "_id" : "female", 
      "total" : 1, 
      "hobbies" : [ 
       { 
        "name" : "tennis", 
        "count" : 1 
       }, 
       { 
        "name" : "football", 
        "count" : 1 
       } 
      ] 
    }, 
    { 
     "_id" : "male", 
     "total" : 2, 
     "hobbies" : [ 
      { 
       "name" : "swimming", 
       "count" : 1 
      }, 
      { 
       "name" : "tennis", 
       "count" : 2 
      }, 
      { 
       "name" : "football", 
       "count" : 2 
      } 
     ] 
    } 
] 

因此,最初的$group確實每「性別」的計數和愛好堆疊成一個數組的數組。然後,將您的$unwind取消歸一化以得到單數項目$group以獲得每種性別下每項業餘愛好的總數,並最終重新組合每個性別的陣列。

這是相同的數據,它具有一致和有機的結構,易於處理,而且MongoDB和聚合框架在生成此輸出時非常高興。

如果您確實必須將您的數據轉換爲密鑰名稱(並且我仍然建議您不要這樣做,因爲它不是一個好的模式),那麼從最終狀態進行這樣的轉換對客戶來說是相當微不足道的代碼處理。作爲合適的用於殼的鹼性JavaScript示例:

var out = db.people.aggregate([ 
    { "$group": { 
     "_id": "$sex", 
     "hobbies": { "$push": "$hobbies" }, 
     "total": { "$sum": 1 } 
    }}, 
    { "$unwind": "$hobbies" }, 
    { "$unwind": "$hobbies" }, 
    { "$group": { 
     "_id": { 
      "sex": "$_id", 
      "hobby": "$hobbies" 
     }, 
     "total": { "$first": "$total" }, 
     "hobbyCount": { "$sum": 1 } 
    }}, 
    { "$group": { 
     "_id": "$_id.sex", 
     "total": { "$first": "$total" }, 
     "hobbies": { 
      "$push": { "name": "$_id.hobby", "count": "$hobbyCount" } 
     } 
    }} 
]).toArray(); 

out.forEach(function(doc) { 
    var obj = {}; 
    doc.hobbies.sort(function(a,b) { return a.count < b.count }); 
    doc.hobbies.forEach(function(hobby) { 
     obj[hobby.name] = hobby.count; 
    }); 
    doc.hobbies = obj; 
    printjson(doc); 
}); 

然後你基本上處理每個遊標結果爲所需的輸出形式,這實際上是不是真正需要在服務器上反正聚合函數:

{ 
    "_id" : "female", 
    "total" : 1, 
    "hobbies" : { 
     "tennis" : 1, 
     "football" : 1 
    } 
} 
{ 
    "_id" : "male", 
    "total" : 2, 
    "hobbies" : { 
     "tennis" : 2, 
     "football" : 2, 
     "swimming" : 1 
    } 
} 

在哪裏也應該是相當繁瑣實現那種操縱入遊標結果變換分析根據需要,流處理,因爲它基本上是相同的邏輯。

在另一方面,你總是可以實現所有服務器上使用MapReduce的替代操縱:

db.people.mapReduce(
    function() { 
     emit(
      this.sex, 
      { 
       "total": 1, 
       "hobbies": this.hobbies.map(function(key) { 
        return { "name": key, "count": 1 }; 
       }) 
      } 
     ); 
    }, 
    function(key,values) { 
     var obj = {}, 
      reduced = { 
       "total": 0, 
       "hobbies": [] 
      }; 

     values.forEach(function(value) { 
      reduced.total += value.total; 
      value.hobbies.forEach(function(hobby) { 
       if (!obj.hasOwnProperty(hobby.name)) 
        obj[hobby.name] = 0; 
       obj[hobby.name] += hobby.count; 
      }); 
     }); 

     reduced.hobbies = Object.keys(obj).map(function(key) { 
      return { "name": key, "count": obj[key] }; 
     }).sort(function(a,b) { 
      return a.count < b.count; 
     }); 

     return reduced; 
    }, 
    { 
     "out": { "inline": 1 }, 
     "finalize": function(key,value) { 
      var obj = {}; 
      value.hobbies.forEach(function(hobby) { 
       obj[hobby.name] = hobby.count; 
      }); 
      value.hobbies = obj; 
      return value; 
     } 
    } 
) 

哪裏的MapReduce有它輸出的獨特的風格,但同樣的原則在積累和操作使用如果不是有可能作爲有效的聚合框架可以這樣做:

"results" : [ 
     { 
      "_id" : "female", 
      "value" : { 
       "total" : 1, 
       "hobbies" : { 
        "football" : 1, 
        "tennis" : 1 
       } 
      } 
     }, 
     { 
      "_id" : "male", 
      "value" : { 
       "total" : 2, 
       "hobbies" : { 
        "football" : 2, 
        "tennis" : 2, 
        "swimming" : 1 
       } 
      } 
     } 
    ] 

在一天結束的時候,我還是說,處理的第一種形式是最有效的,並提供了我的腦海裏最自然的和一致的th的工作e數據輸出,甚至沒有嘗試將數據點轉換爲密鑰的名稱。最好考慮遵循這種模式,但如果你真的必須這樣做,那麼就有辦法通過各種處理方法來將結果操縱成所需的形式。

+0

只是想爲你的答案放棄一個'謝謝'...雖然我沒有問這個問題:)我幫了我很多! – chrisdennig