0

I'm looking to query the item data from my mongoose data collection. I only need the "stocked" boolean value for the user "456" item "4", but when I query in my example, I instead receive the entire user object.

Data:

data = [{
    userId: "123",
    items: [{
        item: "2",
        stocked: false
      },
      {
        item: "3",
        stocked: true
      },
      {
        ...more items
      }
    ],
  },

  {
    userId: "456",
    items: [{
        item: "1",
        stocked: true
      },
      {
        item: "4",
        stocked: true
      },
      {
        ...more items
      }
    ],
  },

  {
    ...more users
  }
]

Route:

router.post("/example", (req, res) => {

  Data.findOne({
      userId: "456",
      "items.item": "4"
    }
  }).then((item) => {
  console.log(item) // desired: {item: "4", stocked: true}

  if (item.stocked) {
    console.log("Item is stocked!")
  }
})

})

Problem: The response is the entire user object including all items:

{
  userId: "456",
  items: [{
      item: "1",
      stocked: true
    },
    {
      item: "4",
      stocked: true
    },
    {
      ...more items
    }
  ],
},

Desired response: { item: "4", stocked: true }

Any tips or help would be appreciated!

twominds
  • 1,084
  • 5
  • 20
  • I understand I can use dot notation to access the stocked value within, but I'm looking to do it completely with the mongoose query. Thanks. – twominds Jan 05 '21 at 22:45

1 Answers1

1

Using the aggregation pipeline you can make use of $elemMatch or $unwind.

The $elemMatch operator limits the contents of an field from the query results to contain only the first element matching the $elemMatch condition.

Data.aggregate([
 {$match: {userId: "456", "items.item": "4"}},
 {$project: { items: { $elemMatch: { "items.item": "4"} }}}
]);

OR

The $unwind will return all element that meet your criteria.

Data.aggregate([
 {$match: {userId: "456", "items.item": "4"}},
 {$unwind: "$items"},
 {$match: { "items.item": "4"} }
])
Haniel Baez
  • 1,646
  • 14
  • 19
  • Hey, thank you @Haniel Baez so much! The first option didn't work so I researched it and found that you may not be able to use $elemMatch in aggregate functions: https://stackoverflow.com/a/36651117/14849955 .That aside, the second one did work for my needs. The interesting part is that it still keeps the userId: "456" in the object when it is returned. Here's my response: { userId: "456", item: "4", stocked: true }, works great, but I'm still looking to only get the response { item: "4", stocked: true }. – twominds Jan 05 '21 at 23:09
  • Interestingly enough, Data.aggregate([ {$match: {userId: "456", "items.item": "4"}}]) works just as well as the second approach – twominds Jan 05 '21 at 23:23
  • You can add a $projection state, to eliminate the userId from the result; something like this --> { $project: { userId: 0, item: 1, stocked: 1 } } – Haniel Baez Jan 06 '21 at 15:48