简体   繁体   中英

mongoose updateOne with upsert is not working

let cart = req.body.params.cart // array of objects that needs to be updated if exists in db, if not upsert it. 

let userid = req.body.params.uid

for (let i = 0; i < cart.length; i++) {
    Cart.updateOne({ user: userid, 'cart.product': cart[i].product._id },
        {
            $set: {
                'cart.$.quantity': cart[i].quantity
            }
        },
        { upsert: true }// works without this line of code, updates the objects if exists
    )
}

my cart model:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const CartSchema = new Schema({
    user: {
        type: Schema.Types.ObjectId,
        ref: 'users'
    },
    cart: [{
        product: {
            type: Schema.Types.ObjectId,
            ref: 'productlist'
        },
        quantity: {
            type: Number
        },
        date: {
            type: Date,
            default: Date.now
        }
    }]

})

module.exports = Cart = mongoose.model('cart', CartSchema)

I'm trying to update a users shopping cart with new items that are in the cart array. I need to check if product exists, if yes update quantity, if not push it in to user's cart. Somehow it doesnt work with $upsert. without $upsert document gets updated if object id exists in the user's cart.

Working version with tons of clutter. I feel like there is a better way of doing it like the one I was trying to do above. I would appriceate any help, reducing clutter here.

Cart.find({ user: req.body.params.uid })
        .then(userCart => {
            if (userCart[0].cart.length === 0) {
                for (let i = 0; i < cart.length; i++) {
                    Cart.updateOne({ user: req.body.params.uid }, {
                        $push: {
                            cart: {
                                product: cart[i].product._id,
                                quantity: cart[i].quantity
                            }
                        }
                    }).catch(err => console.log(err))
                }
            }
            else {

                cart.reduce((acc, element) => {
                    let index = userCart[0].cart.findIndex(val => val.product._id == element.product._id)
                    if (index !== -1) {

                        Cart.findOneAndUpdate({ user: req.body.params.uid, 'cart.product': element.product._id },
                            {
                                $set: {
                                    'cart.$.quantity': element.quantity
                                }
                            },
                            { new: true }
                        ).then(a => console.log(a))
                    }
                    else {
                        Cart.updateOne({ user: req.body.params.uid }, {
                            $push: {
                                cart: {
                                    product: element.product._id,
                                    quantity: element.quantity
                                }
                            }
                        }).catch(err => console.log(err))
                    }
                    acc.push(element)
                    return acc
                }, [])
            }
        })

Sample value from array cart

   product: {
     _id: '5eaf8eeac436dbc9b7d75f35',
     name: 'Strawberry',
     category: 'organic',
     image: '/productImages/australian.jpg',
     price: '9.65'
   },
   quantity: 6

sample cart in db:

   _id: 5ec12ea36ccf646ff5aeef0c,
   user: 5ec11a8f69ccf46e0e19c5ef,
   cart: [
     {
       date: 2020-05-18T10:26:38.751Z,
       _id: 5ec262de5829f081b1ea96d7,
       product: 5eaf8eeac436dbc9b7d75f35,
       quantity: 8
     },
     {
       date: 2020-05-18T12:11:57.168Z,
       _id: 5ec27b8dd2949886308bebd6,
       product: 5eaf8f61c436dbc9b7d75f36,
       quantity: 6
     }
   ]

I think the best solution is to match the old cart and new cart first in the node.js code then update it, for that below are the steps and code.

  1. Create an array of only productid and their quantity from the new cart
  2. Then fetch the old cart details from database
  3. Match both the cart and create two different array one which will only contain the products which are new in the cart, and one other which will contain the productid already there in the old cart

Now you have two separate arrays which will be easy to update in 2 queries only, instead of the loop or multiple queries you are using in your code. I also tried to explain it in code comments.

// First get all the new cart productid and quantity in an array
var cartArray = cart.map(obj => {
    return {product: obj.product._id, quantity: obj.quantity}
})

Cart.findOne({ user: req.body.params.uid })
   .then(userCart => {

var productIds = userCart.cart.map(obj => obj.product)

// array of products id and quantity which are already there in old cart
var productsOld = cartArray.filter(obj => {
  return productIds.includes(obj.product)
})

// array of products id and quantity which are not there in old cart
var productsNew = cartArray.filter(obj => {
  return !productIds.includes(obj.product)
})

//First, we will add the all new products which are in the new cart
Cart.findOneAndUpdate({ user: req.body.params.uid }, {
  $push: {
    cart: {
      $each: productsNew
    }
  }
}, {
  new: true
}).then(function(updatedUserCart){
  // Here we are getting now the updated cart with new products added
  // Now we need to update quantity of old products

  // Instead of running query in loop, we will update the quantity in array map
  // and replace the whole cart in the document
  var newCart = updatedUserCart.cart.map(obj => {
    var matchProduct = productsOld.filter(oldObj => {
      return oldObj.product == obj.product
    })
    if (matchProduct.length > 0) {
      obj.quantity = parseInt(obj.quantity) + parseInt(matchProduct[0].quantity)
      return obj
    } else {
      return obj
    }
  })

  Cart.updateOne({ user: req.body.params.uid }, {
      $set: {
          cart: newCart
      }
  })
}).catch(err => console.log(err))
})

Disclaimer: I haven't tried to run the code on my system, so there may be a typo or syntax error, but I hope you understand the flow and logic

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM