Array keep empty after push object from forEach - node.js

Need Help, I have problem Array Keep Empty After pushing object from forEach, do I miss something ? here's the code :
const allStatus = [];
result.forEach(async (element) => {
const count = await BpCandidate.count({
where: {
applicationStatusId: element.id
},
raw: true,
});
})
record = {
id: element.id,
name: element.name,
code: element.code,
description: element.description,
total: count
};
allStatus.push(record);
});
console.log(allStatus);
Thanks in advance

Using for...of is working for you, but can see an improvement here if you use Promise.all
const allStatus = await Promise.all(result.map(async (element) => {
const count = await BpCandidate.count({
where: {
applicationStatusId: element.id
},
raw: true,
});
return {
id: element.id,
name: element.name,
code: element.code,
description: element.description,
total: count
};
}))

for each doesn't perform async task and doesn't resolve promises.
Use for of or Promise.All the loop iterations.
const allStatus = [];
for(let element of result) {
const count = await BpCandidate.count({
where: {
applicationStatusId: element.id
},
raw: true,
});
})
record = {
id: element.id,
name: element.name,
code: element.code,
description: element.description,
total: count
};
allStatus.push(record);
});
console.log(allStatus);

first you need to define element variable in outer scope of foreach part to get the desired result, here is the helper code snippet:
const allStatus = [];
result.forEach(async (element) => {
const count = await BpCandidate.count({
where: {
applicationStatusId: element.id
},
raw: true,
});
record = {
id: element.id,
name: element.name,
code: element.code,
description: element.description,
total: count
};
allStatus.push(record);
})
});
console.log(allStatus);

Related

MONGODB store value in variable to use later

i have a code that inserts information from a 3rd party API every 24hours the api information is ID, Name, Status, Position, Score the only information that can get updated if there is any change are Name, Status, Position, Score, ID will always be the same.
I'm trying to show information based on if there is any change when new data is inserted to the database for example
08/14/2022 ------------- 08/15/2022
Name: yandr1 ========== NO CHANGES
position 1 ========== NO CHANGES
id 123 ========== NO CHANGES
score 100 ========== CHANGES TO: 200 ( POINTS GAINED 100 )
based on the new data inserted to MongoDB Database Score changed from 100 to 200, how can i show on the website the OLD SCORE AND POINTS GAINED? Position, Name,old score, POINTS GAINED
CODE TO INSERT DATA INTO DB FROM API
//POST DAILY PLAYERS DATA//
cron.schedule('56 23 * * *', async () => {
const postSchema = new mongoose.Schema({
id: {
type: Number,
required: true
},
name: {
type: String,
required: true
},
status: {
type: String,
required: false
},
});
const Post = mongoose.model('players', postSchema);
async function getPosts() {
const getPlayers = await fetch("http://localhost:3008/api/players");
const response = await getPlayers.json();
for (let i = 0; i < response.players.length; i++) {
const post = new Post({
id: response.players[i]['id'],
name: response.players[i]['name'],
status: response.players[i]['status'],
});
post.save();
}
}
console.log("Table submitted successfully")
await getPosts();
});
//POST DAILY HIGHSCORE DATA//
cron.schedule('55 23 * * *', async () => {
const postSchema = new mongoose.Schema({
position: {
type: Number,
required: false
},
id: {
type: Number,
required: true
},
score: {
type: Number,
required: false
},
});
const Post = mongoose.model('highscores', postSchema);
async function getPosts() {
const getHighscore = await fetch("http://localhost:3008/api/highscore/players");
const response = await getHighscore.json();
for (let i = 0; i < response.players.length; i++) {
const post = new Post({
position: response.players[i]['position'],
id: response.players[i]['id'],
score: response.players[i]['score'],
});
post.save();
}
}
console.log("Table submitted successfully")
await getPosts();
});
router.get('/export', async function(req, res, next) {
let data
try {
data = await Promise.all([
Posts.list(),
Events.list(),
Messages.list(),
Users.list()
]);
// at this point, data is an array. data[0] = Posts.list result, data[1] = Events.list result etc..
res.status(200).json(data)
} catch (e) {
res.status(500).send('error');
}
});
Hey Yandry try this code-
async function getPosts() {
const getPlayers = await fetch("http://localhost:3008/api/players");
const response = await getPlayers.json();
for( let i = 0;i < response.players.length; i++){
id= response.players[i]['id'];
name= response.players[i]['name'];
status= response.players[i]['status'];
const updateScore = await findOneAndUpdate({id:id},{$set:{name:name,status:status}})
console.log("Updated Score",updateScore)
}
}
Please, try this code, hope you will get solution, if you still facing issue just lemme know, i will help you more.
Thank

MongoDB UpdateMany Method

how can i use the update Many Method inside my code?. Right now this code will insert the data over and over inside the table, i need to make it so it will update the old data and add new data if new data is available in the third party api. I'm new to MongoDB any help will be much appreciate it! thanks.
cron.schedule('0 0 * * *', async () => {
const postSchema = new mongoose.Schema({
id: {
type: Number,
required: true
},
name: {
type: String,
required: true
},
status: {
type: String,
required: false
},
});
const Post = mongoose.model('players', postSchema);
async function getPosts() {
const getPlayers = await fetch("http://localhost:3008/api/players");
const response = await getPlayers.json();
for( let i = 0;i < response.players.length; i++){
const post = new Post({
id: response.players[i]['id'],
name: response.players[i]['name'],
status: response.players[i]['status'],
});
post.save();
}
}
console.log("Task submitted successfully")
await getPosts();
});
what i was thinking
const post = await Post.updateMany({
id: response.players[i]['id'],
name: response.players[i]['name'],
status: response.players[i]['status'],
});
I believe you can use bulkWrite and inside bulkWrite you can write updateOne operation with the upsert flag set to true
You can construct an array of operations as follows
let operations = [];
for (let player of players) {
operations.push({
updateOne: {
filter: { id: player.id },
update: { $set: { name: player.name } },
upsert: true,
}
})
}
And finally you can make a call to bulkWrite. Please read the documentation for the operations above

Mongoose and express insert many many-to-many relationships in one request

I want to make a request in which I add several meals to order and a request that removes several meals from order. It is many-to-many relationship because order can have several meals and meal can have several orders.
This is my order model:
const mongoose = require("mongoose");
const orderObject = {
cook: { type: mongoose.Types.ObjectId, ref: "CookRoleUser" },
remark: { type: String },
delivery_address: { type: String, required: true },
orderer: {
type: mongoose.Types.ObjectId,
ref: "RegularRoleUser",
required: true,
},
meals: [{ type: mongoose.Types.ObjectId, ref: "Meal" }],
active: { type: Boolean, default: true },
date_ordered: { type: Date, default: () => Date.now() },
};
const Order = mongoose.model("Order", new mongoose.Schema(orderObject));
module.exports = { Order };
And this is my meal model:
const mongoose = require("mongoose");
const mealCategories = {
MAIN: "MAIN",
DESSERT: "DESSERT",
APPETIZER: "APPETIZER",
DRINK: "DRINK",
};
const mealTypes = {
REGULAR: "REGULAR",
SPECIAL: "SPECIAL",
};
const mealObject = {
name: { type: String },
ingredients: [
{ name: { type: String }, allergen: { type: Boolean, default: false } },
],
is_offered: { type: Boolean, default: false },
orders: [{ type: mongoose.Types.ObjectId, ref: "Order" }],
cook: { type: mongoose.Types.ObjectId, ref: "CookRoleUser" },
category: {
type: String,
enum: Object.values(mealCategories),
default: mealCategories.MAIN,
},
type: {
type: String,
enum: Object.values(mealTypes),
default: mealTypes.REGULAR,
},
date_created: { type: Date, default: () => Date.now() },
};
const mealSchema = new mongoose.Schema(mealObject);
const Meal = mongoose.model("Meal", mealSchema);
module.exports = { Meal, mealTypes };
I've tried to do this:
For addition:
const addMealsToOrder = async (req, res, next) => {
const order = await Order.findById(req.body.orderId);
let mealCount = 0;
await req.body.mealIds?.forEach(async (mealId, index) => {
const meal = await Meal.findById(mealId);
if (order.meals.includes(mealId)) {
return next(
new HttpError(
`Meal ${meal.name} already in order ${order.remark}.`,
400
)
);
}
if (order.cook.toString() != meal.cook.toString()) {
return next(
new HttpError(
`Meal ${meal.name} cook not same as order ${order.remark} cook.`
)
);
}
meal.orders.push(order.id);
order.meals.push(meal.id);
await meal.save();
mealCount = index;
});
if (mealCount == req.meals.length - 1) {
order.save();
res.json(order);
}
};
or without checking is are meals already in order and are cooks the same:
const addMealToOrderTest = async (req, res, next) => {
await req.body.mealIds.forEach(async (mealId) => {
const order = await Order.findById(req.body.orderId);
const meal = await Meal.findById(mealId);
order.meals.push(mealId);
meal.orders.push(order.id);
await order.save();
await meal.save();
});
res.json(2);
};
And for deletion:
const removeMealsFromOrder = async (req, res, next) => {
const { orderId, mealIds } = req.body;
const order = await Order.findById(orderId);
await req.body.mealIds.forEach(async (mealId, index) => {
const meal = Meal.findById(mealId);
meal.orders = meal.orders.filter((oId) => oId != order.id);
order.meals = order.meals.filter((mId) => mId != mealId);
await meal.save()
});
await order.save()
res.json(order);
};
I've tested adding and deleting in postman. Behaviour is very weird and random. Some times it does not add meals, or adds only few, etc. I test the result by fetching single order and/or single meal.
My question:
What is the best way to implement many-to-many update where you want to add for a single entity (order) multiple entities (meals)?
Furthermore I would also want to check, in addtion request, does the certain meal already exist in that order and are the order cook and certain meal cook not the same. If some of these condition is true, I want to inform the request issuer about it and stop all adding and in deletion request does the order contain certain meal and also inform the request issuer if that is true.
EDIT 04/08/22
Now i've tried to make my controllers as such
Addition
const addMealToOrderTest = async (req, res, next) => {
const { orderId, mealIds } = req.body;
await mealIds.forEach(async (mealId) => {
const order = await Order.findById(orderId);
const meal = await Meal.findById(mealId);
order.meals.push(mealId);
meal.orders.push(order.id);
await order.save();
await meal.save();
});
res.json(2);
};
Removal
const removeMealsFromOrder = async (req, res, next) => {
const { orderId, mealIds } = req.body;
await mealIds.forEach(async (mealId) => {
const order = await Order.findById(orderId);
const meal = await Meal.findById(mealId);
order.meals.pull(mealId);
meal.orders.pull(orderId);
await order.save();
await meal.save();
});
res.json(2);
};
Behaviour is very random. Sometimes it works all fine but sometimes I get:
VersionError if order.save() is in mealIds.forEach function or
ParrallelSaveError if the fetching and saving of the order are outside mealIds.forEach function.
EDIT no:2 04/08/22
Addition
const addMealToOrderTest = async (req, res, next) => {
const { orderId, mealIds } = req.body;
const order = await Order.findById(orderId);
let addedMealCount = 0;
mealIds.forEach((mealId) => {
if (!order.meals.includes(mealId)) {
order.meals.push(mealId);
addedMealCount += 1;
}
});
if (addedMealCount != mealIds.length) {
return res.json({ error: "Meal already in that order" });
}
await mealIds.forEach(async (mealId) => {
const meal = await Meal.findById(mealId);
if (!meal.orders.includes(mealId)) {
meal.orders.push(order.id);
await meal.save();
}
});
await order.save();
res.json(order);
};
Removal
const removeMealsFromOrder = async (req, res, next) => {
const { orderId, mealIds } = req.body;
const order = await Order.findById(orderId);
mealIds.forEach((mealId) => {
order.meals.pull(mealId);
});
await mealIds.forEach(async (mealId) => {
const meal = await Meal.findById(mealId);
meal.orders.pull(orderId);
await meal.save();
});
await order.save();
res.json(order);
};
Addition and removal both seem to work now. But I am wondering how could I check is meal cook and order cook the same when I add meals to order.
Problem with this is that I would have to fetch each meal, and then check are cooks the same as in the order.
When I do so, usually I get Cannot set headers or such. I've tried to initialize counter before forEach loop in which I fetch an meal for mealId and increment it only if cooks are the same. And if they are not, counter is not incremented and response is sent. I sent an ok response with order only if the counter is the same as mealIds length. But, counter does not seem to change if I fetch meal by id.

Import large amounts of data, but do a .find() for each element

I have a collection of customers of 60.000 items. I need to import a list of people, of 50.000. But for each person, I need to find the ID of the customer and add that to the object that is being inserted.
How should this be done most efficently?
export default async ({ file, user, database }: Request, res: Response) => {
try {
const csv = file.buffer.toString("utf8");
let lines = await CSV({
delimiter: "auto" // delimiter used for separating columns.
}).fromString(csv);
let count = {
noCustomer: 0,
fails: 0
};
let data: any = [];
await Promise.all(
lines.map(async (item, index) => {
try {
// Find customer
const customer = await database
.model("Customer")
.findOne({
$or: [
{ "custom.pipeID": item["Organisasjon - ID"] },
{ name: item["Person - Organisasjon"] }
]
})
.select("_id")
.lean();
// If found a customer
if (!isNil(customer?._id)) {
return data.push({
name: item["Person - Navn"],
email: item["Person - Email"],
phone: item["Person - Telefon"],
customer: customer._id,
updatedAt: item["Person - Oppdater tid"],
createdAt: item["Person - Person opprettet"],
creator: users[item["Person - Eier"]] || "5e4bca71a31da7262c3707c5"
});
}
else {
return count.noCustomer++;
}
} catch (err) {
count.fails++;
return;
}
})
);
const people = await database.model("Person").insertMany(data)
res.send("Thanks!")
} catch (err) {
console.log(err)
throw err;
}
};
My code just never sends an response If I use this as a Express request.

async await with promise all

I want to know if I am using promise.all correctly with async await.
Basically, I need to get the house data based on the ID, then I need to get all the reviews for that house as well as the count of reviews.
server.get("/api/houses/:id", async (req, res) => {
const { id } = req.params;
const house = await House.findByPk(id);
if (!house) {
return res.status(400).send("No house found");
}
const reviews = await Review.findAndCountAll({
where: {
houseId: house.id
}
});
house.dataValues.reviewsCount = reviews.count;
const results = await Promise.all([house.dataValues, reviews.rows]);
console.log(results);
res.send(results);
});
In the front end when I console.log the response after making the http request, I get back the below which seems okay since Promise.all gives you arrays. But I don't know if this is the best way to do this or if there is a better way.
[
{
id: 2329,
host: 2,
picture: '/img/houses/1.jpg',
type: 'Entire house',
town: 'Some town',
title: 'Some title',
price: 50,
description: 'Some description',
guests: 4,
bedrooms: 1,
beds: 2,
baths: 1,
wifi: true,
reviewsCount: 2
},
[
{
id: 1,
houseId: 2329,
userId: 1,
comment: 'An awesome review',
createdAt: '2019-01-11T22:00:00.000Z',
updatedAt: '2019-01-11T22:00:00.000Z'
},
{
id: 2,
houseId: 2329,
userId: 2,
comment: 'Another awesome review',
createdAt: '2019-01-11T22:00:00.000Z',
updatedAt: '2019-01-11T22:00:00.000Z'
}
]
]
You're not using Promise.all correctly. The code is working, because you're awaiting each promise individually.
Since Review.findAndCountAll depends on House.findByPk result, Promise.all won't do any good here.
You're using Promise.all with the already resolved values of the both promises, so you can just drop it.
server.get("/api/houses/:id", async (req, res) => {
const { id } = req.params;
const housePromise = await House.findByPk(id);
const reviews = await Review.findAndCountAll({
where: {
houseId: house.id
}
});
house.dataValues.reviewsCount = reviews.count;
res.send([house.dataValues, reviews.rows]);
});
Basically you're doing:
const res = await Promise.all([1, 5]); // [1, 5]
Which can be translated directly to:
const res = [1, 5];
Instead of sending it in an array, I think it's better to send an object:
{
house: house.dataValues,
reviews: reviews.rows
}
You can ignore async await and use Promise. You can try following code
server.get("/api/houses/:id", async (req, res) => {
const { id } = req.params;
return House.findByPk(id)
.then( house => {
// !house might be 'true' if house is 'undefined'
if( house === undefined || !house ) {
return res.status(400).send("No house found");
}
return Review.findAndCountAll({ where: { houseId: house.id } })
.then(reviews => {
house.dataValues.reviewsCount = reviews.count;
return {house, reviews};
})
.catch(error => res.send(error));
})
.then( result => {
return res.send(results);
})
.catch(error => {
return res.send(error);
});
})

Resources