MongoDB获取子文档

21

我想从MongoDB中的文档中检索子文档。我有以下文档:

{
    "_id" : "10000",
    "password" : "password1",
    "name" : "customer1",
    "enabled" : true,
    "channels" : [ 
        {
            "id" : "10000-1",
            "name" : "cust1chan1",
            "enabled" : true
        }, 
        {
            "id" : "10000-2",
            "name" : "cust1chan2",
            "enabled" : true
        }
    ]
}

我希望的结果是:

{
    "id" : "10000-1",
    "name" : "cust1chan1",
    "enabled" : true
}

然而,到目前为止,我能做的最好的就是使用以下查询:

db.customer.find({"channels.id" : "10000-1"}, {"channels.$" : 1, "_id" : 0})

但这给了我以下结果:

{
    "channels" : [ 
        {
            "id" : "10000-1",
            "name" : "cust1chan1",
            "enabled" : true
        }
    ]
}

有人知道是否可以编写查询以给我所需的结果吗?任何帮助都将不胜感激。


如果您是在mongoshell中执行此操作,可以使用类似以下命令:db.customer.find({"channels.id" : "10000-1"}, {"channels.$" : 1, "_id" : 0}).channels[0] - Mzzl
谢谢您的建议。我正在使用Spring MongoTemplate(Java驱动程序),所以我不认为这对我有用。我在Mongo shell中尝试了这个方法,但是出现了错误:TypeError: Cannot read property '0' of undefined。 - Stuart
3个回答

14

您可以使用聚合框架完成此操作。查询将类似于:

db.customer.aggregate([
    {$unwind : "$channels"},
    {$match : {"channels.id" : "10000-1"}},
    {$project : {_id : 0, 
                 id : "$channels.id", 
                 name : "$channels.name", 
                 enabled : "$channels.enabled"}}
])

谢谢您的快速回复。这个很接近,但不完全是我需要的。它给了我: { "result" : [ { "name" : "cust1chan1", "enabled" : true, "id" : "10000-1" } ], "ok" : 1 } 而不是: { "id" : "10000-1", "name" : "cust1chan1", "enabled" : true } - Stuart
1
我认为你想要的是不可能实现的。为什么不在应用层从结果中提取字段呢? - Parvin Gasimzade
1
我认为你说得对,这是不可能的。是的,我可以提取字段,但最好不要这样做,这样会更加清晰和简洁。我发布了这个问题,因为我查看了Mongo文档,没有看到任何关于如何做到这一点的参考,所以我想知道是否有其他人已经做过。另一种选择是在一个文档中引用另一个子文档,并将它们存储在单独的集合中,但这样就会出现Mongo不支持事务的问题。 - Stuart
1
我认为你可以在$project中使用$cond来完成它,但不确定。 - Jonathan Muller

3
使用MongoDB 3.4.4及以上版本,聚合框架提供了许多运算符,可以用来返回所需的子文档。
考虑运行一个聚合管道,使用单个$replaceRoot阶段将筛选出的子文档提升到顶层,并替换所有其他字段。
过滤子文档需要$filter运算符,它根据指定条件选择要返回的数组子集,即仅返回与条件匹配的元素的数组。然后,您可以使用$arrayElemAt运算符将单个数组元素转换为文档。
总体而言,运行此聚合操作将产生所需的结果:
db.customer.aggregate([
    { "$replaceRoot": { 
        "newRoot": {
            "$arrayElemAt": [
                { "$filter": {
                   "input": "$channels",
                   "as": "channel",
                   "cond": { /* resolve to a boolean value and determine if an element should be included in the output array. */
                       "$eq": ["$$channel.id", "10000-1"]
                    } 
                } },
                0 /* the element at the specified array index */
            ]
        }
    } }
])

输出

{
    "id" : "10000-1",
    "name" : "cust1chan1",
    "enabled" : true
}

0

我知道可能有点晚了,但我曾经遇到过类似的情况,并找到了解决方案。在聚合管道中,您可以首先通过匹配子文档来过滤数据,然后仅展开嵌套数组字段,然后替换根。

db.customer.aggregate([
    [
        { $match: { "channels.id": "10000-1" }},
        { $unwind: "$channels" },
        { $replaceRoot: { newRoot: "$channels" } }
    ]
])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接