Empty result when querying MongoDB database in Node.JS - node.js

I appologise for the horrendous title for this submission but I have been tearing my hair out for at least 8 hours now trying to solve this.
I initially took some guidance from sova's result on Express displaying mongodb documents in Jade Express displaying mongodb documents in Jade
However whenever I try and do the query it fails.
my index.js code is this.
app.get('/test', function (req, res) {
var MongoClient = require('mongodb').MongoClient
var url = 'mongodb://localhost/quizmaster';
var results_from_mongo = [];
MongoClient.connect(url, function (err, db) {
var str = db.collection('qmquestions').find();
str.each(function (err, doc) {
//console.log(doc);
results_from_mongo.push(doc);
console.log(results_from_mongo) //Push result onto results_array
});
//now we have a results array filled like this:
// results_from_mongo = ["some string", "some string", "some string"]
//so let's pass them to the jade file to render them.
res.render('test', {results_from_mongo : results_from_mongo });
});
});
My test.jade code is this
block content
h1= title
h2= "results from mongo:"
select
each results_from_mongos, i in results_from_mongo
option(value=i) #{results_from_mongos}
I have even tried the pug variation of this (test.jade)
table
thead
tr
th Question name
th Question
th Answer
tbody
each results_from_mongo, i in results
tr
td= results_from_mongo.questionTitle
td= results_from_mongo.question
td= results_from_mongo.answer
The db.collections.find direct from MongoDB result is
{ "_id" : ObjectId("58af574c4fef02081c32da2f"), "question" : { "questionTitle" : "Test", "question" : "Test", "answer" : "Test" } }
I have just tried so many different ways of trying to get it but all it equals is an empty result no matter what I do if anybody could help me out on this I would be greatly appreciated as I am just ripping my hair out on this and I feel it must be something so simple which I am missing. If anymore code is needed then I will edit the post with the code asked for.

Your index.js seems to be incomplete, but the biggest issue was that you didn't have a callback for the .find() call for mongo. Looking at the docs - https://www.npmjs.com/package/mongodb - it seems as though they use .toArray() which takes a callback. You need to place your res.render inside of that callback so that it gets executed after mongo returns results.
As of now you are executing res.render() when mongo hasn't gotten any data yet, due to the asynchronous nature of node.
index.js
const express = require('express')
const app = express()
var MongoClient = require('mongodb').MongoClient
app.set('view engine', 'pug')
app.get('/test', function (req, res) {
var url = 'mongodb://localhost/quizmaster';
//var url = 'mongodb://localhost:27017/quizmaster';
var results_from_mongo = [];
MongoClient.connect(url, function (err, db) {
//var str = db.collection('qmquestions').find();
var str = db.collection('qmquestions').find({}).toArray(function (err, docs){
if(err){
return res.send('error')
}
console.log(docs)
//return res.render('test', {results_from_mongo : results_from_mongo });
return res.render('test', {results_from_mongo : docs });
})// callback
//str.each(function (err, doc) {
// //console.log(doc);
// results_from_mongo.push(doc);
// console.log(results_from_mongo) //Push result onto results_array
//});
//now we have a results array filled like this:
// results_from_mongo = ["some string", "some string", "some string"]
//so let's pass them to the jade file to render them.
});
});
app.listen('3030', function(){
console.log("listening on 3030")
})
views/test.pug
block content
h1= title
h2= "results from mongo:"
select
// each results_from_mongos, i in results_from_mongo
each results_from_mongos, i in results_from_mongo
option(value=i) #{results_from_mongos}
ul
each item in results_from_mongo
li= item.question.questionTitle

Use the method toArray After your find method call. As This:
collection(...).find().toArray(function(err,doc)=>
//Do what you want
)

Related

How can I create jade elements dynamically in nodejs?

I am new at coding and stuck somewhere, cause I need a bit of help. I have a basic jade template with a, h2, and div tags
h2
a(href='/'+ '#{postId}') #{title}
.content #{contentText}
and here I find and send the first title, text, and postId to my jade template
router.get('/', function(req, res, next) {
const findPost = postInfo.find({}).then((data) => {
posts.push(...data);
res.render('index' , {
title: posts[0].title,
contentText: posts[0].content,
postId: posts[0].postId
});
});
});
Now I need to create the same pug elements but the values have to come from the posts[1] array then create pug again and now values come from posts[2]. I have to do this multiple times but don't know how. Can anyone give me a tip?
I finally do it. You can send you whole array to the jade template and use your data like this:
var posts = []
router.get('/', function(req, res, next) {
const findPost = postInfo.find({}).then((data)=>
{
posts = [...data]
res.render('index' , {posts: posts});
});
});
- for(var i = 0; i<5;i++){
.content-div
h2
a(href='/'+ '#{posts[i].postId}') #{posts[i].title}
.content #{posts[i].content}
-}

app.get() cannot find and show the right template(jade) page

I am studying about very basic Serverside JS using express, jade, node.JS.
At the first page (localhost/topic), there is a list. And List's data from MySQL.
(MySQL fields are id, title, description, and author)
Also, there is a link to open the form (the form is in add.jade file) that can add an item on the list.
My problem is that if I clicked the link, the page still shows the view.jade. But, the address is changed to "localhost/topic/add".
I've checked the "app.js" file to see if app.get() isn't set appropriately. But I did not find any.
Actually, the template was written as jade at first. But I tried to convert jade to pug. After this error, I put them back all to jade.
Is that a problem?
Thank you.
var express = require("express");
var app = express();
app.set("views", "./views_mysql");
app.set("view engine", "jade");
app.listen(3000, function() {
console.log("Connected, 3000 port!");
});
app.locals.pretty = true;
var mysql = require("mysql");
var conn = mysql.createConnection({
host: "localhost",
user: "jimin",
password: "****",
database: "o2"
});
conn.connect();
app.get(["/topic", "/topic/:id"], function(req, res) {
var sql = "SELECT id, title FROM topic";
conn.query(sql, function(err, topics, fields) {
var id = req.params.id;
if (id) {
var sql = "SELECT * FROM topic WHERE id=?";
conn.query(sql, [id], function(err, id_topics, fields) {
if (err) {
console.log(err);
res.status(500).send("Internal Server Error");
} else {
res.render("view", { topics: topics, topic: id_topics[0] });
}
});
} else {
res.render("view", { topics: topics });
}
});
});
app.get("/topic/add", function(req, res) {
var sql = "SELECT id, title FROM topic";
conn.query(sql, function(err, topics, fields) {
if (err) {
console.log(err);
res.status(500).send("Internal Server Error");
} else {
res.render("add", { topics: topics });
}
});
});
<!-- begin snippet: js hide: false console: true babel: false -->
// add.jade
doctype html
html
head
meta(charset='utf-8')
body
h1
a(href='/topic') Server Side JavaScript
ul
each topic in topics
li
a(href='/topic/' + topic.id)= topic.title
article
form(action='/topic/add' method='post')
p
input(type='text' name='title' placeholder='title')
p
textarea(name='description' placeholder='description')
p
input(type='text' name='author' placeholder='author')
p
input(type='submit')
//view.jade
doctype html
html
head
meta(charset='utf-8')
body
h1
a(href='/topic') Server Side JavaScript
ul
each topic in topics
li
a(href='/topic/' + topic.id)= topic.title
article
if topic
h2= topic.title
= topic.description
div= 'by ' + topic.author
else
h2 Welcome
| This is Server Side JS Tutorial
div
a(href='/topic/add') add Topic
app.get("/topic/add", function(req, res) {
var sql = "SELECT id, title FROM topic";
conn.query(sql, function(err, topics, fields) {
if (err) {
console.log(err);
res.status(500).send("Internal Server Error");
} else {
res.render("add", { topics: topics });
}
});
});
This is an issue with the arrangement of the routes.
TLDR
Move the route definition of app.get above the previously declared route.
Explanation
Route definitions in express are from a top-to-bottom approach. So, in runtime, your routes are seen as follows:
/topic
/topic/:id
topic/add
When you visit the page /topic/add, you would expect it to match to the third on the list but Express actually matches it to the second on the list... It will actually match everything if you put anything after /topic.

Rendering view after multiple SELECT queries in Express

I'm a bit new in Node.JS and Express framework and I have a great problem with the code below:
app.get('/student', function(req, res) {
var dbRequest = 'SELECT * FROM Students WHERE IDCard = \'' + req.query['id'] + '\'';
db.all(dbRequest, function(error, rows) {
if(rows.length !== 0) {
/* Save data. */
}
else
res.render('incorrect_student'); /* Render the error page. */
});
dbRequest = 'SELECT * FROM Groups WHERE Name = \'' + req.query['group'] + '\'';
db.all(dbRequest, function(error, rows) {
/* Add selected data to previous saved data. */
}
});
res.render('student', {data: /* data from both queries above */});
});
As I have written in comment blocks, I would like to: execute first select query, save data from rows object, execute second query, again save received data in other object and then finally render the page passing data from both queries. My question is, what is the best way to do that?
I know that there is a problem caused by anonymous function. I have tried to fix the problem for over five hours as follows:
Clone rows object to another in anonymous function and then pass it to res.render. This solution dosen't work, because values of copied object are not visible (undefined) outside this function - only inside it.
Render the student page twice - it was really naive of course.
Change db.all command to db.prepare and then db.run - it wasn't working too.
Return object by the anonymous function and then assign it to external object defined between app.get and var dbRequest. The result was as described in 1st point.
I have also an idea to create "subpages" containig parts of student page, which need variables from only one query. The other idea is to use some other functions of db, req, res or app objects. But, as I said before, I'm new in Express and I don't know how to realize my above ideas.
Please note that it is impossible to join tables - in fact, I want to make 4-5 queries and then render my view. I'm using SQLite3 database.
Thank you very much for your help! I hope that you'll help me to solve my problem.
In your situation, I would split up the database calls into separate calls, and make use of the next middleware function.
It would looks something like:
function findStudent(req, res, next) {
var dbRequest = 'SELECT * FROM Students WHERE IDCard = \'' + req.query['id'] + '\'';
db.all(dbRequest, function(error, rows) {
if(rows.length !== 0) {
req.students = rows;
return next();
}
res.render('incorrect_student'); /* Render the error page. */
});
}
function findGroups(req, res, next) {
dbRequest = 'SELECT * FROM Groups WHERE Name = \'' + req.query['group'] + '\'';
db.all(dbRequest, function(error, rows) {
/* Add selected data to previous saved data. */
req.groups = rows;
next();
}
});
}
function renderStudentsPage(req, res) {
res.render('student', {
students: req.students,
groups: req.groups
});
}
app.get('/student', findStudent, findGroups, renderStudentsPage);
When you GET /student, you first call findStudent. Once the db call is finished, it will either render an error page, or call next(). Calling next goes to the next function, findGroups, which will then call renderStudentsPage. You can store the data on the req object as you move down the line of functions.
Hope this helps, and here is more info:
http://expressjs.com/guide/using-middleware.html
edit/note:
I did not mention it earlier, but if you pass in an argument when calling next(), you will trigger the error handling state. Convention dictates next() is left parameter-less unless you have met an error instance.
You want to separate out the UI rendering aspect from the database call, so going further, your code could look like:
function findStudent(req, res, next) {
var dbRequest = 'SELECT * FROM Students WHERE IDCard = \'' + req.query['id'] + '\'';
db.all(dbRequest, function(error, rows) {
if (error || !rows.length) {
return next(error);
}
req.students = rows;
return next();
});
}
And then elsewhere in your code you can handle rendering error pages.
I know this is an old question, but for anybody still having problems and using MongoDB instead this is what worked for me.
//index.js
const express = require('express');
const router = express.Router();
function getData (req, res, next) {
var db = req.db;
var collection = db.get('usercollection');
collection.find({}, {}, function(e, docs) {
req.data = docs;
return next();
});
}
function getVendor (req, res, next) {
var db = req.db;
var collection = db.get('usercollection');
collection.distinct("vendor", function(e, docs) {
req.vendor = docs
next();
});
}
function getType (req, res, next) {
var db = req.db;
var collection = db.get('usercollection');
collection.distinct("type", function(e, docs) {
req.type = docs
next();
});
}
function renderData(req, res) {
res.render('index', {
data: req.data,
vendor: req.vendor,
type: req.type
});
}
/* GET home page. */
router.get('/', getData, getVendor, getType, renderData);
module.exports = router;
Then inside your ejs file
//index.ejs
<body>
<div id="app">
<h1>Choose a Vendor</h1>
<template v-for="vendor in values.vendor">
<label :for="vendor">{{ vendor }}</label>
<input type="radio" :value="vendor" v-model="flagpole.vendor">
</template>
<div>
<template v-for="type in values.type">
<label :for="type">{{ type }}</label>
<input type="radio" :value="type" v-model="flagpole.type">
</template>
</div>
</div>
<script type="text/javascript">
var vendor = <%- JSON.stringify(vendor) %>
var type = <%- JSON.stringify(type) %>
var vm = new Vue({
el: '#app',
data: {
values: {
vendor: vendor,
type: type
},
flagpole: {
vendor: '',
type: ''
}
},

Attempting to return collection with node.js hangs system

I have a node.js application that uses a mongodb database that I've created. Within it, I have a simple collection named comments with the contents { "author": "me", "comment": "this is a comment" } when I call db.comments.find({}).
However, when I attempt to access this collection for display within a jade view I have, it times out after an incrediable amount of time. Console.log for the error object shows it's either a MongoError or connection was destroyed by application. The question I have is why this is happening? I have no errant while loops and connection parameteres seem to check out. Here's what I have to connect with, stored in app.js
var app = express();
var mongodb = require('mongodb'),
serverdb = new mongodb.Server('127.0.0.1', 27017, {}),
db = new mongodb.Db('acl', serverdb, {safe:true});
app.use(function(req,res,next){
req.db = db;
next();
});
and the code I have in the middleware file, stored as a js file in /routes
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res) {
var db = req.db;
var collection = db.collection('comments');
collection.find().toArray(function(err, docs) {
console.log("Printing docs from Array");
if (err) {
console.log(err);
} else {
console.log(docs);
}
});
db.close();
});
module.exports = router;
Like #legalize said, its best to get a mongo connection pool going instead of opening and closing the connection on every request. Perhaps something like this SO answer
As far as why you are getting errors, its probably because your db.close() needs to be in the collection.find().toArray() callback because otherwise it'll start closing the connection before the query even happens.
Lastly, you need to render the template somewhere so the response gets sent back to the client.
Putting it all together, you probably want something like this:
router.get('/', function(req, res) {
var db = req.db;
var collection = db.collection('comments');
collection.find().toArray(function(err, docs) {
console.log("Printing docs from Array");
db.close();
if (err) {
console.log(err);
} else {
console.log(docs);
res.render( 'yourJadeTemplate', { docs : docs } );
}
});
});
(but you really don't want to be closing the connection for every request, especially because you aren't opening it for every request)
Oddly enough replacing this code
var mongodb = require('mongodb'),
serverdb = new mongodb.Server('127.0.0.1', 27017, {}),
db = new mongodb.Db('acl', serverdb, {safe:true});
with this
var db = require("mongojs").connect("localhost:27017/acl", ["comments"]);
made all the difference. No more timeouts. A bit of tweeking to get it to return data.

MongoDB is not producing output with Node.js

I am having trouble getting the output of a MongoDB query. When I call the save method:
db.users.save({email: "test#gmail.com", password: "test", sex: "male"},
function(err, saved) {
});
The code works, and stores the entry. However, the function (err, saved) isn't called, as far as I know. When I run this code:
var mongojs = require("mongojs");
var MONGOHQ_URL="mongodb://testUser:testPassword#paulo.mongohq.com:10085/app********";
app.get('/', function(request, response) {
var m = "hello";
var databaseUrl = MONGOHQ_URL;
var collections = ["users"];
var db = mongojs.connect(databaseUrl, collections);
var d = db.users.find({"sex":"male"}).limit(1);
response.send(d.email);
});
Then I get nothing. Is there something I'm missing?
var d = db.users.find({"sex":"male"}).limit(1) has no call back function so it goes out of scope and you dont see the response.
Two things first off i would use findOne instead of limit(1) and secondly you are missing the callback function, try this
db.users.findOne({"sex":"male"}, function (err, result) {
if(err){
console.log(err);
}else{
console.log(result);
//then do your response here
response.send(result.email);
}
});
Hope that makes sense.

Resources