I want to find a single element from embedded document in MongoDB. Here are my documents of user collection in MongoDB:-
[
{
"userId":"001",
"userName":"abc"
"subscriptionDetails" : [
{
"planId" : "1",
"isExpired" : false,
"current" : true,
"timeStamp" : 1592570927981,
},
{
"planId" : "2",
"isExpired" : false,
"current" : true,
"timeStamp" : 1592570927909,
},
{
"planId" : "3",
"isExpired" : false,
"current" : false,
"timeStamp" : 1592570978,
}
]
},
{
"userId":"002",
"userName":"abc"
"subscriptionDetails" : [
{
"planId" : "1",
"isExpired" : false,
"current" : true,
"timeStamp" : 1592570927981,
}
]
}
]
My nodejs function to find the single element from embedded documents by nodejs native driver:-
var Plan=await db.collection("user").findOne(
{
userId:001,
'subscriptionDetails.current':false,
'subscriptionDetails.isExpired':false
},
{
'projection':{ "_id": 0,'subscriptionDetails.$':1 }
}
)
console.log(plan)
But the response of this function is:-
{
subscriptionDetails:
[ {
"planId" : "1",
"isExpired" : false,
"current" : true,
"timeStamp" : 1592570927981,
}
]
}
But the expected output is:-
{
subscriptionDetails:
[ {
"planId" : "3",
"isExpired" : false,
"current" : false,
"timeStamp" : 1592570978,
}
]
}
How can I solve this?
Why the query is not working as expected?
There are certain limitations when using the positional operator $
for query result projection. One of them is:
The query document should only contain a single condition on the array field being projected. Multiple conditions may override each other internally and lead to undefined behavior.
Source - Array fields limitations
In your query,
db.collection("user").findOne(
{
userId:001,
'subscriptionDetails.current':false,
'subscriptionDetails.isExpired':false
},
{
'projection':{ "_id": 0,'subscriptionDetails.$':1 }
}
)
you have 2 conditions on the array field being projected. This is why the query result is not what is expected.
The fix:
The easy fix would be to reduce the filter conditions on the subscriptionDetails
to just one. However, if reducing the filter conditions is not an option, you can make use of $elemMatch to compose the filter conditions. $elemMatch
would limit the content of the subscriptionDetails
array to contain only the first element matching the condition specified. Something like this:
db.collection("user").findOne(
{
userId: "001",
subscriptionDetails: {
$elemMatch: {
current: false,
isExpired: false,
}
}
},
{ _id: 0, 'subscriptionDetails.$':1 }
);