Node.js and Redis - node.js

I am trying to link up a redis database with a Node.js application I am building to be able to store comments about items. I'm using the node_redis library to handle the connection. When I attempt to retrieve the comments out of the database however only "[true]" is returned. For testing purposes I have stuffed everything into one method and I have hardcoded the values in, but I still receive "[true]".
exports.getComment = function (id){
var comments = new Array();
rc.hmset("hosts", "mjr", "1", "another", "23", "home", "1234");
comments.push(rc.hgetall("hosts", function (err, obj) {
var comment = new Array();
if(err){
comment.push("Error");
} else {
comment.push(obj);
}
return comment;
}));
return comments;
}
Updated the code according to the tutorial and here is the result:
Retrieving the comment:
exports.getComment = function (id, callback){
rc.hgetall(id, callback);
}
Adding the comment:
exports.addComment = function (id, area, content, author){
//add comment into the database
rc.hmset("comment",
"id", id,
"area", area,
"content", content,
"author" , author,
function(error, result) {
if (error) res.send('Error: ' + error);
});
//returns nothing
};
Code to render:
var a = [];
require('../lib/annotations').addComment("comment");
require('../lib/annotations').getComment("comment", function(comment){
a.push(comment)
});
res.json(a);

Node.js is asynchronous. Which means it asynchronously does the redis stuff, and then gets the result back in the callback function.
I suggest you read this tutorial and fully understand it before getting further: http://howtonode.org/node-redis-fun
Basically, this way won't work:
function getComments( id ) {
var comments = redis.some( action );
return comments;
}
But it has to be this way:
function getComments( id, callback ) {
redis.some( action, callback );
}
This way, you use the API like this:
getComments( '1', function( results ) {
// results are available!
} );

The problem lays within the actual Redis-Node library when the call to addComment is made as it is below.
require('../lib/annotations').getComment("comment", function(comment){
a.push(comment)
});
This call is missing an argument in the callback function. The first argument is the error report which should return null if everything is ok, the second is the actual data. So it should be structured like the call below.
require('../lib/annotations').getComment("comment", function(comment){
a.push(err, comment)
});

Related

NodeJS "synchronous" function call returns [object Promise]

I wanted to create a platform to learn the power of NodeJS, so I arranged a web application that uses SQLite. I wanted to make the web app show a random post, so I made the web app select one:
var post = db.run("SELECT post_name FROM posts ORDER BY RANDOM() LIMIT 3;")
When I try to use post to get the entire row that it's results are included in:
var postrow = db.all("SELECT * FROM posts WHERE post_name = ${post}") // return object promise
As you can see, it will return [object Promise], causing a database error since there is no column named like that. But this really confuses me because the column is post_name, post is supposed to be the value, not the column. I have tried a lot (if not all) of the methods I could find, and it still would return [object Promise] no matter what.
I am using NodeJS v18.2.
My full code is:
app.get("/home", function(req,res){
var post1 = db.run("SELECT post_name FROM posts ORDER BY RANDOM() LIMIT 3;").then(function(value) {
return value;
});
res.render(__dirname + "/public/index.html", {
post1: post1,
postowned: db.run(`SELECT ownedby FROM posts WHERE post_name = ${post1}`)
})})
Edit: The definition of db is:
dbWrapper
.open({
filename: dbFile,
driver: sqlite3.Database
})
.then(async dBase => {
db = dBase;
try {
if (!exists) {
await db.run(
"CREATE TABLE posts ( post_name VARCHAR(20), post_text TEXT, ownedby VARCHAR(15), cdate TIMESTAMP );"
);
}
console.log(await db.all("SELECT post_name from post"));
} catch (dbError) {
console.error(dbError);
} });
db.run and db.all are both asynchronous functions, so they will return [object Promise]. Nonetheless, it is possible to change the value to the actual response by using db.runs optional callback parameter, The code below selects a random post:
var posts = []
db.run("SELECT * FROM posts ORDER BY RANDOM() LIMIT 3;", function(err, row) {
if (row.post_name != null){
// if the post name is not null then push it to posts
posts.push({ title:row.post_name, text:row.post_text, date:row.cdate, owned:row.ownedby})
}
})
// the columns will be available in posts as 0, 1, it goes on as more posts are selected
It is possible to only select columns that you want by using the comma (for instance SELECT post_name, post_text FROM posts)
Since app.get is a synchronous function, and you can't call it with async, this does not use await, but this would still work as it won't give the promise response.

MongoDB returning a null, but query works separately

In a post function, I am trying to retrieve the nth activity of a user (since I have a dropdown that return the index number of the activity). When I run the query
collection.find({'local.email':req.user.local.email},
{'local.activities':{$slice : [currActivity,1]}});
I receive the correct activity object in Robo3T.
But, when I call the same query in Node inside a post function, it returns an undefined.
app.post('/addlog',function(req,res){
var currActivity = req.body.curAct;
var score = req.body.score;
var comment = req.body.reason;
mongoose.connect('mongodb://****:****#ds044907.mlab.com:44907/intraspect',function (err, database) {
if (err)
throw err
else
{
db = database;
var collection = db.collection('users');
var retrievedAct = collection.find({'local.email':req.user.local.email},
{'local.activities':{$slice : [currActivity,1]}}).toArray().then(console.log(retrievedAct));
if (retrievedAct.length > 0) { printjson (retrievedAct[0]); }
console.log(currActivity);
console.log(retrievedAct[0]);
// console.log(req.body.newAct);
collection.update({'local.activities.name':retrievedAct[0]},
{$push: {'local.activities.log' : {
comments: comment,
score: score,
log_time: Date.now()
}}})
.then(function(){
res.redirect('/homepage');
})
.catch(function() {
console.log('Error');
});
}
});
});
I checked that the currActivity variable does infact contain the integer value for the nth activity.
If you want the result of collection.find().toArray(), as specified in the docs, you have two options:
Passing a callback to .toArray() like you did with mongoose.connect()
Using the Promise that it returns if you don't pass a callback
Now you are doing neither of them.
Also, you are mixing callback style and Promises in your code. I recommend you unificate your code. If you are using a Node.js version bigger than 8, using async/await could be nice, it makes it simpler.

Too many callbacks.. nodeJS airtable api

First of all I am a newbie in NodeJS and want to imporve my skills on it.
I have a table in Airtable and want to get all the elements from it.
Easy with the airtable api for nodejs.
But what I want to do is push and save these elements in a tab for the future(JSON, excel ...).
To do so, I am using callbaks since the call is async.. I heared about Promises but it's very new to me, and I am hardly uderstanding it..
Here is my code for now:
var Airtable = require('airtable');
Airtable.configure({
endpointUrl: 'https://api.airtable.com',
apiKey: 'keyKWYJPOEObWhNt2'
});
var base = Airtable.base('app4qIwfmG0ZKAdBH');
var view = "Main View";
var tab = [];
base('Table 1').select({
view : view}).eachPage(function page(records, fetchNextPage){records.forEach(function(record){
tab.push({
"Name": record.get('Name'),
"Notes": record.get('Notes')
});
});
fetchNextPage();
pushToArray(tab);
}, function done (error){
if(error){ console.log(error);
console.log(tab);}
});
function pushToArray(tab) {
TabToJson(tab);
return tab;
};
function TabToJson(tab){
console.log(tab);
return JSON.stringify(tab);
};
How can I implements promises? Is it necessary here? I don't want to end up with dozen of callback functions..
Thank you all and have a nice day!
Careful here! You're on the right track with realizing that this function is async and that you want to wait until every iteration of #eachpage has resolved before writing outputting your JSON, like a Promise would. But Airtable was kind enough to already provide what you're looking for: the callback
function done (error){
if(error){ console.log(error);
console.log(tab);
}
}
will run immediately after the last successful call to #fetchNextPage. This is where you should have your JSON-writing logic. You would want something like
function done (error){
TabToJson(tab);
if(error){ console.log(error);
console.log(tab);
}
}
You don't need your function pushToArray, as you've already pushed the individual records from Airtable into your array 'tab' in each call to #page. Furthermore, if you want to do more than log your JSON output, which your question makes it seem, you should look into Node's File System Library. checkout the fs#writeFile method.
You can use async/await for it. Please make sure you can use await in an async function.
try {
const records = await base('Table 1').select({ view }).all()
records.map((record) => {
tab.push({
"Name": record.get('Name'),
"Notes": record.get('Notes')
});
pushToArray(tab)
})
} catch (e) {
console.error(e)
}

callback in callback with waterfall in nodejs

I am using MEAN (Mongo Express Angulars NodeJs) for a project. The problem is I have to add one extra attribute to data received from query object. And make new data array with exactly old data array but have one extra attribute. I know how to add attribute and pass them into callback using waterfall model, as I am using multiple callback functions and for loops I am not able to get expected result.
code:
var fetchRevenue = function(restaurantArray, type, startDate, endDate, fn) {
_.forEach(restaurantArray, function(rest) {
fetchDateWiseReport(new Date('07/10/2015'), new Date('07/16/2015'), rest._id, type, function(orders) {
var newOrders = [];
async.waterfall([
function(callback) {
if(orders && orders.length > 0){
async.forEach(orders, function(order) {
getSellingPriceOfItems(order.orders, function(sp) {
order.sp = sp;
newOrders.push(order);
if (newOrders.length === orders.length)
callback(null, newOrders);
});
});
} else {
newOrders.push([]);
}
},
function(newOrders, callback) {
var restArr = []
//get sum of all orders of each restaurant and add into restArr
callback(null, restArr);
},
function(restArr, callback) {
callback(null, newOrders);
}
], function(err, result) {
fn(result);
});
});
});
};
where my functions:
fetchDateWiseReport = fetches restaurant record for given date and send result in callback
getSellingPriceOfItems = query to item model find price for each item and return selling price of given array and send result in callback.
my complete code including all functions is here.
now I want orders should be equal to newOrders with additional attibute 'sp'. But I am unable to get this. Will you suggest me something to proceed?
Use Express way to handle callback problem
in you route
app.get('you/route',fetchDateWiseReport(), second(),finalReturningREsult())
your first function will be doing first async loop function assiggn reult in req.body.firstResult and pass to second function. and so on

Get a collection and add a value to the response

I want to create in the Server script a function that can return a collection plus some extra value.
For example:
Meteor.publish("users", function () {
var users;
users = Meteor.users.find();
users.forEach(function (user){
user.profile.image = "some-url";
});
return users;
});
But this don't work proper. My question is: What is the right way to add a value to a collection reponse in a publish function.
There are 2 ways you can implement a publish function:
By returning a cursor (or an array of cursors)
By using this.added(), this.changed() and this.removed().
Only method 2 allows to modify returned documents.
Please refer to Meteor documentation here. However, since the provided sample code might look complex, here is another one:
// server: publish the rooms collection
Meteor.publish("rooms", function () {
return Rooms.find({});
});
is equivalent to:
// server: publish the rooms collection
Meteor.publish("rooms", function () {
var self = this;
var handle = Rooms.find({}).observeChanges({
added: function(id, fields) { self.added("rooms", id, fields); },
changed: function(id, fields) { self.changed("rooms", id, fields); },
removed: function(id) { self.added("rooms", id); },
}
});
self.ready();
self.onStop(function () { handle.stop(); });
});
In the second sample, you can modify the 'field' parameter before sending it for publication, like this:
added: function(id, fields) {
fields.newField = 12;
self.added("rooms", id, fields);
},
Source: this post.
Is this important to do with the server? You could use the transform function on the client:
Client JS
//Somewhere where it can run before anything else (make sure you have access to the other bits of the document i.e services.facebook.id otherwise you'll get a services is undefined
Meteor.users._transform = function(doc) {
doc.profile.image = "http://graph.facebook.com/" + doc.services.facebook.id + "/picture";
return doc;
}
Now when you do:
Meteor.user().profile.image
=> "http://graph.facebook.com/55592/picture"
I have opened an issue before with regards to sharing a transform onto the client: https://github.com/meteor/meteor/issues/821

Resources