簡體   English   中英

MongoDB:如何返回查詢列表中存在的數組元素

[英]Mongodb: how to return elements of array that are present in the query list

我有一個叫做商店的收藏。 結構就像:

[
     {
          '_id' : id1,
          'details' : {name: 'shopA'},
          'products' : [{
               _id: 'p1',
               details:  {
                    'name': 'product1'
               }
          },{
               _id: 'p2',
               details:  {
                    'name': 'product2'
               }
          }, {
               _id: 'p4',
               details:  {
                    'name': 'product4'
               }
          }
     },{
          '_id' : id2,
          'details' : {name: 'shopB'},
          'products' : [{
               _id: 'p1',
               details:  {
                    'name': 'product1'
               }
          },{
               _id: 'p4',
               details:  {
                    'name': 'product4'
               }
          }, {
               _id: 'p5',
               details:  {
                    'name': 'product5'
               }
          }
     },{
          '_id' : id3,
          'details' : {name: 'shopC'},
          'products' : [{
               _id: 'p1',
               details:  {
                    'name': 'product1'
               }
          },{
               _id: 'p2',
               details:  {
                    'name': 'product2'
               }
          }, {
               _id: 'p3',
               details:  {
                    'name': 'product3'
               }
          }
     },{
          '_id' : id4,
          'details' : {name: 'shopOther'},
          'products' : [{
               _id: 'p10',
               details:  {
                    'name': 'product10'
               }
          },{
               _id: 'p12',
               details:  {
                    'name': 'product12'
               }
          }, {
               _id: 'p13',
               details:  {
                    'name': 'product13'
               }
          }
     }
]

現在,用戶可以從菜單中選擇一些產品,然后嘗試找到這些商店。 結果應該是所有提供至少一項選定商品的商店。

例,

假設用戶選擇['p1', 'p2', 'p3'] //ids那么只會列出三個商店id1,id2,id3(id4沒有這些項目),加上這樣的結構可以刪除其余的結果數組中來自文檔的商店產品(未列出)。

有沒有辦法,我可以直接從mongodb獲得這樣的結果?

由於您確實提出了很好的要求,而且格式也很合理,因此需要考慮一些類似的答案可能實際上並不適合作為參考,特別是如果您對MongoDB產品的經驗水平很低時。

$redact類的選項似乎很簡單,通常很適合。 但這不是您需要如何構造語句的情況:

db.collection.aggregate([
  { "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
  { "$redact": {
    "$cond": {
      "if": {
        "$or": [
          { "$eq": [ "$_id", "p1" ] },
          { "$eq": [ "$_id", "p2" ] },
          { "$eq": [ "$_id", "p3" ] }
        ]
      },
      "then": "$$DESCEND",
      "else": "$$PRUNE"
    }
  }}
])

這與$or在聚合運算符中的“不太明顯”用法一起使用。 至少就正確的語法和形式而言,但這實際上是“完全失敗”。 原因是因為$redact通常是“遞歸”操作,並且它在文檔的“所有級別”進行檢查,而不僅僅是在特定級別進行檢查。 因此,在您的“頂層”中, _id斷言將失敗,因為同名的頂層字段將不符合該條件。

實際上,您沒有任何其他可以做的事情,但是考慮到數組中的_id實際上是一個“唯一”元素,那么您始終可以在$project階段借助$map$setDifference來執行此操作:

db.collection.aggregate([
  { "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
  { "$project": {
    "details": 1,
    "products": {
      "$setDifference": [
        { "$map": {
          "input": "$products",
          "as": "el",
          "in": {
            "$cond": {
              "if": { 
                "$or": [
                  { "$eq": [ "$$el._id", "p1" ] },
                  { "$eq": [ "$$el._id", "p2" ] },
                  { "$eq": [ "$$el._id", "p3" ] }
                ]
              },
              "then": "$$el",
              "else": false
            }
          }
        }},
        [false]
      ]
    }
  }}
])

似乎很長,但實際上非常有效。 $map運算符為每個文檔“內聯”處理數組,並作用於每個元素以產生新的數組。 $setDifference相比,通過考慮結果的“集合”,可以平衡在條件不匹配的$cond下作出的false斷言,后者可以有效地“過濾”結果數組中的false結果,僅保留有效的匹配項。

當然,如果_id值或整個對象不是真正的“唯一”,則“集合”將不再有效。 考慮到這一點,以及上述提到的運算符不可用於2.6之前的版本的事實,那么更傳統的方法是$unwind數組成員,然后通過$match操作“過濾”它們。

db.collection.aggregate([
  { "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
  { "$unwind": "$products" },
  { "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
  { "$group": {    
      "_id": "$_id",
      "details": { "$first": "$details" },
      "products": { "$push": "$products" }
  }}
])

考慮到按照其他示例, $match階段應首先在管道中執行,以減少匹配條件的“可能”文檔。 具有$match的“第二”階段以“非規范化”形式對數組內的文檔元素進行實際的“過濾”。

由於數組是由$unwind “解構”的,因此$group的目的是“重建”數組,並從與條件不匹配的元素中“過濾”出來。

MongoDB還提供了位置$運算符,以便從查詢條件中選擇匹配的數組元素。 像這樣:

db.collection.find(
    { "products._id": { "$in": ["p1","p2","p3"] },
    { "details": 1, "products.$": 1 }
)

但是這里的問題是,此運算符僅在查詢文檔中提供的條件上支持“第一”匹配。 這是設計目的,到目前為止,還沒有嚴格的運算符語法可以滿足多個匹配項。

因此,您的最終方法是當前使用.aggregate()方法,以便在所需的內部數組上實際實現匹配過濾。 要么過濾內容,要么在客戶端代碼中響應自己,這取決於最終對您的可口性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM