2

I have a collection (summary) like this.

    {
    "id":"summaryid",
    "locations": [
        {
            "id": "loc1",
            "datacenters": [
                {
                    "id": "dc1.1",
                    "clusters": [
                        {
                            "id": "cl1.1",
                            "servers": [
                                {
                                    "id": "srvr1.1",
                                    "services": [
                                        {
                                            "id": "srvc1.1"
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                },
                {
                    "id": "dc1.2",
                    "clusters": [
                        {
                            "id": "cl1.2",
                            "servers": [
                                {
                                    "id": "srvr1.2",
                                    "services": [
                                        {
                                            "id": "srvc1.2"
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        },
        {
            "id": "loc2",
            "datacenters": [
                {
                    "id": "dc2.1",
                    "clusters": [
                        {
                            "id": "cl2.1",
                            "servers": [
                                {
                                    "id": "srvr2.1",
                                    "services": [
                                        {
                                            "id": "srvc2.1"
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                },
                {
                    "id": "dc2.2",
                    "clusters": [
                        {
                            "id": "cl2.2",
                            "servers": [
                                {
                                    "id": "srvr2.2",
                                    "services": [
                                        {
                                            "id": "srvc2.2"
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }

            ]
        }
    ]
}

Now I want only the clusters that are for datacenter with id dc1.1. I would like to exclude servers for the clusters.

I have tried using find query with $elemMatch and projections as below.

db.summary.find({}, {"locations": { $elemMatch: { "datacenters._id" : 
"dc1.1" } }, "locations.datacenters.clusters":0, 
"locations.datacenters.servers":0, "locations.datacentercount" : 0, 
"locations.clustercount" : 0, "locations.servercount" : 0}).pretty()

I am still getting all the datacenters instead of just 1 that matches the id. I am not sure if I am doing this right.

Thank you!

5
  • Can you post your expected json result? are you hoping for just { "id": "dc1.1", "clusters": [ {"id": "cl1.1",} ] } Commented Oct 10, 2017 at 15:49
  • Your $elemMatch instruction is using "datacenters._id" but the correct path (based on the example document you supplied) is "datacenters.id" Commented Oct 10, 2017 at 16:06
  • find is to find documents, not parts of the documents. Projection applies to all properties unconditionally, and is not the right tool here. You need to use aggregation to retrieve sub-documents. Commented Oct 10, 2017 at 16:13
  • Thank you Alex. I tried the aggregations and was get what I want. Following is what I am using. ` db.summary.aggregate([ {"$unwind": "$locations"}, {"$unwind": "$locations.datacenters"}, { $match: { "locations.datacenters._id" : "dc1.1" } }, { $project : {"locations.datacenters.servers" : 0, "locations.datacenters.clusters.servers" : 0} } ]).pretty() ` Commented Oct 10, 2017 at 16:42
  • New challenge is that I am using spring-mongodb-data's ProjectionOperation along with mongoTemplate. I keep getting the following error. ProjectionOperation projectStage = new ProjectionOperation().andExclude("locations.datacenters.servers", "locations.datacenters.clusters.servers"); Exclusion of field locations.datacenters.servers not allowed. Projections by the mongodb aggregation framework only support the exclusion of the _id field! Commented Oct 10, 2017 at 16:46

3 Answers 3

6

It is not possible with $elemMatch to project the nested array element.

You can try the below aggregation in 3.4 server.

Use $unwind couple of times to reach the nested array and apply $match to pick the nested array element.

   db.summary.aggregate([
      {
        "$match": {
          "locations.datacenters._id": "dc1.1"
        }
      },
      {
        "$unwind": "$locations"
      },
      {
        "$unwind": "$locations.datacenters"
      },
      {
        "$match": {
          "locations.datacenters._id": "dc1.1"
        }
      },
      {
        "$project": {
          "locations.datacenters.clusters.servers": 0
        }
      }
    ])

{"$project": {"locations.datacenters.clusters.servers": 0}} will remove the servers field while keep all the other fields in the final output.

From the docs

If you specify the exclusion of a field other than _id, you cannot employ any other $project specification forms: i.e. if you exclude fields, you cannot also specify the inclusion of fields, reset the value of existing fields, or add new fields.

Reference: https://docs.mongodb.com/manual/reference/operator/aggregation/project/#exclude-fields

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

1 Comment

Thanks. It did gave me resources to figure out how to handle my requirement.
1

If you want to project only the clusters field for datacenters dc1.1 then below query will do it.

db.summary.find({"locations.datacenters": {$elemMatch: {"id": "dc1.1"}}},{"locations.datacenters.clusters":1, _id : 0})

Note, the _id:0 in projection is used to exclude the field _id to be displayed.

More information can be found at the following link: https://docs.mongodb.com/manual/reference/operator/projection/elemMatch/index.html

Comments

0

Nested Array, Push/Pull

 let doc = await Chat.findOneAndUpdate(
      {
        $and: [
          {
            _id: chatId,
          },
          {
            $or: [
              {
                user: req.user.id,
              },
              {
                'messages.$.recipients': {
                  $elemMatch: {
                    id: recipientId
                  },
                },
              },
            ],
          }
        ],
      },
      {
        $pull: {
          //@ts-ignore
          'messages.$.recipients': { id: recipientId },
        },
      },
      { new: true }
    );

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.