I have this mongo-db.js file:
var MongoClient = require('mongodb').MongoClient,
ObjectID = require('mongodb').ObjectID;
exports.MongoDB = function() {
this.DB_NAME = "myDBNAME";
this.MongoClient = MongoClient;
return this;
};
exports.MongoDB.prototype.openDB = function(action) {
console.log("Open DB");
var scope = this;
this.MongoClient.connect(this.generateMongoUrl(), function(err, db) {
if (!err) {
console.log("Open DB Success");
if (action && typeof action === "function") {
action(db, scope);
}
} else {
console.log("DB Connect Error: " + err);
}
});
};
exports.MongoDB.prototype.closeDB = function(db, action) {
var scope = this;
return function() {
console.log("Close DB and apply action with arguments");
db.close();
action.apply(this, arguments);
};
};
exports.MongoDB.prototype.getItemById = function(id, callback) {
this.openDB(function(db, scope) {
console.log("Retrieving item: " + id);
db.collection("items", function(err, collection) {
if (err) { callback(err); }
else {
collection.findOne({ _id: ObjectID.createFromHexString(id) }, scope.closeDB(db, callback));
}
});
});
};
exports.MongoDB.prototype.getAllItems = function(callback) {
this.openDB(function(db, scope) {
console.log("Retrieving all items");
db.collection("items", function(err, collection) {
if (err) { callback(err); }
else {
collection.find({ }).toArray(scope.closeDB(db, callback));
}
});
});
};
And I run the following:
var scope = this;
var _items = [];
var counter = 0;
var done = function() {
console.log("done!");
};
var getItem = function(error, item) {
if (error) { }
else {
console.log("done loading item " + item._id);
_items.push(item);
if (_items.length === counter) {
done();
}
}
};
this.db.getAllItems(function(error, items) {
if (error) { }
else {
for (var i = 0; i < items.length; i++) {
var id = items[i]._id;
if (id) {
counter++;
console.log("load item " + id);
scope.db.getItemById(id, getItem);
}
}
}
});
When running this code I get:
Open DB
Open DB Success
Retrieving all items
Close DB and apply action with arguments
load item 50fb26263d47b70000000001
Open DB
load item 50fb277f172a5d0000000001
Open DB
load item 50fb2aa7865d870000000001
Open DB
load item 5102b7ddfe581ce5c7000001
Open DB
load item 5109678839aefde9fe000001
Open DB
load item 51096a91d0b50572ff000001
Open DB
load item 51096b06405d398bff000001
Open DB
load item null
Open DB
load item 51098b6b58bc1d0000000001
Open DB
load item 51098e16fb0e710000000001
Open DB
load item 51098e31a725100000000001
Open DB
load item 510991f20bc7690000000001
Open DB
load item 51099261258c710000000001
Open DB
load item 5109928b7edf7a0000000001
Open DB
load item 510992f0c73ccc0000000001
Open DB
load item 51099336e8a0090000000001
Open DB
load item 5109938d5fc9ce0000000001
Open DB
load item 510993cc8159610000000001
Open DB
load item 51099530ab74fb0000000001
Open DB
load item 5109956e8b059e0000000001
Open DB
load item 510995965f38da0000000001
Open DB
load item 510995ca14c0610000000001
Open DB
load item 5109963f2acd750000000001
Open DB
load item 5109966fc7001b0000000001
Open DB
Open DB Success
Retrieving item: 5109928b7edf7a0000000001
/myproj/node_modules/mongodb/lib/mongodb/connection/server.js:481
throw err;
^
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
at Function.createFromHexString (/myproj/node_modules/mongodb/node_modules/bson/lib/bson/objectid.js:212:11)
at exports.MongoDB.getItemById (/myproj/js/mongo-db.js:95:52)
at Db.collection (/myproj/node_modules/mongodb/lib/mongodb/db.js:496:44)
at exports.MongoDB.getItemById (/myproj/js/mongo-db.js:92:12)
at exports.MongoDB.openDB (/myproj/js/mongo-db.js:72:17)
at MongoClient.connect (/myproj/node_modules/mongodb/lib/mongodb/mongo_client.js:112:5)
at _finishConnecting (/myproj/node_modules/mongodb/lib/mongodb/db.js:2126:7)
at Db.open (/myproj/node_modules/mongodb/lib/mongodb/db.js:249:14)
at Server.connect.connectCallback (/myproj/node_modules/mongodb/lib/mongodb/connection/server.js:277:7)
at g (events.js:192:14)
When I put console.log(typeof id); before Object.createFromHexString(...), I get Object.
Trying to change the id by doing id=id+""; made the same error.
Why do I get this error?
When using the MongoClient, the documents returned contain BSON ObjectID values and not strings. A BSON ObjectId using the native MongoClient has a structure like:
_id = {
id : "{12 bytes of binary data}",
_bsontype : "ObjectID"
}
So, if you try to run it on the createFromHexString method, it will crash, as it's already a BSON ObjectID.
Here's a simple example showing how no conversion is necessary from the Id that was returned originally.
var
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient;
MongoClient.connect("mongodb://localhost:27017/default?w=1", function (err, db) {
db.collection("research").find({}, {'_id' : 1}).toArray(function(err, results) {
results.forEach(function(doc) {
console.log(doc._id + " type " + typeof doc._id);
// now async get all docs
db.collection("research").findOne({'_id': doc._id}, function(err, doc) {
console.log("# " + doc._id);
console.log(doc);
});
});
});
});
Related
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 ?
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');
});
I'm writing a webservice using Node.js.
This webservice makes calls to a MongoDB and MSSQL.
For MSSQL i've use npm mssql library, for mongo i use the native npm mongodb library.
I use Q as my promise library.
I've a memory leak issue running a find over a MongoDB collection. I simply need to get elements from a connection. And eventually update the status of elements i get.
See my sample code below.
var Q = require('q');
var connection = require('..\connection.js'); //the connection module open a connection that can be used with pools.
function list(req, res) {
return Q.Promise(function(resolve, reject, notify) {
var collection = null;
var result = [];
var cursor = null;
Q.fcall(function(){}).then(function() {
collection = connection.collection(collectionName);
})
.then(function() {
cursor = collection.find({ fieldstatus : 0 });
return Q.Promise(function(resolve, reject, notify) {
Q.allSettled(cursor.each(function(err, item){
return Q.fcall(function(){
try {
if(item != null) {
result.push({
field1 : item.field1,
field2 : item.field2,
fieldstatus : item.fieldstatus
});
collection.update({_id: item._id}, {$set: {fieldstatus : 1}});
}
resolve(result);
} catch (err){
reject(err);
}
})
.fin(function() {
cursor.close();
});
}));
});
})
.then(function(ret) {
resolve(ret);
})
.fail(function(err) {
reject([ err.toString() ]);
})
.fin(function() {
result = null;
cursor = null;
collection = null;
});
})
.fail(function(err) {
throw([ err.toString() ]);
});
}
}
UPDATED : Answer 1
The code below seems to works without leak issuess.
var Q = require('q');
var connection = require('..\connection.js'); //the connection module open a connection that can be used with pools.
function list(req, res) {
return Q.Promise(function(resolve, reject, notify) {
var collection = null;
var result = [];
var cursor = null;
Q.fcall(function(){}).then(function() {
collection = connection.collection(collectionName);
})
.then(function() {
return Q.npost(
collection,
"find",
[
{ fieldstatus : 0 }
]
).then(function(ret){
return Q.npost(ret, "toArray").then(function(item){
return item;
});
})
.then(function(ret){
var result = [];
ret.forEach(function (item) {
result.push({
field1 : item.field1,
field2 : item.field2,
fieldstatus : item.fieldstatus
});
collection.update({_id: item._id}, {$set: {fieldstatus : 1}});
});
return result;
})
})
.then(function(ret) {
resolve(ret);
})
.fail(function(err) {
reject([ err.toString() ]);
})
.fin(function() {
collection = null;
});
})
.fail(function(err) {
throw([ err.toString() ]);
});
}
}
I'm having a weird issue with MongoDB. My database collection is closing, which I suppose is what it's supposed to do (I'm following along from the mongo boilerplate) BUT I see no reason why the docs would be null value. I've checked this every way I can think of, but I don't quite understand the cursor object.
Console.logging it seems to give me a bunch of native mongo properties ( which look like functions ie each, toArray, etc) so it seems right, but it's not a regular object with a data field that I can see.
After it hits that if block with the if(docs==null), the connection gets closed and it will not execute the each block in the else if.
Ideally if there was a way to help troubleshoot or figure out how to make this execute that would be great.
More background:
in the mongo shell I can ask for
use weather // no issues
and get the results of the data object which is 3000 records with an empty find();
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost:27017/weather', function(err, db) {
if(err){
console.log("line 7" + err);
}
var query = {};
var projection = { 'State' : 1, 'Temperature' : 1 };
var cursor = db.collection('data').find(query, projection);
console.log("cursor" + cursor); // [object Object]
var state = '';
var operator = {'$set' : {'month_high' : true } };
cursor.each(function(err, doc) {
if (err) throw err;
if (doc == null) {
console.log("docs have value:" + doc); //NULL VALUE so will close on line 23
return db.close();
} else if (doc.State !== state) {
// first record for each state is the high temp one
state = doc.State;
db.collection('data').update( {'_id':doc._id}, operator, function(err, updated) {
if (err) console.log(err);
// return db.close(); ?
});
}
});
});
{ [MongoError: Connection Closed By Application] name: 'MongoError' } //doh
{ [MongoError: Connection Closed By Application] name: 'MongoError' } //doh
{ [MongoError: Connection Closed By Application] name: 'MongoError' } //doh
Figuring out when to call db.close() can be a bit messy. Here it is rewritten with find().toArray() plus some logic to test when you're updating the last matched doc. This works for me.
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var Q = require('q');
MongoClient.connect('mongodb://localhost:27017/weather', function(err, db) {
assert.equal(null, err);
var query = {};
var projection = { 'State' : 1, 'Temperature' : 1 };
var state = '';
var operator = {'$set' : {'month_high' : true } };
var promises = [];
db.collection('data').find(query, projection).toArray(function(err, docs) {
assert.equal(null, err);
docs.forEach(function(doc, index, arr) {
var deferred = Q.defer();
promises.push(deferred.promise);
if (null !== doc && state !== doc.State) {
db.collection('data').update( {'_id':doc._id}, operator, function(err, updated) {
assert.equal(null, err);
console.log("Updated "+updated+" documents.");
deferred.resolve();
});
} else {
deferred.resolve();
}
});
Q.all(promises).done(function() {
console.log("closing");
db.close()
});
});
});
EDIT: Added Q since db.close() was still called prematurely in some cases.
There is a small code i have written below and issue is explained within comments. Problem is with the bold callback function while creating the collection. There is an error while creating the collection and the message should be displayed as soon as the main function for creating the collections is ending but message appears randomly as seen in the output below:
It is called unexpected on line marked with question mark. I am running this js file on node.js and mongoDB environment.
Thanks.
var mongo = require("mongodb");
var Db = mongo.Db;
var mongoC = mongo.MongoClient;
var assert = require('assert');
mongoC.connect("mongodb://localhost:27017/social", {
native_parser: true
}, function (err, db) {
if (!err) {
console.log("We are Connected!\n");
//creating the Collection
db.createCollection("node", {
strict: true
}, function (err, coll) {
if (!err) {
console.log("*********Collection Created Succesfully*********");
console.log("Collection created Successfully\n" + JSON.stringify(coll) + "\n-------------------\n");
}
else{
console.log("Cannot create Collection because " + err);
}
});
//Collection created now
console.log("*********************************inserting documents in the selected collection***********************");
var coll = db.collection('node');
var doc1 = {"name":"doc1","age":26};
var manydocs = [{"name":"doc2","age":45},{"name":"doc3","age":19}];
//coll.insert(doc1,{w:1},function(err,result){if(err){console.log("Error while inserting doc1 " + err);}else{console.log(result);}});
//coll.insert(manydocs,{w:1},function(err,result){if(err){console.log("Error while inserting manydocs " + err);}});
console.log("***************************documents are now updated successfully***********************************");
console.log("*******************Now let us update the documents*******************");
var query = {"name": "doc1"};
var update= {$set : {"age":86}};
//coll.update(query,update,function(err,result){if(!err){console.log(result + "'s age has been successfully update to " + result);}});
console.log("***************Docments updated");
console.log("*******************Querying the items**************************");
coll.find().each(function(err,myDoc){console.dir(myDoc);console.dir("hey");});
//coll.findOne(query,function(err,result){});
var stream = coll.find(query).stream();
stream.on("data",function(item){console.log(item._id);});
stream.on("end",function(){});
}
else {
console.log("Cannot connect because : " + err);
}
});
Below is the output.
We are Connected!
*********************************inserting documents in the selected collection***********************
***************************documents are now updated successfully***********************************
*******************Now let us update the documents*******************
***************Docments updated
*******************Querying the items**************************
Cannot create Collection because Error: Collection node already exists. Currently in strict mode.
You should work on node collection inside the db.createCollection's callback:
UPDATE: run this code:
var mongo = require("mongodb");
var Db = mongo.Db;
var mongoC = mongo.MongoClient;
var assert = require('assert');
mongoC.connect("mongodb://localhost:27017/social", {
native_parser: true
}, function (err, db) {
if (!err) {
console.log("We are Connected!\n");
//creating the Collection
db.createCollection("node", {
strict: true
}, function (err, coll) {
if (!err) {
console.log("*********Collection Created Succesfully*********");
console.log("Collection created Successfully\n" + JSON.stringify(coll) + "\n-------------------\n");
//Collection created now
console.log("*********************************inserting documents in the selected collection***********************");
var doc1 = {
"name": "doc1",
"age": 26
};
var manydocs = [{
"name": "doc2",
"age": 45
}, {
"name": "doc3",
"age": 19
}];
//coll.insert(doc1,{w:1},function(err,result){if(err){console.log("Error while inserting doc1 " + err);}else{console.log(result);}});
//coll.insert(manydocs,{w:1},function(err,result){if(err){console.log("Error while inserting manydocs " + err);}});
console.log("***************************documents are now updated successfully***********************************");
console.log("*******************Now let us update the documents*******************");
var query = {
"name": "doc1"
};
var update = {
$set: {
"age": 86
}
};
//coll.update(query,update,function(err,result){if(!err){console.log(result + "'s age has been successfully update to " + result);}});
console.log("***************Docments updated");
console.log("*******************Querying the items**************************");
coll.find().each(function (err, myDoc) {
console.dir(myDoc);
console.dir("hey");
});
//coll.findOne(query,function(err,result){});
var stream = coll.find(query).stream();
stream.on("data", function (item) {
console.log(item._id);
});
stream.on("end", function () {});
} else {
console.log("Cannot create Collection because " + err);
}
});
} else {
console.log("Cannot connect because : " + err);
}
});