MongoDB get SubDocument

2019-01-22 00:20发布

问题:

I would like to retrieve a sub document from a document in MongoDB. I have the following document:

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

The result I would like is:

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

However, the best I can do so far is using the following query:

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

But this gives me the following result:

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

Does anyone know if it is possible to write a query that will give me my desired result? Any help would be much appreciated.

回答1:

You can do it with Aggregation Framework. Query will be something like :

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


回答2:

Using MongoDB 3.4.4 and newer, the aggregation framework offers a number of operators that you can use to return the desired subdocument.

Consider running an aggregate pipeline that uses a single $replaceRoot stage to promote the filtered subdocument to the top-level and replace all other fields.

Filtering the subdocument requires the $filter operator which selects a subset of an array to return based on the specified condition i.e. returns an array with only those elements that match the condition. You can then convert the single array element to a document by using the $arrayElemAt operator

Overall running this aggregate operation will yield the desired result:

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 */
            ]
        }
    } }
])

Output

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