0

So I have this data stored in a mongodb database:

{
    _id: 'string',
    email: 'string',
    purchases: [
        { name: 'item 1', cost: 45, quantity: 3 },
        { name: 'item 2', cost: 62, quantity: 2 },
        { name: 'item 3', cost: 14, quantity: 7 },
    ]
}

What I am trying to do is search for the user id (._id) and the item in the purchases array by the item name, so for item 1:

{ _id: req.body._id, purchases: { $elemMatch: { name: 'Item 1' }}}

This query works fine. However the behavior I need is if the item exists, modify the quantity value to reset to one. If the item doesn't exist, i.e. its a new item, I want to insert a new item object.

I thought that .findOneAndReplace would work for this, so my code is:

    User.findOneAndReplace(
        { _id: req.body._id, purchases: { $elemMatch: { name: 'Item 1' }}},
        { $push: { purchases: { name: req.body.name, cost: req.body.cost, quantity: 1 }}},
        {
            upsert: true
        }
    ).then((result) => {
        res.status(200).json({
            message: 'Added score :D'
        })
    }).catch((error) => {
        res.status(400).json({
            message: 'Failed to add score...' + error
        })
    })

but this obviously doesn't work. I can insert a new value into the purchases array but I cant update a current result if it exists.

1 Answer 1

1

What you are doing is actually not a good way to handle documents.

A better way to handle your problem is to create a different model for purchases.

const purchases = mongoose.Schema({
      name: String,
      cost: Number,
      quantity: Number,
      user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
})

Instead of adding purchases to the User model, you will create a new entry under purchases.

To update purchases, you can just update each purchase document directly.

If you want to retrieve all purchases for a user, you can simply

 Purchases.find({ user: user._id })

To see if item exist.

 Purchases.find({ user: user._id, name: 'Item 1'});

You can also find the purchases through the user model using $lookup.

 Users.aggregate([
       {
          $match: { _id: user._id }
       },
       {
          $lookup: {
              from: 'purchases',
              localField: "_id",
              foreignField: "user",
              as: "purchases"
          }
       },
       {
          $project: {
               ....fields you want to return ....
          }
       }
 ])

In the long run you will find this model of database more efficient and easier to up keep, and there's more pros than cons separating the documents into different collections.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks - I guess I was trying to avoid creating multiple reads where possible but this is by far a nicer solution. Much appreciated!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.