2017-03-13 157 views
0

我的文檔集合的數量是這樣的:查詢包含在MongoDB中某些領域的數組元素

{ 
    my_array : [ 
     { 
     name : "...", 
     flag : 1 // optional 
     }, 
     ... 
    ] 
} 

我如何可以查詢文檔只有一個它們的元素都包含標誌字段?

這將返回的文檔,其中my_array長度正好是1:

db.col.find({ "my_array" : { $size : 1 } }) 

這將返回其中至少一個my_array的對象的包含標誌字段文件:

db.col.find({ "my_array.flag" : { $exists : true } }) 

而這將查找標記爲大小爲1的數組的文檔:

db.col.find({ "my_array.flag" : { $size : 1 } }) 

我想以某種方式結合前兩個查詢,檢查是否有一些內部的場的存在,然後查詢過濾的數組的大小

+1

爲什麼在示例文檔中「標記」整數字段?也許我在這裏錯過了一些東西。 – styvane

+0

my_array是一個文檔數組,每個內部文檔可能包含或不包含帶有某個值的字段「flag」(這並不重要) – marmor

回答

1

你可以嘗試用$redact$filter您的查詢。

$filter$ifNull,以保持匹配的元素,然後$size$redact和比較1結果,以保持和其他人取出文件。

db.col.aggregate(
    [{ 
     $match: { 
      "my_array.flag": { 
       $exists: true 
      } 
     } 
    }, { 
     $redact: { 
      $cond: { 
       if: { 
        $eq: [{ 
         $size: { 
          $filter: { 
           input: "$my_array", 
           as: "array", 
           cond: { 
            $eq: [{ 
             $ifNull: ["$$array.flag", null] 
            }, "$$array.flag"] 
           } 
          } 
         } 
        }, 1] 
       }, 
       then: "$$KEEP", 
       else: "$$PRUNE" 
      } 
     } 
    }] 
) 
+1

,雖然這聽起來可能有效,但它需要全表掃描,而不是使用我的索引「my_array.field」,我已經增加了一個使用聚合的解決方案,但我並不十分喜歡這兩種解決方案。 – marmor

+0

我應該提到'$ redact'不使用索引。我認爲這幾乎是兩種解決方案。 '$ unwind'通過展開每個數組確實增加了內存開銷。所以根據你的數據,你可以比其他人表現得更好。不知道這是否會有所不同,但你可以改變解決方案使用'$ project'和'$ match'而不是'$ redact'。 – Veeram

+0

你也可以'{$ match:{「my_array.flag」:{$ exists :true}}}在'$ redact'前面過濾數組,然後通過'$ redact'發送它。我已經更新了答案。 – Veeram

0

使用聚合框架在MongoDB中,

流水線階段1:匹配,

  checking for some inner field existence, 

     { "my_array.flag" : { $exists : true } } 

pipleline階段2:項目,

  querying for the size of the filtered array, 

     {$project:{ "my_array":{$size:"$my_array"}}} 

mongoshe ll命令,

db.col.aggregate([{ 
     $match: { 
      "my_array.flag": { 
       $exists: true 
      } 
     } 
    }, { 
     $project: { 
      "my_array": { 
       $size: "$my_array" 
      } 
     } 
    }]) 
+0

第一個階段不會**過濾數組**,它會匹配至少有一個數組元素包含標誌的文檔。然而,我認爲這可以通過聚合來完成,我會嘗試使用 – marmor

0

以下有關使用集合當前的答案,這裏是一個有效的解決方案使用聚合:

db.col.aggregate([ 
{ $match : { "my_array.flag" : { $exists : true } } }, 
{ $unwind : "$my_array" }, 
{ $match : { "my_array.flag" : { $exists : true } } }, 
{ $group : { _id : "$_id", count_flags : { $sum : 1 } } }, 
{ $match : { "count_flags" : 1 } } 
]) 

步驟如下解釋:

  1. 過濾,僅包含至少一個文件flag字段(這將使用我的索引來顯着減少我需要處理的文檔數量)
  2. Unwin d的my_array陣列,以便能夠每個元素處理爲單獨的文檔不再含有flag
  3. 組從元件分離的文檔通過_id
  4. 過濾器,和計數(包含flag字段)中的元素
  5. 由新count_flags領域

過濾我真的不喜歡這樣的解決方案,這似乎是一個很大的步驟,得到的東西,應該有基礎保障標準mongofind查詢。