Inconsistent behaviour with concurrent postgresql access through node.js - node.js

A rather trivial use case of node.js and postgresql puzzles me.
It seems that the results from the first query executed by this code linger and are erroneously included in the results array from the second query.
Expected output:
running queries ---------------
[ { name: 'Company 0' } ]
[ { id: 0, company_id: 0 }, { id: 1, company_id: 0 } ]
Usual output:
running queries ---------------
[ { name: 'Company 0' } ]
[ { name: 'Company 0' },
{ id: 0, company_id: 0 },
{ id: 1, company_id: 0 } ]
Occasionally I get the correct result. Below the code, include a 'fix' which ensures I always get the correct result:
var pg = require('pg');
var connectionString = require('./dbconfig.js');
var doQuery = function(queryString) {
// Get a Postgres client from the connection pool
pg.connect(connectionString, function(err, client, done) {
results = [];
// results.push(queryString); // PART OF 'FIX'
var query = client.query(queryString);
// Stream results back one row at a time
query.on('row', function(row) {
results.push(row);
});
// After all data is returned, close connection and return results
query.on('end', function() {
client.end();
// results.splice(0, 1); // PART OF 'FIX'
console.log(results);
});
// Handle Errors
if(err) {
console.log(err);
}
});
};
var company_id = 0;
console.log('running queries ---------------');
doQuery('SELECT companies.name FROM companies WHERE companies.id = '+company_id+';');
doQuery('SELECT * FROM users WHERE users.company_id = ' + company_id + ';');
If anyone is interested in attempting to reproduce this (and assuming it is not a silly coding error by me) I will gladly provide the rest of the code to do so.

JavaScript is a terrible language (see my comment for solution).

Related

Node Js forEach unable to get result

Im trying to get iterate the results array variable.
I am able to get the array values inside the function, but when i try to log it outside it shows null.
Wen i googled few posts, i see that forEach was not recommended, suggested was for loop, i tried even that and i get null for result.
What is the issue in cursor.forEach()...
router.get('/getlist', function(req, res, handleError) {
client.connect('mongodb://localhost', function(err, client) {
if (err) throw err;
var db = client.db('angular-demo');
var collection = db.collection("api_details");
var query = {};
var cursor = collection.find(query);
var results = new Array();
var results = cursor.forEach(
function(result) {
return result;
console.log("insert")
console.log(results);
}
);
console.log("append")
console.log(results); //results here shows null
});
});
Log result:
append
[]
insert
[ { _id: 5a6867c8e54f6120709eabc5,
app_id: 'CaseRegistration',
description: 'API to register cases in the system',
cost_per_usage: '0.5',
__v: 0 } ]
insert
[ { _id: 5a6867c8e54f6120709eabc5,
app_id: 'CaseRegistration',
description: 'API to register cases in the system',
cost_per_usage: '0.5',
__v: 0 },
{ _id: 5a6867fde54f6120709eabc6,
app_id: 'CheckCreation',
description: 'CREs create the case with minimal data and assigns it to case initiation team to create checks',
cost_per_usage: '1',
__v: 0 } ]
If you want to grab results from find you can use toArray
cursor.toArray(function (error, documents) {
console.log(documents)
})
// or
cursor.toArray().then(function (documents) {
console.log(documents)
})
// or in async function
const documnets = await cursor.toArray()
or if you need to transform them somehow use map
It's not relevant if you use forEach or for loop in this case. The results variable isn't being populated because you're trying to populate it with the return result from the forEach which doesn't return anything useful.
What you want to do, is just iterate over the results, and fill the results array while iterating.
Something more like this :
var results = [];
cursor.forEach(
function(result) {
results.push(result);
}
);
console.log(results); // This should be populated now

Order and limit results in a query with a callback

Using Mongoose, I'd like to make a query with MongoDB and order and limit the results I get. I am doing this with Node.js so I am using callbacks.
So far, I have managed to order my results like this:
myModel.find({ $query: {}, $orderby: { created_at : -1 }}, function (err, items) {
callback( null, items )
});
How can I limit the results I get selecting and index and the number of items I want to get?
Using mongodb native:
http://mongodb.github.io/node-mongodb-native/api-generated/collection.html#find
myModel.find(filter)
.limit(pageSize)
.skip(skip)
.sort(sort)
.toArray(callback);
You can also specify the items in your query:
myModel.find(filter, {sort: {created_at: -1}, limit: 10}, function(err, items){
});
There is no $orderby in node mongodb native, so I'm not sure what library or other tool you're using.
...
Now that you've clarified Mongoose (which in general I recommend against):
myModel.find(filter).limit(10).exec(function(err, items){
//process
});
To sort documents, we can apply sort on a cursor object. To enforce order of sort, instead of passing an object, we need to pass an array to the sort method.
var MongoClient = require('mongodb').MongoClient,
commandLineArgs = require('command-line-args'),
assert = require('assert');
var options = commandLineOptions();
MongoClient.connect('mongodb://localhost:27017/crunchbase', function(err, db) {
assert.equal(err, null);
console.log("Successfully connected to MongoDB.");
var query = queryDocument(options);
var projection = {
"_id": 0,
"name": 1,
"founded_year": 1,
"number_of_employees": 1
};
var cursor = db.collection('companies').find(query);
cursor.project(projection);
cursor.limit(options.limit);
cursor.skip(options.skip);
cursor.sort([
["founded_year", 1],
["number_of_employees", -1]
]);
var numMatches = 0;
cursor.forEach(
function(doc) {
numMatches = numMatches + 1;
console.log(doc.name + "\n\tfounded " + doc.founded_year +
"\n\t" + doc.number_of_employees + " employees");
},
function(err) {
assert.equal(err, null);
console.log("Our query was:" + JSON.stringify(query));
console.log("Documents displayed: " + numMatches);
return db.close();
}
);
});
function queryDocument(options) {
console.log(options);
var query = {
"founded_year": {
"$gte": options.firstYear,
"$lte": options.lastYear
}
};
if ("employees" in options) {
query.number_of_employees = {
"$gte": options.employees
};
}
return query;
}
function commandLineOptions() {
var cli = commandLineArgs([{
name: "firstYear",
alias: "f",
type: Number
}, {
name: "lastYear",
alias: "l",
type: Number
}, {
name: "employees",
alias: "e",
type: Number
}, {
name: "skip",
type: Number,
defaultValue: 0
}, {
name: "limit",
type: Number,
defaultValue: 20000
}]);
var options = cli.parse()
if (!(("firstYear" in options) && ("lastYear" in options))) {
console.log(cli.getUsage({
title: "Usage",
description: "The first two options below are required. The rest are optional."
}));
process.exit();
}
return options;
}
One thing to notice is the order in which MongoDB applies skip, limit and sort
sort
skip
limit
There's also a possibility that we can sort data on the MongoDB side as well, provided that we've setup the indexing.
Notice that MongoDB driver will send a query when we call a cursor method passing a callback function to process query results.

mongoose findOneAndUpdate callback with array of ids

I am using findOneAndUpdate in a forEach loop to create/update multiple entries.
I would like it to return an array of all the object id's it has created or updated.
During the loop, I can see it adding data to the array, but one it leaves the loop, the array is empty.
Should the array not be populated?
here is my code.
var softwareArray = ["Software1","Software2","Software3"],
updatedArray = [];
softwareArray.forEach(function(software){
Software.findOneAndUpdate(
{
Name: software
},
{
Name: software
},
{upsert:true},
function(err, rows){
updatedArray.push(rows._id);
console.log(updatedArray); //This has data in it....
}
);
});
console.log(updatedArray); //This has no data in it...
Edit: Updated with my working changes for Thiago
var softwareArray = ["Software1","Software2","Software3"],
updatedArray = [];
loopSoftware(softwareArray, function(updatedArray){
console.log(updatedArray);
//carry on....
}
function loopSoftware(input, cb){
var returnData = [];
var runLoop = function(software, done) {
Software.findOneAndUpdate(
{Name: software},
{Name: software},
{upsert:true},function(err, rows){
returnData.push(rows._id);
done()
}
);
};
var doneLoop = function(err) {
cb(returnData);
};
async.forEachSeries(input, runLoop, doneLoop);
}
I decorated your code to make you see when what is happening:
var softwareArray = ["Software1","Software2","Software3"],
updatedArray = [];
// TIMESTAMP: 0
softwareArray.forEach(function(software){
// TIMESTAMP: 1, 2, 3
Software.findOneAndUpdate(
{
Name: software
},
{
Name: software
},
{upsert:true},
function(err, rows){
// TIMESTAMP: 5, 6, 7
updatedArray.push(rows._id);
console.log(updatedArray); // This has data in it....
// want to use the result?
if (updatedArray.length == softwareArray.length) {
console.log(updatedArray);
}
}
);
});
// TIMESTAMP: 4
console.log(updatedArray);
Of course this will happen - just like any other networking on Node, it's asynchronous!
This means that the callback you specified for your findOneAndUpdate operation have not run yet when it reaches the console.log(updatedArray); code.
Take a look at Q for working around this common problem.

Node.JS cradle and couchDB assistance

I am a noob with Node.JS.
I am using CouchDB and Cradle.
In couchDB I have a database named 'test' and inside it I have a document named 'exercise'.
The document has 2 fields: "FullName" and "Age".
The code in order to save the data is as follows:
var cradle = require('cradle');
var connection = new(cradle.Connection)('http://127.0.0.1', 5984, {
auth: { username: 'toto_finish', password: 'password' }
});
var db = connection.database('test');
db.save('exercise', {
FullName: param_name, Age: param_age
}, function (err, res) {
if (err) {
// Handle error
response += ' SAVE ERROR: Could not save record!!\n';
} else {
// Handle success
response += ' SUCESSFUL SAVE: The record was saved in CouchDB!\n';
}
http_res.end(response);
});
this code works well and it saves the data to the CouchDB.
My problem is when I want to read the data.
The code that I wrote is:
var cradle = require('cradle');
var connection = new(cradle.Connection)('http://127.0.0.1', 5984, {
auth: { username: 'toto_finish', password: 'password' }
});
var db = connection.database('test');
db.view('exercise/all', {descending: true}, function(err, res)
{
console.log(res);
res.forEach(function (row) {
response = 'FullName: ' + row.FullName + '\n Age: ' + row.Age + '\n';
});
});
http_res.end(response);
when I am trying to print response, response is empty and I don't know what I am doing wrong. I know that it does not go inside the forEach loop but I don't understand why.
the console output is:
[ { id: 'exercise',
key: null,
value:
{ _id: 'exercise',
_rev: '1-7042e6f49a3156d2099e8ccb3cc7d937',
FullName: 'Toto Finish',
Age: '30' } } ]
Thanks in advance for any response or answer.
Try moving the http_res.send() call inside the callback provided to db.view - the anonymous function( err, res ) { }.
I'm not sure however about the .forEach statement, you'll only get the last value from your query in the response variable, you should look into that as well.
spotirca is right
The db.view function is async so http_res.end(response) gets called before the view returns any data.
You can prove this by returning the date in both the console.log and http_res.end
console.log(res, new Date())
and
http_res.end(response, new Date());
The http response will have the earlier date/Time.

mongoJS parsing JSON output

I'm playing with nodeJS and mongoJS. I've successfully queried the DB and returned a set of data:
var databaseUrl = "redirect"; // "username:password#example.com/mydb"
var collections = ["things", "reports"]
var db = require("mongojs").connect(databaseUrl, collections);
db.things.find({url: "google.com"}, function(err, things) {
if( err || !things) {
console.log("URL not Found");
}
else things.forEach( function(RedirectURL) {
console.log(RedirectURL);
} );
});
This returns this:
{ _id: 4fb2c304f2dc05fe12e8c428,
url: 'google.com',
redirect:
[ { url: 'www.goolge.com', weight: 10 },
{ url: 'www.google.co.uk', weight: 20 } ] }
What I need to do now is select certain elements of the array such as redirect.weight or redirect.url.
Does mongoJS allow me to do this easily or is there a better way?
Any pointers greatly appreciated as ever!
Ric
MongoJS implements the mongo API. Calls to document contents use the dot notation.
So in the example above,
var weight = RedirectURL.redirect.0.weight
// equal to 10, since it is the first item of the redirect array.
The same principle can be used with the find commands. So if you wish to find documents with weight = 10, use the following command
db.things.find({redirect.weight: 10})

Resources