Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams
Closed 5 years ago .

The community reviewed whether to reopen this question 6 months ago and left it closed:

Original close reason(s) were not resolved

The result return one element in array

{ "_id" : ObjectId("512e28984815cbfcb21646a7"), "list" : [ { "a" : 4 } ] }

and I try to use aggregate with $match but not work

db.test.aggregate({$match:{_id:ObjectId("512e28984815cbfcb21646a7"), 'list.a':{$gte:5}  }})

It's return all element in array

"_id" : ObjectId("512e28984815cbfcb21646a7"), "list" : [ "a" : 1 "a" : 2 "a" : 3 "a" : 4 "a" : 5

Can I filter element in array to get result as expect result?

Using aggregate is the right approach, but you need to $unwind the list array before applying the $match so that you can filter individual elements and then use $group to put it back together:

db.test.aggregate([
    { $match: {_id: ObjectId("512e28984815cbfcb21646a7")}},
    { $unwind: '$list'},
    { $match: {'list.a': {$gt: 3}}},
    { $group: {_id: '$_id', list: {$push: '$list.a'}}}

outputs:

"result": [ "_id": ObjectId("512e28984815cbfcb21646a7"), "list": [ "ok": 1

MongoDB 3.2 Update

Starting with the 3.2 release, you can use the new $filter aggregation operator to do this more efficiently by only including the list elements you want during a $project:

db.test.aggregate([
    { $match: {_id: ObjectId("512e28984815cbfcb21646a7")}},
    { $project: {
        list: {$filter: {
            input: '$list',
            as: 'item',
            cond: {$gt: ['$$item.a', 3]}

$and: get data between 0-5:

cond: { 
    $and: [
        { $gt: [ "$$item.a", 0 ] },
        { $lt: [ "$$item.a", 5) ] }
                How would you go about it, If you wanted to publish the result of that query to the client?
– samson
                Mar 7, 2017 at 23:01
                Does this solution utilises the indexes, inside the array elements (if the array element is a document in itself) ? Suppose there is an index on some path in the array element.
– kartik
                Sep 23, 2020 at 15:00
                Use cond: { $eq: ['$$item.a', <value> ] } to filter elements based on exact equivalent values.
– Raphael
                Jan 12, 2021 at 17:55

Above solution works best if multiple matching sub documents are required. $elemMatch also comes in very use if single matching sub document is required as output

db.test.find({list: {$elemMatch: {a: 1}}}, {'list.$': 1})

Result:

"_id": ObjectId("..."), "list": [{a: 1}] This was the best solution for me. But the second argument works only with primitive types. As i had an object in the array, my solution was: ShoppingCartModel.findOne({"_id": ctx.params.idCart, "active" : true, products: {$elemMatch : {"products.active" : true}}}) (I was filtering only the active: true carts and products) – Ulises Layera Mar 8, 2018 at 14:23

Selects a subset of the array to return based on the specified condition. Returns an array with only those elements that match the condition. The returned elements are in the original order.

db.test.aggregate([
    {$match: {"list.a": {$gt:3}}}, // <-- match only the document which have a matching element
    {$project: {
        list: {$filter: {
            input: "$list",
            as: "list",
            cond: {$gt: ["$$list.a", 3]} //<-- filter sub-array based on condition