Search code examples
node.jsmongodbnode-mongodb-nativemongodb-nodejs-driver

How to find the single element from embedded document in nodejs mongodb native driver?


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?


Solution

  • 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 }
    );