2014-02-12 40 views
-2

我有一種情況,我想要查詢具有數組字段「forms」下的項目數的文檔集合。要解決的問題是想返回有載於「形式」與「關閉」特定狀態的文件所有的文件。如何在彙總後恢復原始文檔

所以這是兩個不同的文件集合中的一個樣本:

{ 
    "_id" : "Tvq444454j", 
    "name" : "Jim", 
    "forms" : [ 
     { 
      "name" : "Jorney", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Women", 
      "status" : "void" 
     }, 
     { 
      "name" : "Child", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Farm", 
      "status" : "closed" 
     } 
    ] 
}, 

{ 
    "_id" : "Tvq579754r", 
    "name" : "Tom", 
    "forms" : [ 
     { 
      "name" : "PreOp", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Alert", 
      "status" : "closed" 
     }, 
     { 
      "name" : "City", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Country", 
      "status" : "closed" 
     } 
    ] 
} 

和預期的結果:

{ 
    "_id" : "Tvq579754r", 
    "name" : "Tom", 
    "forms" : [ 
     { 
      "name" : "PreOp", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Alert", 
      "status" : "closed" 
     }, 
     { 
      "name" : "City", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Country", 
      "status" : "closed" 
     } 
    ] 
} 

由於沒有標準查詢運算符來匹配的所有在這種情況下數組的元素,解決方案是通過使用聚合找到的。這將返回集合中具有設置爲「關閉」狀態的所有「表單」元素的_id。

db.forms.aggregate([ 
    {$unwind: "$forms" }, 
    {$group: { _id: "$_id", status: {$addToSet: "$forms.status" }}}, 
    {$unwind: "$status"}, 
    {$sort: { _id: 1, status: -1 }}, 
    {$group: {_id: "$_id", status: {$first: "$status"}}}, 
    {$match:{ status: "closed" }} 
]) 

因此,正如我將期待返回的結果很多文件,我想避免發出新的發現,或一系列的發現只是拿到賽返回_id的這些文件。

考慮到這一點,有沒有什麼辦法,我可以得到原始文件從後面聚集,正是因爲他們都在收集相同的形式,同時還在做這種類型的過濾?

回答

5

歸類於愚蠢的聚合技巧是一種常常被忽視的技巧。

查詢做這一切的分組也將圍繞文檔_id,是此文檔的唯一標識符。所以要考慮的要點是整個文檔實際上已經是一個唯一的標識符。所以,而不是隻存儲在_id鍵,使用整個文檔。

{$project: { 
     _id: { _id: "$_id", name: "$name", forms: "$forms" }, forms: "$forms"} 
    }, 

在完成此操作後,由_id捲起的任何內容都將以原始形式保留文檔。

{$project: { _id: "$_id._id", name: "$_id.name", forms: "$_id.forms"}} 

然後,你將有你想要的篩選結果:在所有其他的聚集階段結束,爲了真實的還原原始文件形式發出最後$project。與高級篩選一起使用時(例如在此查詢的情況下),此技術可以非常方便,因爲它無需在所有結果上發佈額外的查找

而且,在這樣的地方,你知道你只是在尋找一組要匹配一組特定條件的結果的情況下,使用$match運營商作爲管道聚集的第一階段。這不僅減少了工作集的大小是有用的,但它也是唯一在哪個階段,你可以使用一個指數並在那裏你可以顯著提高查詢性能。

整個過程在一起:

db.forms.aggregate([ 
    {$match: { "forms.status": "closed" } }, 
    {$project: { 
     _id: { _id: "$_id", name: "$name", forms: "$forms" }, forms: "$forms"} 
    }, 
    {$unwind: "$forms"}, 
    {$group: { _id: "$_id", status: {$addToSet: "$forms.status"}}}, 
    {$unwind: "$status"}, 
    {$sort: { _id: 1, status: -1} }, 
    {$group: { _id: "$_id", status: {$first: "$status"} }}, 
    {$match: { status: "closed"}}, 
    {$project: { _id: "$_id._id", name: "$_id.name", forms: "$_id.forms"}} 
]) 
+0

應該有一個''什麼的運營商很快將能夠以完整形式返回原始文檔中聚合管道 – Sammaye

+0

@Sammaye我相信你是指的是ROOT,這是爲了不同的目的,但可以在這裏使用。這應該在版本2.6中提供。這是現在可以使用的一種技術,但可能已經避開了許多人。 –