How to Iterate over array using async and foreach - node.js

I am trying to create an api that will create a group with members in expressjs. This is how it works till now:
Make a post request with a JSON object in req.body, using which I will create a new group. If the members array of the req.body object contains member id, add it to the group members array, else create a new user and then add its id to the array.
Basically an existing user just gets added, new user will be created and then added. For this I need to loop through the members array in the req.body and check for an object.
But the code below doesn't seem to work properly. I am getting strange results for console.log(group_info.members);. I am expecting this to contain objects with id in an array, but getting random results. Something seems to be wrong in the foreach loop. Please help me figure it out.
var express = require('express');
var router = express.Router();
var Group = require('../models/GroupModel');
var User = require('../models/UserModel');
var async = require("async");
router.post('/', function (req, res, next) {
var group_members = [];
var group_info = req.body;
//see if a member object is sent, create user for that else just add the user id to group_members array
async.forEach(group_info.members, function (member, callback) {
if (typeof member == "object") {
//create new user and add the _id to members array
var user = new User(member);
user.save(function (err) {
if (err) return res.status(500).send(err);
var member_object = {id: user._id};
group_members.push(member_object);
}).then(callback);
} else {
var member_object = {id: member };
group_members.push(member_object);
callback();
}
}, function (err) {
//final call back
group_info.members = group_members; //replace the original array in request body with the new array of users
console.log(group_info.members);
var group = new Group(group_info);
group.save(function (err) {
if (err) return res.status(500).send(err);
res.json(group);
});
});
});

Looks like you made a mistake its eachSeries not forEach, so just replace :
async.forEach(group_info.members, function (member, callback)
with:
async.eachSeries(group_info.members, function (member, callback)
Update
As pointed out in the comments forEach is an alias for async each API, You can read the docs here,Thank You #megawac for pointing this out.

var group_members = [];
var group_info = req.body;
var todo = group_info.members.length;
var done = 0;
if(todo == 0)
{
return saveGroup(res, group_members);
}
else
{
group_info.members.forEach(function (member, index, array) {
if (typeof member == "object") {
//create new user and add the _id to members array
var user = new User(member);
user.save(function (err, savedObject) {
if (err || !savedObject)
{
return res.status(500).send(err);
}
else
{
var member_object = {id: savedObject._id};
group_members.push(member_object);
if(++done >= todo)
{
return saveGroup(res, group_info, group_members);
}
}
});
} else {
var member_object = {id: member };
group_members.push(member_object);
if(++done >= todo)
{
return saveGroup(res, group_info, group_members);
}
}
});
}
function saveGroup(res, group_info, group_members)
{
group_info.members = group_members; //replace the original array in request body with the new array of users
console.log(group_info.members);
var group = new Group(group_info);
group.save(function (err) {
if (err) return res.status(500).send(err);
res.json(group);
});
}

Related

Can't access fields of MongoDB document in Node.Js

I'm using mongoose and express on my nodejs project.
Trying to get the data from here
app.get('/offers/:id', (req, res) =>{
//store the id from the url
var id = req.params.id;
//just a placeholder
var data = {title: "title", description:"description"};
//store the returned object in a variable
var oop = offers.findById(id, function (err, user) {
if(err){
return err;
}else{
title = user.title;
description = user.description;
this.obj = {
title:title,
description:description
}
console.log(obj)
return obj;
}
} );
console.log(oop)
res.render('single', {data:data});
});
so my idea is to grab the post id from the url, find it in the database, then display the title and description in the corresponding place on the ejs template, but for some reason I can't access the returned data, and what I get is a long list of objects that belongs to mongodb, without the presence of "title" or "description"
Try this, your code has couple of issues & also you need use .lean() to get raw Js objects rather than mongoDB documents :
app.get('/offers/:id', (req, res) => {
//store the id from the url
var id = req.params.id;
//just a placeholder
var data = { title: "title", description: "description" };
//store the returned object in a variable
offers.findById(id).lean().exec((err, user) => {
if (err) {
console.log(err);
res.send(err)
} else {
data.title = user.title;
data.description = user.description;
this.obj = {
title: title,
description: description
}
console.log(obj);
res.render('single', { data: data });
// (Or) res.render('single', { data: obj });
}
});
});
I just modified your code and added comments (all starting with "***").
app.get('/offers/:id', (req, res) =>{
//store the id from the url
var id = req.params.id;
//just a placeholder
var data = {title: "title", description:"description"};
//store the returned object in a variables
// var oop = ***no need for this, the data you want will be in the user variable.
offers.findById(id, function (err, user) {
if(err){
return err;
}else{
// ***this needs to be changed to...
// title = user.title;
// description = user.description;
// ***that...
data.title = user.title;
data.description = user.description;
// ***what's that for??
// this.obj = {
// title:title,
// description:description
// }
// ***this needs to be inside mongoose's callback
res.render('single', {data:data});
}
});
});

Sqlite .all() function returns a promise, but I need the database items...?

I'm new to NodeJS, and I'm trying to learn it by building a Shopping cart web app. I'm storing the data in an SQLite database but I can't seem to access it. The .all() method returns a Promise object and I can't figure out how to obtain the items in the database instead.
I followed this tutorial: https://stackabuse.com/a-sqlite-tutorial-with-node-js/ to build a data access object and two models: ItemRepository and CartRepository.
This is the get method in my data access object script:
get(sql, params = []) {
return new Promise((resolve, reject) => {
this.db.get(sql, params, (err, result) => {
if (err) {
console.log('Error running sql: ' + sql)
console.log(err)
reject(err)
} else {
resolve(result)
}
})
}
And this is my index.js
router.get('/', function(req, res, next) {
var dao = new AppDAO('./database.sqlite3');
var itemRepo = new ItemRepository(dao);
var cartRepo = new CartRepository(dao);
items = itemRepo.getAll();
console.log(items);
var itemRows = [];
var rowSize = 3;
for (var i = 0; i < items.length; i += rowSize){
itemRows.push(items.slice(i, i+rowSize));
}
res.render('shop/index', { title: 'sHOP', items: itemRows })
});
I'm a little lost as for how to get the table's content and not the Promise object.
I will recommend you to read promises and async/await or bluebird promises. you will need to use then method on promise to get the actual result as below.
router.get('/', function(req, res, next) {
var dao = new AppDAO('./database.sqlite3');
var itemRepo = new ItemRepository(dao);
var cartRepo = new CartRepository(dao);
itemRepo.getAll().then(( items) =>{
console.log(items);
var itemRows = [];
var rowSize = 3;
for (var i = 0; i < items.length; i += rowSize){
itemRows.push(items.slice(i, i+rowSize));
}
res.render('shop/index', { title: 'sHOP', items: itemRows })
})
});

Saving MongoJS select result to an array in NodeJS and then modifying it

Having a hard time saving and modifying the result of a MongoJS query in NodeJS.
router.post('/getMySubjects', function (req, res) {
var data = [];
if (req.body.type == 'Professor') {
db.subjects.find({ contractorID: req.body.userId }, function (err, subjects) {
data = subjects; // SUBJECTS ARE NOW SAVED TO DATA SUCCESSFULLY
data.forEach(function(subject) {
db.faculties.find({ _id: mongojs.ObjectID(subject.subjectFor_faculty)}, function (err, faculty) {
subject.faculty = faculty; // BUT HERE I WANT TO ADD A FACULTY (object)
// BASED ON THE subjectFor_faculty (id)
// WHICH IS LOCATED IN EVERY (subject)
// ELEMENT IN DATA ARRAY
});
});
res.send(data); // THE DATA HERE IS UNMODIFIED
// SAME AS DATA ON LINE 6
});
}
});
I presume that I don't yet fully understand how the response works (btw the app is made with express framework), because when the data is first saved on line 6, the next step is sending the data, and only THEN the app goes goes into the forEach loop...
You are making async mongo queries. you have to make them work sync for getting right data. here's implementation using promises.
router.post('/getMySubjects', function (req, res) {
var data = [];
if (req.body.type == 'Professor') {
db.subjects.find({ contractorID: req.body.userId }, function (err, subjects) {
data = subjects;
var promises = [];
data.forEach(function(subject) {
var promise = new Promise(function(resolve, reject) {
db.faculties.find({ _id: mongojs.ObjectID(subject.subjectFor_faculty)}, function (err, faculty) {
resolve(faculty);
});
});
promises.push(promise);
});
Promise.all(promises).then(function(values){
for(var i = 0; i< values.length;i++){
data[i].faculty = values[i];
}
res.send(data);
});
});
}
});

Trouble in using forEach function in node.js

I have been learning about q promises and tried to build up some mock APIs to implement its functionality,While doing so I came across the following error,
Enterprise.forEach is not a function
My API code is as follows,
var mongoose = require('mongoose');
var Enterprise = mongoose.model('Enterprise_gpy');
var q = require('q');
var displayEnterprise = function(req, res) {
function displayEnterpriseName() {
var deferred = q.defer();
Enterprise.forEach(function(err, doc) {
if (err) {
console.log('Error Finding Files');
deferred.reject(err);
} else {
var name = Enterprise.enterprise_name;
deferred.resolve({
name: name
});
}
return deferred.promise;
});
}
function displayEnterpriseEmail() {
var deferred = q.defer();
Enterprise.forEach(function(err, doc) {
if (err) {
console.log('Error Finding Files');
deferred.reject(err);
} else {
var email = Enterprise.enterprise_email;
deferred.resolve({
email: email
});
}
return deferred.promise;
});
}
q.all([
displayEnterpriseName(),
displayEnterpriseEmail()
])
.then(function(success) {
console.log(500, success);
})
.fail(function(err) {
console.log(200, err);
});
}
module.exports = {
displayEnterprise: displayEnterprise
}
In your code Enterprise is a mongoose schema so when you try to do loop using forEach then got
Enterprise.forEach is not a function
you can use forEach after Enterprise.find(). so use
Enterprise.find({}, function(err, docs) {
if (err) {
console.log('Error Finding Files');
deferred.reject(err);
} else {
var names = [];
docs.forEach (function(doc) {
var name = doc.enterprise_name;
names.push(name);// pushed in names array
//.....
});
deferred.resolve({
names: names
}); // return all names
}
});
instead of
Enterprise.find().forEach
and should use
var name = doc.enterprise_name; instead of var name = Enterprise.enterprise_name;
and
var email = doc.enterprise_email; instead of var email = Enterprise.enterprise_email;
forEach only works for arrays, and you're using it on a mongoose model.
try this instead:
Enterprise.find().exec(function(err, docs) {
docs.forEach(function(doc) {
// do something with all the documents
}
// do something outside the loop
})

unable to return in node.js from a function

I have three blocks of code where block one executes first and the result of first block is passed to bloack 2 and then the final result is then passed to the third block which has to send data to the route.
But at the end the return is undefined.
function getUserKey(userRole, callback) {
//keys value is stored and returned
var keys = base.menuModel.find({ 'name' : userRole }, function (err, result) {
if (!err) {
var menu = JSON.stringify(result);
menu = JSON.parse(menu);
var menuKeys = [];
for(i = 0;i < Object.keys(menu[0].permissions[0]).length;i++) {
menuKeys.push((Object.keys(menu[0].permissions[0])[i]));
}
callback(null,menuKeys);
//returns menukeys to be shown
}
else {
return err;
}
});
}
n is holding the menu keys
function userMenuData(n, callback) {
var filterResult = base.globalMenuModel.find({"title" : { $in : n}},function (err, result) {
if (!err) {
callback(null,result);
}
else {
return err;
}
});
}
var userMenu = function(userRole,callback) {
var userMenuTemp = async.compose(userMenuData, getUserKey);
var sendData = userRole is passed and the result is obtained
userMenuTemp(userRole,function(err,result) {
return result; // data success
});
console.log(sendData); //undefined
return sendData;
}
here i want to pass sendData to route in node.js
but at the console i am getting undefined.
Thanks for any help
It's the async nature of node that's getting you. The console.log is happening before any of those functions are returned. You want to check out a library that does promises like Q http://documentup.com/kriskowal/q/

Resources