2014-08-27 112 views
0

我有一種情況,我需要通過基於總結字段值出現的數組值的操作來執行組。然後過濾計數並準備結果,以便可以根據條件顯示結果。從本質上講,如果你只是簡單地使用find函數,這些文檔就會變成如何呈現它們。由於matchedDocuments數組中收集的項目數量太多,我遇到了臨時文檔太大的問題。有關如何改善這一點的任何建議都會有所幫助。複雜的MongoDB聚合

db.collection1.aggregate([ 
{ 
    '$unwind': '$arrayOfValues' 
}, { 
    '$group': { 
     '_id': '$arrayOfValues', 
     'x_count': { 
      $sum: { 
       $cond: [{ 
         $eq: ['$field.value', 'x'] 
        }, 
        1, 0 
       ] 
      } 
     }, 
     'y_count': { 
      $sum: { 
       $cond: [{ 
         $eq: ['$field.value', 'y'] 
        }, 
        1, 0 
       ] 
      } 
     }, 
     'matchedDocuments': { 
      '$push': '$$CURRENT' 
     } 
    } 
}, 
{'$match': {'$or': [{'x_count': {'$gte': 2}}, {'y_count': { '$gte': 1}}]}}, 
{'$unwind': '$matchedDocuments'}, 
{ 
    '$group': { 
     '_id': '$matchedDocuments.key', 
     'document': { 
      '$last': '$$CURRENT.matchedDocuments' 
     } 
    } 
} 
], { 
    allowDiskUse: true 
}) 

下面是一些樣本文檔和預期的結果基於上述標準:

// Sample documents 

{ "_id" : ObjectId("5407c76b7b1c276c74f90524"), "field" : "x", "arrayOfValues" : [ "a", "b", "c" ] } 
{ "_id" : ObjectId("5407c76b7b1c276c74f90525"), "field" : "x", "arrayOfValues" : [ "b", "c" ] } 
{ "_id" : ObjectId("5407c76b7b1c276c74f90526"), "field" : "z", "arrayOfValues" : [ "a" ] } 
{ "_id" : ObjectId("5407c76b7b1c276c74f90527"), "field" : "x", "arrayOfValues" : [ "a", "c" ] } 
{ "_id" : ObjectId("5407c76b7b1c276c74f90528"), "field" : "z", "arrayOfValues" : [ "b" ] } 
{ "_id" : ObjectId("5407c76b7b1c276c74f90529"), "field" : "y", "arrayOfValues" : [ "k" ] } 


// Expected Result 

[ 
    { "_id" : ObjectId("5407c76b7b1c276c74f90524"), "field" : "x", "arrayOfValues" : [ "a", "b", "c" ] } 
    { "_id" : ObjectId("5407c76b7b1c276c74f90525"), "field" : "x", "arrayOfValues" : [ "b", "c" ] } 
    { "_id" : ObjectId("5407c76b7b1c276c74f90527"), "field" : "x", "arrayOfValues" : [ "a", "c" ] } 
    { "_id" : ObjectId("5407c76b7b1c276c74f90529"), "field" : "y", "arrayOfValues" : [ "k" ] } 
] 
+1

問題無疑是用你的第一個分組鍵。但是,由於你對數組中的值進行了分組,所以你很快就會看到你實際想要在這裏做什麼。文檔樣本和預期結果通常可以最好地解釋您的意圖。 – 2014-08-27 22:50:20

+0

我添加了示例文檔和預期結果以幫助查看問題。 – user1595702 2014-08-28 14:47:53

+0

爲什麼不直接指望'arrayOfValues'數組大小的文檔呢?這將使聚合變得簡單,找到每個「field」值。 – wdberkeley 2014-08-29 15:44:55

回答

2

我覺得最終你所要求有點太多了從單一的查詢,因爲顯然是最大這裏的問題是試圖存儲數組元素出現的所有原始文檔,同時嘗試聚合總數。

對我來說,我只是試圖確定文檔上的哪些條件會導致匹配,然後發出單獨的查詢來獲取實際的文檔。您可以調整下面的聚合以嘗試返回文檔,但是我認爲這樣做很可能會失敗,因爲這與您應該使用數組的情況相反。

該過程通常在匹配方式上的效率通常高得多,首先是「選擇您感興趣的元素和匹配條件」,其次是「使用自然分組條件而不是依靠有條件的總和「。

var cursor = db.collection.aggregate([ 
    { "$match": { "field": { "$in": ["x", "y"] } } }, 
    { "$unwind": "$arrayOfValues" }, 
    { "$group": { 
     "_id": { 
      "elem": "$arrayOfValues", 
      "field": "$field" 
     }, 
     "count": { "$sum": 1 } 
    }}, 
    { "$match": { 
     "$or": [ 
      { "_id.field": "x", "count": { "$gte": 2 } }, 
      { "_id.field": "y", "count": { "$gte": 1 } } 
     ] 
    }}, 
    { "$group": { 
     "_id": "$_id.field", 
     "values": { "$push": "$_id.elem" } 
    }} 
]) 

var query = { "$or": [] }; 

cursor.forEach(function(doc) { 
    query["$or"].push({ 
     "field": doc._id, 
     "arrayOfValues": { "$in": doc.values } 
    }); 
}); 

db.collection.find(query) 

備案查詢應該走出這樣的,給予所提供的數據:

{ 
    "$or" : [ 
     { 
      "field" : "x", 
      "arrayOfValues" : { 
       "$in" : [ 
        "c", 
        "b", 
        "a" 
       ] 
      } 
     }, 
     { 
      "field" : "y", 
      "arrayOfValues" : { 
       "$in" : [ 
        "k" 
       ] 
      } 
     } 
    ] 
} 

的基本邏輯是通過只是尋找一個你感興趣的「場」的值滿足,至少從可能的結果中排除所有其他人。然後,您基本上想要統計每個「字段」值下的每個數組元素的計數,並測試滿足所需事件的位置。

這可能會或可能不會最好的反其道而行,但這裏的示例顯示了「arrayOfValues」的最大變化,因此作爲第二級分組是有意義的。

如前所述,我認爲要求基本上將所有父文檔信息「填充」到每個「arrayOfValues」元素的數組太多太多了,因爲這超出了合理模式的基本原則,這種關係自然會被存儲爲單獨的文檔。因此,這裏的最終原則就是找到與最終結果出來的文件相匹配的「條件」。

然後針對集合發出轉換後的查詢,其中將返回符合先前分析確定的條件的所有文檔。在一天結束時,將「匹配」文檔的「提取」職責轉移到另一個查詢上,而不是試圖存儲與數組匹配的文檔。

這似乎是最合乎邏輯和可擴展的方法,但如果您主要傾向於在這種類型的結果中使用數據,則應該考慮重新設計架構以更好地適應此問題。但是這裏沒有足夠的具體信息來進一步評論。

+0

我很欣賞你在答案中的努力。這與我提出的相似。但是,我不確定是否有更好的方法可用。有用戶定義的前提條件可以進一步過濾結果,從而減少大型文檔的問題。此外,每個收藏總是會少於550萬個文檔。我試圖儘可能多地抽象出特定領域的東西,以避免複雜性。 – user1595702 2014-09-05 16:27:53

+0

@ user1595702如果你正在談論發佈單獨的查詢,那麼沒有更好的方法,我想我解釋了這一點。聚合框架是強大的,但要小心「尾巴搖擺狗」。明智地做事情。 – 2014-09-05 16:44:25