colymore
colymore

Reputation: 12326

Reduce the response of mongoDB query

I have documents like this:

{
    "_id" : ObjectId("53340d07d6429d27e1284c77"),
    "worktypes" : [ 
        {
            "name" : "Pompas",
            "works" : [ 
                {
                    "name" : "work 1",
                    "code" : "0001"
                }
            ]
        },
        {
            "name" : "Pompas "",
            "works" : [ 
                {
                    "name" : "work 2",
                    "code" : "0002"
                }
            ]
        }
    ]
}

I did a query for get ONLY the works of one of worktype for this document, this is the query:

db.categories.find({$and: [
        { "_id": ObjectId('53340d07d6429d27e1284c77')},
        {"worktypes.name": "Pompas"}
    ]},{"worktypes.works.$":1})

But i got

    {
    "_id" : ObjectId("53340d07d6429d27e1284c77"),
    "worktypes" : [ 
        {
            "name" : "Pompas",
            "works" : [ 
                {
                    "name" : "work 1",
                    "code" : "0001"
                }
            ]
        }
    ]
}

But i only need:

"works" : [ 
                    {
                        "name" : "work 1",
                        "code" : "0001"
                    }
                ]

How can i reduce this?

Upvotes: 1

Views: 487

Answers (3)

Anand Jayabalan
Anand Jayabalan

Reputation: 12934

I think Neil Lunn's answer is mostly correct, but in my opinion it needs a few tweaks to get the expected result:

  1. Match against "worktypes.name" rather than "worktypes.works.name"
  2. In the $group phase, use $first instead of $push to get the first element alone
  3. Add a $project phase to just get the "works"
db.categories.aggregate([
    { "$unwind": "$worktypes" },
    { "$unwind": "$worktypes.works" },
    { "$match": {
        "worktypes.name": "Pompas"
    }},
    { "$group": {
        "_id": "$_id",
        "works": { "$first": "$worktypes.works" }
    }},
    { "$project": {"_id":0, "works":1} }
])

Output:

{
        "result" : [
                {
                        "works" : {
                                "name" : "work 1",
                                "code" : "0001"
                        }
                }
        ],
        "ok" : 1
}

Upvotes: 13

Neil Lunn
Neil Lunn

Reputation: 151162

You need to use the $unwind operator when working with arrays:

db.catefories.aggregate([

    // Unwind the first array
    { "$unwind": "$worktypes" },

    // Then unwind the embedded array
    { "$unwind": "$worktypes.works" },

    // Match the item you want
    { "$match": {
        "worktypes.works.name": "work 1"
    }},

    // Group to reform the array structure
    { "$group": {
        "_id": "$_id",
        "worktypes": { "$push": "$worktypes" }
    }}
])

And to get back as an array use $group after the $unwind.

Upvotes: 2

JonM
JonM

Reputation: 1374

You can selectively remove fields from the projection like so:

db.categories.find({$and: [
    { "_id": ObjectId('53340d07d6429d27e1284c77')},
    {"worktypes.name": "Pompas"}
]},{"worktypes.works.$":1, _id:0 })

this will prevent the _id field from being projected.

Upvotes: 0

Related Questions