1

I have a mongo documnet of

{
    "locationId": "5f2084f756dc2428e96fcda4",
    "information": [{
        "category": ["5e5150c6c52a3904b74d6ff7"],     
        "date": {
            "$date": {
                "$numberLong": "1601407317343"
            }
        },
        "private": true
    }, {     
     "category": ["5e5150c6c52a3904b74d6ff7"], 
        "date": {
            "$date": {
                "$numberLong": "1601407317343"
            }
        },
        "private": false
    },
    }],
}

So far, what I have is to query the nested array.

const f = await info.aggregate([
  {
    $match: {
      $and: [
          {'locationId': '5f2084f756dc2428e96fcda4'},
          {'information.private': true}
      ]
    },
  },
]);

I am trying to query information.private = true. However, I receive both 'private: false' and 'private: true'. The outcome is

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "information": [
      {
        "category": [
          "5e5150c6c52a3904b74d6ff7"
        ],
        "date": ISODate("2020-09-29T19:21:57.343Z"),
        "private": true
      },
      {
        "category": [
          "5e5150c6c52a3904b74d6ff7"
        ],
        "date": ISODate("2020-09-29T19:21:57.343Z"),
        "private": false
      }
    ],
    "locationId": "5f2084f756dc2428e96fcda4"
  }
]

I also tried with elemMatch and returns the same results. I'd looked up multiple answers on Stackoverflow but the dot notation and elemMatch do not work in this case. I know that I have done something wrong but I can't figure it out.

1 Answer 1

1

You can try $filter,

  • $match your conditions
  • $addFields to add field information and $filter to iterate loop of array and get match documents as per condition
const f = await info.aggregate([
  {
    $match: {
      locationId: "5f2084f756dc2428e96fcda4",
      "information.private": true
    }
  },
  {
    $addFields: {
      information: {
        $filter: {
          input: "$information",
          cond: { $eq: ["$$this.private", true] }
        }
      }
    }
  }
])

Playground


Other option you can try $redact,

  • $match your condition
  • $redact Restricts the contents of the documents based on information stored in the documents themselves.
const f = await info.aggregate([
  { $match: { locationId: "5f2084f756dc2428e96fcda4" } },
  {
    $redact: {
      $cond: [{ $eq: ["$private", false] }, "$$PRUNE", "$$DESCEND"]
    }
  }
])

Playground


The find projection can accept aggregation expressions and syntax starting from MongoDB 4.4.

const f = await info.find([
  locationId: "5f2084f756dc2428e96fcda4",
  "information.private": true
},
{
  locationId: 1,
  information: {
    $filter: {
      input: "$information",
      cond: { $eq: ["$$this.private", true] }
    }
  }
})

Playground

Sign up to request clarification or add additional context in comments.

5 Comments

Is there any reason why information.private is not working?
Its a behavior, we can't access array element direct from match condition, match will just match condition. it will return whole document where array element match.
This happens when I use .find() as well. Do I always have to get the document and work it out?
If you know there is single element in array and i want only one that, so you can use find({"information.private": true}, {"information.$": 1}) this will return single matching element from array, this is a only limited option, otherwise you can use aggregate() like my answer.
Added one more option in find() method from mongodb4.4.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.