Set Variable to result of Mongoose Find - javascript

I'm trying to do something like this
function retrieveUser(uname) {
var user = User.find({uname: uname}, function(err, users) {
if(err)
console.log(err);
return null;
else
return users[0];
return user;
But this returns a document instead of a user object. The parameter users is an array of user objects matching the query, so how would I store one of the objects into a variable that my function could return?

The function User.find() is an asynchronous function, so you can't use a return value to get a resultant value. Instead, use a callback:
function retrieveUser(uname, callback) {
User.find({uname: uname}, function(err, users) {
if (err) {
callback(err, null);
} else {
callback(null, users[0]);
}
});
};
The function would then be used like this:
retrieveUser(uname, function(err, user) {
if (err) {
console.log(err);
}
// do something with user
});

Updated on 25th Sept. 2019
Promise chaining can also be used for better readability:
Model
.findOne({})
.exec()
.then((result) => {
// ... rest of the code
return Model2.findOne({}).exec();
})
.then((resultOfModel2FindOne) => {
// ... rest of the code
})
.catch((error) => {
// ... error handling
});
I was looking for an answer to the same question.
Hopefully, MongooseJS has released v5.1.4 as of now.
Model.find({property: value}).exec() returns a promise.
it will resolve to an object if you use it in the following manner:
const findObject = (value) => {
return Model.find({property: value}).exec();
}
mainFunction = async => {
const object = await findObject(value);
console.log(object); // or anything else as per your wish
}

Basically, MongoDB and NodeJS have asynchronous functions so we have to make it to synchronous functions then after it will work properly as expected.
router.get('/', async function(req, res, next) {
var users = new mdl_users();
var userData = []; // Created Empty Array
await mdl_users.find({}, function(err, data) {
data.forEach(function(value) {
userData.push(value);
});
});
res.send(userData);
});
In Example, mdl_users is mongoose model and I have a user collection(table) for user's data in MongoDB database and that data storing on "userData" variable to display it.In this find function i have split all documents(rows of table) by function if you want just all record then use direct find() function as following code.
router.get('/', async function(req, res, next) {
var users = new mdl_users();
var userData = await mdl_users.find();
res.send(userData);
});

Related

Return value inside a collection is returned as undefined but works as a response send

I have the following GET call which works as intended, connecting to a couchbase db and performing some updates.
databaseRouter.put('/update/:id', (req, res) => {
updateDocument(req, res);
});
export const updateDocument = (req, res) => {
collection.get(req.params.id, (err, result) => {
if (err) {
res.status(404).send(err);
} else {
const document = result.value;
document.product_id = req.body.id || document.product_id;
collection.replace(req.params.id, document, (err) => {
if (err) {
res.status(500).send(err);
}
}).then(() => res.json(document));
}
}).catch(e => console.log(e));
}
This is for external clients to use.
But I want this to logic to be reusable within the project in another instance for batch processing. Not a rest call.
Thus I am looking to refactor the updateDocument function to return the document value or errors instead of performing res.send();
But I can't just modify as follows. result is undefined.
And I am also not gonna be able to maintain the status codes for errors.
Unless I explicitly return like a object with a key called status.
export const updateDocument = (req, res) => {
.....
}).then(() => document); // instead of }).then(() => res.json(document));
.....
}
databaseRouter.put('/update/:id', (req, res) => {
const result = updateDocument(req, res); // result is undefined
res.send(result);
});
Is there a way I could elegantly extract the logic so that I can continue to achieve what I have for the GET call for clients
but also be able to reuse the same logic internally within the project?

Node.js MSSQL package query function not returning data

I'm writing my first Node.js REST API. I'm using MS SQL Server as my database. I am using the npm package mssql to work with my SQL server DB.
I took the code below directly from the mssql docs example page. I simply wrapped it into a function.
function getServices() {
sql
.connect(config)
.then((pool) => {
// Query
return pool
.request()
.input("SID", sql.Int, 1)
.query(
"select * from [dbo].[Services] where ServiceId = #SID"
);
})
.then((result) => {
//console.dir(result); //this has data.
return result;
})
.catch((err) => {
console.log(err);
return err;
});
}
The above code works just fine and gets the data from my DB. The issue happens when I try to make this code into a function that I can use on my express route, as shown below.
router.get("/", (req, res, next) => {
const data = getServices();
console.log("data: ", data); //this comes back as undefined
res.status(200).json({
message: "Handling GET request from /services router.",
});
});
From reading the docs and all the others posts on stackoverflow. I am using the .then() promise mechanism, so why is my "result" not getting back to the function on my express route? The "data" const on the express route is always undefined. What am I missing here?
Rule of thump: calling a function const data = getServices(); means that this function doesn't contain any asynchronous work like async/await/.then/.catch/Promise.
Once a function containes one of those, it should always be called with one of the above.
SO #1 you must change
router.get("/", async (req, res, next) => {
const data = await getServices();
console.log("data: ", data); //this comes back as undefined
res.status(200).json({
message: "Handling GET request from /services router.",
});
});
Then the function itself
function getServices() {
try {
return sql
.connect(config)
.then((pool) => {
// Query
return pool
.request()
.input("SID", sql.Int, 1)
.query(
"select * from [dbo].[Services] where ServiceId = #SID"
);
})
.then((result) => {
//console.dir(result); //this has data.
return result;
})
} catch(ex){
return ex;
}
}
I kept digging through some more SO posts and found my working solution. Updated, working code is posted below.
router.get("/", (req, res, next) => {
getServices().then((result) => {
res.status(200).json(result.recordset);
});
});
function getServices() {
return new Promise((resolve) => {
sql
.connect(config)
.then((pool) => {
// Query
return pool
.request()
.input("SID", sql.Int, 1)
.query(
"select * from [dbo].[Services] where ServiceId = #SID"
);
})
.then((result) => {
//console.dir(result);
resolve(result);
})
.catch((err) => {
console.log(err);
resolve(err);
});
});
}

using async/await for sql data insert

function Recursive_scan_and_Insert (path_dir) { //scanning path_dir recursively and insert filepath into temporary list
Recursive_Scan(path_dir, (err, files) => { //it's from npm recursive_readdir
if(err) {
console.log(err);
res.status(500).send('server error');
}
files.forEach(elements => {
let params = [elements]; //
DB("GET", "INSERT INTO filelist_t VALUES (null, ?, NOW(), 0, 0)", params).then(function(res) {
console.log('data input');
});
});
});
};
function Add_to_DB () { //moving temporal list to main list without duplicate
DB("GET", "INSERT INTO filelist (id, path, addeddate, isdeleted, ismodified) SELECT NULL, filelist_t.path, filelist_t.addeddate, filelist_t.isdeleted, filelist_t.ismodified FROM filelist_t LEFT JOIN filelist ON filelist.path = filelist_t.path WHERE filelist.id IS NULL; DELETE FROM filelist_t; ").then(function(res) {
console.log('data moving');
});
};
app.get('/db', (req, res) => { //PROBLEM PART
async function async_Two_Functions () {
var object_path = '/want/to/scan/path';
await Recursive_scan_and_Insert(object_path).then( () => {
return Add_to_DB()
})
}
async_Two_Functions();
res.send(res);
});
app.get('/dbp', (req, res) => { //show main list to my web
DB("GET", "SELECT * FROM filelist").then(function(res2) {
res.send(res2.row);
});
});
here's the thing.
there are 4 stage in my dream algorithm.
recursively scan all the path.
insert each data into temporary table.
moving temporal data on main table, without duplicate
present main table
it's very important to things get order. but I don't understand about async await exactly...
Well, here's a cleaned up version of the code with lots of changes.
const {promisify} = require('util');
const Recursive_ScanP = promisify(Recursive_Scan);
function Recursive_scan_and_Insert(path_dir) { //scanning path_dir recursively and insert filepath into temporary list
return Recursive_ScanP(path_dir).then(files => {
return Promise.all(files.map(elements => {
let params = [elements];
return DB("GET", "INSERT INTO filelist_t VALUES (null, ?, NOW(), 0, 0)", params).then(function(res) {
console.log('data input');
// what should the return value be here?
});
}));
});
};
function Add_to_DB () { //moving temporal list to main list without duplicate
return DB("GET", "INSERT INTO filelist (id, path, addeddate, isdeleted, ismodified) SELECT NULL, filelist_t.path, filelist_t.addeddate, filelist_t.isdeleted, filelist_t.ismodified FROM filelist_t LEFT JOIN filelist ON filelist.path = filelist_t.path WHERE filelist.id IS NULL; DELETE FROM filelist_t; ").then(function(res) {
console.log('data moving');
return res;
});
};
app.get('/db', async (req, res) => {
try {
let object_path = '/want/to/scan/path';
await Recursive_scan_and_Insert(object_path);
await Add_to_DB();
res.send(somethingHere); // you fill in what response you want to send here
} catch(e) {
console.log(e);
res.status(500).send("Server Error");
}
});
app.get('/dbp', (req, res) => { //show main list to my web
DB("GET", "SELECT * FROM filelist").then(function(res2) {
res.send(res2.row);
}).catch(err => {
console.log(err);
res.status(500).send("Server Error");
});
});
Changes:
Return a promise from every function that has an asynchronous operation in it
Return whatever you want the resolved value to be for the promise from every .then() handler
Promisify anything that uses a regular callback so you can do all control flow with promises
Use Promise.all() to know when multiple promises are done and to collect results in order from doing a set of asynchronous operation in parallel
Use async/await as desired, but particularly when you want to sequence multiple asynchronous operatoins
Use try/catch around any await to catch rejected promise that aren't being returned to a higher level where they will be caught
Use .catch() with any .then() that isn't being returned to a higher level where it will be caught
Open Questions:
Don't know what response you want to send from app.get('/db', ...). You will have to fill that in.
Are you expecting any resolved value from Recursive_scan_and_Insert()?
Does Add_to_DB() really accept no input? It just reorganizes things already in the database?

How to run request sequentially in a loop?

This is a pseudo code of what I am trying to achieve. First I need to get a list of URLs from the request body then pass those URLs to request function (using request module) which will get the data from each url and then save those data to MongoDB. After all the requests are finished including saving data to the server only then it should send a response.
app.post('/', (req, resp) => {
const { urls } = req.body;
urls.forEach((url, i) => {
request(url, function (err, resp, body) {
if (err) {
console.log('Error: ', err)
} else {
// function to save data to MongoDB server
saveUrlData(body);
console.log(`Data saved for URL number - ${i+1}`)
}
})
});
// Should be called after all data saved from for loop
resp.send('All data saved')
})
I have tried this code and of course the resp.send() function will run without caring if the request has completed. Using this code I get a result on the console like this:
Data saved for URL number - 3
Data saved for URL number - 1
Data saved for URL number - 5
Data saved for URL number - 2
Data saved for URL number - 4
I could write them in nested form but the variable urlscan have any number of urls and that's why it needs to be in the loop at least from my understanding. I want the requests to run sequentially i.e. it should resolve 1st url and then second and so on and when all urls are done only then it should respond. Please help!
app.post('/', async (req, resp) => {
const {
urls
} = req.body;
for (const url of urls) {
try {
const result = await doRequest(url)
console.log(result)
} catch (error) {
// do error processing here
console.log('Error: ', err)
}
}
})
function doRequest(url) {
return new Promise((resolve, reject) => {
request(url, function(err, resp, body) {
err ? reject(err) ? resolve(body)
})
})
}
using async await
You should look at JavaScript Promises
Otherwise, you can do a recursive request like so:
app.post('/', (req, resp) => {
const { urls } = req.body;
sendRequest(urls, 0);
})
function sendRequest(urlArr, i){
request(urlArr[i], function (err, resp, body) {
if (err) {
console.log('Error: ', err)
}
else {
saveUrlData(body);
console.log(`Data saved for URL number - ${i+1}`)
}
i++;
if(i == urlArr.length) resp.send('All data saved') //finish
else sendRequest(urlArr, i); //send another request
})
}
All I had to do is create a separate function I can call over and over again, passing the url array and a base index 0 as arguments. Each success callback increments the index variable which I pass in the same function again. Rinse and repeat until my index hits the length of the url array, I'll stop the recursive loop from there.
You want to wait till all api response you get and stored in db, so you should do async-await and promisify all the response.
You can use Request-Promise module instead of request. So you will get promise on every requested api call instead of callback.
And use promise.all for pushing up all request(module) call inside array.
Using async-await you code execution will wait till all api call get response and stored in db.
const rp = require('request-promise');
app.post('/', async (req, res) => {
try{
const { urls } = req.body;
// completed all will have all the api resonse.
const completedAll = await sendRequest(urls);
// now we have all api response that needs to be saved
// completedAll is array
const saved = await saveAllData(completedAll);
// Should be called after all data saved from for loop
res.status(200).send('All data saved')
}
catch(err) {
res.status(500).send({msg: Internal_server_error})
}
})
function sendRequest(urlArr, i){
const apiCalls = [];
for(let i=0;i < urlArr.length; i++){
apiCalls.push(rp(urlArr[i]));
}
// promise.all will give all api response in order as we pushed api call
return Promise.all(apiCalls);
}
You can refer these links:
https://www.npmjs.com/package/request-promise
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Looking at the intention(a crawler) you can use Promise.all because the urls are not dependant upon each other.
app.post('/', (req, resp) => {
const { urls } = req.body;
const promises = urls.map((url, i) => {
return new Promise((resolve, rej)=>{
request(url, function (err, resp, body) {
if (err) {
rej(err);
} else {
resolve(body);
}
})
})
.then((body)=>{
//this should definitely be a promise as you are saving data to mongo
return saveUrlData(body);
})
});
// Should be called after all data saved from for loop
Promise.all(promises).then(()=>resp.send('All data saved'));
})
Note: Need to do error handling as well.
there are multiple ways to solve this.
you can use async/await
Promises
you can also use the async library
app.post('/', (req, res, next) => {
const { urls } = req.body;
async.each(urls, get_n_save, err => {
if (err) return next(err);
res.send('All data saved');
});
function get_n_save (url, callback) {
request(url, (err, resp, body) => {
if (err) {
return callback(err);
}
saveUrlData(body);
callback();
});
}
});

query multiple items to send to view in node using mssql module

I am trying to query multiple tables and pass those values to my view, this is my code which is producing the error.
router.get('/', function(req, res, next) {
sql.connect(config).then(() => {
return sql.query`select Project_Type_Desc from Project_Type`
}).then(result => {
return sql.query`select Curency_Code from Currency_List`
}).then(cur => {
res.render('newProject', {projects: result} , {currency : curr})
}).catch(err => {
console.log(err)
});
});
Please let me know what i am doing wrong. Thanks is advance.
It looks to me like you're turning a value from your first query to the variable result. However you then make another query and don't retain the value as you lose scope of result, you'll want to do something like this.
router.get('/', function(req, res, next) {
// I've added a variable here which we will use to save the query
// result to.
let projectResult;
sql.connect(config).then(() => {
return sql.query`select Project_Type_Desc from Project_Type`
}).then(result => {
// Now we save the query result
projectResult = result;
return sql.query`select Curency_Code from Currency_List`
}).then(cur => {
// We are now outside of the scope from the last block (above), so we use
// the value we saved in the parent scope (router.get).
res.render('newProject', {projects: projectResult} , {currency : curr})
}).catch(err => {
console.log(err)
});
});

Categories