2014-03-28 43 views
0

我有一個Mongo文檔,其中有一個項目列表。每個項目都有一些與之相關的狀態。

我想用$elemMatch返回誰擁有符合某些標準,其狀態的文檔。

請考慮以下文件。它代表一個訂單,並在訂貨每個項目是不同的(它們代表眼鏡):

{ 
    "_id" : ObjectId("5335dcd6888a4f21dd77657c"), 
    "items" : [ 
     { 
      "lens_type" : "STANDARD_INDEX_LENS", 
      "status" : {} 
     }, 
     { 
      "lens_type" : "NO_LENS", 
      "status" : { 
       "shipped" : true 
      } 
     }, 
     { 
      "lens_type" : "RX_SUN_LENS", 
      "status" : { 
       "received_from_lab" : true, 
       "sent_to_lab" : true, 
       "shipped" : true 
      } 
     } 
    ] 
} 

我想找到它們是「未發貨」的所有項目 - 也就是說,items.status.shipped不存在。我想找到所有準備發貨的物品。

但是,如果該物品有處方鏡片 - lens_type要麼是STANDARD_INDEX_LENS要麼是RX_SUN_LENS - 如果從實驗室收到,我只會認爲它沒有發貨。 item.status.received_from_lab存在。

以上文件不應該由我的查詢返回。這是因爲其中兩件物品已發貨,另一件未發貨,但尚未received_from_lab

但是,我的查詢確實是返回這個文件!

這裏是我的查詢:

{ 
    "$and": [ 
     { 
      "items": { 
       "$elemMatch": { 
        "lens_type": { 
         "$in": [ 
          "STANDARD_INDEX_LENS", 
          "RX_SUN_LENS" 
         ] 
        }, 
        "status.received_from_lab": {"$exists": true} 
       } 
      } 
     }, 
     { 
      "items": { 
       "$elemMatch": { 
        "status.shipped": {"$exists": false} 
       } 
      } 
     } 
    ] 
} 

什麼是我的查詢中的邏輯錯誤?我應該使用什麼構造呢?我需要在客戶端進行這種過濾嗎?

+0

我現在讀了幾遍。我看到了兩個可能的答案,但這取決於你期望**看到的結果。你能否將你的「結果」添加到問題中,或者至少確認你期望這個「整個」文檔是否匹配。 –

+0

@NeilLunn你好!我的期望是這個文件不*匹配。因爲: 1.第一項有'STANDARD_INDEX_LENS',但沒有'status.received_from_lab'鍵。因此,即使它也缺少關鍵'status.shipped',它不符合條件。 2.第二個和第三個項目的關鍵是'status.shipped',所以它也不符合裝運條件。 因爲沒有任何物品可能被運送,那麼文件不應該被退回。 – poundifdef

回答

0

與邏輯的問題是,鑑於該文件,與類型「STANDARD_INDEX_LENS」匹配第二個條件,和匹配「RX_SUN_LENS」的項目的第一個條件相匹配。因此,因爲兩個都是真的,那麼該文件被返回。

你沒有做的是說你的「處方​​」鏡片可能有這些條件評估。所以大概寫這個最簡單的方法如下:

{ 
    "$and": [ 
     { 
      "items": { 
       "$elemMatch": { 
        "lens_type": { 
         "$in": [ 
          "STANDARD_INDEX_LENS", 
          "RX_SUN_LENS" 
         ] 
        }, 
        "status.received_from_lab": {"$exists": true} 
       } 
      } 
     }, 
     { 
      "items": { 
       "$elemMatch": { 
        "lens_type": { 
         "$nin": [ 
          "STANDARD_INDEX_LENS", 
          "RX_SUN_LENS" 
         ] 
        }, 
        "status.shipped": {"$exists": false} 
       } 
      } 
     } 
    ] 
} 

所以$nin運營商那裏可以確保檢查是檢查一下是不是在「藥方」類型的執行,是「發貨」狀態時,只在其他項目上完成,因此它不能在這些「處方」之一上評估爲真。


正如我所說,如果您認爲您的邏輯在此限制中更爲複雜,還有另一種方法可以實現。因此,您需要一種方法將條件應用於陣列中元素的全部。你可以做,使用.aggregate()

db.order.aggregate([ 

    // Document level filtering still makes sense 
    { "$match": { 
     "$and": [ 
      { 
       "items": { 
        "$elemMatch": { 
         "lens_type": { 
          "$in": [ 
           "STANDARD_INDEX_LENS", 
           "RX_SUN_LENS" 
          ] 
         }, 
         "status.received_from_lab": {"$exists": true} 
        } 
       } 
      }, 
      { 
       "items": { 
        "$elemMatch": { 
         "status.shipped": {"$exists": false} 
        } 
       } 
      } 
     ] 
    }}, 

    // Unwind to de-normalize 
    { "$unwind": "$items" }, 

    // Then actually "filter" each of the un-wound documents 
    { "$match": { 
     "$and": [ 
      { 
       "items.lens_type": { 
        "$in": [ 
         "STANDARD_INDEX_LENS", 
         "RX_SUN_LENS" 
        ] 
       }, 
       "items.status.received_from_lab": {"$exists": true} 
      }, 
      { 
       "items.status.shipped": {"$exists": false} 
      } 
     ]   
    }} 

    // Group back the results of any found "elements" 
    { "$group": { 
     "_id": "$_id", 
     "items": { "$push": "$items" } 
    }} 

]) 

那麼這基本上做的是做初始$match後,數組中的文件是「分裂」分開或一般「去標準化」使用$unwind,使他們現在出現如個人文件本身。

附加$match然後確保沒有任何剩餘的符合這些條件。或者你可能想把邏輯改爲另一種情況。然後最後你可以使用$group將任何結果返回到數組中。但是這個數組只包含沒有被過濾的文檔。所以文件的格式會被改變。

您可以使用here所示的技術「保留」原始文檔的格式。

但它「取決於」您的需求是什麼。看來第一個方法應該適合您的需求,但至少您有幾件事要嘗試。