NodeJS Express: how to get object item - node.js

I try to create a simple login form and fail to validate the password from the MongoDB.
First I create the .post route for the form validation and then I get the MongoDB data which I want to compare with the form.
Here is my code:
app.post('/users', (req, res) => {
const reqUser = req.body.params.name
const reqPW = req.body.params.password
// connect to mongoDB
const collection = client.db().collection("users")
collection.find({name: reqUser}).toArray(function (err, results) {
if (err) {
console.log(err)
res.send([])
return
}
else {
console.log('RESULT', results) // returns the object
console.log('RES PW', results.password) // returns undefined
// this does not work
Object.keys(results).forEach(function(key) {
console.log('key is: ', key); // returns 0
});
// validate user+pw
if (!reqUser || !reqPW/*|| reqPW !== password*/) {
return res.status(401).end()
}
// send result to frontend
res.send(results)
res.end
}
})
})
So, I get my object returned in results but I cannot get the data from the object.
I also tried to convert it to an array with Array.from() but that didn't work either.
Please note that I did not yet implement hashing and salting the passwords yet, as I thought I want a working validation first. Do I need to implement those first?

I just checked the doc:
The toArray() method returns an array that contains all the documents from a cursor. The method iterates completely the cursor, loading all the documents into RAM and exhausting the cursor.
So toArray() will return a array, not object, therefore your results will be an array containing all the items(object) you get from the db. If you console.log(results), it should print an array rather than object.
Assuming there won't be two users have the same name, the results you get will be just an array containing one object, so you can just do:
results[0].password // get the first object's password field
Not sure if this slove your question, but based on your code thats the problem i found in it.

Related

result prints undefined after invoking a new method

I am trying to loop though all users data in my mongo database and check and see if an email is in the database. The loop currently works and correctly identifies if an email is in a database but the problem is once I verify the email exist I get the id of the same object and use findById() to find the specific object the email was found in then update it. Once I find the object when I try and print the result I got from the first find() it logs undefined but when I log it before the findById() method it logs the result without no problem. Why is this happening and how can I log the previous result after invoking findById(). Take a look at the comments on my code to understand the question better. Thanks in advance.
const Users = require('pathToSchema')
const email = 'test#gmail.com'
Users.find()
.then(async(result) => {
for (var i = 0; i < result.length; i++) {
if (result[i].email == email) {
//this prints result successfully
console.log(result[i])
Users.findById(result[i].id)
.then((result2) => {
//this prints undefiend
console.log(result[i])
})
.catch((err) => {
console.log(err)
})
} else {
if (i === result.length - 1) {
console.log('email not found')
}
}
}
})
.catch((err) => {
console.log(err)
})
From the code snippet it looks like you are trying to print a value from result and not result2. result is not available inside the findById() method callback handler.
Continuing the discussion from the comments, you can use the findOneAndUpdate method in mongodb to find a user with a given email and update. With this, you will not have to find the user before you update. You can do that in a single DB command.
Users.findOneAndUpdate({email: 'test#gmail.com'},{updates},{options});
This will return the original document before update. If you need the updated document in the response, pass returnNewDocument: true in the options.
Link to the documentation for this function
https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndUpdate/

res.render() calls promise twice expressjs

Hello I am still a beginner in Nodejs and MongoDB. When I am running res.render() function as shown below I get 2 console logs, first with the correct result from mongodb find query and second with null. If I remove res.render() It works properly with only one console.log() output and not the second null.
router.get('/:areaslug', (req, res) => {
// Query results
let areavar = {}
return client.db("AwesomeApartments")
.collection("areas")
.findOne({slug: req.params.areaslug
})
.then((datadb) => {
// first time shows correct results next it shows null
console.log(" bug-testing : "+JSON.stringify(datadb));
//storing result of first query to my object
areavar = datadb;
return client.db("AwesomeApartments")
.collection("apartments")
.find({area:datadb._id})
.toArray()
})
.then((apartmentdb) => {
// Storing results of second query
areavar.apartments = apartmentdb;
//removing this solves the issue
return res.render('area',areavar);
}).catch(err =>
{
console.log(err);
return;
})
});
To those who find their way here, browser was generating two get requests. First one for query and second one for favicon.ico. Handling favicon.ico request solved this issue.

Using node.js and promise to fetch paginated data

Please keep in mind that I am new to node.js and I am used with android development.
My scenario is like this:
Run a query against the database that returns either null or a value
Call a web service with that database value, that offers info paginated, meaning that on a call I get a parameter to pass for the next call if there is more info to fetch.
After all the items are retrieved, store them in a database table
If everything is well, for each item received previously, I need to make another web call and store the retrieved info in another table
if fetching any of the data set fails, all data must be reverted from the database
So far, I've tried this:
getAllData: function(){
self.getMainWebData(null)
.then(function(result){
//get secondary data for each result row and insert it into database
}
}
getMainWebData: function(nextPage){
return new Promise(function(resolve, reject) {
module.getWebData(nextPage, function(errorReturned, response, values) {
if (errorReturned) {
reject(errorReturned);
}
nextPage = response.nextPageValue;
resolve(values);
})
}).then(function(result) {
//here I need to insert the returned values in database
//there's a new page, so fetch the next set of data
if (nextPage) {
//call again getMainWebData?
self.getMainWebData(nextPage)
}
})
There are a few things missing, from what I've tested, getAllData.then fires only one for the first set of items and not for others, so clearly handling the returned data in not right.
LATER EDIT: I've edited the scenario. Given some more research my feeling is that I could use a chain or .then() to perform the operations in a sequence.
Yes it is happening as you are resolving the promise on the first call itself. You should put resolve(value) inside an if statement which checks if more data is needed to be fetched. You will also need to restructure the logic as node is asynchronous. And the above code will not work unless you do change the logic.
Solution 1:
You can either append the paginated response to another variable outside the context of the calls you are making. And later use that value after you are done with the response.
getAllData: function(){
self.getMainWebData(null)
.then(function(result){
// make your database transaction if result is not an error
}
}
function getList(nextpage, result, callback){
module.getWebData(nextPage, function(errorReturned, response, values) {
if(errorReturned)
callback(errorReturned);
result.push(values);
nextPage = response.nextPageValue;
if(nextPage)
getList(nextPage, result, callback);
else
callback(null, result);
})
}
getMainWebData: function(nextPage){
return new Promise(function(resolve, reject) {
var result = [];
getList(nextpage, result, function(err, results){
if(err)
reject(err);
else{
// Here all the items are retrieved, you can store them in a database table
// for each item received make your web call and store it into another variable or result set
// suggestion is to make the database transaction only after you have retrieved all your data
// other wise it will include database rollback which will depend on the database which you are using
// after all this is done resolve the promise with the returning value
resolve(results);
}
});
})
}
I have not tested it but something like this should work. If problem persists let me know in comments.
Solution 2:
You can remove promises and try the same thing with callback as they are easier to follow and will make sense to the programmers who are familiar with structural languages.
Looking at your problem, I have created a code that would loop through promises.
and would only procede if there is more data to be fetched, the stored data would still be available in an array.
I hope this help. Dont forget to mark if it helps.
let fetchData = (offset = 0, limit= 10) => {
let addresses = [...Array(100).keys()];
return Promise.resolve(addresses.slice(offset, offset + limit))
}
// o => offset & l => limit
let o = 0, l = 10;
let results = [];
let process = p => {
if (!p) return p;
return p.then(data => {
// Process with data here;
console.log(data);
// increment the pagination
o += l;
results = results.concat(data);
// while there is data equal to limit set then fetch next page
// otherwise return the collected result
return (data.length == l)? process(fetchAddress(o, l)).then(data => data) : results;
})
}
process(fetchAddress(o, l))
.then(data => {
// All the fetched data will be here
}).catch(err => {
// Handle Error here.
// All the retrieved data from database will be available in "results" array
});
if You want to do it more often I have also created a gist for reference.
If You dont want to use any global variable, and want to do it in very functional way. You can check this example. However it requires little more complication.

Error when using moment.max with the results of a mongoDB query

I am working on a nodeJS application that inserts documents into a mongoDB collection. The document has a createdAt field that records the time of creation. I also have a function that logs the most recent match of a query to the console.
Code:
function getResult(player){
let timeList = [];
MongoClient.connect(url, (err, db) => {
if(err){
console.log(err);
}
else{
let count = db.collection('PokeBook').count({ 'player1': player }); //count function returns a promise
count.then((val) => {
db.collection('PokeBook').find({ 'player1': player }).forEach((doc) => {
timeList.push(doc.createdAt);
console.log(doc.createdAt);
if(val===timeList.length){
myEmitter.emit('full', timeList);
}
});
});
}
});
}
//code for the emitter:
myEmitter.on('full', (arr) => {
console.log('full emitted');
console.log(moment.max(arr));
});
the code returns an error saying moments[i].isValid is not a function. Commenting the moment.max line of code results in successfully logging "full emitted" to the console.
Any advice on why this happens and how to fix this will be much appreciated. :)
moment.max() expects an array of Moment objects, but doc.createdAt is (probably) a regular Date object. You can try to replace:
timeList.push(doc.createdAt);
with:
timeList.push(moment(doc.createdAt));
so arr would be an array of Moment objects.
Alternatively, you can implement max yourself w/o using moment, assuming doc.createdAt is either Date or Number:
console.log(arr.sort()[arr.length-1]);

returning JSON Array in Node

Im still trying to get my head around the asynchronous flow in Node. Im trying to read list of files in db and convert result to json array and return back. The code works fine for one json object. But there is no output when reading the complete array
So this is the code
Function call - receive ajax request from browser and send back result
module.retrieveSourceContent(req, res, result, function(err, result){
if(err){
console.log(err);
return;
}
console.log(result);
res.contentType('json');
res.write(JSON.stringify(result));
res.end();
});
Code
retrieveSourceContent : function(req, res, sourceList, callback){
var sourceContent = new Array();
MongoClient.connect(config.mongoPath+config.dbName, function(err, db) {
if(err){
return callback(new Error("Unable to Connect to DB"));
}
var collection = db.collection(config.source);
for( i=0;i<sourceList['sources_FOR'].length;i++){
//build the source JSON Array
collection.find({'_id':sourceList['sources_FOR'][i]}).nextObject(function(err, doc) {
if(err)
return callback(new Error("Error finding data in DB"));
var sourceObject = {
title :doc.name,
tagCount :doc.tag.length,
tags :doc.tag,
format :doc.type, // Differentiate text, image, video and urls
content :doc.data // Content
};
sourceContent.push(sourceObject);
//if(i == sourceList['sources_FOR'].length - 1)
return callback(null, sourceContent);
});
}
});
}
This code return one json object to client. If i uncomment if(i == sourceList['sources_FOR'].length - 1) i have no output and no error. But sourceContent.push(sourceObject); does create the json array successfully.
Since this flow works on other language, i suspect it has something to do with asyn flow of node. Im at a loss here on how to solve this.
Any help would be great..
It's a little bit difficult following the flow of the program due to the formatting. But I think you are correct assuming that this has to do with async behaviour. If I'm not fooled by the formatting, it looks to me like you're doing the callback in every iteration of the loop, which would only work for the first iteration. I'm not sure what your sourcesList is, but I would try to construct a query that includes all the items in sourcesList. Then you would not need the foor loop. Maybe you can use the $in operator.

Resources