I am using NodeJs and MongoDb as a back-end service.In my collection i have several documents having fields named _id and Name but Node is returning only first document and showing error in console.I want to fetch only Name field of a document.
Error:
Here is my code:
var express = require('express');
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
app.post('/offers',(req, res) => {
MongoClient.connect(url, (err, db) => {
if(err) throw err;
var dbo = db.db('Tiffino_db');
dbo.collection("Offers")
.find({},{ projection: { _id: 0 } })
.toArray((err, result) => {
if (err) {
console.log("Error:", +err);
}
else {
for(var i = 0;i<result.length;i++){
res.send(result[i].Name);
}
db.close();
}
});
});
});
Please let me know what I did wrong in above code.
THANKS
It looks like you're trying to send multiple responses in a loop using Response.Send() as a response is only sent once.
This won't work, you'll need to create an array of names, and then use Response.Send() to do this once.
Using res.send(result.map(r => r.name)) would probably work
You should aggregate the results into a variable and then call res.send() only once:
let resultString;
for(var i = 0;i<result.length;i++){
resultString += result[i].Name + '\n';
}
res.send(resultString);
If you want to stream the results, you can use res.write() and when done call res.end()
You can not use res.send() multiple time in a for loop, try somthing like this
var data=[];
for(var i = 0;i<result.length;i++){
data.push(result[i].Name);
}
res.send(data);
app.post('/offers',(req, res) => {
MongoClient.connect(url, (err, db) => {
if(err) throw err;
var dbo = db.db('Tiffino_db');
dbo.collection("Offers")
.find({},{ projection: { _id: 0 } })
.toArray((err, results) => {
if (err) {
console.log("Error:", +err);
}
else {
response = results.map(result => result.Name);
//res.send(response);
// in json format
res.json(response);
db.close();
}
});
});
});
...
...
else {
output = result.map(offer => offer.name);
res.send({output});
db.close();
}
This should work as you can only send response once on a single response object and you are calling that for results.length times.
I'm pretty sure you're not supposed to be calling res.send(...) multiple times. Since you're calling it inside a loop, it will send the first document and fail in the next as expected.
Related
Here is the code snippet I am using where sourceUri is the connection string, nsfrom is the db. How do I block the rest of the code until I get console.log (`Database "${nsFrom}" exists.`);
MongoClient(sourceUri, { useUnifiedTopology: true },function (err, sourceMongoClient){
const sourceAdminDb = sourceMongoClient.db(nsFrom);
sourceAdminDb.admin().listDatabases((err, result) => {
if (err) throw err;
console.log(result);
const sourceDbExists = result.databases.some(sourceAdminDb => sourceAdminDb.name === nsFrom);
if (sourceDbExists){
console.log (`Database "${nsFrom}" exists.`);
}
else{
console.log (`Database "${nsFrom}" doesn't exist.`);
}
})
})
I can successfully fetch all data stored in a collection called "topics" by using find() with cursor and foreach.
However, when I try to set the fetched information to variable call "data" and send it back to the page, it always display an empty array.
How can I fix it ?
app.get('/api/get_topics', (req, res)=>{
let data = [];
MongoClient.connect(url, (err, db)=>{
const dbo = db.db('adoption');
let cursor = dbo.collection('topics').find();
cursor.forEach((el)=>{
const obj = {
img : el.img,
title : el.title,
content : el.content
}
data.push(obj);
});
db.close();
});
res.json(data); // [] ---> always empty
res.end();
});
try this
app.get('/api/get_topics', (req, res)=>{
let data = [];
MongoClient.connect(url, (err, db)=>{
const dbo = db.db('adoption');
let cursor = dbo.collection('topics').find();
cursor.forEach((el)=>{
const obj = {
img : el.img,
title : el.title,
content : el.content
}
data.push(obj);
});
db.close();
res.json(data); // [] ---> always empty
res.end();
});
});
You need to return the data after your connection to the database is done.
Before you close the connection and after your forEach: because "forEach" is syncron & blocks the event loop
app.get('/api/get_topics', (req, res)=>{
let data = [];
MongoClient.connect(url, (err, db)=>{
const dbo = db.db('adoption');
let cursor = dbo.collection('topics').find();
cursor.forEach((el)=>{
const obj = {
img : el.img,
title : el.title,
content : el.content
}
data.push(obj);
});
res.json(data);
res.end();
db.close();
});
});
Note: You should create a global connection and not for every request.
A connection can be re used.
MongoClient.connect(url, callback) ...
callback (function) – this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the initialized db object or null if an error occured. (https://mongodb.github.io/node-mongodb-native/api-generated/mongoclient.html)
Which hints that the connect() fnc is an async function. Hence res.json(data); res.end(); will be executed before the callback of the connect function is being called where you populate your array.
The reason for the empty data is because MongoClient.connect() is an asynchronous function, meaning the code is not waiting for your callback to push the content to your data array before setting the response data.
As others have pointed out, you can set and end your response within your callback, but I am skeptical that that will work. Another approach would be to use an async/await method. You can see this usage from the official MongoDB docs.
In addition, I believe you can omit res.end() or move it to the catch.
For example:
app.get('/api/get_topics', (async (req, res) => {
try {
let data = [];
const db = await MongoClient.connect(url);
const dbo = db.db('adoption');
const cursor = dbo.collection('topics').find();
cursor.forEach((el)=>{
const obj = {
img : el.img,
title : el.title,
content : el.content
}
data.push(obj);
});
db.close();
res.json(data);
} catch (err) {
// Handle an error here...
console.log(err);
res.end();
}
}));
I have set up an express server to handle different requests one of which is a delete request. It works some times and gives a 404 other times. I noticed that the url it is sending is different. So if I change my server code to handle one path it works until the client sends a different path. I am unable to understand why it is sending different urls and not consistent. I am very new to web programming; still a student. May be I am missing something very basic.
The request is being sent from
http://localhost:3000/notes
page.
Yesterday the request was sent with this path:
Today the request is :
Just in case the images do not load, These are the urls:
http://localhost:3000/api/notes/id
http://localhost:3000/notes/api/notes/id
This is the client side request: (I have verified that its calling the delete with correct value)
var deleteNote = function(id) {
return $.ajax({
url: "api/notes/" + id,
method: "DELETE"
});
};
This is the server code:
app.delete("/api/notes/:id", (req, res) => {
let chosenNoteToDelete = req.params.id;
fs.readFile(__dirname + "/db/db.json", (err, data) => {
if(err){
throw err;
}
let json = JSON.parse(data);
for(let i=0; i<json.length; i++){
if(json[i].id === chosenNoteToDelete){
json.splice(i,1);
}
}
fs.writeFile(__dirname + "/db/db.json", JSON.stringify(json), (err) => {
if(err){
throw err;
}
res.send("Successfully deleted");
})
})
});
Can someone help me understand why its inconsistent? And how do I handle it on the server?
Change the client code from this:
var deleteNote = function(id) {
return $.ajax({
url: "api/notes/" + id,
method: "DELETE"
});
};
to this:
var deleteNote = function(id) {
return $.ajax({
url: "/api/notes/" + id,
method: "DELETE"
});
};
Your relative path tells jQuery to combine your path with the path from the page's URL. You don't want a relative path. You always want it to be /api/notes/id so you need the leading slash.
Some other things to cleanup in your server code.
Log all possible errors with console.log(err) or some similar logging mechanism.
NEVER, EVER write if (err) throw err in your server inside an asynchronous callback. That does you no good as nobody can catch that error. Instead, you must always log the error and then HANDLE the error by sending an error response.
When parsing JSON from an external source that can throw an error use try/catch around it.
When you .splice() an array that you are iterating, you need to either return after processing the .splice() or you need to correct the iterating index (because you just moved the array elements down that are after it so you will miss the next item in the array) or you need to iterate the array backwards so a .splice() operation won't affect the iteration.
Here's a fixed version of your code:
app.delete("/api/notes/:id", (req, res) => {
let chosenNoteToDelete = req.params.id;
fs.readFile(__dirname + "/db/db.json", (err, data) => {
if (err) {
console.log(err);
res.sendStatus(500);
return;
}
try {
let json = JSON.parse(data);
} catch(e) {
console.log(err);
res.sendStatus(500);
return;
}
for (let i = 0; i < json.length; i++) {
if (json[i].id === chosenNoteToDelete) {
json.splice(i, 1);
return;
}
}
fs.writeFile(__dirname + "/db/db.json", JSON.stringify(json), (err) => {
if (err) {
console.log(err);
res.sendStatus(500);
return;
}
res.send("Successfully deleted");
});
});
});
And, here's a cleaner implementation using fs.promises and async/await with more centralized error handling and detection if the chosenNote is not found:
const fsp = require('fs').promises;
const path = require('path');
app.delete("/api/notes/:id", async (req, res) => {
let chosenNoteToDelete = req.params.id;
let dataFilename = path.join(__dirname, "/db/db.json");
try {
let data = await fsp.readFile(dataFilename);
let dataArray = JSON.parse(data);
// iterate array backwards so .splice() doesn't cause us to miss elements of the array
let found = false;
for (let i = dataArray.length - 1; i >= 0; i--) {
if (dataArray[i].id === chosenNoteToDelete) {
found = true;
dataArray.splice(i, 1);
}
}
if (found) {
await fsp.writeFile(dataFilename, JSON.stringify(dataArray));
res.send("Successfully deleted");
} else {
res.status(404).send(`Note id ${chosenNoteToDelete} not found.`);
}
} catch(e) {
console.log(e);
res.sendStatus(500);
}
});
I want to access the variable "result" from the function which contains the query.
When I want to access it from another file, in which I am trying to work with the output after a POST Request, the variable is declared as "undefined".
This is the file in which i execute the query:
const db = require('../db/connect');
module.exports = {
getID(name){
db.query(`SELECT CWID FROM user WHERE surname = '${name}'`, function(error, result, fields){
if(error) console.log(error);
console.log(result);
});
}
}
And this is the file where I want to work with the data:
router.post('/test', function(req, res){
const data = queries.getID(req.body.name);
console.log(data);
res.render('new test', {title: "test"});
})
Can anybody help me with this?
Here's an example of querying using mysql and async/await. This should do what you would like:
Query file
const db = require('./db/connect');
module.exports = {
getID(name) {
return new Promise((resolve, reject) => {
db.query(`SELECT CWID FROM user WHERE surname = '${name}'`, function(error, result, fields) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
}
Main File
router.post('/test', async function(req, res){
const data = await queries.getID(req.body.name);
console.log("Query result: ", data);
res.render('new test', {title: "test"});
});
The reason your result is undefined in your initial example is that you're using asynchronous i/o (normal in Node.js). By returning a Promise from getID, we can make async. calls easily and with some nice code syntax.
I am trying to write the results of a MongoDB query to a file using the native Node.js driver. My code is the following (based on this post: Writing files in Node.js):
var query = require('./queries.js');
var fs = require('fs');
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://localhost:27017/test", function(err, db) {
if(err) { return console.dir(err); }
var buildsColl = db.collection('blah');
collection.aggregate(query.test, function(err, result) {
var JSONResult = JSON.stringify(result);
//console.log(JSONResult);
fs.writeFile("test.json", JSONResult, function(err) {
if(err) {
console.log(err);
} else {
console.log("The file was saved!");
}
});
});
collection.aggregate(query.next, function(err, result) {
var JSONResult = JSON.stringify(result);
//console.log(JSONResult);
db.close();
});
});
The file is written, but the contents are 'undefined.' Printing the result to the console works though.
Your code is not checking the err on the aggregate callback.
You are likely getting an Mongo error and the result is undefined in that case...
Other thing I could suspect is that you are getting multiple callbacks -- each one of them creates a new files, erasing the content.
Try using fs.appendFile instead of fs.writeFile and see if you are getting the expected data (plus the unwanted undefined)
For anyone stumbling across this the solution on where to put the db.close() is below:
collection.aggregate(query.test, function(err, result) {
var JSONResult = JSON.stringify(result);
//console.log(JSONResult);
fs.writeFile("test.json", JSONResult, function(err) {
if(err) {
console.log(err);
} else {
console.log("The file was saved!");
}
});
collection.aggregate(query.next, function(err, result) {
var JSONResult = JSON.stringify(result);
//console.log(JSONResult);
db.close();
});
});