Reputation: 27515
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
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