Retrieve only the queried element in an object array in MongoDB collection Retrieve only the queried element in an object array in MongoDB collection mongodb mongodb

Retrieve only the queried element in an object array in MongoDB collection


MongoDB 2.2's new $elemMatch projection operator provides another way to alter the returned document to contain only the first matched shapes element:

db.test.find(    {"shapes.color": "red"},     {_id: 0, shapes: {$elemMatch: {color: "red"}}});

Returns:

{"shapes" : [{"shape": "circle", "color": "red"}]}

In 2.2 you can also do this using the $ projection operator, where the $ in a projection object field name represents the index of the field's first matching array element from the query. The following returns the same results as above:

db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});

MongoDB 3.2 Update

Starting with the 3.2 release, you can use the new $filter aggregation operator to filter an array during projection, which has the benefit of including all matches, instead of just the first one.

db.test.aggregate([    // Get just the docs that contain a shapes element where color is 'red'    {$match: {'shapes.color': 'red'}},    {$project: {        shapes: {$filter: {            input: '$shapes',            as: 'shape',            cond: {$eq: ['$$shape.color', 'red']}        }},        _id: 0    }}])

Results:

[     {        "shapes" : [             {                "shape" : "circle",                "color" : "red"            }        ]    }]


The new Aggregation Framework in MongoDB 2.2+ provides an alternative to Map/Reduce. The $unwind operator can be used to separate your shapes array into a stream of documents that can be matched:

db.test.aggregate(  // Start with a $match pipeline which can take advantage of an index and limit documents processed  { $match : {     "shapes.color": "red"  }},  { $unwind : "$shapes" },  { $match : {     "shapes.color": "red"  }})

Results in:

{    "result" : [        {            "_id" : ObjectId("504425059b7c9fa7ec92beec"),            "shapes" : {                "shape" : "circle",                "color" : "red"            }        }    ],    "ok" : 1}


Caution: This answer provides a solution that was relevant at that time, before the new features of MongoDB 2.2 and up were introduced. See the other answers if you are using a more recent version of MongoDB.

The field selector parameter is limited to complete properties. It cannot be used to select part of an array, only the entire array. I tried using the $ positional operator, but that didn't work.

The easiest way is to just filter the shapes in the client.

If you really need the correct output directly from MongoDB, you can use a map-reduce to filter the shapes.

function map() {  filteredShapes = [];  this.shapes.forEach(function (s) {    if (s.color === "red") {      filteredShapes.push(s);    }  });  emit(this._id, { shapes: filteredShapes });}function reduce(key, values) {  return values[0];}res = db.test.mapReduce(map, reduce, { query: { "shapes.color": "red" } })db[res.result].find()