How to use for loop in mongoose find function? - javascript

Here is my code:
app.post('/update', async (req, res, done) => {
let drive = req.app.get('drive');
let fileId = req.body.id;
let subject = req.body.subject;
let grade = req.body.grade;
let industry = req.body.industry;
await TagFile.findOne({id: fileId}, (err, res) => {
if (err) return console.log(err);
//console.log(res);
//console.log(subject);
takeRes(res);
})
function takeRes(res) {
for (let i = 0; i > subject.length; i++) {
if(!res[0].subject.includes(subject[i])) {
res[0].subject.push(subject[i]);
console.log(res[0].subject)
console.log("first was called")
}
else {
console.log(res[0].subject)
console.log("else was called.")
}
}
}
})
This is a post request that is made from my site. When it gets to the step where the for loop needs to be used, I don't get anything in the console from the if else statements. I know this probably has something to do with async, but I don't fully understand it, and would love a push in the right direction. Thank you!

When you are using await you don't need to use the callback function. Use following snippet to query with mongoose, rest of the parts depends on your business logic:
const res = await TagFile.findOne({id: fileId}).exec();
takeRes(res)

result of findOne() is object and result of find() is array and when you use findOne() the res[0] is incorrect and you can use lean()to convert model to object, so use find like this:
let res = await TagFile.find({ id: fileId }).lean();
for (let i = 0; i > subject.length; i++) {
if (!res[0].subject.includes(subject[i])) {
res[0].subject.push(subject[i]);
console.log(res[0].subject);
console.log("first was called");
} else {
console.log(res[0].subject);
console.log("else was called.");
}
}
when you use findOne try like this:
let res = await TagFile.findOne({ id: fileId });
for (let i = 0; i > subject.length; i++) {
if (!res.subject.includes(subject[i])) {
res.subject.push(subject[i]);
console.log(resizeTo.subject);
console.log("first was called");
} else {
console.log(res.subject);
console.log("else was called.");
}
}

Related

Callback function inside for loop - Nodejs

Hope you're having a good day. I recently discovered that it is not as easy to handle callbacks inside a for loop. I have tried a few things but couldn't find a solution.
Here is the code:
var book = new Array;
var chapters = Object.keys(epub.spine.contents).length;
for (let i = 0; i < chapters; i++) {
let cacheArray = [];
epub.getChapter(epub.spine.contents[i].id, function (err, data) {
if (err) {
return console.log(err);
}
//remove html tags
let str = data.replace(/<\/?[\w\s]*>|<.+[\W]>/g, '');
book.push(str)
})
}
console.log(book)//returns empty array ofc
After this code is executed, I need to loop over the array to search its contents. If that was not the case, my approach would be to just send it to a db.
My approach was the following:
var recursiveChapter = function (n) {
var book = new Array;
if (n < chapters) {
// chapter function
epub.getChapter(epub.spine.contents[n].id, function (err, data) {
if (err) {
throw err
}
//remove HTML tags
let str = data.replace(/<\/?[\w\s]*>|<.+[\W]>/g, '');
book.push(str)
recursiveChapter(n + 1)
});
}
}
//start the recursive function
recursiveChapter(0)
console.log(book)//returns an empty array
I am stuck and can't think of a way of using this data without storing it in a db.
Any help would be appreciated .
There are a few ways you can tackle this. One way is to use the async library, this allows to to run async. calls in parallel, or in series.
For example, we can use async.mapSeries() to get the result of a series of asynchronous calls as an array.
You input your array of ids, then the callback will return an array of the data returned, for example, I've mocked out the getChapter() function to give you an idea of how it would work:
// Mock out epub object
const epub = {
getChapter(id, callback) {
setTimeout(() => callback(null, "Data for id " + id), 250);
}
}
let ids = [1,2,3,4];
console.log("Calling async.mapSeries for ids:", ids);
async.mapSeries(ids, epub.getChapter, (err, result) => {
if (err) {
console.error(err);
} else {
console.log("Result:", result)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/async/3.2.0/async.min.js" integrity="sha512-6K6+H87tLdCWvY5ml9ZQXLRlPlDEt8uXmtELhuJRgFyEDv6JvndWHg3jadJuBVGPEhhA2AAt+ROMC2V7EvTIWw==" crossorigin="anonymous"></script>
You could also promisify the epub call and use Promise.all to get the result, like so:
epub = {
getChapter(id, callback) {
setTimeout(() => callback(null, "Data for id " + id), 250);
}
}
let ids = [1,2,3,4];
function getChapterPromisified(id) {
return new Promise((resolve, reject) => {
epub.getChapter(id, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
})
})
}
// Create a list of promises, one for each call
const promises = ids.map(id => getChapterPromisified(id))
Promise.all(promises)
.then(result => console.log("Result:", result))
.catch(error => console.error("An error occurred:", err));

Awaiting for a for loop to end to continue function in javascript

i have one question! I want the function getMealSummary() to return after the for loop has finished already finished (I didnt include the return key because the function is very big and i just had a doubt in this section).
How can I do it with async/await or promises?
Thanks
export const getMealMicroSummaryHelper = async function getMealSummary(array, populatedFoods, customMeasurements, isRecipe,expertId){
var [totalZinc, totalCalcium,totalAlphaCarotene,totalBetaCarotene,totalCholine,totalCopper,totalBetaCrypto,totalFluoride,totalVitaminB9,totalIron,
totalLutein,totalLycopene,totalMagnesium,totalManganese,totalNiacin,totalVitaminB5,totalPhosphorus,totalPotassium,totalRetinol,totalRiboflavin,
totalSelenium,totalSodium,totalTheobromine,totalVitaminA,totalVitaminB12,totalVitaminB6,totalVitaminC,totalVitaminD,totalVitaminD2,totalVitaminD3,
totalVitaminE,totalThiamin] = [0, 0, 0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
let recipesArray = []
for(let i = 0; i < populatedFoods.length; i++){
if(populatedFoods[i].foodType === 4){
await apiCall("get",`/api/experts/${expertId}/recipes/${populatedFoods[i]._id}`)
.then(recipe =>{
recipesArray.push(recipe)
})
.catch((err)=>console.log(err))
}
You can use a combination of map() and Promise.all for this like:
async function getMealSummary(array, populatedFoods, customMeasurements, isRecipe, expertId) {
try {
let recipesArray = await Promise.all(
populatedFoods.map(async populatedFoods => {
if (populatedFoods[i].foodType === 4) {
let recipe = await apiCall("get", `/api/experts/${expertId}/recipes/${populatedFoods[i]._id}`)
return recipe;
}
})
)
} catch (err) {
console.log(err)
}
}
This is just a simple version to give you a basic idea. I have not included all the variables that you have declared above recipesArray. Those will need to be included as needed.

Async functions in for loops javascript

I am not experienced with async functions and I would like to perform a request in a for loop. Here's my code:
app.route('/friendlist').post((req, res) => {
var body = req.body;
var list = "";
con.query(`SELECT * FROM player_friends WHERE main_user_id = '${body.player_id}'`, (err, row, fields) => {
if (err) throw err;
async function queryOutUserData(data) {
var rows = await new Promise((resolve, reject) => {
con.query(`SELECT * FROM players WHERE player_id = '${data.player_id}'`, (error, player, field) => {
if (error) {
console.log(error);
return reject(error);
}
resolve(player);
});
});
rows.then(message => {
return message
});
}
for (var i = 0; i <= row.length; i++) {
console.log(row[i].main_user_id);
var result = await queryOutUserData(row[i]);
list = list + ";" + result[0].player_id + ":" + result[0].player_username;
}
console.log(list);
return list;
});
});
Actually here's the full problem: I did some debugging and apparently value i in for loop increases before the promise is resolved. Also as I mentioned I am not familiar with async functions, could you provide me a descriptive resource about how promises and async functions work?
Thanks
NOTE: For better indentation, here's the code: https://hastebin.com/fovayucodi.js
Instead of using async/await I suggest doing everything in one query using WHERE IN rather than one query per player. See if the following fits your needs:
app.route('/friendlist').post((req,res) => {
var body = req.body;
var list = "";
con.query(`SELECT * FROM player_friends WHERE main_user_id = '${body.player_id}'`, (err, row, fields) => {
if (err) throw err;
const playerIds = row.map(player => player.player_id);
con.query(`SELECT * FROM players WHERE player_id IN ${playerIds}`, (error, players, field) => {
for (let player of players) {
list += `;${player.player_id}:${player.player_username}`;
}
});
console.log(list);
return list;
});
});
If you await a promise, it evaluates to the result of that promise, so rows is not a promise, it's the result. So this:
rows.then(message => {return message});
Doesn't make much sense, just do:
return message;
Also, you have an await inside of a regular function, thats a syntax error.
Additionally return list; doesn't do much (if that is express), you might want to return res.json({ list });.
: I did some debugging and apparently value i in for loop increases before the promise is resolved.
I doubt that you can debug code if you can't actually run it because of the syntax errors.
try to use for-of instead just a for.
something like this:
Async Function:
async function test() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
return
}, 1000)
})
}
Here another function using for and waiting for the finish of loop
async function myFunction() {
const data = [1,2,3,4,5,6,7,8]
for(let i of data) {
const value = await test();
console.log(value)
}
console.log("finish");
}

Push into an array from foreach and make it available outside foreach

I stuck by looping through an array that receive values from a promise and push values into a new array which is available outside the foreach.
What i have:
app.post('/submit', function (req, res) {
uploadPics(req, res, function (err) {
if (err instanceof multer.MulterError) {
res.send(JSON.stringify({UploadResult: err.message}));
console.log(err.message + ' ' +'Redirect /home');
} else if (err) {
console.log(err);
} else {
res.send(JSON.stringify({UploadResult: 'Success'}));
var filesarray = req.files;
var picinfos = [];
filesarray.forEach(function(file){
GetFileMetaInfo.filemetainfo(file.path).then(function (metadata){
//Stuck here! Can push values into an array (picinfos) but only available in the foreach. not outside..
})
})
//I need picinfos array here....
}
})
})
How i receive my metadata:
var exif = require('exif-parser');
var fs = require('fs');
exports.filemetainfo = function (filepath) {
return new Promise((resolve) => {
var file = filepath;
var buffer = fs.readFileSync(file);
var parser = exif.create(buffer);
var result = parser.parse();
resolve (result);
}).then(function (metadata){
if (metadata.tags.CreateDate !== undefined){
date = new Date (metadata.tags.CreateDate*1000);
datevalues = [
date.getFullYear(),
date.getMonth()+1,
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
];
CreateDate = date.getFullYear()+'-'+(date.getMonth()+1)+'-'+date.getDate();
CreateTime = date.getHours()+':'+date.getMinutes()+':'+date.getSeconds();
console.log("CrDate:" +CreateDate, "CrTime:" +CreateTime );
} else {
console.log("No Metadata Creation Infos found in " +filepath);
CreateDate = "";
CretaeTime = "";
}
if (metadata.tags.GPSLatitude !== undefined){
GPSLat = metadata.tags.GPSLatitude;
GPSLon = metadata.tags.GPSLongitude;
console.log("GPSLat:" + GPSLat , "GPSLon:" +GPSLon);
}
else {
console.log("No Metadata GPS Infos found in " +filepath)
GPSLat = "";
GPSLon = "";
}
return MetaData = {
GPSLat: GPSLat ,
GPSLon: GPSLon,
CreateDate: CreateDate,
CreateTime: CreateTime,
}
})
}
May i ask someone to give a hand. How can i make my array available outside the foreach. thank you very much!
The reason you're getting empty array at the end of forEach is because, GetFileMetaInfo.filemetainfo() returns a promise and forEach won't wait for async actions.
You could use async/await with for...of loop to get your desired result.
app.post('/submit', function (req, res) {
uploadPics(req, res, async function (err) { // note async here
if (err instanceof multer.MulterError) {
res.send(JSON.stringify({UploadResult: err.message}));
console.log(err.message + ' ' +'Redirect /home');
} else if (err) {
console.log(err);
} else {
res.send(JSON.stringify({UploadResult: 'Success'}));
var filesarray = req.files;
var picinfos = [];
for(let file of filesarray) {
const metadata = await GetFileMetaInfo.filemetainfo(file.path);
// push metadata into your array here
picinfos.push(metadata);
}
// You will have picinfos here
}
})
})
Although the question is already answered by Dinesh Pandiyan there are still some adjustments that can be made. The following code in his answer runs sequential, meaning that every async request is made after the previously returned result is resolved.
for(let file of filesarray) {
const metadata = await GetFileMetaInfo.filemetainfo(file.path);
// ^- pauses the execution of the current running code
// push metadata into your array here
picinfos.push(metadata);
}
async call #1 ╌╌await╌╌> async call #2 ╌╌await╌╌> async call #3 ╌╌await╌╌> result
You could make the code concurrent by first executing all async statements and then wait until all results are resolved. This can be done by simply changing the following:
// execute all the async functions first, reducing the wait time
for(let file of filesarray) {
const metadata = GetFileMetaInfo.filemetainfo(file.path);
// ^- remove the await
// push metadata into your array here
picinfos.push(metadata);
}
// wait for all results to be resolved
picinfos = await Promise.all(picinfos);
// ^- instead await here
async call #1 ╌╌┐
async call #2 ╌╌┼╌╌await all╌╌> result
async call #3 ╌╌┘
The above could be further simplified by simply using an Array.map() in combination with the already shown Promise.all().
var filesarray = req.files;
var picinfos = await Promise.all(filesarray.map(file => {
return GetFileMetaInfo.filemetainfo(file.path);
}));
// picinfos should be present
Or if you want to avoid working with async/await:
var filesarray = req.files;
Promise.all(filesarray.map(file => {
return GetFileMetaInfo.filemetainfo(file.path);
})).then(picinfos => {
// picinfos should be present
});

How to get data from database read from another file node js

I have problem when I try to use data from another file in my nodejs program
the example is like this.
const Search = require('./model/Search');
getData = () => {
let result = []
Search.find({}, (err, data) => {
if (err) {
console.log(err);
}
for (let i = 0; i < data.length; i++) {
result.push = data[i].title;
}
});
return result;
}
and I will get the result in another file
const coba = require('./data');
console.log(coba);
and I get undefined in my terminal. Can you help me solve this problem?
getData = () => {
let result = []
Search.find({}, (err, data) => {
if (err) {
console.log(err);
}
for (let i = 0; i < data.length; i++) {
result.push = data[i].title;
}
// return statement should be inside Search.find callback
return result;
});
}
Hi, your Search.find is asynchronous function as it is fetching results from DB. and you have your return statement outside that asynchronous function because of which you are getting undefined. Please find the correction in above snippet. By the way Sorry, I completely got it wrong in my first attempt to answer. Edited the same post. Cheers!

Categories

Resources