Jab
Jab

Reputation: 27515

How to return a nested document in an array

I have documents with a schema such as follows:

{
    "user_id": 123,
    "services":[
         {"name": "test",
          "data": ...
         },
         {"name": "test1",
          "data": ...
         },
         {"name": "test2",
          "data": ...
         }
    ]
}

I'm trying to get a service by name for a specific user_id returned as follows:

{"name": "test2",
    "data": ...
}

I'm having difficulty wrapping my head around how to do this and seems an aggregation shouldn't be needed for something as simple as this but maybe I'm wrong. I'm sure a projection would work in a find_one statement but I'm not sure what to use. I'm using Motor btw not sure if that helps.


I have tried:

async def get_service_by_name(user_id, name):
    return await db.guilds.find_one({
        'user_id': 123,
        'services': {'$elemMatch': {'name': "test"}}},
        {'user_id: 0, 'services.$': 1}))

But this returns:

{"services":[{"name" : "test", "data" : "blah" }]}

And that's fine as it's close to what I want and all I'd need to do is:

service = await get_service_by_name(123, "test")
service = service['service'][0]

But is there a way to get the data back as just the service without an aggregation? And if not, then what should the aggregation look like?

Edit

I came up with an aggregation that does this but want to make sure there's no better way:

await db.guilds.aggregate([
    {'$unwind': '$services'},
    {'$match':{
        '_id': 123,
        'services.name': "test"}},
    {'$project': {
        '_id': 0,
        'name': '$services.name',
        'data': '$services.data'}}
])

Upvotes: 1

Views: 61

Answers (1)

mickl
mickl

Reputation: 49985

You need to run $unwind to get single document from services and $replaceRoot to promote it to root level:

db.guilds.aggregate([
    {
        $match: { user_id: 123, "services.name": "test" }
    },
    {
        $unwind: "$services"
    },
    {
        $match: { "services.name": "test" }
    },
    {
        $replaceRoot: { newRoot: "$services" }
    }
])

Upvotes: 3

Related Questions