Firebase NodeJs OrderByChild - node.js

I want to have my list sorted alphabetically by the child name 'name'. Currently this is the Server file I am using to render the page:
app.get('/testpage', function (request, response) {
var testDBref = database.ref('testdb')
testDBref.once('value', function (snapshot) {
var result = snapshot.val()
if (!result) {
result = {}
};
response.render('TestPage.ejs', {
items: result,
pageTitle: "My Lists"
});
});
});
However, when I do the following:
app.get('/testpage2', function (request, response) {
var testDBref2 = database.ref('testdb').orderByChild('name')
testDBref2 .once('value', function (snapshot) {
snapshot.forEach(function(child){
console.log(child.val()) // NOW THE CHILDREN PRINT IN ORDER
var result = snapshot.child('name').snapshot.key
response.render('testpage2.ejs', {
items: result,
pageTitle: "My Lists"
});
});
});
});
It errors out saying: cannot set headers after they are sent to the client, req.next is not a function.
When I just have it console to the log:
app.get('/testpage2', function (request, response) {
var testDBref2 = database.ref('testdb').orderByChild('name')
testDBref2 .once('value', function (snapshot) {
snapshot.forEach(function(child){
console.log(child.val())
});
});
});
It is sorted alphabetically in json (but of course it doesn't display in the web page). I'm new to the nosql db's so any assistance would be appreciated :)

If you want the keys in the order of the name property, you will have to use the forEach operator. That's because the order of keys in a JSON object is undefined, but most JSON processors will return them alphabetically.
But as you also found, you can only call response.render once, so if you want to return multiple child nodes, you'll have to render them outside of the forEach loop.
The solution is to convert the child nodes to an array, and (if needed) put the key of each child node in the object in the array. For example:
app.get('/testpage2', function (request, response) {
var testDBref2 = database.ref('testdb').orderByChild('name')
testDBref2 .once('value', function (snapshot) {
var result = [];
snapshot.forEach(function(child){
result.push({ key: child.key, name: child.child('name').val() });
});
response.render('testpage2.ejs', {
items: result,
pageTitle: "My Lists"
});
});
});

Related

page renders before getting all the values sorted

I think the rendering takes place before the searching of the string on the files, i have tried different methods but don't seems to get this working. any help will be appreciated. im a noob on to the nodejs. im trying to get the id of the user and query and get all the data and there after see if he is in any of the lists given and finally render the page.
const j = [];
let name = '';
const filename = [];
var ext = '';
module.exports = function(app, express) {
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('/cusdetails', isLoggedIn, function (req, res) {
var cusid=req.body.cusid;
var insertQuerys = "SELECT * FROM customer WHERE cusid=? ORDER BY rowid DESC LIMIT 1";
connection.query(insertQuerys,[cusid],
function(err, rows){
rows.forEach( (row) => {
name=row.fncus;
});
fs.readdir('./views/iplist', function(err, files) {
if (err)
throw err;
for (var index in files) {
j.push(files[index])
}
j.forEach(function(value) {
var k = require('path').resolve(__dirname, '../views/iplist/',value);
fs.exists(k, function(fileok){
if(fileok) {
fs.readFile(k, function(err, content) {
if (err) throw err;
if (content.indexOf(name) > -1) {
ext = path.extname(k);
filename.push(path.basename(k, ext));
}
});
}
else {
console.log(" FileNotExist ");
}
});
});
});
console.log(filename);
res.render('cusdetails.ejs', {rows: rows, user:req.user , aml: filename });
});
})
You can create simple Promise wrapper and then use it inside async/await function to pause execution until resolved.
// use mysql2 package as it provides promise, less work to write promise wrappers
const mysql = require('mysql2/promise');
// create the connection to database
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test'
});
// sample wrapper
function some(k) {
// more advisable to have local variables, why do you need this to be array?
var filename = [];
return new Promise((resolve, reject) => {
// doing this is also not recommended check nodejs documentation **fs.exists** for more info
fs.exists(k, function(fileok){
if(fileok) {
fs.readFile(k, function(err, content) {
if (err) reject(err);
if (content.indexOf(name) > -1) {
ext = path.extname(k);
filename.push(path.basename(k, ext));
resolve(filename)
}
});
}
else {
// reject(new Error("FileNotExist"))
console.log(" FileNotExist ");
}
});
})
}
// note the use of async
app.post('/cusdetails', isLoggedIn, async function (req, res) {
var cusid=req.body.cusid;
var insertQuerys = "SELECT * FROM customer WHERE cusid=? ORDER BY rowid DESC LIMIT 1";
// using await to pause excution, waits till query is finished
const [rows] = await connection.query(insertQuerys,[cusid])
rows.forEach( (row) => {
name=row.fncus;
});
// then you can
var result = await some(k)
...
Note however this way you loose the advantage of concurrent execution, as it's kindoff blocking. If the result of one call is not used in another, you can execute in parallel and await for result to achieve sequencing like
const [rows] = connection.query(insertQuerys,[cusid])
var result = some(k)
console.log(await rows) // do something
console.log(await result) // do something
JavaScript is asynchronous. This means that if you have a function with a callback (i.e. your query), the callback will be called asynchronously, at an unknown time, while the other code executes.
You need to look up some tutorials how to deal with callbacks, to get a proper understanding of it. Another method is using async/await and/or promises.
Basically, if you take the following code:
console.log("this will print first");
setTimeout(function () {
console.log("this will print last");
}, 1000);
console.log("this will print second");
If you run the code above, the top level is executed synchronously, so, it first calls console.log, then it executes setTimeout, which is synchronous. It sets a timeout, then says "I'm ready", and the code continues to the other console.log. After 1 second (1000 milliseconds), the callback in the setTimeout function is executed, and only then that console.log is called. You can not make the rest of the code wait this way, you need to restructure your code or read into promises.

Assign keystonejs callback function data to array

I'm new to node.js and currently working on a project using keystonejs cms and MongoDB. Now I'm stuck in getting data related to multiple collections. Because of this callback functions, I couldn't return an array with relational data. My code something similar to this sample code.
var getAgenda = function(id, callback){
callback = callback || function(){};
if(id){
AgendaDay.model.find({summit:id}).exec(function (err, results3) {
var arr_agenda = [];
var arr_agenda_item = [];
for(var key3 in results3){
AgendaItem.model.find({agendaDay:results3[key3]._id}).exec(function (err, results2){
for(var key2 in results2){
arr_agenda_item.push(
{
item_id: results2[key2]._id,
item_name: results2[key2].name,
from_time: results2[key2].time_from,
to_time: results2[key2].time_to,
desc: results2[key2].description,
fatured: results2[key2].featured,
}
);
}
arr_agenda.push(
{
name: results3[key3].name,
date: results3[key3].date,
description: results3[key3].description,
item_list:arr_agenda_item
}
);
return callback(arr_agenda);
});
}
});
}
}
exports.list = function (req, res) {
var mainarray = [];
Summit.model.find().exec(function (err, resultssummit) {
if (err) return res.json({ err: err });
if (!resultssummit) return res.json('not found');
Guest.model.find().exec(function (err, resultsguset) {
for(var key in resultssummit){
var agen_arr = [];
for(var i=0; i<resultssummit[key].guests.length; i++){
var sumid = resultssummit[key]._id;
//this is the function im trying get data and assign to mainarray
getAgenda(sumid, function(arr_agenda){
agen_arr = arr_agenda;
});
mainarray.push(
{
id: resultssummit[key]._id,
name: resultssummit[key].name,
agenda_data: agen_arr,
}
);
}
res.json({
summit: mainarray,
});
}
});
}
}
If anyone can help me out, that would be really great :)
You need to restructure this whole thing. You should not be calling mongo queries in a for loop and expecting their output at the end of the loop. Also, your response is in a for loop. That won't work.
I'll tell you how to do it. I cannot refactor all of that code for you.
Instead of putting mongodb queries in a for loop, you need to convert it in a single query. Just put the _ids in a single array and fire a single query.
AgendaItem.model.find({agendaDay:{$in:ARRAY_OF_IDS}})
You need to do the same thing for AgendaDay.model.find({summit:id}) as well.

Render after db connection is completed - Nodejs LevelDB

I'm new to Nodejs, Express and Leveldb.
I created db using level and want to pass parameter.
exports.index = function(req, res) {
var models_array = [];
db.models.createValueStream()
.on('data', function (data) {
console.log(data.name);
models_array.push(data.name);
console.log(models_array); // 1st
});
console.log(models_array); //2nd
res.render('home', {
title: 'Home',
models:models_array
});
};
This is my code but 2nd console.log(models_array) is returning null because they are running asynchronously.
Even 1st console.log is returning what I expected.
How can make this work properly?
So that I can pass proper data to template.
I found myself. I can use .once()
var models_array = [];
db.models.createValueStream()
.on('data', function (data) {
models_array.push(data.name);
}).once('end', function() {
res.render('home', {
title: 'Home',
models:models_array
});
});

async foreach inside async series nodejs

I'm working on node async library. I'm not able to execute in the sequence i want. i don't know where I'm going wrong
here is the code.. in comments i have defined order number..
currently its executing in 2,3,4,5,1 order i want in 1,2,3,4,5 order ....kindly help
function getAsExhibitors(req, res) {
//getting all exhibitors against an event
var exhibitors = [];
var eac_app_names = [];
async.series([function(callback){
models.EacExhibitorsExt.find({ deleted: false,userid: req.user._id}).sort({ modified: -1 }).exec(function(err, myExhibitors) {
exhibitors = myExhibitors;
callback();
});
},function(callback){
async.forEach(exhibitors,function(exhibitor,callback){
models.Eac.findById(exhibitor.eventid).exec(function(err,eac){
eac_app_names[exhibitors.indexOf(exhibitor)]=eac;
console.log("-----------------1--------------"+eac_app_names);
});
console.log("-----------------2--------------"+eac_app_names);
callback();
},function(err) {
console.log("-----------------3--------------"+eac_app_names);
callback();
});
}],function(err) { //This function gets called after the two tasks have called their "task callbacks"
if (err) return next(err);
//Here locals will be populated with 'exhibitors' and 'apps'
console.log("-------------------------4------"+eac_app_names);
console.log("-------------------------5------"+eac_app_names.name);
res.locals.exhibitors = exhibitors;
res.locals.eac_app_names = eac_app_names;
res.render('eac/eac_reg_as_exhibitor', { title: "My Event Exhibitors", asexhibitor: exhibitors,app_names:eac_app_names});
});
};
All mongoose methods work as asynchronous.In your scenario try this way:
function getAsExhibitors(req, res) {
//getting all exhibitors against an event
var exhibitors = [];
var eac_app_names = [];
async.series([function(callback){
models.EacExhibitorsExt.find({ deleted: false,userid: req.user._id}).sort({ modified: -1 }).exec(function(err, myExhibitors) {
exhibitors = myExhibitors;
callback();
});
},function(callback){
async.forEach(exhibitors,function(exhibitor,callback){
models.Eac.findById(exhibitor.eventid).exec(function(err,eac){
eac_app_names[exhibitors.indexOf(exhibitor)]=eac;
console.log("-----------------1--------------"+eac_app_names);
console.log("-----------------2--------------"+eac_app_names);
callback();
});
},function(err) {
console.log("-----------------3--------------"+eac_app_names);
callback();
});
}],function(err) { //This function gets called after the two tasks have called their "task callbacks"
if (err) return next(err);
//Here locals will be populated with 'exhibitors' and 'apps'
console.log("-------------------------4------"+eac_app_names);
console.log("-------------------------5------"+eac_app_names.name);
res.locals.exhibitors = exhibitors;
res.locals.eac_app_names = eac_app_names;
res.render('eac/eac_reg_as_exhibitor', { title: "My Event Exhibitors", asexhibitor: exhibitors,app_names:eac_app_names});
});
};
You are welcome to use es6 with generator.
Try out co-foreach-series to get each of the array element and execute async function one by one.
ForEach Series Example
foreach(yourArray, function(element, index) {
// Each of this function will be executed one after one
co(function*() {
// Do some async task, and wait until this task be finished
yield yourAsyncFunc();
yield doOtherAsyncTasks();
})
})

How to write a test for an http request that checks the response's returned data?

I have the following method in my AccountsApiHandler module, and I would like to write a test that whether the accounts are correctly returned. If I am calling this method from one of my tests, how can I retrieve the list of accounts?
AccountsApiHandler.prototype.accounts = function (req, res) {
var self = this
/*
* Get list of accounts
*/
if (req.method === 'GET') {
return self.server.accounts.listAccountAsJson()
.pipe(JSONStream.stringify())
.pipe(res);
}
};
Here is my test, using the hammock npm module to generate my mock request and response objects. This test assumes that the existing accounts are already in the test's database:
var test = require('tape')
var hammock = require('hammock')
var accountsApiHandler = require('../../handlers/accounts-api')()
test('create initial accounts', function (t) {
request = hammock.Request({
method: 'GET',
headers: {
'content-type': 'application/json'
},
url: '/somewhere'
})
request.method = 'GET'
request.end()
response = hammock.Response()
var accounts = accountsApiHandler.accounts(request, response)
console.log("test.accountsApiHandler: accountsApiHandler.accounts: accounts", accounts) // `accounts` is undefined!
var accountsList = []
var actualAccounts = getAccountsFromDatabase() // the actual accounts from our db
accounts
.on('data', function (data) {
accountsList.push(data)
})
.on('error', function (err) {
t.fail(err)
})
.on('end', function () {
console.log("results:", accountsList)
t.equals(accountsList, actualAccounts)
})
}
When I run this test, my accounts stream is empty, thus giving me an empty list of accounts for accountsList. Is there a decent way to get the accounts data from my AccountsApiHandler.accounts method?
It looks like my implementation in the above question was close, but instead of streaming the accounts like this:
accounts
.on('data', function (data) {
accountsList.push(data)
})
.on('error', function (err) {
t.fail(err)
})
.on('end', function () {
console.log("results:", accountsList)
t.equals(accountsList, actualAccounts)
})
}
I needed to stream the response itself, which also doesn't have a data event:
accounts
.on('end', function (err, data) {
t.equals(data, actualAccounts)
})
Hopefully this helps someone. More documentation is in the hammock site on npm

Resources