How to use variable in promise? NodeJS - node.js

I'm writing a shop-ecomerce website. I want to add product to my cart, so there are two circumstances
1. The product with that id is in the cart => add more quantity (SOLVED)
2. The product is not exist => create new one
So how to check if a product (with specific id) exist in cart?
I use another variable (let exist) to check but it seems doesn't work (due to promise, I think)
// Add product to cart
router.post('/add', checkToken, (req, res) => {
let _idProduct = req.body._idProduct;
let quantity = req.body.quantity;
let exist = false;
Cart
.findOne({ user: req.decoded.userId })
.exec()
.then(cart => {
cart.items.map(item => {
// Product exist => add more quantity
if (item.product == _idProduct) {
item.quantity += quantity;
}
})
// How to check if no product with that id in Cart ??
cart.save(err => console.log(err));
res.json({
cart: cart
})
})
.catch(err => { console.log(err)});
})
Cart model
var Cart = new mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
items: [
{
product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product' },
quantity: { type: Number }
}
],
})

You can use a boolean/flag here to see if the item is found.
cart.items.map(item => {
let found = false; // boolean to see if we found the item
// Product exist => add more quantity
if (item.product == _idProduct) {
item.quantity += quantity;
found = true; // if we find an existing item, set the flag to true
}
if (!found){ // if the item isn't found, we can add it to the cart.
// add item to cart
}
})

This structure might make a bit more sense - instead of using map, you can filter your cart.items array to find the matching product. If it's there, increment the quantity. If not, add it to the array or whatever it is that you need to do.
.then(cart => {
const existingItem = cart.items.filter(item => item.product == _idProduct)[0]
if(existingItem) existingItem.quantity += quantity
else {
//item does not exist, do what you need to do
}
cart.save(err => console.log(err));
res.json({
cart: cart
})
})

Related

Cart total returns multiple numbers node js express

Cart total is returning multiple numbers
My cart
It seems to me that the problem is when multiplying the quantity by the value of the product
Cart.js Route
router.post("/cart", Auth, async (req, res) => {
const owner = req.user._id;
const { itemId, quantity } = req.body;
try {
const cart = await Cart.findOne({ owner });
const item = await Item.findOne({ _id: itemId });
if (!item) {
res.status(404).send({ message: "item not found" });
return;
}
const price = item.price;
const name = item.name;
//If cart already exists for user,
if (cart) {
const itemIndex = cart.items.findIndex((item) => item.itemId == itemId);
//check if product exists or not
if (itemIndex > -1) {
let product = cart.items[itemIndex];
product.quantity += quantity;
cart.bill = cart.items.reduce((acc, curr)=>{
let cur = curr.cost.match(/\d./g).join('')
return acc + Number(cur);
}, 0)
cart.items[itemIndex] = product;
await cart.save();
res.status(200).send(cart);
} else {
cart.items.push({ itemId, name, quantity, price });
await cart.save();
res.status(200).send(cart);
}
} else {
//no cart exists, create one
const newCart = await Cart.create({
owner,
items: [{ itemId, name, quantity, price }],
bill: parseInt(quantity * price),
});
return res.status(201).send(newCart);
}}});
Model Cart
Cart.js Model
Can anyone help me with this problem?
Because of how javascript handles floating point numbers, weird things like this can happen (instead of 109.99 * 3 to equal 329.97, it equals 329.96999999999997).
One solution for this, if you know how many decimals you want, is to call Number.prototype.toFixed method (for example cart.bill = cart.bill.toFixed(2)

Delay in return value - nodejs & mongoose

I'm fairly new to nodejs and I'm doing a full stack developer challenge from devchallenges.io (Shoppingify). Below, I'm trying to increase the quantity value based on whether the user clicks to increase or decrease the item quantity. However, there's a slight delay between the return value from the request and the actual value in the database. It seems that the value updates immediately which is great however, the return value in the request is the previous value rather than being the current quantity value in the database.
mongoDB Database
// #route PUT api/list/item/quantity/:id
// #desc update item quantity
// #access Private
router.put('/item/quantity/:id', auth, async (req, res) => {
const { action } = req.body;
try {
let list = await List.findOne({ user: req.user.id });
// find current quantity
const item = list.items.find((item) => {
return item._id.toString() === req.params.id;
});
// increase quantity
if (action === 'increase') {
list = await List.findOneAndUpdate(
{ 'items._id': req.params.id },
{ $set: { 'items.$.quantity': item.quantity + 1 } },
{ new: true }
);
} else {
// decrease quantity
list = await List.findOneAndUpdate(
{ 'items._id': req.params.id },
{ $set: { 'items.$.quantity': item.quantity - 1 } },
{ new: true }
);
}
res.json(item.quantity);
} catch (error) {
console.error(error.message);
res.status(500).send('Server Error');
}
});
You are defining item in here:
const item = list.items.find((item) => {
return item._id.toString() === req.params.id;
});
At this point list is the "old" version of the object, you want to be doing the same after the update when the list object is updated and only then to return it.
// this is the original "list" item
let item = list.items.find((item) => {
return item._id.toString() === req.params.id;
});
...
update list
...
// now "list" is updated
item = list.items.find((item) => {
return item._id.toString() === req.params.id;
});
I will just add two additional tips to improve performance, they are mutually exclusive so you'll have to choose one of the two.
in the update query add the list._id to it, If I were to guess the collection does not have an index on the items field ( and if it does it's a bad idea usually ). this means when you updated just using the item._id field it takes longer for mongo to find the object. it's quick change to both updates:
list = await List.findOneAndUpdate(
{ _id: list._id, 'items._id': req.params.id },
{ $set: { 'items.$.quantity': item.quantity - 1 } },
{ new: true }
);
(my preferred option) do it in a single call, using the update arrayFilters option, like so:
const list = await List.findOneAndUpdate(
{
user: req.user.id,
},
{
$inc: {
'items.$[elem].quantity': action === 'increase' ? 1 : -1,
},
},
{
arrayFilters: [
{
'elem._id': new ObjectId(req.params.id),
},
],
new: true,
});
const item = list.items.find((item) => {
return item._id.toString() === req.params.id;
});
Mongo Playground
In my opinion now your route looks much better, you're also cutting down from 2 db calls to 1.

Loop through array and add data

I have an array called "content". In this array is the "asin-id" (It's a kind of product ID). I'm trying to loop through the content array and get from the asin-id the specific product so I can add the product to the array and save the new content-array in the db.
Something is wrong. I don't know how to do it.
Expected result:
content: [{ asin: "asin-ID", product: product }, ...]
What I tried:
exports.createBlogPost = async (req, res) => {
try {
const content = req.body.content
content.map(async (element) => {
const asin = element.asin
const product = await Product.findOne({ asin: asin })
element.product = product
return element
})
console.log(content)
const post = new BlogPost({
postTitle: req.body.postTitle,
postQuery: req.body.postQuery,
content: content,
mainImage: req.file
})
console.log("Saved: " + post)
await post.save()
if(post) {
return res.status(200).json({
success: true,
message: 'Saved blog post successfully',
post: post
})
}
} catch(err) {
console.log(err)
}
}
I think the problem may be simply that you're using map without assigning the result to a variable. Try replacing your code with something similar to the following:
let updatedContent = content.map(async (element) => {
const asin = element.asin
const product = await Product.findOne({ asin: asin })
element.product = product
return element
})
console.log(updatedContent)

How to get data from a mongoose aggregate query in nodejs

The mongoose query is fetching the correct results but i am unable to access the data.
I want to get the current max id in the database, increment the value by 1 and save the data to the database
// add a new book
router.route('/books/add').post(jsonParser, (req, res) => {
Book.aggregate([{ $group: { _id: "id", id: { $max: "$id" } } }], (err, books) => {
if (!books)
return next(new Error("Could not load book!"))
else {
let book = new Book(req.body);
// log the max id returned, currently returns undefined
console.log("Id found ", books.id);
book.id = books.id + 1;
console.log("BookId ", book.id);
res.status(200).json({ 'book': 'Added successfully!' });
// book.save().then(book => {
// res.status(200).json({ 'book': 'Added successfully!' });
// }).catch(err => {
// res.status(400).send('Failed to create new record.');
// });
}
});
});
console.log(books.id) returns undefined whereas console.log(books) does show me the result which is [ { _id: 'id', id: 5 } ]. What i want to do is get the id i.e. 5, how do i do that ? Thank you.
The aggregate query result is an array,, and you have to use the zero index to get the first element which is an object contains your result. Try this:
console.log(books[0].id)

node mongoose updating an Object in Array doesn't work

I am trying to update an Array of objects wo any success..
the update statement does not work ..
I may have to update the table instance array and update it with the new valueand then save the table...
const picked = table.meta.permissions.find(obj => obj._id == req.params.permissionId);
console.log('picked: %j', picked); // need to update it !
// how can I update this permission object with the new value from req.body ?
table.save()
.then(savedTable => res.json(savedTable))
.catch(e => next(e););
I have an Array of permissions in a 'meta' field:
MODEL
const Schema = mongoose.Schema;
const Permission = new Schema({
permission: {
role_id: { type: String },
canWrite: { type: Boolean }
}
});
const TableSchema = new mongoose.Schema({
meta: {
name: { type: String, required: true },
permissions: [Permission],
...
},
...
);
In the controller , I firstly load the requested table and append it to the req object, then I execute the updatePermission function, and try to update the table instance permission with new values using $set
CONTROLLER
import Table from '../../models/table.model';
/**
* Load table and append to req.
*/
function load(req, res, next, id) {
Table.get(id)
.then((table) => {
req.table = table;
return next();
})
.catch(e => next(e));
}
function updatePermission(req, res, next) {
const table = req.table;
console.log('Current table.meta.permissions: %j', table.meta.permissions, '\n');
console.log('update permission: ', req.params.permissionId, ' with: ', req.body, '\n');
const query = { 'meta.permissions._id': req.params.permissionId }
const update = { $set: { 'meta.permissions.$.permission': req.body } };
const options = { new: true};
table.update(query, update, options)
.then(savedTable => res.json(savedTable))
.catch((e) => { next(e); });
}
The console log displays the current table permissions and the req.params and req.body
Why the update statement doesn't run correctly
thanks for feedback
I found a way to solve this issue but I don't know if it's the best one ?
I update the table object using some() then I save the table..
function updatePermission(req, res, next) {
const table = req.table;
table.meta.permissions.some((obj, idx) => {
if (obj._id == req.params.permissionId) {
table.meta.permissions[idx].permission = req.body;
return true;
}
return false;
});
table.save()
.then(savedTable => res.json(savedTable))
.catch(e => next(e); );
}

Resources