首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Mongodb:根据聚合管道中的表达式删除子文档

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

Stack Overflow用户
提问于 2017-05-23 14:23:35
回答 1查看 1.7K关注 0票数 1

我正在对mongo集合执行聚合查询。在管道的特定阶段,如果某个表达式结果为false,我想从结果中删除一个子文档。

这是我希望删除子文档的查询。

代码语言:javascript
复制
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

到目前为止,我尝试的是:

代码语言:javascript
复制
---- 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和订阅者密钥,则返回供应信息。信息应按“虚拟”仓库分组(如果适用的话)。我的意思是,如果有人订阅了一个“虚拟”仓库,他就应该收到如下数据:

代码语言:javascript
复制
---- 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,他仍然会收到这样的数据:

代码语言:javascript
复制
{
    "_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
        }
    ]
}

但是,我希望在数组中为没有父级的仓库提供数据,因为该人没有订阅父仓库,因此不应该能够接收到该数据,如下所示:

代码语言:javascript
复制
{
    "_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):

代码语言:javascript
复制
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没有那么多的经验。提前谢谢。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-05-23 15:47:34

使用Mongo版本3.2

使用$filter

可以使用$filter匹配数组上的字段,然后使用$size + $gt将布尔值投影到聚合管道中的$cond运算符中。

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

使用$setIsSubset

代码语言:javascript
复制
{
    $project: {
        warehouseP: {
            $cond: {
                if: {
                    $setIsSubset: [
                        ["localhost"], "$warehouseP.subs.sKey"
                    ]
                },
                then: "$warehouseP",
                else: null
            }
        }
    }
}

使用Mongo版本3.4

您可以使用$in

代码语言:javascript
复制
{
    $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/

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44137503

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档