I am trying to number the id's of my documents with node and mongodb. I am getting a 500 internal server error: 'Cannot read property 'seq' of null.
// submit a ticket - this is returning a 500 error - not updating ID
router.post('/ticketform', function(req, res){
var db = req.db;
function getNextSequence(name, callback){
db.counters.findAndModify(
{
query:{ _id:name},
update:{$inc:{seq:1} },
new:true
}, callback
);
}
getNextSequence('ticket', function(err, obj) {
if (err) console.error(err);
db.users.insert(
{
'_id': obj.seq,
'firstname':req.body.firstname,
'lastname':req.body.lastname,
'email':req.body.email,
'phone':req.body.phone,
'issue':req.body.issue
}, function(err,docs) {
if (err) console.error(err);
console.log(docs);
res.end();
});
});
});
You appear to be using the node native driver in which the syntax is a bit different from the mongodb shell. The .findAndModify() function in particular works quite differently.
As a complete working example for you to work into your application there is this code, along with the usage of the async module make the logic look a little cleaner:
var async = require("async"),
mongodb = require("mongodb"),
MongoClient = mongodb.MongoClient;
MongoClient.connect('mongodb://localhost/test',function(err,db) {
async.waterfall(
[
function(callback) {
db.collection("counters").findAndModify(
{ "_id": "ticket" },
[],
{ "$inc": { "seq": 1 } },
{ "upsert": true, "new": true },
function(err,doc) {
callback( err, doc.seq );
}
);
},
function(seq, callback) {
var doc = { "_id": seq };
db.collection("users").insert(
{ "_id": seq },
function(err, doc) {
callback( err, doc );
}
);
}
], function( err, result ) {
console.log( result );
}
);
});
The parameters on the function are the "query", "sort" which is an array and required even if blank, then the "update" document, "options" in which you want the updated document returned as well as "upsert" to create a new document where no matching document exists,
and finally the callback.
The async waterfall allows you to "pass through" results without embedding your functionality within the callback of the method you use to get the sequence. You don't need to do it that way, but the code looks a little cleaner and direct in it's purpose.
Alternately there are options such as mongoose if you want to use that which has a helper function in .findOneAndUpdate(). You might find connection management a little easier with that library, but it is up to you.
You're probably referring to this tutorial.. In the example they are showing the implementation in the MongoDB shell, where findAndModify is synchronous. As JohnnyHK pointed in the comments, findAndModify in Node.js is asynchronous and the result is returned in the callback.
In Node.js you would do something like this:
function getNextSequence(name, callback){
db.counters.findAndModify(
{ "_id": "ticket" },
[],
{ "$inc": { "seq": 1 }},
{ "new": true, "upsert": true },
callback
);
}
getNextSequence('ticket', function(err, obj) {
if (err) console.error(err);
db.users.insert(
{
'_id': obj.seq,
'firstname':req.body.firstname,
'lastname':req.body.lastname,
'email':req.body.email,
'phone':req.body.phone,
'issue':req.body.issue
}, function(err,docs) {
if (err) console.error(err);
console.log(docs);
res.end();
});
});
You should also check the documentation for the command to better understand the options.
Related
Why does this
router.get('/eventTest/:id', function (req, res) {
var event = Event.getEventById(req.params.id, function (err, rows) {
if(err) {
return err;
} else {
return rows;
}
});
res.json(event);
});
returns
{
"domain": null,
"_events": {},
"_eventsCount": 0,
"_callSite": {},
"_ended": false,
"_idleNext": null,
"_idlePrev": null,
"_idleStart": null,
"_idleTimeout": -1,
"_repeat": null,
"sql": "select * from event where id=?",
"values": [
"1"
],
"typeCast": true,
"nestTables": false,
"_resultSet": null,
"_results": [],
"_fields": [],
"_index": 0,
"_loadError": null
}
this and example of one of the methods that I'm calling
`getEventById:function(id,callback){
return db.query("select * from event where id=?",[id],callback);
}
`
rather than the actual values like it does when I use res.json(rows) inside the Event.getEventById callback?
I need this in order to call multiple methods and add them to an object and then send that object in JSON
Thank you
As per your code, yes you need to send response from method.
Since as Node.js nature is async so you need to write your code in that way
Try it in your route
Event.getEventById(req.params.id, function (err, rows) {
if(err) {
res.status(500).send(err);
} else {
res.status(200).send(rows);
}
});
UPDATE
To execute multiple methods and send all records at single time then go through
async module or promise
I tried "express-promise" like this:
`router.get('/eventTest/:id', function (req, res) {
res.json({
details: Event.getEventById(req.params.id),
stages: Event.getEventStages(req.params.id)
});
});`
and still got the same result (i.e. the query and not the values). I'm guessing that my problem is the way that I managing the callbacks from the methods. Is this right?
router.get('/eventTest/:id', function (req, res, next) {
Event.getEventById(req.params.id, function (err, rows) {
if(err)
return next(err);
res.json(rows);
});
});
I'm trying to setup (this is my first attempt) a function that receives input, passes it through a function, then the results of that through a follow up.
I'm getting the following error
TypeError: Cannot read property 'exec' of undefined
I've never used Promises before and I believe that's what I should be doing in this instance, I don't think I'm quite understanding how to structure things with the .exec and .then parts.
app.get('/api/gameobjects/:userid/:golng/:golat/:dist', (req, res) =>
{
console.log("First part");
GameObject.getNearGameObjects(req.params.golng, req.params.golat, req.params.dist)
.exec((err, gameobject) =>
{
if (err)
{
res.json(err);
}
})
.then((gameobject) =>
{
console.log("Third part");
User.addGameObjectToUser(req.params.userid, gameobject)
})
});
EDIT:
Code for the getNearGameObjects:
// Get Near Game Objects
module.exports.getNearGameObjects = function( long, lat, dist, callback, limit )
{
var coords = [];
coords[ 0 ] = long;
coords[ 1 ] = lat;
var query =
{ geometry: { $near: { "$geometry": { "type": "Point", "coordinates": [long, lat] }, $maxDistance: dist } } };
GameObject.find( query, callback ).limit( limit );
};
Code for the addGameObjectToUser
// Add object to user
module.exports.addGameObjectToUser = function(id, gameobject, callback)
{
var query = { _id: id };
User.findByIdAndUpdate( query, { "$addToSet" : { "inventory" : gameobject } } );
};
If you don't return anything from the function, by default it returns undefined
You're getting
TypeError: Cannot read property 'exec' of undefined
because .exec() will be called on the returned undefined.
If you want to call .exec() on the find(), you should remove callback parameter on it. So that it returns promise
Making those changes,
module.exports.getNearGameObjects = function (long, lat, dist, callback, limit) {
return GameObject.find({
geometry: {
$near: {
"$geometry": {
"type": "Point",
"coordinates": [long, lat]
},
$maxDistance: dist
}
}
}).limit(limit);
};
Similarly, .catch() will handle error when it occurs.
GameObject.getNearGameObjects(req.params.golng, req.params.golat, req.params.dist)
.exec().then((gameobject) => {
console.log('Third part');
return User.addGameObjectToUser(req.params.userid, gameobject);
}).catch((err) => {
res.json(err);
});
I decided to go back to the code I know and nested by called based on result, this means more code, but more importantly it works.
I'd still be very interested in understanding how to do promise based code in the API, as it does look like it would be a tidier format to work with.
Here's the working code:
app.get('/api/gameobjects/:userid/:golng/:golat/:dist', function (req, res)
{
console.log("starting");
GameObject.getNearGameObject(req.params.golng, req.params.golat, req.params.dist, function (err, gameobject)
{
if (err)
{
res.json(err);
}
console.log("got a game object");
User.addGameObjectToUser(req.params.userid, gameobject, function (err, user)
{
if (err)
{
res.json(err);
}
console.log("updated a user");
GameObject.removeGameObjectByLocation(req.params.golng, req.params.golat, function(err, gameobject)
{
if (err)
{
res.json(err);
}
console.log("removed a game object");
res.json(gameobject);
});
});
});
});
I have an array of documents with unique _id and I want to insert them to my database. Some of them already in db, and for those I want to update an array property (push in array an item). All of this I need to make asyncronuosly, so after all inserted/updated I want to write response back (with callback) to client than all ok or write an error. After googling on subject I've found this solution with async module I've tried to implement it for my case. Now my code looks like this:
function processUsers(arr, listName, callback) {
var users = global.db.collection('vkusers');
var q = async.queue(function(task, cb) {
console.log('upsert butch');
users.insert(task.doc, function(err, doc) {
if (err) {
users.update({
_id : task.doc._id
}, {
$addToSet : {
mLists : listName
}
}, function(error, result){ console.log(error); console.log(result); });
}
});
}, arr.length);
for ( var doc in arr) {
q.push({
doc : arr[doc]
}, function(err) {
if (err)
callback(err, null);
})
}
q.drain = function() {
// this is the queue's callback, called when the queue is empty,
// i.e. when all your documents have been processed.
console.log('drain');
callback(null, { result: "success", upserted: arr.length });
}
}
Callback has signature callback(error, result), arr - my array of documents. I've tested it and with database everything is OK, i am getting the right result. But callback, and q.drain never fired!
You need to call async.queue's callback (cb in your code) when your insert/update is complete. Something like this:
var q = async.queue(function(task, cb) {
console.log('upsert butch');
users.insert(task.doc, function(err, doc) {
if (err) {
users.update({
_id : task.doc._id
}, {
$addToSet : {
mLists : listName
}
}, function(error, result) {
console.log(error);
console.log(result);
cb(error); // Update finished; call cb and pass in "error" so that it can bubble up if it exists
});
} else {
cb(); // Insert succeeded; call cb
}
});
}, arr.length);
I'm fixing some bugs in one project. I need synchronous cursor, to load data in loop.
I have code:
var mongo = require('mongojs'),
dataMaps = [];
mongo.penguin_friends.find({ user_id_1: id_user }, function(err, friends) {
friends.forEach(function(data) {
var cursor = mongo.penguin_user_maps3.find({ user_id: data.id_user }, { fields: { maps: 1 } });
//I need to do dataMaps.push(cursor.data);
});
console.log("processingThisSomething()");
processSomething();
});
I need to complete this request before calling processSomething(); So I need to process mongodb query inside a loop synchronously.
It's not possible to make the queries synchronous as the API doesn't support it.
You'll have to provide a callback to .find() or a cursor method to receive the results:
cursor.toArray(function (err, maps) {
if (!err) {
dataMaps.push(maps);
}
});
But, you can replace the iterator with one that's asynchronous-aware, such as async.each(), to continue when they've completed:
async.each(
friends,
function (data, callback) {
var cursor = mongo....;
cursor.toArray(function (err, maps) {
if (!err) {
dataMaps.push(maps);
}
callback(err);
});
},
function (err) {
if (!err) {
processSomething();
}
}
);
i am inserting data into mongodb using mongodb driver in nodejs.
var mongodb = require('mongodb');
var insert = function(uri, collectionName, data, next) {
mongodb.MongoClient.connect(uri, function(err, driverDb) {
if(err) {
next(err);
} else {
driverDb.collection(collectionName).insert(data,function(err,result) {
if(err) {
next(err);
} else {
driverDb.close(function (err) {
if(err) {
next(err);
} else {
next(null,result);
}
});
}
});
}
});
};
insert('mongodb://localhost/mean-test','testcol',{
a : 'Apple',
b : [ { ba: 'Boy' }, {bb : 'Bird'} ]
}, function(err,models) {
console.log(models);
});
The above result into following:
[{a:'Apple', b : [[Object]] }]
How do i achieve this :
[{_id:ObjectId("someid"), a:'Apple', b : [{_id:ObjectId("someid"), ba: 'Boy' }, {_id:ObjectId("someid"), bb : 'Bird'}] }]
Please note i do not want to use any other npm module except of mongodb.
also i want to insert in one db query.
Your objects are inserting correctly, it's just that console.log only shows two levels of object detail by default. To show all object levels you need to call util.inspect directly so you can control that:
console.log(util.inspect(models, {depth: null}));