Indraraj26
Indraraj26

Reputation: 1966

return match item only from array of object mongoose

[
  {
    item: "journal",
    instock: [
      {
        warehouse: "A",
        qty: 5,
        items: null
      },
      {
        warehouse: "C",
        qty: 15,
        items: [
          {
            name: "alexa",
            age: 26
          },
          {
            name: "Shawn",
            age: 26
          }
        ]
      }
    ]
  }
]
db.collection.find({
  "instock.items": {
    $elemMatch: {
      name: "alexa"
    }
  }
})

This returns whole items array where as i just want items array with one item {name: 'alexa', age: 26}

Playground Link : https://mongoplayground.net/p/0gB4hNswA6U

Upvotes: 1

Views: 1469

Answers (3)

ambianBeing
ambianBeing

Reputation: 3529

An alternative approach where $filter is the key at the project stage.

db.collection.aggregate([
  {
    $match: {
      "instock.items.name": "alexa"
    }
  },
  {
    $unwind: "$instock"
  },
  {
    $project: {
      "item": "$item",
      qty: "$instock.qty",
      warehouse: "$instock.warehouse",
      items: {
        $filter: {
          input: "$instock.items",
          as: "item",
          cond: {
            $eq: [
              "$$item.name",
              "alexa"
            ]
          }
        }
      }
    }
  },
  {
    $group: {
      _id: "$_id",
      instock: {
        $push: "$$ROOT"
      }
    }
  }
])

The idea is to:

  • have $match as top level filter
  • then $unwind instock array items to prepare for the $filter
  • Use $project for rest of the fields as they are, and use $filter on items array field
  • Finally $group them back since $unwind was used previously.

Play link

Upvotes: 1

Dori Lahav Waisberg
Dori Lahav Waisberg

Reputation: 970

You can use the .aggregate(pipeline) function.

Your code will look like:

db.collection.aggregate([{
    $unwind: {
        path: "$instock"
    }
}, {
    $unwind: {
        path: "$instock.items"
    }
}, {
    $replaceRoot: {
        newRoot: "$instock.items"
    }
}, {
    $match: {
        name: "alexa"
    }
}])

Commands used in this pipeline:

$unwind - deconstructs an array of items into multiple documents which all contain the original fields of the original documents except for the unwinded field which now have a value of all the deconstructed objects in the array.

$replaceRoot - takes the inner object referenced on newRoot and puts it as the document.

$match - a way to filter the list of documents you ended up with by some condition. Basically the first argument in the .find() function.

For more information about aggregation visit MongoDB's website:

EDIT

The wanted result was to get single item arrays as a response, to achieve that you can simply remove the $replaceRoot stage in the pipeline.

Final pipeline:

db.collection.aggregate([{
    $unwind: {
        path: "$instock"
    }
}, {
    $unwind: {
        path: "$instock.items"
    }
}, {
    $match: {
        "instock.items.name": "alexa"
    }
}])

Upvotes: 3

MahanTp
MahanTp

Reputation: 744

You have to use $elemMatch in projection section:

db.collection.find({
  // whatever your query is
}, {
  "instock.items": {
    $elemMatch: {
      name: "alexa"
    }
  }
});

UPDATE

Here is a good example of this usage. From the official mongodb documentation.

Upvotes: 1

Related Questions