Mongodb:根据聚合管道中的表达式删除子文档

Mongodb:根据聚合管道中的表达式删除子文档,mongodb,aggregation-framework,Mongodb,Aggregation Framework,我正在mongo集合上执行聚合查询。在管道的特定阶段,如果某个表达式的结果为false,我希望从结果中删除一个子文档 这是我希望删除子文档的查询点 db.getCollection('[module].[virtualwarehouses].supplies').aggregate([ { $match: { $or: [ { artNr: "ART01ds" }, { GTIN:

我正在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编号匹配的供应品。我做了2次查找以获取此可用文章所属的仓库。(仓库显然可以容纳许多供应品,因此我们选择单独收集供应品,否则将超过文件限制)

我进行两次查找的原因是,在我们的数据模型中,仓库可以是仓库的组合,因此我需要检查包含本文的仓库是否是仓库组的一部分

这就是我的问题所在。人们可以订阅仓库,只能从这些订阅的商店中检索库存信息。订阅存储在subs字段中。(参见上面代码字段中的查询结果)

在上面的查询中,如果子文档warehouseP(parentwarehouse)的订阅字段不包括某个订阅者,我希望删除该子文档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),因为它的subs字段不包含
localhost
。(如果包含供应品的仓库没有父仓库,则此字段可能已经为空)我尝试了上述方法,但没有一个有效


编辑,以澄清情况

我有两个收藏品,一个有仓库,一个有供应品。这两个系列中的一个

supplies集合包含一些简单的对象,其中包含有关文章的一些信息。它还包含对仓库的ObjectId引用

仓库集合包含具有关联数据的仓库。这里的关键是仓库可以是“虚拟的”,这意味着它们只是一组其他仓库。如果是这种情况,他们在字段cIds(子ID)中有一个objectID数组。否则,仓库就是一个真实的仓库,它可以在其他集合中有相关的库存。第二个重要元素是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
订阅了warehouse 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
        }
    ]
}
但我希望在阵列中为没有父仓库的仓库提供的数据中提供数据,因为此人没有订阅父仓库,并且应该无法接收该数据,如下所示:

{
    "_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):


也许这个查询的整个方法都是错误的,我可以做得更容易些吗?我对mongoDB没有那么多经验。提前感谢。

使用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版

您可以在中使用
$

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


如果不提供一组“小”样本数据,并且至少显示您对预期结果的近似值,实际上很难判断在这里做什么是“正确的”。这将大大有助于将一个问题摆在其他人面前,让他们能够猜测解决方案。因此,它有助于让别人清楚地了解你自己。如果没有数据,我们真的不能说你正在做的任何一个过程是最好做的还是不做的。还要注意你自己的陈述“否则会超过文档限制”,这也适用于
$lookup
,因为这是BSON规范的一个基本限制。如果您无法引用文档中的所有数据,因此无法将其放入其他集合中,那么
$lookup
所做的一切就是“尝试将所有数据填充回文档”。因此,可能会有几种措施来实际“过滤”返回的结果,以便在任何时候都不打破这一限制。这是一个问题需要澄清的例子。@NeilLunn谢谢你对这个问题的兴趣。我一直计划在codereview上问一个问题,问我所做的是否是最好的方式。我计划在那里对我的数据模型进行更详细的描述。但由于在需求方面,代码是完全功能的,并且仍然存在我的查询没有给出我想要的结果的情况,所以我不想冒险在那里发布并被否决。谢谢你的联合国