选择字段值位于某个数组中的子文档

2023-12-02

我想根据子文档进行过滤,但实际上我正在为每个子文档重复该文档。如果是这种情况,我想要一份文档和一份子文档列表。

我的数据如下所示:

{
    "_id" : ObjectId("582eeb5f75f58055246bd22d"),
    "filename" : "file1",
    "cod" : NumberLong(90),
    "subdocs" : [
        {
            "length" : NumberLong(10),
            "desc" : "000"
        },
        {
            "length" : NumberLong(15),
            "desc" : "011"
        },
        {
            "length" : NumberLong(30),
            "desc" : "038"
        }
    ]
}
{
    "_id" : ObjectId("582eeb5f75f58055246bd22e"),
    "filename" : "file2",
    "cod" : NumberLong(95),
    "subdocs" : [
        {
            "length" : NumberLong(11),
            "desc" : "000"
        },
        {
            "length" : NumberLong(21),
            "desc" : "018"
        },
        {
            "length" : NumberLong(41),
            "desc" : "008"
        }
    ]
}

我正在使用此查询来过滤desc(000, 011) 上subdocs

db.ftmp.aggregate( 
    { $match: 
        { "subdocs.desc": 
            { $in: ["000", "011"] } 
        }
    }, 
    { $unwind : "$subdocs" }, 
    { $match : 
        { "subdocs.desc" : 
            { $in:["000", "011"] } 
        }
    }
)

但结果显示 3 个文档,每个与该查询匹配的子文档对应 1 个文档。

{
    "_id" : ObjectId("582eeb5f75f58055246bd22d"),
    "filename" : "file1",
    "cod" : NumberLong(90),
    "subdocs" : {
        "length" : NumberLong(10),
        "desc" : "000"
    }
}
{
    "_id" : ObjectId("582eeb5f75f58055246bd22d"),
    "filename" : "file1",
    "cod" : NumberLong(90),
    "subdocs" : {
        "length" : NumberLong(15),
        "desc" : "011"
    }
}
{
    "_id" : ObjectId("582eeb5f75f58055246bd22e"),
    "filename" : "file2",
    "cod" : NumberLong(95),
    "subdocs" : {
        "length" : NumberLong(11),
        "desc" : "000"
    }
}

不过我想得到: file1 的子文档为 desc 000 和 011,file2 的子文档为 000

{
    "_id" : ObjectId("582eeb5f75f58055246bd22d"),
    "filename" : "file1",
    "cod" : NumberLong(90),
    "subdocs" : [
        {
            "length" : NumberLong(10),
            "desc" : "000"
        },
        {
            "length" : NumberLong(15),
            "desc" : "011"
        }
    ]
}
{
    "_id" : ObjectId("582eeb5f75f58055246bd22e"),
    "filename" : "file2",
    "cod" : NumberLong(95),
    "subdocs" : {
        "length" : NumberLong(11),
        "desc" : "000"
    }
}

这样做的正确方法是什么?任何想法?


首先使用$unwind如本文中提到的运算符answer会导致应用程序性能下降,因为展开数组会导致管道中需要处理更多文档。从 MongoDB 2.6 开始,有更好的方法来实现这一点。

话虽如此,这对于$filterMongoDB 3.2 中新增的运算符。

最有效的方法是在 MongoDB 3.4 中。 MongoDB 3.4 引入了$in聚合框架的数组运算符,可用于$filter cond当计算结果为 true 时,该表达式将子文档包含在结果数组中。

let values = [ '000', '011' ];

db.collection.aggregate([ 
    { "$project": { 
        "filename": 1, 
        "cod": 1, 
        "subdocs": { 
            "$filter": { 
                "input": "$subdocs", 
                "as": "s", 
                "cond": { "$in": [ "$$s.desc", values ] }
            } 
        } 
    }} 
])

在 MongoDB 3.2 中,我们需要一种稍微不同的方法,因为我们可以使用$in那里的操作员。但幸运的是我们有$setIsSubset运算符,正如您可能猜测的那样,对两个数组执行集合操作,如果第一个数组是第二个数组的子集,则返回 true。因为$setIsSubset第一个表达式必须是一个数组,需要使desc在我们的管道中字段一个数组。为此,我们只需使用[]将创建的内容括起来MongoDB 3.2 新增的数组字段

db.collection.aggregate([ 
    { "$project": { 
        "filename": 1, 
        "cod": 1, 
        "subdocs": { 
            "$filter": { 
                "input": "$subdocs", 
                "as": "s", 
                "cond": { "$setIsSubset": [ [ "$$s.desc" ], values ] }
            } 
        } 
    }} 
])

MongoDB 3.0 对我来说已经死了,但如果由于某些原因你正在运行该版本,你可以使用$literal运算符返回集合操作所需的单元素数组,$setDifference操作员。这留给读者作为练习。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

选择字段值位于某个数组中的子文档 的相关文章

随机推荐