我正在对mongo集合执行聚合查询。在管道的特定阶段,如果某个表达式结果为false,我想从结果中删除一个子文档。
这是我希望删除子文档的查询。
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编号匹配的集合中的供应品。I 2查找以获得此可用物品所属的仓库。(仓库显然可以包含许多供应品,因此我们选择单独收集供应品,否则就会超出文件限制)
我之所以进行2次查找,是因为在我们的数据模型中,仓库可以是仓库的组合,所以我需要检查包含本文的仓库是否属于仓库组。
这就是我的问题所在。人们可以订阅仓库,并且只应该从这些订阅的商店中检索库存信息。订阅存储在subs字段中。(请参阅上面代码字段中的查询结果)
在上面的查询中,如果子文档warehouseP (父仓库)的订阅字段不包括某个订阅者,我希望删除它。在这种情况下,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 (将其设置为null),因为它的子字段不包含localhost。(如果包含供应的仓库没有父级,则该字段可能已经为null )我尝试过上述方法,但都没有工作。
编辑,以澄清情况。
我有两个收藏品,一个有仓库,一个有用品。两个集合的数据示例。
供应品收集包含一些简单的对象,并提供了关于本文的一些信息。它还包含和ObjectId对仓库的引用。
仓库集合包含具有关联数据的仓库。这里的关键是仓库可以是“虚拟的”,这意味着它们只是一组其他的仓库。如果是这种情况,则在字段中有一个ObjectIds数组,cIds (子Ids)。否则,仓库就是一个真正的仓库,它可以在另一个集合中拥有相关的库存。第二个重要元素是subs字段。在此字段中,我存储有关订阅该仓库的人的数据。这里的想法是,人们可以订阅某些仓库(在这里,谁能订阅哪个仓库,谁不能订阅哪个仓库,谁不能订阅仓库背后的逻辑)。应该只能检索他们订阅的仓库中物品供应的信息。
为了简单起见,我想要一个查询,如果我给出文章编号/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,但没有订阅warehouse Combo,他仍然会收到这样的数据:
{
"_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
}
]
}我现在有一个完整的查询,它生成上面的示例(当然,最后一个除外)是这样的(与问题顶部的查询相同,还有一个附加的$group):
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没有那么多的经验。提前谢谢。
发布于 2017-05-23 15:47:34
使用Mongo版本3.2
使用$filter
可以使用$filter匹配数组上的字段,然后使用$size + $gt将布尔值投影到聚合管道中的$cond运算符中。
$project: {
warehouseP: {
$cond: {
if: {
$gt: [{
$size: {
$filter: {
input: "$warehouseP.subs",
as: "result",
cond: {
$eq: ['$$result.sKey', "localhost"]
}
}
}
}, 0]
},
then: "$warehouseP",
else: null
}
}
}使用$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/
https://stackoverflow.com/questions/44137503
复制相似问题