Node JS Loop Through Array Before Creating Property - javascript

I have a JSON input which contains data linking it to a secondary model (Users). I need to loop through listingData.Agents to get the index ID and then look up this index id to get the user. I push this to the user id to an array but due to the async the array is blank when the create property function is run. How you manipulate and get data from the array and then run the create once all your data is in place.
Thanks.
exports.createProperty = function(req,res,next) {
var listingData = req.body;
listingData.User = [];
_.forEach( listingData.Agents , function(n, key) {
User.findOne({ agentId : n.AgentId},function(err,user) {
listingData.User.push(user._id);
});
});
Property.create(listingData, function(err,property) {
if (err) {
res.status(400);
return res.send({reason:err.toString()});
}
res.send(req.property);
})}

If you don't mind introducing new library into your code, node-async could solve your problem.
Using node-async, you code would be:
var async = require('node-async')
exports.createProperty = function(req,res,next) {
var listingData = req.body;
listingData.User = [];
async.each(listingData.User,
function(n, key) {
User.findOne({ agentId : n.AgentId},function(err,user) {
listingData.User.push(user._id);
});
},
function (asyncErr){
//handle asyncErr first
Property.create(listingData, function(err,property) {
if (err) {
res.status(400);
return res.send({reason:err.toString()});
}
res.send(req.property);
});
});

Related

Creating my own sql wrapper for nodejs/express

I'm trying to create my own wrapper for mysql for my nodejs application. I have two questions here one of which theres a work around and one where I'm unsure what to do as my javascript skills are still in the learning phase.
First thing: As of right now when you navigate to /api/finance it directs you to the finance controller and the index method. This is currently just for testing purposes trying to figure out how to this kind of stuff.
FinanceController:
const sql = require('../../sql.js')
module.exports = {
index: (req, res, next) => {
sql.get('test').then((result) => {
res.status(200).json(result);
})
}
}
sql.js
const mysql = require('mysql');
const { DB } = require('./config')
var connection = mysql.createConnection(DB)
module.exports = {
get: function(table, columns = '*') {
return new Promise((resolve, reject) => {
connection.query('SELECT ?? FROM ?? ', [columns, table], function (error, results, fields) {
if (error) throw error;
resolve(results);
});
})
},
all: function(table) {
return new Promise((resolve, reject) => {
connection.query('SELECT * FROM ?? ', table, function (error, results, fields) {
if (error) throw error;
resolve(results);
});
})
},
where: function(){
console.log('where called')
}
}
As you can see, I have a get() and all(). get() allows you to pass the table name and an array of columns for example: ['id', 'name'] would get you the id column and name column. columns = '*' was an attempt on being able to use one function to either get all columns of the table or specify specific columns however it returns an error: Unknown column in 'field list' so all() was my "workaround" however i'd like it to be one function.
Next I can't figure out how to stack/pipe methods? if thats the word.
The goal here would be so I could call the function like this:
index: (req, res, next) => {
sql.all('test').where().then((result) => {
res.status(200).json(result);
})
}
}
obviously within the .where() I would have it like: .where('id', '=', 'userID') or something along those lines.
however I'm unsure on how to go about doing that and would like some guidance if its possible. I receive the error: sql.all(...).where is not a function
Instead of immediately launching the SQL, you should simply register the provided information in an instance object (having the same methods) and return that object, and let each method enrich the SQL until the end of the chain is reached and you call a method that will launch the SQL.
The object that is passed from one method to the next (as this) maintains state, and collects the different elements of the SQL statement.
Here is one way to do it.
NB: In this demo I used a mock-object for connection. This mock object will not actually connect to a database. Its query method will just produce the final SQL (with all ? resolved) instead of a real data set.
// Mock connection object for this snippet only. Just produces the final SQL, not the result
var connection = {
query(sql, params, callback) {
let i = 0;
sql = sql.replace(/\?\??/g, (m) => {
if (m.length > 1) return [].concat(params[i++]).map(p => "`" + p + "`").join(", ");
if (typeof params[i] === "string") return "'" + params[i++].replace(/'/g, "''") + "'";
return params[i++];
});
setTimeout(callback(null, sql));
}
}
// Function to create an instance when a first method call is made on the `sql` object directly
// Chained method calls will keep using that instance
function getInstance(inst) {
if (inst.columns) return inst; // Keep using same instance in the chain
inst = Object.create(inst); // No instance yet: create one
inst.table = null;
inst.params = [];
inst.columns = [];
inst.conditions = [];
inst.order = [];
return inst;
}
// This sql object serves a starting point as well
// as proto object for the instance object that
// passes through the chain:
var sql = {
from(table) {
let inst = getInstance(this);
inst.table = table;
return inst;
},
select(...columns) {
let inst = getInstance(this);
inst.columns = inst.columns.concat(columns);
return inst;
},
where(column, cmp, value) {
if (!["<",">","<=",">=","=","!="].includes(cmp)) throw "invalid operator";
let inst = getInstance(this);
inst.params.push(column, value);
inst.conditions.push(cmp);
return inst;
},
orderBy(...columns) {
let inst = getInstance(this);
inst.order = inst.order.concat(columns);
return inst;
},
promise() {
if (!this.table) throw "no table specified";
// build SQL and parameter list
let sql = "SELECT *";
let params = [];
if (this.columns.length && this.columns != "*") {
sql = "SELECT ??";
params.push(this.columns);
}
sql += " FROM ??";
params.push(this.table);
if (this.conditions.length) {
sql += " WHERE " + this.conditions.map(cmp => `?? ${cmp} ?`).join(" AND ");
params.push(...this.params);
}
if (this.order.length) {
sql += " ORDER BY ??";
params.push(this.order);
}
return new Promise(resolve => {
connection.query(sql, params, function (error, results) {
if (error) throw error;
resolve(results);
});
});
}
};
// demo
sql.from("customer")
.select("id", "name")
.where("name", ">", "john")
.where("name", "<", "thomas")
.orderBy("name", "id")
.promise()
.then(console.log);
Note that in this implementation it does not matter in which order you chain the from, select, where and order method calls. You could even do the following if you wanted to:
sql .orderBy("name", "id")
.where("name", ">", "john")
.from("customer")
.where("name", "<", "thomas")
.select("id", "name")
.promise()
.then(console.log);

How to scan dynamoDB in lambda?

so I have a table of 10 items, each item has about 5 keys (name,experience,level, etc). Now, I want to scan that table, get each item as an object and add it to an array and then JSON stringify that array and return it.
I just need help with the scanning code and getting all items and putting it into an array.
Here's my code I have currently.
var dynamodb = new AWS.DynamoDB.DocumentClient();
exports.handler = function(event, context, callback)
{
var returnArray = {
"cards": {}
}
getCards();
function getCards() {//Not sure how to write this function
var params = {
TableName : "toBeApprovedTable",
Key: {//not sure what to put here, since I want all items, and not searching through keys.
},
};
dynamodb.scan(params,function(err,data)
{
if(err)
{
console.log("error in scanning");
}
else
{
console.log("scanning success!");
//Not sure what to do here.
}
});
}
};
I figured it out after scrapping through Google + AWS docs.
Here is how to scan a table for all elements in the table. My return is a map, which contains an array of elements. Each element is a map of my object.
exports.handler = function(event, context, callback)
{
var returnArray = {
"cardsToBeApproved":[]
};
getCards();
function getCards() {//Not sure how to write this function
var params = {
TableName : "toBeApprovedTable"
};
dynamodb.scan(params,onScan);
}
function onScan(err,data)
{
if(err)
{
console.log("unable to scan table");
}
else
{
console.log("scan succeeded");
data.Items.forEach(function(card)
{
console.log("my card name is " + card.name);
var cardStringified = JSON.stringify(card);
returnArray.cards.push(card);
});
callback(null,JSON.stringify(returnArray));
}
}
};

nodeJS create dynamic array and use as response

Hi i am creating an empty array then populating it with data from a mongo query using a forEach Loop.
i have been attempting this now for 4 days and nothing i seem to do is working i know im close but being a newbie to javascript and MEAN stack i just cant figure it out.
i have attached the code with comments on everything i am trying to do.
please any help would be awesome..
var mongoose = require('mongoose'),
User = require('../../models/UserModel'),
async = require('async');
module.exports.getfollowing = function(req, res){
//grab the Users ID from the body
var thefollower = req.body.follower;
//create empty array that i want to populate with the followee's ID and Avatar url
var obj = [];
//query mongo for the user
User.findOne({ _id: thefollower }, function (err, user) {
if (err) {
console.log(err);
res.json(err);
} else {
//grab the following element of the users mongo schema -- should return all the followee's ID's -- tested works
var following = user.following;
//iritate throught all the followee's
async.forEach(following, function(item, callback) {
//current followee
var user = item;
//query mongo for the followee
User.findOne({_id: user}, function(err, followee, callback){
//get followee's ID and Avatar url
var id = followee._id;
var avatar = followee.avatar;
//add the followee's ID and Avatar url to the obj array
obj.push({
id: id,
avatar: avatar
});
});
//see if this worked - returns empty
console.log(obj);
callback();
}, function(err) {
//see if this worked - returns empty
console.log(obj);
//respond to the client - returns empty
res.json(obj);
});
}
});
};
You need to move your callback(); that is at the end of your async.forEach() callback to inside the User.findOne({_id: user}, ...) callback (right after you call obj.push()) because that is when you are actually done with item. With your current code you are telling the async module immediately that you are done with item, before your mongo query has a chance to complete.
mscdex
Was spot on his answer solved my issue for future help to others here is the code
var mongoose = require('mongoose'),
User = require('../../models/UserModel'),
async = require('async');
module.exports.getfollowing = function(req, res){
//grab the Users ID from the body
var thefollower = req.body.follower;
//create empty array that i want to populate with the followee's ID and Avatar url
var obj = [];
//query mongo for the user
User.findOne({ _id: thefollower }, function (err, user) {
if (err) {
console.log(err);
res.json(err);
} else {
//grab the following element of the users mongo schema -- should return all the followee's ID's -- tested works
var following = user.following;
//iritate throught all the followee's
async.forEach(following, function(item, callback) {
//current followee
var user = item;
//query mongo for the followee
User.findOne({_id: user}, function(err, followee){
//get followee's ID and Avatar url
var id = followee._id;
var avatar = followee.avatar;
//add the followee's ID and Avatar url to the obj array
obj.push({
id: id,
avatar: avatar
});
//see if this worked - returns empty
console.log(obj);
callback();
});
}, function(err) {
//see if this worked - returns empty
console.log(obj);
//respond to the client - returns empty
res.json(obj);
});
}
});
};

Push new field and value to json array object in node.js

I'm new to node.js. I need to display Name in jqgrid, but I stored only id of one document into another document.
Example
I have 2 documents like Student master and student mark document. I have to display mark details in jqgrid. In mark document I stored student id instead of name. How do I fetch the name and send a new object to jqgrid?
My code is as follows:
exports.getAllstudentsmark = function(req, callback)
{
studentsmarks.find(function(error, studentsmarks_collection) {
if( error ) callback(error)
else {
studentsmarks_collection.toArray(function(error, results) {
if( error ) callback(error)
else {
newresult = results;
for(i=0;i<results.length;i++)
{
newresult[i]['studentname'] = getStudentName(results[i].studentid);
}
console.log(newresult);
callback(null, newresult)}
});
}
});
}
var getstudentObjectId = function(id)
{
return student.db.bson_serializer.ObjectID.createFromHexString(id);
}
var getStudentName = function(id)
{
student.findOne({_id: getstudentObjectId (id)}, function(e, o){
console.log(o.name);
return o.name;
});
}
newresult[i]['studentname'] is always getting undefined. But if I log into getStudentName function I can get answer into getStudentName function.
My callback function is only getting this problem. How to resolve and get my result in an easy way. Please help any one.
try this inside your for loop
newresult.push({'studentname': getStudentName(results[i].studentid) });
exlpanation:
by the time you access newresult[i] it doesn't exist, so accessing studentname field of it is impossible
Your problem here is that you are not setting the name of the user into the array, but the return value of student.findOne, since this is an asynchronous method. Maybe try this thing
exports.getAllstudentsmark = function(req, callback)
{
studentsmarks.find(function(error, studentsmarks_collection) {
if( error ) callback(error)
else {
studentsmarks_collection.toArray(function(error, results) {
if( error ) callback(error)
else {
newresult = [];
for(i=0;i<results.length;i++)
{
getStudentName(results[i].studentid, function (studentName) {
newresult.push({studentname: studentName});
})
}
console.log(newresult);
callback(null, newresult)}
});
}
});
}
var getstudentObjectId = function(id)
{
return student.db.bson_serializer.ObjectID.createFromHexString(id);
}
var getStudentName = function(id, callback)
{
student.findOne({_id: getstudentObjectId (id)}, function(e, o){
console.log(o.name);
callback(o.name);
});
}
I hope it helps

Node/Express - How to wait until For Loop is over to respond with JSON

I have a function in my express app that makes multiple queries within a For Loop and I need to design a callback that responds with JSON when the loop is finished. But, I'm not sure how to do this in Node yet. Here is what I have so far, but it's not yet working...
exports.contacts_create = function(req, res) {
var contacts = req.body;
(function(res, contacts) {
for (var property in contacts) { // for each contact, save to db
if( !isNaN(property) ) {
contact = contacts[property];
var newContact = new Contact(contact);
newContact.user = req.user.id
newContact.save(function(err) {
if (err) { console.log(err) };
}); // .save
}; // if !isNAN
}; // for
self.response();
})(); // function
}; // contacts_create
exports.response = function(req, res, success) {
res.json('finished');
};
There are a few problems with your code besides just the callback structure.
var contacts = req.body;
(function(res, contacts) {
...
})(); // function
^ you are redefining contacts and res in the parameter list, but not passing in any arguments, so inside your function res and contacts will be undefined.
Also, not sure where your self variable is coming from, but maybe you defined that elsewhere.
As to the callback structure, you're looking for something like this (assuming contacts is an Array):
exports.contacts_create = function(req, res) {
var contacts = req.body;
var iterator = function (i) {
if (i >= contacts.length) {
res.json('finished'); // or call self.response() or whatever
return;
}
contact = contacts[i];
var newContact = new Contact(contact);
newContact.user = req.user.id
newContact.save(function(err) {
if (err)
console.log(err); //if this is really a failure, you should call response here and return
iterator(i + 1); //re-call this function with the next index
});
};
iterator(0); //start the async "for" loop
};
However, you may want to consider performing your database saves in parallel. Something like this:
var savesPending = contacts.length;
var saveCallback = function (i, err) {
if (err)
console.log('Saving contact ' + i + ' failed.');
if (--savesPending === 0)
res.json('finished');
};
for (var i in contacts) {
...
newContact.save(saveCallback.bind(null, i));
}
This way you don't have to wait for each save to complete before starting the next round-trip to the database.
If you're unfamiliar with why I used saveCallback.bind(null, i), it's basically so the callback can know which contact failed in the event of an error. See Function.prototype.bind if you need a reference.

Categories