I am trying to remove a record from MongoDB using nodeJS. But the record is not getting deleted.
Please find the below code:
exports.remove = function(studentId, cb) {
var collection = db.get().collection('students');
collection.remove({_id: studentId}, function(err) {
if (err) {
throw err;
}
else {
cb(err);
console.log("Record deleted.");
}
});
}
I have tried the studentId with ObjectID() as below:
exports.remove = function(studentId, cb) {
var collection = db.get().collection('students');
collection.remove({_id: new mongodb.ObjectID(studentId)}, function(err) {
if (err) {
throw err;
}
else {
cb(err);
console.log("Record deleted.");
}
});
}
But getting an error as :
"Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"
Please help on this issue!!!!!
When you call your remove function, make sure you are passing an actual string as the first argument, not a numeric.
Or, in your remove function, cast studentId to a string like so:
collection.remove({_id: studentId.toString()}, function(err) {...
at first, you should remove unnecessary get() method on db:
var collection = db.collection('students');
next step, don't dismiss new keyword, it's just fine to only wrap:
collection.remove({_id: mongodb.ObjectID(studentId)}, function(err) {
perhaps creating the id from this method will help :
mongodb.ObjectID.createFromHexString(studentId);
https://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html
I got the solution for this, why I was getting the error - "Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters" when using new mongodb.ObjectID(studentId). I want this to be shared with all of us.
My angularJS controller is as follows:
mainApp.controller("deleteStudentController", function($scope,$http) {
var resData = {};
$scope.student = {};
var urlGet = "/students/all";
$http.get(urlGet)
.then(function(response) {
$scope.students = angular.fromJson(response.data);
});
$scope.deleteStudent = function(){
var urlDelete = "/students/remove:"+$scope.studentRadio;
$http.delete(urlDelete)
.success(function(response, status, headers, config){
$scope.output = "Student successfully deleted.";
})
.error(function(response, status, headers, config){
$scope.output = "Error in processing. Please try again.";
});
}
});
In the above controller we can see the URL as
var urlDelete = "/students/remove:"+$scope.studentRadio;
which inturn calls my node controller :
router.delete("/remove:studId", function(req,res){
Students.remove(req.params.studId, function(err) {
if (err) {
throw err;
}
else {
var respOut = "Student deleted";
res.send(respOut);
}
});
});
The Angular code is setting the endpoint like this:
"/students/remove:"+$scope.studentRadio
I want the : to be there, so the URL will look something like this:
/students/remove:576c1d4781aaa4f16a68af24
The Express route is as below:
router.delete("/remove:studId", ...)
: is a special character in Express routes (it declares a named parameter called studId). This means that the route will take everything after /remove to be the value of studId, including the colon that's in the URL. So req.params.studId is :576c1d4781aaa4f16a68af24, which has a length of 25 characters.
If we want to use this sort of URL scheme, we need to make the colon to be part of the match by escaping it (so it loses its special meaning):
router.delete("/remove\::studId", ...)
Related
I'm creating a RESTful API.
I wanna use GET method to check if lastName exists. If it can find lastName, return "YES", otherwise, call a POST method to create a data with lastName entered.
The problem is that it can create a new data, but the body is empty. Ideally, it should contain a value with lastName, like "lastName": "James",
{
"_id": "58a22c3c3f07b1fc455333a5",
"__v": 0
}
Here is my code.
router.route("/findLastName/:id")
.get(function(req,res){
var response;
mongoOp.findOne({deviceID: req.params.id}, function(err, result){
if (err) {
response = {"error" : true,"message" : "Error fetching data"};
res.json(response);
}
if (result) {
response = "YES";
res.send(response);
} else {
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xhr = new XMLHttpRequest();
var POSTurl = "http://localhost:6002/users";
var params = "lastName=" + req.params.id;
xhr.open("POST", POSTurl, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(params);
}
});
})
PS: GET method works well, not a issue.
Let me modify a bit of your code and add comments as pointers:
// changed findLastName to find-last-name. It's a common convention,
// urls need to be case insensitive. It doesn't concern lastName, as
// that's a parameter, internal to your app so it's fine.
// even better if you name the route `find-or-create` or something, to better
// reflect what you're doing.
router.route("/find-last-name/:lastName")
.get(function(req,res){
var response;
mongoOp.findOne({deviceID: req.params.lastName}, function(err, result){
if (err) {
response = {"error" : true,"message" : "Error fetching data"};
// Adding a `return statement here. If you don't return, you'll tell
// the user that there was an error, but your code continues running
// potentially calling that res.json twice.
// Also, since it's an internal error, it's common to tell the client
// about it, by setting the status to 500
return res.status(500).json(response);
}
if (result) {
// turning the message to JSON instead. You started that above,
// and for the sake of your clients (your frontend), it's
// better to stick to JSON. Also you can pass useful info, such as
// _id of the document.
// Again adding a `return` here, and then the rest of the code
// is nested one level less. not required, but some people like that.
response = {
message: "Last name exists."
};
return res.json(response);
}
// Here begins the new code. I'm typing what I can infer from your code,
// I don't know if your MongoDB driver looks like that exactly.
mongoOp.insert({
deviceId: req.params.lastName
// add other optional properties here.
}, function (err, response) {
if (err) {
var message = {
error: true,
message: 'Cannot save new entry.'
}
return res.status(500).json(message);
}
// if we're here, everything went ok. You can probably return
// the _id of the given user.
return res.json({
message: 'New user created.',
_id: response._id
});
});
});
})
I'm trying to get data from MongoDB collection and then save it to a global object.Later I need to parse it to HTML template.
Here is my code:
When user log onto his profile: then we need to get his projects and here we call findeprojects() function
usrRouter.route('/profile')
.all(function (req,res,next) {
if(!req.user){
res.redirect('/');
}
next();
})
.get(function (req,res,userObj) {
// var proj = findprojects();
userObj = req.user;
var pro = {};
pro = findprojects(userObj);
res.render('index',{name:userObj.username, email:userObj.email});
//res.sendFile('profile.html',{root:path.join(__dirname,'../public'),},{name:userObj.username});
});
Here is findeprojects function code:
var findprojects = function(obj) {
var usern = obj.username;
mongodb.connect(url,function(err, db){
if(err) throw err;
var collection = db.collection('projects');
//console.log(usern);
collection.find({'pusername':usern});
cursor =db.collection('projects').find({ 'pusername': usern }).toArray(function(err,items){
//console.log(items);
var i;
for(i=0; i<items.length;){
userProjects.createdBy = items[i].pusername;
userProjects.proName = items[i].projectName;
userProjects.proType = items[i].projectType;
userProjects.proDesc = items[i].projectDesc;
//return userProjects;
i = i+1;
}
});
console.log(userProjects);
});
};
I have declared global object at the top like:
userProjects = {
createdBy:'',
proName:'',
proType:'',
proDesc:''
};
But when I console userprojects object after calling the findeprojects() function it displays empty values.
why dont you use mongoose to model your stuff.
its more intuitive and you no need to declare the global object and do the mapping in the for loop that you are doing.
also your approach is a bit wrong in terms of when you iterate through for aren't you overwriting ?
say you have two documents where pusername is abdul.
so in your case you loose first object which will get overwritten by the second one.
i see that you commented out a return statement but even that wont work properly.
from a design point of view your approach is not efficient.
in mongoose you can do:
{
var userProjectSchema = new mongoose.Schema({
createdBy: { type: String }
, proName: String
, proType: String
, proDesc: String
});
// Find a single document by username.
userProjectSchema.findOne({ pusername : 'abdul' }, function(err, resDoc) {
if (err) return console.error(err);
// do your html stuff here
});
// Find all documents.
userProjectSchema.find(function(err, results) {
if (err) return console.error(err);
// do your html stuff here
});
}
In the code
var stuff_i_want = '';
stuff_i_want = get_info(parm);
And the function get_info:
get_info(data){
var sql = "SELECT a from b where info = data"
connection.query(sql, function(err, results){
if (err){
throw err;
}
console.log(results[0].objid); // good
stuff_i_want = results[0].objid; // Scope is larger than function
console.log(stuff_i_want); // Yep. Value assigned..
}
in the larger scope
stuff_i_want = null
What am i missing regarding returning mysql data and assigning it to a variable?
============ New code per Alex suggestion
var parent_id = '';
get_info(data, cb){
var sql = "SELECT a from b where info = data"
connection.query(sql, function(err, results){
if (err){
throw err;
}
return cb(results[0].objid); // Scope is larger than function
}
==== New Code in Use
get_data(parent_recording, function(result){
parent_id = result;
console.log("Parent ID: " + parent_id); // Data is delivered
});
However
console.log("Parent ID: " + parent_id);
In the scope outside the function parent_id is null
You're going to need to get your head around asynchronous calls and callbacks with javascript, this isn't C#, PHP, etc...
Here's an example using your code:
function get_info(data, callback){
var sql = "SELECT a from b where info = data";
connection.query(sql, function(err, results){
if (err){
throw err;
}
console.log(results[0].objid); // good
stuff_i_want = results[0].objid; // Scope is larger than function
return callback(results[0].objid);
})
}
//usage
var stuff_i_want = '';
get_info(parm, function(result){
stuff_i_want = result;
//rest of your code goes in here
});
When you call get_info this, in turn, calls connection.query, which takes a callback (that's what function(err, results) is
The scope is then passed to this callback, and so on.
Welcome to javascript callback hell...
It's easy when you get the hang of it, just takes a bit of getting used to, coming from something like C#
I guess what you really want to do here is returning a Promise object with the results. This way you can deal with the async operation of retrieving data from the DBMS: when you have the results, you make use of the Promise resolve function to somehow "return the value" / "resolve the promise".
Here's an example:
getEmployeeNames = function(){
return new Promise(function(resolve, reject){
connection.query(
"SELECT Name, Surname FROM Employee",
function(err, rows){
if(rows === undefined){
reject(new Error("Error rows is undefined"));
}else{
resolve(rows);
}
}
)}
)}
On the caller side, you use the then function to manage fulfillment, and the catch function to manage rejection.
Here's an example that makes use of the code above:
getEmployeeNames()
.then(function(results){
render(results)
})
.catch(function(err){
console.log("Promise rejection error: "+err);
})
At this point you can set up the view for your results (which are indeed returned as an array of objects):
render = function(results){ for (var i in results) console.log(results[i].Name) }
Edit
I'm adding a basic example on how to return HTML content with the results, which is a more typical scenario for Node. Just use the then function of the promise to set the HTTP response, and open your browser at http://localhost:3001
require('http').createServer( function(req, res){
if(req.method == 'GET'){
if(req.url == '/'){
res.setHeader('Content-type', 'text/html');
getEmployeeNames()
.then(function(results){
html = "<h2>"+results.length+" employees found</h2>"
html += "<ul>"
for (var i in results) html += "<li>" + results[i].Name + " " +results[i].Surname + "</li>";
html += "</ul>"
res.end(html);
})
.catch(function(err){
console.log("Promise rejection error: "+err);
res.end("<h1>ERROR</h1>")
})
}
}
}).listen(3001)
Five years later, I understand asynchronous operations much better.
Also with the new syntax of async/await in ES6 I refactored this particular piece of code:
const mysql = require('mysql2') // built-in promise functionality
const DB = process.env.DATABASE
const conn = mysql.createConnection(DB)
async function getInfo(data){
var sql = "SELECT a from b where info = data"
const results = await conn.promise().query(sql)
return results[0]
}
module.exports = {
getInfo
}
Then, where ever I need this data, I would wrap it in an async function, invoke getInfo(data) and use the results as needed.
This was a situation where I was inserting new records to a child table and needed the prent record key, based only on a name.
This was a good example of understanding the asynchronous nature of node.
I needed to wrap the all the code affecting the child records inside the call to find the parent record id.
I was approaching this from a sequential (PHP, JAVA) perspective, which was all wrong.
Easier if you send in a promise to be resolved
e.g
function get_info(data, promise){
var sql = "SELECT a from b where info = data";
connection.query(sql, function(err, results){
if (err){
throw err;
}
console.log(results[0].objid); // good
stuff_i_want = results[0].objid; // Scope is larger than function
promise.resolve(results[0].objid);
}
}
This way Node.js will stay fast because it's busy doing other things while your promise is waiting to be resolved
I've been working on this goal since few weeks, without any result, and I finally found a way to assign in a variable the result of any mysql query using await/async and promises.
You don't need to understand promises in order to use it, eh, I don't know how to use promises neither anyway
I'm doing it using a Model class for my database like this :
class DB {
constructor(db) {
this.db = db;
}
async getUsers() {
let query = "SELECT * FROM asimov_users";
return this.doQuery(query)
}
async getUserById(array) {
let query = "SELECT * FROM asimov_users WHERE id = ?";
return this.doQueryParams(query, array);
}
// CORE FUNCTIONS DON'T TOUCH
async doQuery(queryToDo) {
let pro = new Promise((resolve,reject) => {
let query = queryToDo;
this.db.query(query, function (err, result) {
if (err) throw err; // GESTION D'ERREURS
resolve(result);
});
})
return pro.then((val) => {
return val;
})
}
async doQueryParams(queryToDo, array) {
let pro = new Promise((resolve,reject) => {
let query = queryToDo;
this.db.query(query, array, function (err, result) {
if (err) throw err; // GESTION D'ERREURS
resolve(result);
});
})
return pro.then((val) => {
return val;
})
}
}
Then, you need to instantiate your class by passing in parameter to constructor the connection variable given by mysql. After this, all you need to do is calling one of your class methods with an await before. With this, you can chain queries without worrying of scopes.
Example :
connection.connect(function(err) {
if (err) throw err;
let DBModel = new DB(connection);
(async function() {
let oneUser = await DBModel.getUserById([1]);
let allUsers = await DBModel.getUsers();
res.render("index.ejs", {oneUser : oneUser, allUsers : allUsers});
})();
});
Notes :
if you need to do another query, you just have to write a new method in your class and calling it in your code with an await inside an async function, just copy/paste a method and modify it
there are two "core functions" in the class, doQuery and doQueryParams, the first one only takes a string as a parameter which basically is your mysql query. The second one is used for parameters in your query, it takes an array of values.
it's relevant to notice that the return value of your methods will always be an array of objects, it means that you'll have to do var[0] if you do a query which returns only one row. In case of multiple rows, just loop on it.
I am new to node.js , and use ari client.
I have two .js files that first one is required in second one. I have declared a variable in second one that must contain return value of first file but console.log(variable) is undefined although in first file return value is not null.
in first file :
var db = require("./database.js");
var service_info = db.select(connection,'select * from services where ivr_shortcode = ?',service);
console.log(service_info);
service_info is undefined;
in second file :
this.select = function (connection,query,data){
connection.query(query,[data],function(err, results){
if(err)
{
throw err;
}
else
{
return results;
}
});}
You cannot just return values from callback due to call being asynchronous, you should use another function to get the results:
// file 'database.js'
exports.select = function (connection, query, data, callback) {
connection.query(query, [data], callback);
}
Than in your main:
// I assume `connection` and `service` are defined somewhere before (like in your original code)
var db = require("./database.js");
var service_info = db.select(connection,'select * from services where ivr_shortcode = ?',service, function(err, service_info){
console.log(service_info);
});
P.S.
You should really read some docs and look into Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference to get better understanding of scope visibility and closures
To deal with such condition you needs to implement it using the async/await.
exports.select = async function (connection,query,data){
await connection.query(query,[data],function(err, results){
if(err){
throw err;
}else{
return
}
});
}
This will waits until you get the result. So undefined condition never arise.
you have to export select.
something like this :
exports.select = function (connection,query,data){
connection.query(query,[data],function(err, results){
if(err)
{
throw err;
}
else
{
return
}
});}
I'm getting started in Node.js and I'm completely confused as to why I can't get the results of my SQL query to render on a page, i'm using sqlite3.
This is a part of my index.js file
router.get('/students', function (req, res, module) {
var fs = require("fs");
var file = "./test.db";
var exists = fs.existsSync(file);
if (!exists) {
console.log("Creating DB file.");
fs.openSync(file, "w");
}
var sqlite3 = require("sqlite3").verbose();
var db = new sqlite3.Database(file);
db.serialize(function () {
if (!exists) {
db.run("Create table students ( ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, Nom varchar (255), Prenom varchar (255) );");
}
});
db.all('SELECT * FROM students', function selectCb(err, rows, fields) {
if (err) {
throw err;
}
for (var i in rows) {
console.log(rows[i]);
}
res.render('showResults.jade', {
results: rows
});
});
db.close();
res.render('students', { title: 'list' });
});
This is my Jade (create.jade) file;
block content
form(method='get', action='/students/create')
button(type='submit') Creer
ul
each item, i in results
li #{item.Nom} (#{item.Prenom})
li= JSON.stringify(item)
I put all of this things in my express app, I launch it with my shell, I receive all the sql data like this:
{ ID: 1, Nom: 'zeze', Prenom: 'zeze' }
{ ID: 2, Nom: 'ertty', Prenom: 'uuuuuuuuuuu' }
But my /students page show a message
http://i.stack.imgur.com/ormUG.png
Could you please help me, I'm really desperate.
(Sorry for my bad english I'm French) ^^
res.render('students', { title: 'list' });
and this line
each item, i in results
The variable names do not match.
From the picture, I can see that you are rendering the page called students.jade. For that page, you are sending variable title to Jade but you declared that the variable would be called results in Jade.
Have you tried removing the second res.render call?
It is possible that it is the one being called instead of the first one since the async call to the DB is not yet finished when the execution reaches the second res.render call.
db.close() should be moved inside the query's callback because it closes the db connection before the query has the chance to finish. Here is the revised code:
db.all('SELECT * FROM students', function selectCb(err, rows, fields) {
if (err) {
db.close(); // <<<<<<<<<<<<<<
throw err;
}
for (var i in rows) {
console.log(rows[i]);
}
res.render('showResults.jade', {
results: rows
});
db.close(); // <<<<<<<<<<
});