Node.js callback with node_redis - javascript

I'm still trying to get into node.js and probably getting some things not quite right. What I'm looking foward to accomplish is to query first a hmap containing a list of rooms. This list is going to be iterated trough to get for each room further details like room name etc.
Here's what the query should return:
redis 127.0.0.1:6379> lrange rooms 0 -1
1) "room:5000"
and
redis 127.0.0.1:6379> hgetall room:5000
1) "name"
2) "room1"
3) "admin"
4) "user:1001"
5) "public"
6) "true"
here's my function within the routes.index
exports.index = function(req, res){
var render_rooms = new Array();
req.app.settings.redis.lrange('rooms',0,-1, function(error, rooms) {
if (error) {
console.log('Error: '+ error);
}
else {
rooms.forEach(function(room){
console.log("room: " + room);
req.app.settings.redis.hgetall(room, function(error, roomdetails){
if (error) {
console.log('Error: '+ error);
}
else {
console.log("roomdetails: " + roomdetails.public);
if(roomdetails.public == "true"){
render_rooms.push(roomdetails.name);
}
}
});
});
// console.log('Name: ' + result);
// res.render('account', { title: 'account title', user: req.user.username, votes: result });
}
});
console.log("length: " + render_rooms.length);
res.render('index', { title: 'Index', username: req.user.username, rooms: render_rooms });
};
I'm not sure if I'm using node_redis properly to achieve this. Further I came up with the idea to store all room details in an array which I'm looking forward to send to the view. Apparently the list always display no elements as I guess is called before the list is filled as I'm missing some essential callback functionality. Howeever I'm not able to fiddle it in. Can someone explain me in some more detail how it "should" work?

Your basic problem is that you need to wait to render the render_rooms array until all the asynchronous processing has completed. The way it is written now, res.render is being called before any of the async Redis queries have completed.
Something like this:
exports.index = function(req, res){
var render_rooms = new Array();
req.app.settings.redis.lrange('rooms',0,-1, function(error, rooms) {
// Keep track of the number of rooms we have left to process.
var roomcount = rooms.length;
if (error) {
console.log('Error: '+ error);
}
else {
rooms.forEach(function(room){
console.log("room: " + room);
req.app.settings.redis.hgetall(room, function(error, roomdetails){
if (error) {
console.log('Error: '+ error);
}
else {
console.log("roomdetails: " + roomdetails.public);
if(roomdetails.public == "true"){
render_rooms.push(roomdetails.name);
}
// Render code moves to here and is only run after all rooms
// have been processed.
if (--roomcount === 0) {
console.log("length: " + render_rooms.length);
res.render('index', {
title: 'Index',
username: req.user.username,
rooms: render_rooms
});
}
}
});
});
}
});
};
Once you get comfortable with what this is doing, take a look at cleaning it up a bit by using async.forEach or async.forEachSeries which more cleanly support this type of flow.

Related

Why is this returning false? (MongoDB)

I can't figure out why data is returning false here. I have the console log statement and it's telling me there isn't data. I'm searching for a document with 'userId' which is the id of the person sending the message/command which is also in every document to differentiate. Any help would be greatly appreciated.
module.exports.addItem = async (userId, item) => {
profileSchema.findOne(userId, async(err, data)=> {
console.log('There is data: ' + !!data)
if(data) {
const hasItem = Object.keys(data.items).includes(item)
console.log('Has item: ' + !!hasItem)
if(!hasItem) {
data.items[item] = 1
}
else {
data.items[item]++
}
await profileSchema.findOneAndUpdate(userId, data)
}
I'm not sure about your data model or what libraries are you using, but I think the issue is profileSchema.findOne(userId, async(err, data)=> {...}).
You should use an actual query as the first parameter, e.g. profileSchema.findOne({userId: userId}, async(err, data)=> {...}) (or, using the shorthand version, profileSchema.findOne({ userId }, async(err, data)=> {...}))

how to reload page after delete form in node

This is my first project with nodejs, and probably I am asking something trivial.
I hava a table to display list of items, that can be filtered in between two date.
The table has a link to edit and a form button to delete.
When I click to edit a line, I move to the editing page, I perform my actions and when I go back to the filtered page this is reloaded.
I follow answer 2 of this question, how to force page refresh on browser back click?
My problem is that I cannot achieve the same result with the delete form.
I tried res.redirect('back'); and res.redirect(req.get('referer'));
Can I be pointed to the right direction ?
Thanks Alb
app.search('/search', function(req, res) {
var sdate = req.body.sdate
var edate = req.body.edate
var sdate_full = req.body.sdate+' 00:00:00'
var edate_full = req.body.edate+' 24:59:59'
console.log("post received: %s %s", sdate, edate);
console.log("post received: %s %s", sdate_full, edate_full);
req.getConnection(function(error, conn) {
conn.query("SELECT * FROM item WHERE item_INSERT_DATE >= ? AND item_INSERT_DATE <= ?", [sdate_full, edate_full], function(err, rows) {
if (err) {
req.flash('error', err)
res.render('user/list', {
moment: moment,
title: 'Items',
data: ''
})
} else {
res.render('user/list', {
moment: moment,
title: 'Items',
data: rows
})
}
})
})
})
app.delete('/delete/(:id)', function(req, res, next) {
var user = { item_ID: req.params.id }
req.getConnection(function(error, conn) {
conn.query('DELETE FROM table WHERE item_ID = ' + req.params.id, user, function(err, result) {
if (err) {
req.flash('error', err)
res.redirect('/')
} else {
req.flash('success', 'Item deleted ! id = ' + req.params.id)
res.redirect(??????)
}
})
})
})
The form I start from is '/search', which filter my table list. The problem is that I can only redirect to '/' (which gives me the entire list), but not the search page updated with the line deleted. This is the last tiny bit to end this little job.
the simplest thing you could do is, when you click in the delete button in the frontend, call
const desiredTimeInMilliSeconds = 100;
setTimeout(function() {
window.location = window.location;
}, desiredTimeInMilliSeconds)

Having issues editing an existing DB entry with Sails and Waterline

I'm using SailsJS as an API with Waterline connected to a MongoDB. I'm trying to put together an endpoint to edit existing DB entries but can't seem to get it to work and I'm hitting a wall as to why.
My route:
'post /edit/safety/:id': {
controller: 'SafetyController',
action: 'editSafety'
},
My controller function:
editSafety: function editSafety(req, res) {
var id = req.params.id;
Safety.findOneById(id).then((err, safety) => {
if (err) {
res.send(500, err);
return;
}
if (!safety) {
res.send(404, err);
return;
}
safety.title = req.body.title;
safety.description = req.body.description;
safety.status = req.body.status;
safety.save((err, updatedSafety) => {
if (err) {
re.send(500, err);
return;
}
res.send(200, updatedSafety);
});
});
},
Any push in the right direction would be greatly appreciated.
I don't recognize the Safety.findOneById method - is this something you have custom built? If not, then it is likely your problem.
Try swapping it for either:
Safety.findOne(id)
or
Safety.findOne({id: id})
Note that the returned object will be a model instance if the record exists, and undefined otherwise. If you decide to go with Safety.find instead then the returned value will be an array containing all models matching the query.
Looks like the main issue was transposing the response and err objects. It was successfully completing the query, but loading it into the err object which gets caught and a 500 error is thrown. So I changed that and simplified in a few other places.
editSafety: function editSafety(req, res) {
var id = req.params.id;
Safety.findOne(id).then((response, err) => {
var safety = response;
if (err) {
res.send(500, err);
return;
}
if (!response) {
res.send(404, err);
return;
}
safety.title = req.body.title;
safety.description = req.body.description;
safety.status = req.body.status;
Safety.update({
id: id
}, safety)
.then((result) => {
res.json(200, 'Ok!');
})
.catch((err) => {
sails.log.error('SafetyController.editSafety', err);
})
});
},

Socket.IO - Callback to user who emited only

I am making a chat application which requires users to log in, I have so far managed to get the login system working by using UserApp.io, but I cant seem to find a way which would send a "Callback" back to the user who has emited the information to the server.
So for index.html, when a login form is submitted, it would gather the values of the two fields and emit the data to the backend.
$('form#login').submit(function() {
var data = {};
data.email = $("#login_email").val();
data.password = $("#login_password").val();
socket.emit('user login', data);
});
In the index.js file, it receives the details and checks using the UserApp API that the user is valid and all the details are correct. It also retrieves information like the first and last name.
socket.on('user login', function (user) {
logger.info('Receiving login info for "' + user.email + '"...');
UserApp.User.login({"login": user.email, "password": user.password}, function (error, result) {
if (error) {
logger.error('Login failed: ' + error.message);
} else {
var userToken = result.token;
var userID = result.user_id;
console.log("User has logged in.");
UserApp.User.get({
"user_id": userID
}, function (error, result) {
if (error) {
logger.error(error.message);
} else {
logger.info(result[0]['first_name'] + " " + result[0]['last_name'] + " Has logged in!")
}
});
}
});
});
So here is my issue. I cant seem to find a way of giving a callback to index.html so it can show errors like "Incorrect username".
So is there a way of giving a callback to one person, more specificly, the person who submitted the login form?
Any help would be appreciated.
Thanks.
socket.io has acknowledgement callbacks, here are the docs
http://socket.io/docs/#sending-and-getting-data-(acknowledgements)
Add a callback function as the third argument when emitting
$('form#login').submit(function() {
var data = {};
data.email = $("#login_email").val();
data.password = $("#login_password").val();
socket.emit('user login', data, function (result) {
console.log(result);
});
});
and then the callback function server side can have an additional parameter which is the callback you defined when emitting
socket.on('user login', function (user, callback) {
logger.info('Receiving login info for "' + user.email + '"...');
UserApp.User.login({"login": user.email, "password": user.password}, function (error, result) {
if (error) {
logger.error('Login failed: ' + error.message);
} else {
var userToken = result.token;
var userID = result.user_id;
console.log("User has logged in.");
UserApp.User.get({
"user_id": userID
}, function (error, result) {
if (error) {
logger.error(error.message);
} else {
logger.info(result[0]['first_name'] + " " + result[0]['last_name'] + " Has logged in!")
return callback('your results');
}
});
}
});
});

Node.js & Mongoose, can't recover _id

I'm trying to save a document in my collection and if the save is successful, return the _id of this same document. The problem is I get an undefined value to my _id in both case, either the created model from mongoose or from the callback return. Basically, my only way of getting the _id would be to search the document by one of its properties, and then get the value. This approach isnt what I want, knowing what im currently trying to do should work.
var createTrophy = new Trophy({
name : post.name,
accessCode : post.password,
description : post.description,
members : [id]
});
Trophy.findOne({name:post.name}, function(err, trophy) {
if(err){
console.log('Mongoose: Error: ' + err);
res.send('Error db query -> ' + err);
}
else if(trophy){
console.log('Trophy ' + trophy.name + ' already existant');
res.send('Trophy ' + trophy.name + ' already existant');
}else{
createTrophy.save(function(err, doc){
var uid = createTrophy._id;
if (err) {
console.log('Error in trophy saving:' + err);
res.send('Error in trophy saving:' + err);
}else{
User.findOne({_id:post.id}, function(err, user) {
if(err){
console.log('Mongoose: Error: ' + err);
res.send('Error db query -> ' + err);
}
else if(user){
console.log(doc._id + ' ' + uid);
user.trophyLink = doc._id;
res.send(user);
//user.save(function(err){
// if(err){res.send('Couldnt update trophy of profile');}
//});
}
else{
console.log('User id Inexistant');
res.send('User id Inexistant');
}
});
}
});
}
});
});
The Schema
var Trophy = new Schema({
_id : ObjectId,
name : String,
accessCode : String,
description : String,
//reference to User ID
members : [Number],
comments :[Comment]
});
you don't have to supply _id in your Schema, it'll be generated automatically. and if you want the name to be unique you can also configure this in the Schema. if members are supposed to be "real" user _ids, than try sth like [ObjectId].
var TrophySchema = new Schema({
name: {type:String, required:true, unique:true},
accessCode: String,
description: String,
//reference to User ID
members: [ObjectId],
comments: [Comment]
});
and i don't know if this works
var trophy = new Trophy({...data...});
like you did it, i always do it like this:
var trophy = new Trophy();
trophy.name = "my name";
// ...
and the _id should be set as soon as you create the object (http://stackoverflow.com/questions/6074245/node-mongoose-get-last-inserted-id).
so just do it this way:
trophy.save(function (err) {
if (err) {
if (err.toString().indexOf('duplicate key error index') !== -1) {
// check for duplicate name error ...
}
else {
// other errors
}
res.send('Error in trophy saving:' + err);
}
else {
User.findOne({_id:post.id}, function(err2, user) {
if (err2) {/* ... */}
else if (user) {
user.trophyLink = trophy._id;
res.send(user);
}
}
}
});
important is, that save doesn't return the trophy you have to use the one you created yourself.

Categories