1

I am struggling to write this query.

From the following dataset, I want to find out the documents who's resources array have 'EC2' and print out ONLY that element from the array.

Data:

[ 
  { 
    "projectName": "first"
    "resources": [
      {
          "resource": "EC2",
          "region": "ap-south-1",
          "params": {
              "ImageId": "ami-0bcf5425cdc1d8a85",
              "InstanceType": "t2.micro",
          }
      },
      {
          "resource": "S3",
          "region": "ap-south-1",
          "params": {
              "Bucket": "test-bucket"
          }
      }
    ],
  }
] 

Expected output:

{
    "resource": "EC2",
    "region": "ap-south-1",
    "params": {
      "ImageId": "ami-0bcf5425cdc1d8a85",
      "InstanceType": "t2.micro",
    }
}

The query that I have tried :

const projects = await Project.aggregate([
      {
        $match: {
          resources: {
            $elemMatch: {
              resource: { $eq: 'EC2' },
            },
          },
        },
      },
      {
        $project: {
          resourceName: '$resources.resource',
          region: '$resources.region' // and so on
        },
      },
    ]);

^^ This is returning other elements from the resources array as well

2 Answers 2

1
  • $filter to iterate loop of resources array and find matching resource
  • $arrayElemAt to get first matching element from above filtered result
  • $replaceRoot to replace above return object to root
const projects = await Project.aggregate([

  { $match: { resources: { $elemMatch: { resource: { $eq: "EC2" } } } } },
  // or below match is equal to above match condition
  // { $match: { "resources.resource": "EC2" } },

  {
    $replaceRoot: {
      newRoot: {
        $arrayElemAt: [
          {
            $filter: {
              input: "$resources",
              cond: { $eq: ["$$this.resource", "EC2"] }
            }
          },
          0
        ]
      }
    }
  }

])

Playground

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

7 Comments

Hi, thanks, filter worked for me. I used this $filter: { input: '$resources', as: 'each', cond: { $eq: ['$$each.resource', 'EC2'] }, }, One QUESTION, why did I have to use 2 $$ symbols? I assume first is for the document and second is to refer the as that is each variable?? CONFUSED
single $ is refer to current root document' fields like you can access projectName field in filter using $projectName, and $$ refers $filter's as variable name.
is there a way to select the fields of the matching elements and returning them?
i am not getting you exactly, can you please explain more, you can check the playground it is exactly same as your expected result.
I am getting the elements filtered by the filtered operator. What if I want to project specific fields of the filtered elements. Like I want to project only the params property of each filtered element.
|
1

Thanks to @turivishal

I came up with this solution:

const projects = await Project.aggregate([
      {
        $match: {
          resources: {
            $elemMatch: {
              resource: { $eq: 'EC2' },
            },
          },
        },
      },
      {
        $project: {
          resource: {
            $filter: {
              input: '$resources',
              as: 'each', // use $$ to refer
              cond: { $eq: ['$$each.resource', 'EC2'] },
            },
          },
        },
      },
    ]);

Comments

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.