2017-05-23 39 views
0

我在mongo集合上執行聚合查詢。在管道的特定階段,如果某個表達式結果爲false,我想從結果中刪除子文檔。Mongodb:根據彙總管道中的表達式刪除子文檔

這是查詢在我希望刪除子文檔的位置。

db.getCollection('[module].[virtualwarehouses].supplies').aggregate([ 
    { 
     $match: { 
      $or: [ 
       { artNr: "ART01ds" }, 
       { GTIN: "GTIN0001" } 
      ] 
     } 
    }, 
    { 
     $lookup: { 
      from: '[module].[virtualwarehouses].warehouses', 
      localField: 'wId', 
      foreignField: '_id', 
      as: 'warehouse' 
     } 
    }, 
    { 
     $unwind: '$warehouse' 
    }, 
    { 
     $lookup: { 
      from: '[module].[virtualwarehouses].warehouses', 
      localField: 'warehouse._id', 
      foreignField: 'cIds', 
      as: 'warehouseP' 
     } 
    }, 
    { 
     $unwind: { 
      path: '$warehouseP', 
      preserveNullAndEmptyArrays: true 
     } 
    }, 
    { 
     $match: { 
      $and: [ 
       {'warehouse.isDel' : false}, 
       {$or: [ 
        { 'warehouseP.isDel' : false }, 
        { 'warehouseP' : { $exists: false } } 
       ]}, 
       {$or: [ 
        { 'warehouse.subs' : { $elemMatch: { sKey: "localhost" } } }, 
        { 'warehouseP.subs' : { $elemMatch: { sKey: "localhost" } } }   
       ]} 
      ] 
     } 
    } 
]) 

------------ RESULT --------------- 
{ 
    "_id" : ObjectId("5922eae4f576274033147127"), 
    "GTIN" : "GTIN0001", 
    "status" : 0, 
    "stock" : 2, 
    "wId" : ObjectId("5922e378c4352e2b3ccc7b65"), 
    "warehouse" : { 
     "_id" : ObjectId("5922e378c4352e2b3ccc7b65"), 
     "name" : "Warehouse 2", 
     "pId" : "Test Company", 
     "type" : 0, 
     "source" : 0, 
     "cIds" : [], 
     "isDel" : false, 
     "isEnabled" : true, 
     "srcSettings" : { 
      "dataSource" : 0, 
      "ftpUrl" : "ftps.test.com", 
      "ftpDir" : "\\\\serv-s1\\importer", 
      "ftpFile" : "test.csv", 
      "dropImport" : true 
     }, 
     "subs" : [ 
      { 
       "sKey" : "localhost", 
       "order" : 500000 
      } 
     ] 
    }, 
    "warehouseP" : { 
     "_id" : ObjectId("5922e441de7c2c0eaca93e9b"), 
     "name" : "Warehouse Combo", 
     "pId" : "Test Company", 
     "type" : 1, 
     "source" : 0, 
     "cIds" : [ 
      ObjectId("5922e263c4352e2b3ccc7b64"), 
      ObjectId("5922e378c4352e2b3ccc7b65"), 
      ObjectId("5923f49ef5762740331fadd5") 
     ], 
     "isDel" : false, 
     "isEnabled" : true, 
     "srcSettings" : null, 
     "subs" : [ 
      { 
       "sKey" : "fakeSubscriber", 
       "order" : 500000 
      } 
     ] 
    } 
} 

在此查詢中,我查找與物品/ GTIN編號匹配的集合中的耗材。我做了2個查找來獲得這個可用文章所屬的倉庫。 (倉庫顯然可以包含許多物料,因此我們選擇單獨收集物料,否則將超出文檔限制)

我做2次查找的原因是因爲在我們的數據模型中,倉庫可以是倉庫,所以我需要檢查包含這篇文章的倉庫是否是倉庫組的一部分。

現在,這是我的問題出現的地方。人們可以認購倉庫,應該只從這些訂閱的商店檢索股票信息。訂閱存儲在一個subs字段中。 (請參閱上面代碼字段中的查詢結果)

在上面的查詢中,如果訂閱字段不包含特定訂戶,我希望刪除子文檔warehouseP(parentwarehouse)。在這種情況下,localhost

我試過到目前爲止有這些:

---- attempt 1, does nothing, always true 
    { 
     $project: { 
      warehouseP: { 
       $cond: { 
        if: {'warehouseP' : { "subs": { sKey: "localhost" } } }, 
        then: "$warehouseP", 
        else: null 
       } 
      } 
     } 
    } 
---- attempt 2, results in 
---- errmsg: FieldPath field names may not contain '.'." 
{ 
    $project: { 
     warehouseP: { 
      $cond: { 
       if: { 'warehouseP.subs' : { $elemMatch: { sKey: "localhost" } } }, 
       then: "$warehouseP", 
       else: null 
      } 
     } 
    } 
} 
---- attempt 3, results in: 
---- errmsg: Unrecognized expression '$elemMatch' 
{ 
    $project: { 
     warehouseP: { 
      $cond: { 
       if: { 'warehouseP' : { 'subs' : { $elemMatch: { sKey: "localhost" } } } }, 
       then: "$warehouseP", 
       else: null 
      } 
     } 
    } 
} 

因此,得出結論。在上部查詢的輸出中,我希望刪除字段warehouseP(將其設置爲空),因爲它的subs字段不包含localhost。 (如果包含供應的倉庫沒有父項,那麼此字段可能已經爲空)我已經嘗試了上述內容,但都沒有成功。


編輯,以澄清情況。

我有2個系列,一個有倉庫和一個有耗材。 Data examples的2個系列。

用品集合包含簡單的對象以及有關該文章的一些信息。它還包含對ObjectId引用的倉庫。

倉庫集合包含具有關聯數據的倉庫。關鍵在於倉庫可以是「虛擬」的,這意味着它們只是一組其他倉庫。如果是這種情況,他們在字段cIds(兒童ID)中有一個ObjectIds數組。否則倉庫就是一個真正的倉庫,它可以在其他倉庫中擁有相關庫存。第二個重要組成部分是字段。在此字段中,我存儲有關誰訂閱了此倉庫的數據。這裏的想法是人們可以訂閱某些倉庫(這裏的邏輯是誰可以或不可以訂閱哪個倉庫與此無關)。應該只能檢索他們訂閱的倉庫中的物品供應信息。

爲了簡單起見,我想要一個查詢,如果我給一個物品編號/ GTIN和一個訂戶鍵,返回供應信息。如果適用,信息應該由「虛擬」倉庫分組。我的意思是,如果有人訂閱了「虛擬」倉庫他應該接受,像這樣的數據:

---- Warehouses that do not have a parent, all end up in this array 
{ 
    "_id" : null, 
    "artNr" : "ART01", 
    "GTIN" : null, 
    "n" : null, 
    "p" : null, 
    "source" : null, 
    "cIds" : null, 
    "warehouses" : [ 
     { 
      "_id" : ObjectId("5922e576f576274033145a3f"), 
      "n" : "Supplier Warehouse 1", 
      "p" : "Bosch", 
      "status" : 0, 
      "stock" : 5 
     } 
    ] 
} 

---- Warehouses that DO have a parent, should be grouped under a document for every 'virtual' (parent) warehouse 
{ 
    "_id" : ObjectId("5922e441de7c2c0eaca93e9b"), 
    "artNr" : "ART01", 
    "GTIN" : null, 
    "n" : "Warehouse Combo", 
    "p" : "D Soft", 
    "source" : 0, 
    "cIds" : [ 
     ObjectId("5922e263c4352e2b3ccc7b64"), 
     ObjectId("5922e378c4352e2b3ccc7b65"), 
     ObjectId("5923f49ef5762740331fadd5") 
    ], 
    "warehouses" : [ 
     { 
      "_id" : ObjectId("5922e263c4352e2b3ccc7b64"), 
      "n" : "Warehouse 1", 
      "p" : "D Soft", 
      "status" : 0, 
      "stock" : 5 
     }, 
     { 
      "_id" : ObjectId("5922e378c4352e2b3ccc7b65"), 
      "n" : "Warehouse 2", 
      "p" : "D Soft", 
      "status" : 0, 
      "stock" : 5 
     } 
    ] 
} 

{ 
    *** potentially many other 'virtual' warehouses *** 
} 

我上面貼的查詢實現了這一點,但做錯了事1例: 如果有人訂閱屬於某個組的倉庫,該組信息始終顯示。即使用戶沒有訂閱該「虛擬」倉庫。

如果我在哪裏使用如上面例子中的數據,如果有人,說localhost被預訂的倉庫1,而不是倉庫的組合,他依然會收到類似這樣的數據:

{ 
    "_id" : ObjectId("5922e441de7c2c0eaca93e9b"), 
    "artNr" : "ART01", 
    "GTIN" : null, 
    "n" : "Warehouse Combo", 
    "p" : "D Soft", 
    "source" : 0, 
    "cIds" : [ 
     ObjectId("5922e263c4352e2b3ccc7b64"), 
     ObjectId("5922e378c4352e2b3ccc7b65"), 
     ObjectId("5923f49ef5762740331fadd5") 
    ], 
    "warehouses" : [ 
     { 
      "_id" : ObjectId("5922e263c4352e2b3ccc7b64"), 
      "n" : "Warehouse 1", 
      "p" : "D Soft", 
      "status" : 0, 
      "stock" : 5 
     } 
    ] 
} 

但我想在數據被提供在陣列中被提供用於倉庫不具有父,因爲該人未訂閱的父倉庫,並且不應當能夠接收該數據,這樣的數據:

{ 
    "_id" : null, 
    "artNr" : "ART01", 
    "GTIN" : null, 
    "n" : null, 
    "p" : null, 
    "source" : null, 
    "cIds" : null, 
    "warehouses" : [ 
     { 
      "_id" : ObjectId("5922e263c4352e2b3ccc7b64"), 
      "n" : "Warehouse 1", 
      "p" : "D Soft", 
      "status" : 0, 
      "stock" : 5 
     } 
    ] 
} 

我現在有完整的查詢,t帽子產生上面的例子(除了最後,當然)是本(同在的問題的頂部的查詢時,與另外的$組):

db.getCollection('[module].[virtualwarehouses].supplies').aggregate([ 
    { 
     $match: { 
      $or: [ 
       { artNr: "ART01" }, 
       { GTIN: "GTIN001" } 
      ] 
     } 
    }, 
    { 
     $lookup: { 
      from: '[module].[virtualwarehouses].warehouses', 
      localField: 'wId', 
      foreignField: '_id', 
      as: 'warehouse' 
     } 
    }, 
    { 
     $unwind: '$warehouse' 
    }, 
    { 
     $lookup: { 
      from: '[module].[virtualwarehouses].warehouses', 
      localField: 'warehouse._id', 
      foreignField: 'cIds', 
      as: 'warehouseP' 
     } 
    }, 
    { 
     $unwind: { 
      path: '$warehouseP', 
      preserveNullAndEmptyArrays: true 
     } 
    }, 
    { 
     $match: { 
      $and: [ 
       {'warehouse.isDel' : false}, 
       {$or: [ 
        { 'warehouseP.isDel' : false }, 
        { 'warehouseP' : { $exists: false } } 
       ]}, 
       {$or: [ 
        { 'warehouse.subs' : { $elemMatch: { sKey: "D Soft" } } }, 
        { 'warehouseP.subs' : { $elemMatch: { sKey: "D Soft" } } }   
       ]} 
      ] 
     } 
    }, 
    { 
     $group: { 
      _id: '$warehouseP._id', 
      artNr: { $first: '$artNr' }, 
      GTIN: { $first : '$GTIN' }, 
      n: { $first: '$warehouseP.name' }, 
      p: { $first: '$warehouseP.pId' }, 
      source: { $first: '$warehouseP.source' }, 
      cIds: { $first: '$warehouseP.cIds' }, 
      warehouses: { 
       $addToSet: { 
        _id: '$warehouse._id', 
        n : '$warehouse.name', 
        p : '$warehouse.pId', 
        status: '$status', 
        stock: '$stock', 
        etaStock: '$etaStock' 
       } 
      } 
     } 
    } 
]) 

或許這個查詢的整個方法是錯誤的,我可以做得更容易?我對mongoDB沒有太多的經驗。提前致謝。

+0

它實際上是相當困難的說這是「正確」的事情在這裏不提供「小」的樣本數據集,並顯示至少你的預期結果的近似值是。這實際上將問題擺在別人面前,他們可以推測解決方案。所以,當你有機會接觸到自己時,有助於讓其他人瞭解它。沒有數據,我們無法真正地說你正在做的任何過程都是最好的事情。 –

+0

還要注意你自己的語句*「否則文檔限制會被超出」*,這也適用於'$ lookup',因爲它是BSON規範的基本限制。如果你不可能引用文檔中的所有數據,並因此將其放入其他集合中,那麼所有'$ lookup'所做的就是「試圖將所有數據填充到文檔中」。所以大概會有幾個措施來實際「過濾」返回的結果,以便在任何時候都不會打破這個限制。這是一個問題所需清晰度的例子。 –

+0

@NeilLunn感謝您對此問題的關注。我一直計劃在codereview上問一個問題,詢問我所做的是否是最好的方法。我打算在那裏提供我的數據模型的更詳細的描述。但是自從需求以來,代碼是完全可用的,並且仍然存在我的查詢不能提供我想要的結果的情況,我不想冒險在那裏發佈並得到低投票。感謝你的理解。 –

回答

1

使用蒙戈3.2版

使用$filter

您可以使用$filter以匹配陣列,然後$size + $gt領域項目的布爾值轉換成聚集管道

$project: { 
    warehouseP: { 
     $cond: { 
      if: { 
       $gt: [{ 
        $size: { 
         $filter: { 
          input: "$warehouseP.subs", 
          as: "result", 
          cond: { 
           $eq: ['$$result.sKey', "localhost"] 
          } 
         } 
        } 
       }, 0] 
      }, 
      then: "$warehouseP", 
      else: null 
     } 
    } 
} 

$cond運營商使用$setIsSubset

{ 
    $project: { 
     warehouseP: { 
      $cond: { 
       if: { 
        $setIsSubset: [ 
         ["localhost"], "$warehouseP.subs.sKey" 
        ] 
       }, 
       then: "$warehouseP", 
       else: null 
      } 
     } 
    } 
} 

使用Mongo版本3.4

您可以使用$in

{ 
    $addFields: { 
     warehouseP: { 
      $cond: { 
       if: { 
        $in: ["localhost", "$warehouseP.subs.sKey"] 
       }, 
       then: "$warehouseP", 
       else: null 
      } 
     } 
    } 
} 

參考:

https://docs.mongodb.com/manual/reference/operator/aggregation/in/ https://docs.mongodb.com/manual/reference/operator/aggregation/filter/ https://docs.mongodb.com/manual/reference/operator/aggregation/setIsSubset/

+0

謝謝你的回答。除非字段warehouseP不存在,否則這些全部工作,否則warehouseP.subs不存在。然後我得到errmsg:'$ in需要一個數組作爲第二個argumend,找到:missing'。我一直在試圖改變$ cond,因爲我沒有太多的成功。你有什麼主意嗎? –

+1

您可以編寫嵌套'$ cond'來檢查所有值,或者您可以使用'$ switch'來獲得更好的可讀性。類似於'$ addFields:{storeP:{ } $ switch:{ branches:{case:{$ eq:[{$ ifNull:[「$ warehouseP」,null]},null]},則:null}, {case:{$ eq:[{$ ifNull:[「$ warehouseP.subs」,null]},null]},則:null}, \t {case:{$ in:[「localhost 「,」$ warehouseP.subs.sKey「]},然後:」$ warehouseP「} ], 默認值:null } } } }'。我會讓你處理它。看看你能不能清理一下。 – Veeram