updating related models from parent model - node.js

I am trying to create a related model via the parent model using the uRL of the parent model
Parent : Applications
Related : Applicants
This is the below relationship
applications --> hasMany -- > applicants ( foreignkey : application
id )
applicants --> BelongsTo --> applications (foreignkey : applicationid )
I want to do an upsert on both the models togther using the url of the parent model
PATCH /api/applications
Below is the applications.js file
module.exports = function(Application)
{
Application.afterRemote('upsert', function(ctx, instance, next){
var response ={};
response.id = ctx.result.id;
var applicants = ctx.req.body.applicants || undefined;
Application.getApp(function (err, app) {
var resp = { "applicants" :[]};
if (applicants){
for (var i=0; i<applicants.length ; i++)
{
applicants[i].application_id = ctx.result.id;
app.models.Applicants.upsert(applicants[i], function (err, result)
{
if (err) throw err;
if (!result) {
var err = new Error("Insert into Applicant failed");
err.statusCode = 500;
next(err);
}
// This prints the result in the console.
console.log('***** In APP' + JSON.stringify(result));
resp.applicants.push(result);
});
console.log(JSON.stringify(applicants));
}
// This still results in a empty array
response.applicants = resp.applicants;
ctx.result = response;
}
});
next();
});
How can I fetch the result of the upsert to the applicants model and then send it back in the application response for the api.
Thanks

This is what I would do. There is certainly a better way thought but it might be a good start.
'use strict';
var app = require('../../server/server');
var models = app.models;
var Applicants;
app.on('started', function () {
Applicants = models.Applicants;
});
module.exports = function (Application)
{
Application.afterRemote('upsert', function (ctx, instance, next) {
var response = {};
response.id = ctx.result.id;
var applicants = ctx.req.body.applicants || undefined;
if (applicants) {
var resp = {"applicants": []};
var count = 0;
for (var i = 0, applicantsLength = applicants.length; i < applicantsLength; i++) {
applicants[i].application_id = ctx.result.id;
Applicants.upsert(applicants[i], function (err, result) {
if (err)
return next(err);
if (!result) {
var err = new Error("Insert into Applicant failed");
err.statusCode = 500;
return next(err);
}
resp.applicants.push(result);
count++;
if (count === applicantsLength) {
response.applicants = resp.applicants;
ctx.result = response;
next();
}
});
}
} else {
next();
}
});
};
If this is not working, you should look for the 'before save' hook.

Related

Error: NJS-012: encountered invalid bind data type in parameter 2

Even though I have searched for the solution of this error and i found some answers but none of them helped me fix this error, Error: NJS-012: encountered invalid bind data type in parameter 2.Maybe, one error can occur in a different scenarios.
Stored procedure definition
create or replace PROCEDURE SP_MEAL_GETMEALTYPES
(
p_DataSource OUT Sys_RefCursor
)
AS
BEGIN
OPEN p_DataSource FOR
select mealtypeid,description from mealtypes;
END;
File name: menusStoredProc.js
"use strict";
var dbParams = require('../../oracle/dbParams');
function storedProcs() {
this.SP_USER_GETMENUS = {
name: 'sp_meal_getmealtypes',
params: {
dataSource: {val: null, type: dbParams.CURSOR, dir: dbParams.BIND_OUT}
},
resultSetColumns: ['mealTypeId','description']
}
}
module.exports = storedProcs;
File name: menus.js
var express = require('express');
var MenusStoreProc = require('../storedProcedures/menusStoredProc');
var oraDbAssist = require('../../oracle/oracleDbAssist');
var router = express.Router();
router.get('/getmenus', (req, res, next) => {
var sp = new MenusStoreProc().SP_USER_GETMENUS;
oraDbAssist.getConnection(function (err, conn) {
if (err)
return console.log('Connecting to db failed - ' + err);
oraDbAssist.executeSqlWithConn(sp, false, conn, function (err, menus) {
if (err)
return console.log('Executing ' + sp.name + ' failed - ' + err);
res.status(200).json(JSON.stringify(menus));
});
});
});
module.exports = router;
Function definition added - executeSqlWithConn
function executeSqlWithConn(sp, autoCommit, connection, next) {
var sql = createProcedureSqlString(sp.name, sp.params);
var params = buildParams(sp.params);
connection.execute(sql, params, {autoCommit: autoCommit}, function(err, result) {
if (err) {
next(err, null);
return;
}
var allRows = [];
var numRows = 50; // number of rows to return from each call to getRows()
for(var attributeName in result.outBinds) {
if(result.outBinds[attributeName] && result.outBinds[attributeName].metaData) { // db response is a result set
function fetchRowsFromResultSet(pResultSet, pNumRows) {
pResultSet.getRows(pNumRows, function(readErr, rows) {
if(err) {
pResultSet.close(function (err) { // always close the result set
next(readErr);
});
return;
}
allRows.push(rows);
if (rows.length === pNumRows) {
fetchRowsFromResultSet(result.outBinds[attributeName], numRows);
return;
}
var allRowsResult = Array.prototype.concat.apply([], allRows);
generateJsonFromDbResultSet(pResultSet.metaData, allRowsResult, sp, function(resultSet) {
pResultSet.close(function (err) { // always close the result set
next(null, resultSet);
});
});
});
}
fetchRowsFromResultSet(result.outBinds[attributeName], numRows);
return;
}
}
next(null, result.outBinds);
});
}
Function definition added - buildParams
function buildParams(params) {
for(var attributeName in params) {
params[attributeName].val = typeof params[attributeName].val === 'undefined' ? null : params[attributeName].val;
if(params[attributeName].type.is(dbParams.DATE))
params[attributeName].val = params[attributeName].val ? new Date(params[attributeName].val) : null;
params[attributeName].type = params[attributeName].type.value;
params[attributeName].dir = params[attributeName].dir.value;
}
return params;
}
Any help, dear members ?

NODEJS, PUG, EXPRESS - Cannot read property 'length' of undefined

I have an API that i tweaked to make 2 calls to my DB and pull down information .. I can see the results in my console log so i know it is working
The next part is when it renders the view i need to show the results in two places
Here is the code for the API that makes 2 calls to the DB
function apples(req, res, next) {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'apples'", function(err, recordsetapples) {
var arrayLength = recordsetapples.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetapples[i]["price"]);
};
res.render('index', { resultsapples: recordsetapples });
return next();
});
});
};
function pear(req, res, next) {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'pear'", function(err, recordsetpear) {
var arrayLength = recordsetpear.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetpear[i]["price"]);
};
res.render('index', { resultspear: recordsetpear });
next();
});
});
};
app.get('/fruit', apples, pear);
So after that runs I can see the price print in console log .. Then i see this error
Cannot read property 'length' of undefined
What i expect to see if the price appear ... To get that info i have this code
tr
th.hidden-phone Fruit
th.hidden-phone Price
tr
each val in resultsapples
td.hidden-phone Apples
td.hidden-phone !{val.price}
tr
each val in resultspear
td.hidden-phone Pears
td.hidden-phone !{val.price}
The problem is your view expects both lists at the same time but you attempt to render the view twice with each list separately, which means in either scenario one list in the view will be undefined.
Even if you were to fix this, this approach won't work anyway because after the first res.render the HTTP response will end and return to the client. Ideally you would want to make one trip to the DB for both resultsets and then render the view e.g.
sql.connect(config, () => {
const request = new sql.Request();
request.query("select price from table WHERE fruit = 'apples' OR fruit = 'pear'", (err, result) => {
res.render('index', {
resultsapples: result.recordsets[0],
resultspear: result.recordsets[1]
});
});
});
As James mentioned your callbacks are async so you're trying to render the view twice. You also need some error handling in your sql functions
function apples(cb) {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'apples'", function(err, recordsetapples) {
if(err) {
return cb(err);
}
var arrayLength = recordsetapples.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetapples[i]["price"]);
};
cb(false, recordsetapples);
});
});
};
function pear(cb) {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'pear'", function(err, recordsetpear) {
if(err){
return cb(err)
}
var arrayLength = recordsetpear.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetpear[i]["price"]);
};
cb(false,recordsetpear);
});
});
};
app.get('/fruit', (req,res) => {
apples((appleerr,appleset) => {
if(appleerr){
//render error page
} else {
pear((pearerr, pearset) => {
if(pearerr) {
//render error page
} else {
return res.render('index', {
resultapples: appleset,
resultpears: pearset
});
}
})
}
});
});
Now for the record, I'm not a fan of nesting the callbacks like this so I would actually recommend you look at Promises and/or async/await but I'm not sure on your coding level so I didn't want to throw too many concepts at you at once.
Also whereas James has merged your SQL statements into one (which is probably the right approach for you) I kept them separate not knowing if you were reusing these individual pieces of code elsewhere and as such didn't want to combine them.
If you are interested in the promise implementation it might look as follows:
function apples() {
return new Promise((resolve,reject) => {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'apples'", function(err, recordsetapples) {
if(err) {
reject(err);
}
var arrayLength = recordsetapples.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetapples[i]["price"]);
};
resolve(recordsetapples);
});
});
};
function pear() {
return new Promise((resolve,reject) => {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'pear'", function(err, recordsetpear) {
if(err){
reject(err)
}
var arrayLength = recordsetpear.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetpear[i]["price"]);
};
resolve(recordsetpear);
});
});
});
};
app.get('/fruit', (req,res) => {
var applePromise = apples()
var pearsPromise = applePromise.then((appleSet)) {
return pear()
}
Promise.all([applePromise,pearsPromise]).then((([appleSet,pearSet]) => {
res.render('index', {
resultapples: appleSet,
resultpear: pearSet
});
}).catch((err) => {
//render error
})
});

nodejs + Mongodb: Inserting into two collections in sequence repeats last value in second

I am using the following to insert into MongoDB.
var tagData = JSON.parse(data);
var allTags = tagData.tags;
for (var j = 0; j < allTags.length; j++) {
var p = allTags[j].tagId.toString();
for (var k = 0; k < loggerParams.length; k++) {
var q = Object.keys(loggerParams[k]).toString();
if (p === q) {
// Prepare raw data tag
var tagRawDoc = {};
// Simple key-value assignment here
// Document prepared; ready to insert into MongoDB
database.addDocument('tagraw', tagRawDoc, function (err) {
if (err) {
log.info(util.format('Error adding document to tagrawdatas. %s', err.message));
throw err;
} else {
// Prepare history tag
var historyTagDoc = {};
historyTagDoc.tagNameAlias = tagRawDoc.tagNameAlias;
// Simple key-value assignment here
// Document prepared; ready to insert into MongoDB
database.addDocument('taghistory', historyTagDoc, function (err) {
if (err) {
log.info(util.format('Error adding document to tagrawdatas. %s', err.message));
throw err;
}
});
}
});
// Match found; exit loop
break;
}
}
}
The loggerParms is a simple JSON document read from file else-where. It allows for look-up in this code to build the document to be inserted. There will be 12 values in the allTags array. These 12 values are inserted successfully into the tagraw collection. However, in taghistory collection, the values from the last (or most recent) entry made into tagraw collection is repeated 12 times. Why does this happen?
The database.addDocument is shown below. It is a part of this article I am trying to replicate.
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var logger = require('../../util/logger');
var util = require('util');
function DB() {
this.db = "empty";
this.log = logger().getLogger('mongoMange-DB');
}
DB.prototype.connect = function(uri, callback) {
this.log.info(util.format('About to connect to DB'));
if (this.db != "empty") {
callback();
this.log.info('Already connected to database.');
} else {
var _this = this;
MongoClient.connect(uri, function(err, database) {
if (err) {
_this.log.info(util.format('Error connecting to DB: %s', err.message));
callback(err);
} else {
_this.db = database;
_this.log.info(util.format('Connected to database.'));
callback();
}
})
}
}
DB.prototype.close = function(callback) {
log.info('Closing database');
this.db.close();
this.log.info('Closed database');
callback();
}
DB.prototype.addDocument = function(coll, doc, callback) {
var collection = this.db.collection(coll);
var _this = this;
collection.insertOne(doc, function(err, result) {
if (err) {
_this.log.info(util.format('Error inserting document: %s', err.message));
callback(err.message);
} else {
_this.log.info(util.format('Inserted document into %s collection.', coll));
callback();
}
});
};
module.exports = DB;
That's because you are mixing a/multiple synchronous for and asynchronous code with database.addDocument which cause issues with function scope in nodejs.
A simple example of this kind of thing:
for(var i = 0; i < 10; i++){
setTimeout(() => console.log(i), 0);
}
You should use a package like async to handle flow control when iterating arrays/object asynchronously.
Simple example of your code refactored to use async:
var async = require('async');
var tagData = JSON.parse(data);
var allTags = tagData.tags;
async.each(allTags, function(tag, done){
var p = tag.tagId.toString();
var loggerParam = loggerParams.find(function(loggerParam){
var q = Object.keys(loggerParam).toString();
return p === q;
});
var tagRawDoc = {};
// Simple key-value assignment here
// Document prepared; ready to insert into MongoDB
return database.addDocument('tagraw', tagRawDoc, function (err){
if (err) return done(err);
// Prepare history tag
var historyTagDoc = {};
historyTagDoc.tagNameAlias = tagRawDoc.tagNameAlias;
// Simple key-value assignment here
// Document prepared; ready to insert into MongoDB
return database.addDocument('taghistory', historyTagDoc, done);
});
}, (err) => {
if(err) throw err;
console.log('All done');
});

wit.ai : sessionid undefined in my runActions function

I am writing my first apps with wit.ai using a node.js backend. I found some posts here similar to my question, but not really the answer :
I use a socket.io to communicate with my node script.
The two relevant parts of my node are :
io.sockets.on('connection', function (socket) {
socket.on('message',
function(data) {
var json = JSON.parse(data);
var sid = json['sessionid'];
console.log("Received on sid " + sid);
if (_sessions[sid] == undefined) {
_sessions[sid] = {};
_sessions[sid].context = {};
}
_sessions[sid].socket = socket;
client.runActions(sid, json['text'], _sessions[sid].context, 30)
.then((context) => {
_sessions[sid].context = context;
}
)
.catch((err) =>
{
console.error('Oops! Got an error from Wit: ', err.stack || err);
}
);
}
);
}
);
========
const actions = {
send(request, response) {
const {sessionId, context, entities} = request;
const {text, quickreplies} = response;
return new Promise(function(resolve, reject) {
var session = _sessions[sessionId];
console.log("-------------------------------");
console.dir(context);
console.log("-------------------------------");
session.socket.emit("message", JSON.stringify(response));
return resolve();
});
},
gettaxi ({sessionid, context, text, entities}) {
return new Promise(function(resolve, reject) {
console.log(`Session ${sessionid} received ${text}`);
var quand = firstEntityValue(entities, "quand");
if (!quand && context['quand'] != undefined) quand = context['quand'];
var depart = firstEntityValue(entities, "depart");
var arrivee = firstEntityValue(entities, "arrivee");
if (depart) {
console.log("Found depart");
context.depart = depart;
delete context.missing_depart;
}
else {
context.missing_depart = true;
}
if (arrivee) {
console.log("Found arrivee");
context.arrivee = arrivee;
delete context.missing_arrivee;
}
else {
context.missing_arrivee = true;
}
console.dir(context);
if (quand) {
console.log("Found quand");
context.quand = quand;
delete context.missing_quand;
}
else {
context.missing_quand = true;
}
return resolve(context);
}
);
},
};
All is working rather good, except than my gettaxi receives a undefined sessionid.
It's not the case for the send function that receives the correct sessionid.
What am I doing wrong ?

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
})

Resources