Storing db queries in nodejs (with socket.io) - javascript

I have a (possibly dumb) problem with my code. I have something like this:
io.sockets.on('connection', function(socket)
{
var list = {};
socket.on('event', function(data){
if(!list[data.field_name]){
list[data.field_name] = [];
// db and collection already defined
var collection = new mongodb.Collection(db, coll);
collection.find({
field: data.field_name,
}).toArray(function(err, results){
list[data.field_name] = results[results.length-1];
socket.emit('another-event',{
list:list[data.field_name];
});
});
} else {
socket.emit('another-event',{
list:list[data.field_name];
});
}
});
}
I'm basically trying to cache the db results in list, and send it directly (without a db query), if it has already been retrieved from the database earlier.
The problem I'm having is that the assignment list[data.field_name] = results[results.length-1] doesn't work. So if it has tried to retrieve the data before, list[data.field_name] has been initialized to [] but it doesn't contain the data that is assigned to it after.
To be clear, it does retrieve the data from the db fine, and sends it to the client properly the first time when it is retrieved from the database. It's when I try to send the cached copy that the problem arises -- there is no cached copy.
I know this is something to do with nodejs (and the mongodb query) being asynchronous, but I can't seem to figure out how to make this work. Any help will be appreciated.

You are emitting another-event before the query to the collection has completed. If the data has not yet been retrieved when another-event is emitted then list[data.field_name] will still be empty. Try something like:
io.sockets.on('connection', function(socket)
{
var list = {};
socket.on('event', function(data){
var anotherEvent = function() {
socket.emit('another-event',{
list:list[data.field_name];
});
};
if(!list[data.field_name]){
list[data.field_name] = [];
// db and collection already defined
var collection = new mongodb.Collection(db, coll);
collection.find({
field: data.field_name,
}).toArray(function(err, results){
list[data.field_name] = results[results.length-1];
anotherEvent();
});
} else {
anotherEvent();
}
});
}

Related

Retrieve multiple data from firebase database in one cloud function

I am faced with the problem of retrieving two data values of a single node from my firebase database and reference it in my javascript file but don't know how to go about it. I have been able to retrieve just one data value from a node (in this case "message") but I would like to add "from" as well. Most tutorials just reference one so I am really confused. So how do I get multiple data values?
This is my code...
JS file
exports.sendNotification7 = functions.database.ref('/GroupChat/{Modules}/SDevtChat/{SDevtChatId}/message')
.onWrite(( change,context) =>{
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
var str = "New message from System Development Group Chat: " + eventSnapshot;
console.log(eventSnapshot);
var topic = "Management.Information.System";
var payload = {
data: {
name: str,
click_action: "Student_SystemsDevt"
}
};
// Send a message to devices subscribed to the provided topic.
return admin.messaging().sendToTopic(topic, payload)
.then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
return;
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
You can read from however many nodes you want in a Cloud Function. However, only one can trigger the function to run.
To read from your database use the following code:
admin.database().ref('/your/path/here').once('value').then(function(snapshot) {
var value = snapshot.val();
});
You will probably want to read from the same place that the Cloud Function was triggered. Use context.params.PARAMETER to get this information. For the example you posted your code would turn out looking something like this:
admin.database().ref('/GroupChat/'+context.params.Modules+'/SDevtChat/'+context.params.SDevtChatId+'/from').once('value').then(function(snapshot) {
var value = snapshot.val();
});
Just trigger your function one level higher in the JSON:
exports.sendNotification7 =
functions.database.ref('/GroupChat/{Modules}/SDevtChat/{SDevtChatId}')
.onWrite(( change,context) =>{
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
console.log(eventSnapshot);
var str = "New message from System Development Group Chat: " + eventSnapshot.message;
var from = eventSnapshot.from;
...

How to save the result from collection.findone()

i have a simple question and i have read a lot of same issues here, but these are not exact the same or doesn't work for me :-(
I have a REST function called "addevent". The function gets a json input (req) and iterate through the json array to get some IDs to store them in an extra Array. That works perfect!
After that, the function should search in a mongodb for every single id and store some extra informations from this ID (e.g. the stored URL of this ID). With "console.log(result.link)" it works again perfect. But my problem is that, that i need to store this link in an extra Array (urlArray).
So how can i save the result of collection.findone(). I read something about, that findone() doesn't return a document, but a cursor? what does that mean? How do i have to handle that in my case?
That's the code:
exports.addevent = function(req, res) {
var ids = req.body;
var pArray = new Array();
var urlArray = new Array();
var eventName = ids.name;
for(var i in ids.photos) {
photoArray.push(ids.photos[i]);
var id = ids.photos[i]._id;
var collection = db.get().collection('photos');
collection.findOne({'_id':new mongo.ObjectID(id)},function(err, result) {
console.log(result.link);
}
)
}
Many thanks!
-------------------- Update --------------------
Ok, i think that has something to do with the asynch Callbacks. I found an article, but i don't know how to implement it in my case.
http://tobyho.com/2011/11/02/callbacks-in-loops/
And something about "promises" in javascript.
You can save the result of your search doing something like:
var foundPhoto = collection.find({_id':new mongo.ObjectID(id)}, function(err, photo){
if(!err){
return photo;
} else {
console.log(err)
return null;
}
});
This way you get the return statement of your query in the "photo" variable.

Parse send notification when object modified

I am trying to send a Push Notification through Parse Cloud Code when a certain object has been modified - "dirty"
I think I am almost there, but received an error because I believe am creating a new user instead of querying for one.
Parse.Cloud.beforeSave("Fact", function(request, response) {
var dirtyKeys = request.object.dirtyKeys();
for (var i = 0; i < dirtyKeys.length; ++i) {
var dirtyKey = dirtyKeys[i];
if (dirtyKey === "isValid") {
//send push
// Creates a pointer to _User with object id of userId
var targetUser = new Parse.User();
// targetUser.id = userId;
targetUser.id = request.object.userID;
var query = new Parse.Query(Parse.Installation);
query.equalTo('user', targetUser);
Parse.Push.send({
where: query,
data: {
alert: "Your Fact was approved :)"
}
});
return;
}
}
response.success();
});
I found this post related to my problem. My question now is how to integrate the user query in my beforeSave block. Ideally I would create another function for the user query and place that in my beforeSave block.
**5/14 Update
I took #toddg's advice and fixed the before save. Here is a clearer picture of what I am trying to do and the new error.
A couple points (as #Subash noted in the comments) before I get into the code:
Parse.Push.send is an async operation, so you'll want to ensure you call response.success() after your push send completes. I'm going to deal with this using Promises, as I think they are more flexible than callbacks. If you're not familiar, read about them here
The return in your if statement will likely prevent the response.success() from being called.
Here's my recommended way of doing it:
Parse.Cloud.beforeSave("Fact", function(request, response) {
// Keep track of whether we need to send the push notification
var shouldPushBeSent = false;
var dirtyKeys = request.object.dirtyKeys();
for (var i = 0; i < dirtyKeys.length; ++i) {
var dirtyKey = dirtyKeys[i];
if (dirtyKey === "isValid") {
shouldPushBeSent = true;
}
}
if (shouldPushBeSent) {
//send push
// Creates a pointer to _User with object id of userId
var targetUser = new Parse.User();
// targetUser.id = userId;
targetUser.id = request.object.userId;
var query = new Parse.Query(Parse.Installation);
// We want to pass the User object to the query rather than the UserId
query.equalTo('user', targetUser);
Parse.Push.send({
where: query, // Set our Installation query
data: {
alert: "Your fact was approved"
}
}).then(function(){
// Now we know the push notification was successfully sent
response.success();
}, function(error){
// There was an error sending the push notification
response.error("We had an error sending push: " + error);
});
} else {
// We don't need to send the push notification.
response.success();
}
});
By the way, I'm assuming that you have a column on your Installation class that tracks which user is associated with each Installation.

Get json array from sub mongoose query

I have a sub query in mongoose need to get array out of sub query and attach to main json out put/ object.
my first query get user info which contains blocked_users array which is nothing but array of user id's.
i my second query we get profile details of blocker_users array and append to main user object in blocked_users.
var userId = ObjectID(req.body.user_id);
//Get user
newUserModel.findById(userId, function(err, user){
if(err){
utils.getResponse(res, req.url, messages.failure, "");
} else {
var userInfo = {};
var blcked_contacts;
//get users details from blocked contacts userid's array
newUserModel.find({'_id': {$in:user.blocked_contacts}}, function (err,blocked_users) {
if(blocked_users){
//blcked_contacts.push(blocked_users);
console.log(blocked_users);
return;
};
/*else{
blcked_contacts = [];
}*/
});
userInfo['blocked_contacts'].push(blocked_users);
userInfo['user_id'] = user.id;
userInfo['country_code'] = user.country_code;
//userInfo['blocked_contacts'].push(blcked_contacts);
//userInfo['blocked_contacts'] = user.blocked_contacts;
var userData = Array();
}
});
Don't really know what you're looking for. But saw a problem in your code. You've assigned the blocked_users to the blocked_contacts field outside the find method.
Since these calls are asynchronous in nature, it might happen that the assignment takes place even before the documents are fetched from MongoDB. So you should write your assignment statements inside the find methods' callback, just the way Medet did.
Noticed few mistakes in your code like trying to use .push on an object. You cant do
userInfo['blocked_contacts'].push(blocked_users); // incorrect as userInfo is an empty object and you dont have an array defined for userInfo['blocked_contacts']
You probably get cannot push into undefined error for this. So instead do
userInfo['blocked_contacts'] = blocked_users;
Also you have to do this inside the second find() as blocked_users is only available inside it. So your final query should be something like
var userId = ObjectID(req.body.user_id);
//Get user
newUserModel.findById(userId, function(err, user){
if(err){
utils.getResponse(res, req.url, messages.failure, "");
} else {
var userInfo = {};
//get users details from blocked contacts userid's array
newUserModel.find({'_id': {$in:user.blocked_contacts}}, function (err,blocked_users) {
if(blocked_users){
userInfo['user_id'] = user.id;
userInfo['country_code'] = user.country_code;
userInfo['blocked_contacts'] = blocked_users; // assign blocked_users into userInfo
console.log(userInfo) // Your required object
} else {
userInfo['user_id'] = user.id;
userInfo['country_code'] = user.country_code;
userInfo['blocked_contacts'] = []; // assign null array if no blocked users fould
}
});
var userData = Array();
}
});
The result of console.log should be an object like this
{
user_id : "..id of searched user...",
country_code : "..country code of searched user..",
blocked_contacts : [<array containing detais of all blocked users>] // null array if no users found
}

Return all models from mongodb as an object or array?

I am using sockets with mongodb, for a user who is trying to create a new name, I need to check all the models in the database to see if it exists.
I am doing it all wrong, basically I am trying to do something like this.
var allUsers = [];
models.Message.find({}, function(err, data) {
for(var i=0; i < data.length; i++) {
allUsers.push(data[i].username);
}
});
console.log(allUsers)
I'm sitting here struggling even getting the allUsers out of the function, and I am thinking this is not even the best way to do this. With allUsers I was just going to check to see if the new username existed in the array.
So to futher extend what I am doing here is some socket.io code. I was going to run some validation like this if I could get the allUsers to work.
socket.on('new user', function (data, callback) {
if(data in allUsers) {
callback(false);
} else {
callback(true);
socket.userName = data;
socket.connected = true;
users[socket.userName] = socket;
io.sockets.emit('user name', {usernames: users[socket.userName].userName, connected: users[socket.userName].connected});
}
});
But without it working, this is no good. So my question is with what I have provided (socket.io, mongodb) how do I get all the models and validate if a new user which is passed in data exists in the database?
models.Message.find is async, the result of the async operation is only available when the async operation has finished.so console.log(allUsers) will always yield an empty array.
should be something like (pseudo js code):
socket.on('new user', function (data, callback) {
models.User.findOne({username:data.username},function(err,user){
if(err){/*deal with error here */}
else if(user){/*username already taken
respond with appropriate socket message here */
socket.emit('user name already taken',{somemessage});
}
else{/* user with username not found */
/*create new user into database then emit socket message */
var user = new models.User(data);
user.save(function(err,user){
socket.emit('user name',{somemessage});
})
}
});
});

Categories