object is not update within map function and await too - node.js

Actually am getting details from cart table it display an array of objects , that object contains product id and quantity , after that i want to include price and name for my local display so i want to update the object for my reference, but name and price contains another table . here my code
exports.cartList = async (req, res) => {
const CartId = req.profile.CartId;
await Cart.findById(CartId).then(data =>
{
const products = data.products;
const exprd = products;
const length = products.length;
exprd.map((item, index) =>
{
const prodID = item.productId;
const quantity = item.quantity;
Product.findById(prodID).then(prdDet => {
const price = prdDet.price;
const inven = prdDet.inventory;
const name = prdDet.name;
console.log(name);
console.log('CHECKERSP');
if(quantity < inven)
{
({ ...exprd, stock: 'stock', })
}
else
{
({ ...exprd, stock: 'Out of Stock', })
}
({ ...exprd, name: name, price: price })
});
console.log('ex2',exprd);
})
console.log('ex1',exprd);
res.json(exprd)
});
}```
but object is not updated , i don't know what i did wrong because am very new to these things . Help me to overcome this problem, Thanks in advance.

First I think you have missed await before "Product.findById(prodID)"
Further,
In if and else block you have to assign the value after declaration.
Example:
let obj ={name:'XYZ'};
Either it should be:
let obj1 = {...obj, name:'ABC'};
Or
obj = {...obj, name:'ABC'};
Furthermore, If you are doing await inside a loop,
traditional loops are better. Map, filter and all can be tricky and await might not act as desired.
Kindly let me know if there is anything missing or wrong...
Just working things according to the funny experiences I acquired.
Hope this helps.

Related

Mongoose Trouble Adding Subdocs to Array

I am looking to push to an array on my users schema in mongoose. I am following mongoose's Adding Subdocs to Arrays documentation but keep getting an error on user.notes.push(newNotebook)
TypeError: Cannot read properties of undefined (reading 'push')
Here is my setup:
const User = require('../models/userModel')
const addNotebook = async (req, res) => {
...
const user = User.findOne({ userId })
const newNotebook = { userId: userId, notebookId: notebookId, title: title, numNotes: 0 }
user.notebooks.push(newNotebook)
}
Any help would be much appreciated.
Update--
Here is the solution:
const User = require('../models/userModel')
const addNotebook = async (req, res) => {
...
const user = User.findOne({ userId })
if (!user) return
const newNotebook = { userId: userId, notebookId: notebookId, title: title, numNotes: 0 }
user.notebooks = user.notebooks || []; // returns user.notebooks if defined else empty array
user.notebooks.push(newNotebook)
await user.save()
}
Big thanks to Mandeep for showing me the !user condition and .save() method. Special thanks to Sardar for bringing the user.notebooks = user.notebooks || [] solution to light.
Disclaimer: upon watching this video on MongoDB anti patterns, I learned nesting boundless data under a single document can be problematic since each MongoDB document is limited to 16mb. It is also supposedly inefficient relative to saving your data in respective collections. Use caution when adding subdocs to arrays.
It would be like this:
const addNotebook = async(req, res) => {
const user = await User.find({
userId
}).limit(1).exec()
if (!user) return
const newNotebook = {
userId: userId,
notebookId: notebookId,
title: title,
numNotes: 0
}
user.notebooks.push(newNotebook)
await user.save()
}
This is probably caused because "notebooks" is undefined by default for your userModel/schema
You can resolve this by two methods
const User = require('../models/userModel')
const addNotebook = async(req, res) => {
...
const user = User.find({
userId
})
const newNotebook = {
userId: userId,
notebookId: notebookId,
title: title,
numNotes: 0
}
user.notebooks = user.notebooks || []; // returns user.notebooks if defined else empty array
user.notebooks.push(newNotebook)
}
RECOMMENDED
Or you can set default value for notebooks in your userModel like this (might not update your existing user documents)
How to set default value in schema mongoose?

Mongoose: After finding document, iterate over a value in the document and run a new query on each

I have one schema which contains an array of references to another schema (among other fields):
const RecipeIngredient = new Schema({
ingredientId: { // store id ref so I can populate later
type: Schema.Types.ObjectId,
ref: 'ingredients',
required: true
},
// there are a couple other fields but not relevant here
});
const Recipe = new Schema({
ingredients: [RecipeIngredient]
});
I'm trying to write a route which will first find a recipe by _id, populate the ingredients array (already have this working), and finally iterate over each ingredient in that array.
router.get('/:recipeId/testing', async (req, res) => {
const { recipeId } = req.params
let recipe = await Recipe
.findById(recipeId)
.populate({
path: 'ingredients.ingredientId',
model: 'Ingredient',
select: '_id ......' //I'm selecting other fields too
})
.lean()
.exec();
if (recipe) {
const { ingredients } = recipe;
const newIngredients = [];
await ingredients.forEach(async (ingr) => {
// here I'd like to be able to run a new query
// and append the result to an array outside of the forEach
// I do need information about the ingr in order to run the new query
newIngredients.push(resultOfNewQuery);
});
return res.json(newIngredients)
};
return res.status(404).json({ noRecipeFound: 'No recipe found.'});
})
I've tried approaching this in a few different ways, and the closest I've gotten was executing the new query within each iteration, but because the query is async, I return the response before I've actually collected the documents from the inner query.
I also attempted to use .cursor() in the initial query, but that won't work for me because I do need to access the ingredients field on the recipe once it is resolved before I can iterate and run the new queries.
Any ideas would be appreciated! I'm definitely opening to restructuring this whole route if my approach is not ideal.
I was able to make this work by using a for loop:
const newIngredients = [];
for (let idx = 0; idx < ingredients.length; idx++) {
const { fieldsImInterestedIn } = ingredients[idx];
const matchingIngredients = await Ingredient
.find(fieldsImInterestedIn)
.lean()
.exec()
.catch(err => res.status(404).json({ noIngredientsFound: 'No ingredients found' }));
newIngredients.push(ingredientsToChooseFrom[randomIndex]);
};
return res.json(newIngredients);
still a little perplexed as to why this was able to work while forEach wasn't, but I'll happily move on...

update products with the same id

I got products with the option to choose size,
So If I add 'X' product to the cart with sizes 4 and 5 it will add 2 items to the cart like this :
My goal is when you succeed buying that items from your cart the sizes that you just bought will be removed from main product page.
It works good only if I trying to buy 2 different items.
If I will try to buy 2 same items with different size only the first size will be filtered and I will get this error :
(node:21336) UnhandledPromiseRejectionWarning: VersionError: No matching document found for id "62bee4ce92e7c57a195686ae" version 0 modifiedPaths "size"
this is the success buy code :
createOrder: async (_, {}, context) => {
const userAuth = await auth(context);
const cart = await Cart.findOne({ userId: userAuth._id });
cart.cartProducts.forEach(async (p) => { // Here's the filtering product functionallity
const products = await Product.findById(p.productId);
if (products) {
products.size = products.size.filter((s) => s !== +p.size);
}
await products.save();
});
if (!cart) {
throw new UserInputError('No available order!');
}
const newOrder = new Order({
orderProducts: cart.cartProducts,
purchasedBy: userAuth._id,
datePurchased: new Date().toISOString(),
});
await newOrder.save();
return newOrder;
},
as I said it works only if you add 2 different items to your cart.
edited:
Now I am getting product.save is not a function
const userAuth = await auth(context);
const cart = await Cart.findOne({ userId: userAuth._id });
const products = await Product.find({
_id: cart.cartProducts.map((c) => c.productId),
});
cart.cartProducts.forEach(async (p) => {
if (products) {
products.map((product) => {
return (product.size = product.size.filter(
(size) => size !== +p.size
));
});
}
});
await products.save();
The issue is that you are using findById which will return the first matching document. So that is why when you add same product with different size in cart. It will always pick the first matching id of the product. You can try using find operator as it returns the list matching the condition.
Product.find({_id:p.productId});
It will return all of the products instead of matching only the first one.

Sequelize - How to Insert data, and then update

I'm using Sequalize ORM for node.js/maria db project.
What I'm trying to do is, generate new table for product data using raw query.
The sequence of my logic is written below.
Step 1. Destroy table to reset data.
Step 2. Insert product data.
Step 3. Update price data in product data.
Step 4. Update stock data in product data.
The problem is step 3, and 4. It is not working!
What I found is... 'Insert' took some time to finish. So, 'Update' could not fulfilled, because there's no product data yet.
Is there any idea to invoke step 3~4, soon after step 2 is finished?
Thanks in advance.
const generateProductList = () => {
return new Promise( async (resolve, reject) => {
try {
await ProductPresentation.destroy({ truncate: true })
const productSql = `INSERT INTO m_product_presentation (productId, sku, name, status, urlKey, category, shortDescription, imageSmall, imageThumbnail)
SELECT id, sku, name, status, urlKey, category, shortDescription, imageSmall, imageThumbnail FROM m_product;`
const priceSql = `UPDATE m_product_presentation INNER JOIN m_price
ON m_product_presentation.productId = m_price.productId
SET m_product_presentation.priceRrp = m_price.priceRrp, m_product_presentation.priceRegular = m_price.priceRegular, m_product_presentation.priceSpecial = m_price.priceSpecial;`
const stockSql = `UPDATE m_product_presentation INNER JOIN m_inventory
ON m_product_presentation.productId = m_inventory.productId
SET m_product_presentation.stockAvailability = m_inventory.stockAvailability, m_product_presentation.stockQty = m_inventory.stockQty;`
// What I want is, Create initial data first. And then, update price and stock info. But, It fail...
await ProductPresentation.sequelize.query(productSql, { type: QueryTypes.INSERT })
await ProductPresentation.sequelize.query(priceSql, { type: QueryTypes.UPDATE })
await ProductPresentation.sequelize.query(stockSql, { type: QueryTypes.UPDATE })
resolve()
} catch(err) {
reject(err)
logger.error(err)
}
})
}
What you can do is that before updating the values which is dependent on the same product, you can check whether the product is already entered or not.
const generateProductList = () => {
return new Promise(async (resolve, reject) => {
try {
await ProductPresentation.destroy({ truncate: true })
const productSql = `INSERT INTO m_product_presentation (productId, sku, name, status, urlKey, category, shortDescription, imageSmall, imageThumbnail)
SELECT id, sku, name, status, urlKey, category, shortDescription, imageSmall, imageThumbnail FROM m_product;`
const priceSql = `UPDATE m_product_presentation INNER JOIN m_price
ON m_product_presentation.productId = m_price.productId
SET m_product_presentation.priceRrp = m_price.priceRrp, m_product_presentation.priceRegular = m_price.priceRegular, m_product_presentation.priceSpecial = m_price.priceSpecial;`
const stockSql = `UPDATE m_product_presentation INNER JOIN m_inventory
ON m_product_presentation.productId = m_inventory.productId
SET m_product_presentation.stockAvailability = m_inventory.stockAvailability, m_product_presentation.stockQty = m_inventory.stockQty;`
// Inserting the product in the table
const product = await ProductPresentation.sequelize.query(productSql, { type: QueryTypes.INSERT })
// To find if the product is existing the table or not
const isProductEntered = await sequelize.query(`
select exists(select 1 from "m_product_presentation" where "productId"=$1)
`, {
bind: [`${product[0][0].productId}`],
type: QueryTypes.SELECT
});
if (isProductEntered[0].exists) {
await ProductPresentation.sequelize.query(priceSql, { type: QueryTypes.UPDATE })
await ProductPresentation.sequelize.query(stockSql, { type: QueryTypes.UPDATE })
}
resolve()
} catch (err) {
reject(err)
logger.error(err)
}
})
}
If the product will be entered only then the update queries will be executed.
I found that another source code (Some react code in frontend) cause this issue. I'll close this issue.

How to add two records in a row?

When I want to add two records in sequence, only one record is added, on the second it throws an error due to the fact that it cannot create a field with such data:
"NOTES_ID is required","key: NOTES_ID, value: undefined, is not a
number"
How to create an entry for two related tables sequentially from the beginning for the main table, and then for the one that has the foreign key installed.
module.exports.create = async function (req, res) {
const stateMatrix = await StateMatrix.select().exec()
const noteObj = {
DATE: req.body.DATE,
TITLE: req.body.TITLE,
CONTENT: req.body.CONTENT
};
const noteStateObj = {
STATE_DATE: new Date().toLocaleDateString("en-US"),
STATES_ID: stateMatrix[0]._props.STATES_ID_CURR,
NOTES_ID: req.body.NOTE_ID,
USERS_ID: req.decoded.user_id
};
try {
await Notes.create(noteObj);
await NoteStates.create(noteStateObj);
res.status(201).json(noteObj, noteStateObj);
} catch (e) {
errorHandler(res, e);
}
};
Probably NoteStates is related to Notes through note_id field which can not be empty (I guess it's foreign key). It means that you should set it before saving noteStateObj:
// Something like this
const newNote = await Notes.create(noteObj);
noteStateObj.NOTES_ID = newNote.ID;
await NoteStates.create(noteStateObj);

Resources