Reputation: 267
I am trying to get just one field of an specific embedded documents.
The embedded document should be filtered based on two condiditions and then just return one field of that embedded document.
db.usertest.insertMany(
[{
"username" : "userA",
"objects" : [
{
"type" : "fruit",
"origin" : "ABC",
"name" : "banana",
},
{
"type" : "fruit",
"origin" : "XYZ",
"name" : "pineapple",
},
{
"type" : "vegetable",
"origin" : "XYZ",
"name" : "carrot"
}
]
},
{
"username" : "userB",
"objects" : []
}
]
)
I want to find the field "username" and the field "name" of the embedded document.
First I thought of $elemMatch, but I am stuck at the $project part and it just returns the field "name" of all embedded documents when $elemMatch is true.
db.usertest.aggregate(
[{$match:{"objects": {"$elemMatch":{
"type": "fruit",
"origin": "XYZ"}}}},
{$project: {
"username":1,
"objects.name":1
}}]
).pretty()
Then I thought of using $filter, but it obviously just returns the whole embedded document instead of just the field "name".
db.usertest.aggregate(
[{$project: {
"username":1,
"name":{
$filter: {
input: "$objects",
cond: { $and: [
{ $eq: [ "$$this.type", "fruit" ] },
{ $eq: [ "$$this.origin", "XYZ" ] }
]}
}}
}}]
).pretty()
Any ideas?
Upvotes: 1
Views: 1124
Reputation: 75994
There is no $elemMatch operator
in $project
stage. So to use mimic similar functionality you can create $filter
with $arrayElemAt
with index 0 and $let
to hold the intermediate document in a variable and output the name.
Keep the $match
stage so you only look at documents where there is at least one matching object.
Something like
db.usertest.aggregate(
[{"$match":{"objects":{"$elemMatch":{"type":"fruit","origin":"XYZ"}}}},
{"$project":{
"name":{"$let":{
"vars":{"object":{
"$arrayElemAt":[
{"$filter":{
"input":"$objects",
"as":"o",
"cond":{
"$and":[
{"$eq":["$$o.type","fruit"]},
{"$eq":["$$o.origin","XYZ"]}
]
}
}},
0
]
}},
"in":"$$object.name"
}}
}}])
Upvotes: 1
Reputation: 49995
You can combine multiple aggregation operators in following order:
$filter -> $map (to get only name
property) -> $arrayElemAt (to take first element)
db.usertest.aggregate(
[{$project: {
"username":1,
"name": {
$arrayElemAt: [
{
$map: {
input: {
$filter: {
input: "$objects",
cond: { $and: [
{ $eq: [ "$$this.type", "fruit" ] },
{ $eq: [ "$$this.origin", "XYZ" ] }
]}
}},
as: "item",
in: "$$item.name"
}
},0
]
}
}}]
).pretty()
Upvotes: 1