2014-11-03 92 views
0

如果我想通過指定一個電子郵件ID來查詢員工的屬性,我確實有這個工作。在結果中查詢過濾數組中的多個元素

db.employee.find({},{ 
       _id: 0, 
       employee: { 
        $elemMatch: { 
         email: "[email protected]" 
        } 
       }}) 

假設我想通過指定多個電子郵件ID來查詢多個員工的屬性。 我讀通過,它是與$或操作員,但我不知道如何打好,一出..

我MongoDB的數據按照下面的例子:

{ 
"_id" : ObjectId("53dbb05fa976627439d43884"), 
"employee" : [ 
    { 
    "email" : "[email protected]", 
    "deptName" : "x", 

}, 
{ 
    "email" : "[email protected]", 
    "deptName" : "y", 

}, 
{ 
    "email" : "[email protected]", 
    "deptName" : "y", 

} 
] 
} 

回答

1

陣列過濾只能由aggregation framework完成。它允許對文檔進行更多的操作,而不是基本投影。

就像任何查詢一樣,你應該總是先使用$match管道,以便在可能的情況下使用索引。不管之後正在執行什麼其他操作:

db.employee.aggregate([ 

    // Always match first to reduce results 
    { "$match": { 
     "employee.email": { "$in": ["[email protected]", "[email protected]"] } 
    }}, 

    // Unwind to de-normalize the array elements as documents 
    { "$unwind": "$employee" }, 

    // Match to "filter" the array content 
    { "$match": { 
     "employee.email": { "$in": ["[email protected]", "[email protected]"] } 
    }}, 

    // Group back to a document with the array 
    { "$group": { 
     "_id": "$_id", 
     "employee": { "$push": "$employee" } 
    }}, 

    // Optionally project to remove the "_id" field from results 
    { "$project": { 
     "_id": 0, 
     "employee": 1 
    }} 
]) 

這樣就解釋了基本過程。在找到符合條件的「文檔」之後,您可以使用$unwind有效地將數組的每個元素獨立爲文檔,共享任何父字段。額外的$match用於「過濾」結果中的那些元素。當$group完成時,只有匹配的元素被放回到數組中。

對於MongoDB 2.6,你可以用不同的方式來做到這一點,這應該適用於更大的數組。有一些新的運算符,如$map用於在不使用$unwind的情況下處理「內聯」陣列。還有其他「設置」過濾選項,如$setDifference。所以,你可以做到這一點您的文檔始終包含在各自爲陣獨特的「電子郵件」值:

db.employee.aggregate([ 

    // Always match first to reduce results 
    { "$match": { 
     "employee.email": { "$in": ["[email protected]", "[email protected]"] } 
    }}, 

    // Project filtered array content "in-line" 
    { "$project": { 
     "_id": 0, 
     "employee": { 
      "$setDifference": [ 
       { "$map": { 
        "input": "$employee", 
        "as": "el", 
        "in": { 
         "$cond": [ 
          { "$or": [ 
           { "$eq": [ "$$el.email", "[email protected]" ] }, 
           { "$eq": [ "$$el.email", "[email protected]" ] } 
          ]}, 
          "$$el", 
          false 
         ] 
        } 
       }}, 
       [false] 
      ] 
     } 
    }} 
]) 
從前面提到的 $cond操作者用在這裏是數組中的每個元素評估新運營商

除了通過$map看看是否符合條件。如果是這樣,則元素返回結果數組中,否則元素爲false

$setDifference運算符然後「過濾」返回的「set」中的任何false值,就像任何重複數據一樣,所以數組元素在每個文檔中需要如前所述是唯一的。

對於「非唯一」的元素,總有這樣的替代品在現代版本的第一方法,以及:

db.employee.aggregate([ 

    // Always match first to reduce results 
    { "$match": { 
     "employee.email": { "$in": ["[email protected]", "[email protected]"] } 
    }}, 

    // Redact removes document levels that do not match the condition 
    { "$redact": { 
     "$cond": [ 
      { "$or": [ 
       { "$eq": [ 
        { "$ifNull": [ "$email", "[email protected]" ] }, 
        "[email protected]" 
       ]}, 
       { "$eq": [ 
        { "$ifNull": [ "$email", "[email protected]" ] }, 
        "[email protected]" 
       ]} 
      ]}, 
      "$$DESCEND", 
      "$$PRUNE" 
     ] 
    }} 
]) 

這使用$redact以略微做作方法,以消除從文檔的數組元素那與條件不符。這裏的問題是$redact是遞歸的,所以我們測試被測場的存在以及它不存在的地方只是返回一個值來匹配。實際上真的只需要一個$ifNull聲明。

本質上,無論您選擇什麼方法,它都是aggregation framework,它具有對基本投影可以執行的文檔的「加強」操縱。

+0

謝謝@尼爾,這樣一個輝煌的解釋。 – FRizal 2014-11-04 02:10:02

0

如果我有明白你的意思,我認爲你的解決方案是$in運營商,我認爲如果我瞭解你的需求,你應該把你的條件放在選擇器參數中,而不是投影中。所以,你的查詢將是這樣的:

db.employee.find({email:{$in:["[email protected]", "[email protected]oo"]}},{_id:0}) 
+0

我已經在問題裏面添加了mongodb數據......我想這也增加了複雜度 – FRizal 2014-11-03 15:33:57

+0

是的,我不明白你的數據是如何格式化的。所以你不能用投影做到這一點,因爲$ elemMatch只返回數組中的一個元素。你需要一個更復雜的聚集 – mgaido 2014-11-03 15:38:14

0

投影算$ elemMatch只返回數組中的一個元素,所以我覺得你可以考慮,像

var emails = [ "[email protected]", "[email protected]" ]; 
var match = { 
    $match : { 
     "employee.email" : { 
      $in : emails 
     } 
    } 
}; 

db.employee.aggregate([ match, { 
    $unwind : "$employee" 
}, match, { 
    $group : { 
     _id : "$_id", 
     employee : { 
      $push : "$employee" 
     } 
    } 
}, { 
    $project : { 
     _id : 0, 
     employee : 1 
    } 
} ]); 
+0

得到這個未捕獲的異常:總失敗:{ \t 「ERRMSG」: 「異常:壞查詢:BADVALUE未知名頂級運營商:$的」, \t 「代碼」:16810, \t「OK 「:0 } – FRizal 2014-11-03 15:45:28

+0

@Fairul,對不起,** $ match **部分存在錯誤。固定。 – Wizard 2014-11-04 01:08:09

+0

感謝@Wizard,它的作品像魅力 – FRizal 2014-11-04 02:09:34