Node js, Wait, until get all data from MongoDB - node.js

I have a problem with async function and callbacks in Node js. I need to get my friends' all posts and display it. But if i do that without setTimeout(), it returns only some part of data. How can i solve this problem without putting setTimeout? Of course it's absurd to wait for 5-6 or 10 seconds to get all data. I also tried with Promises, but again response is incomplete. Please someone can help me?
//Sending request with axios to Controller
axios.post(packages.proxy+'users/getFriendsPosts',{id: user_id},config)
.then(res => {
// Code for displaying result
})
//User Controller
router.post("/getFriendsPosts", getFriendsPosts);
//Send request body to userService.js
function getFriendsPosts(req, res, next) {
userService.getFriendsPosts(req.body, function(posts, user){
res.json({posts,user});
})
.catch(err => next(err));
}
//userService.js
module.exports = {
getFriendsPosts,
};
async function getFriendsPosts(user,callback){
var arr = [];
var array = [];
MongoClient.connect(url, async function(errr, db) {
if (errr) throw errr;
var dbo = db.db("drone-x");
//Find user
dbo.collection("users").find({_id: ObjectId(user.id)}).toArray(async function(err, result) {
if (err) throw err;
result.forEach(async function(element, index) {
if(element.friends.length != 0){
element.friends.forEach(async function(elem) {
//Find user's friends
dbo.collection("users").find({_id: ObjectId(elem.id)}).toArray(async function(error, res) {
if (error) throw error;
//push user's friends to arr
arr.push(res);
res.forEach(async function(elements) {
//Find user's friends posts
dbo.collection("posts").find({userId: elements._id.toString()}).toArray(async function(errors, results) {
if (errors) throw errors;
//push user's friends posts to array
array.push(results);
//callback results through setTimeout
setTimeout(async function(){ await callback(array, arr); db.close(); }, 2000);
});
});
});
});
}
else
{
await callback("0");
}
});
});
});
}
If i don't use setTimeout function, it just returns 2-3 data, but with setTimeout, it returns all data. And if data will be raise, then i need to increase the setTimeout time. But of course it's not good idea. Someone can help me?

You should use try catch in this code
getFriendsPosts = async (user,callback) => {
const arr = [];
const array = [];
const db = await MongoClient.connect(url);
const dbo = db.db("drone-x");
const results = await dbo.collection("users").find({_id: ObjectId(user.id)})
const resultPromise = _.map(results, async element => {
const friends = _.get(element, 'friends', [])
if(friends.length != 0) {
const friendPromise = _.map(friends, async friend => {
const ress = await dbo.collection("users").find({_id: ObjectId(friend.id)})
arr.push(ress);
const resPromise = _.map(ress, async res => {
const posts = await dbo.collection("posts").find({userId: res._id.toString()})
const postPromise = _.map(posts, post => {
array.push(post);
})
await Promise.all(postPromise)
})
await Promise.all(resPromise)
})
await Promise.all(friendPromise)
}
})
await Promise.all(resultPromise)
return { arr , array }
}
I am not recommand this way it take too much time. You should use mongoose and use aggregation for long Query.

Related

function returning nothing in mongodb query

I'm not capturing the connect function to return in the getAllowedEmails function, when I do console.log in allowedEmails, it returns the emails correctly, but when I assign to the variable emails, it is returning empty. I think it's an async await problem but can't figured out.
static async getAllowedEmails() {
var MongoClient = require("mongodb").MongoClient;
//db url
let emails = [];
await MongoClient.connect(url, async function (err, client) {
const db = client.db("data-admin");
var myPromise = () => {
return new Promise((resolve, reject) => {
db.collection("users")
.find({})
.toArray(function (err, data) {
err ? reject(err) : resolve(data);
});
});
};
var result = await myPromise();
client.close();
let allowedEmails = [];
result.map((email) => allowedEmails.push(email.email));
console.log(allowedEmails)
emails = allowedEmails;
});
console.log(emails)
return emails;
}
Your code has couple of issues, I have fixed few and enhanced it, given below is the basic code, try to test it and if everything works then enhance it as needed:
const MongoClient = require("mongodb").MongoClient;
async function getAllowedEmails() {
let client;
try {
const allowedEmails = [];
client = await MongoClient.connect(url);
const db = client.db("data-admin");
const result = await db.collection("users").find({}).toArray();
result.map((email) => allowedEmails.push(email.email));
console.log(allowedEmails)
client.close();
return allowedEmails;
} catch (error) {
(client) && client.close();
console.log(error)
throw error;
}
}

Async/await func in another async/await func in Node.js, MongoDB

I am trying to search available objects of a MongoDB collection(e.g. ParkingSpot) using specific business logic, the problem is that inside this function which is an async/await func I am looking inside another collection with .find() which I use as an async/await as well. The second function doesn't execute and gets back as an promise " Promise { } " and therefore the business logic is not applied. Can you help me please?
Here is 1st function:
exports.get_parkingSpots_avail_for_parking = async (req, res, next) => {
try {
const radius = req.body.radius;
const userLatitude = req.body.userLatitude;
const userLongitude = req.body.userLongitude;
const reqStartDate = new Date(req.body.reqStartDate);
const reqEndDate = new Date(req.body.reqEndDate);
const parkingSpots = await ParkingSpot.find({
isAvailable: true
}, (err, parkingSpots) => {
if (err) {
return res.status(500).json({
error: err
});
}
const freeParkingSpots = parkingSpots.filter(parkingSpot => {
return distanceCalculator(parkingSpot.latitude, parkingSpot.longitude, userLatitude, userLongitude) < radius
})
const availParkingSpots = freeParkingSpots.filter( freeParkingSpot => {
// console.log(freeParkingSpot._id);
console.log(isParkingAvailable(reqStartDate, reqEndDate, freeParkingSpot._id));
return isParkingAvailable(reqStartDate, reqEndDate, freeParkingSpot._id);
})
}).select('_id userId number address latitude longitude notes isAvailable startOfAvailability endOfAvailability');
console.log(parkingSpots);
if (parkingSpots.length > 0) {
return res.status(200).json({
parkingSpots: parkingSpots
});
}
return res.status(404).json({
message: "No available parking spots for requeste period"
});
} catch (err) {
res.status(500).json({
error: err
})
}
};
Second function which is being called :
module.exports = async function isParkingAvailable(reqStartDate, reqEndDate, parkingSpotId) {
const parkingSpotBookings = await Booking.find({ parkingSpotId: parkingSpotId})
.select('_id parkingSpotId sharerUserId renterUserId startDate endDate');
if (parkingSpotBookings.length == 0) {
return true;
}
parkingSpotBookings.filter(booking => {
//console.log(parkingSpotId);
//console.log("Is overlapping" +!isOverlapping(reqStartDate, reqEndDate, booking.startDate, booking.endDate));
return !isOverlapping(reqStartDate, reqEndDate, booking.startDate, booking.endDate)
})
}
So the problem is that calling second function appears as that in console.log :Promise { }
Await will Return the result to your parkingSpot variable.
For the second function:
You have defined this function as an async, that means it is holding asynchronous process, and in Node JS es6 async process is processed as a promise, so if you won't call it with await it will return a promise only
exports.get_parkingSpots_avail_for_parking = async (req, res, next) => {
try {
const radius = req.body.radius;
const userLatitude = req.body.userLatitude;
const userLongitude = req.body.userLongitude;
const reqStartDate = new Date(req.body.reqStartDate);
const reqEndDate = new Date(req.body.reqEndDate);
const parkingSpots = await ParkingSpot.find({isAvailable: true}).select('_id userId number address latitude longitude notes isAvailable startOfAvailability endOfAvailability');
const freeParkingSpots = parkingSpots.filter(parkingSpot => {
return distanceCalculator(parkingSpot.latitude, parkingSpot.longitude, userLatitude, userLongitude) < radius
});
const availParkingSpots = freeParkingSpots.filter( async freeParkingSpot => {
/* You have defined this function as an async, that means it is holding asynchronous process, and in
Node JS es6 async process is processed as a promise, so if you won't call it with await it will return a promise only */
return await isParkingAvailable(reqStartDate, reqEndDate, freeParkingSpot._id);
});
if (parkingSpots.length > 0) {
return res.status(200).json({
parkingSpots: parkingSpots
});
}
return res.status(404).json({
message: "No available parking spots for requeste period"
});
} catch (err) {
res.status(500).json({
error: err
})
}
};
I hope it will help you.
Thank you

Formatting MongoDb response for API

I'm trying to retrieve a collection in MongoDb and the problem is the API already responds even though the processing is not yet done. So basically what I'm trying to do is retrieve the results using find, iterate through the results using foreach, and push each row to an array.
I've tried a variety of suggestions but none work so far. Below is a rough idea of what I'm trying to pull off.
get: async function (req, res, next) {
let messagesArray = []
let sessionId = req.query.session
client.connect(err => {
try{
const collection = client.db("xxxxxxx").collection("xxxxxxx")
let results = collection.find({},{sessionId:sessionId})
for (const result of results){
order = {"text" : order.partner+", "+order.order+ ", "+order.quantity}
messagesArray.push(order)
}
}
catch(e){
}
client.close()
res.send(200,{messages:messagesArray})
})
},
for loop will execute asynchronous so before your for loop is getting finished res is sent so try for the following
get: async function (req, res, next) {
let messagesArray = []
var counter = 0;
let sessionId = req.query.session
client.connect(err => {
try{
const collection = client.db("xxxxxxx").collection("xxxxxxx")
let results = collection.find({},{sessionId:sessionId})
for (const result of results){
order = {"text" : order.partner+", "+order.order+ ", "+order.quantity}
messagesArray.push(order)
counter++;
if(counter == results.length) {
client.close()
res.send(200,{messages:messagesArray})
}
}
}
catch(e){
}
})
},
use await to wait for result to available :
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
get: async function (req, res, next) {
let messagesArray = []
let sessionId = req.query.session
const client = await MongoClient.connect(url, { useNewUrlParser: true }).catch(err => { console.log(err); });
if (!client) { return;}
try{
const collection = client.db("xxxxxxx").collection("xxxxxxx")
let results = await collection.find({sessionId:sessionId})
console.log(results);
// ...
}
catch(e){
console.log(e);
}
finally {
client.close();
}
},

async save multiple document with mongoose

i am updating 2 documents using mongoose.save(), but i think the way I am doing is is not safe, as far as i know i need to use async to make sure all documents are being executed
// array containing the 2 documents from db
let schedules
let newItem = {
isActive: room.isActive,
name: room.roomname
};
// adding new items to nested array
schedules[0].rooms.push(newItem);
schedules[1].rooms.push(newItem);
// saving / updating documents
var total = schedules.length,
result = [];
function saveAll() {
var doc = schedules.pop();
doc.save(function(err, saved) {
if (err) throw err; //handle error
result.push(saved);
if (--total) saveAll();
else {
// all saved here
res.json(result);
}
});
}
saveAll();
any explanation how to do it correctly
We can use promise.all for this but we need to change your save function to promise based function
...
var total = schedules.length,
result = [];
function saveAll() {
const promises = schedules.map(schedule => save(schedule));
return Promise.all(promises)
.then(responses => {
// all saved processes are finished
res.json(responses);
})
}
// convert callback `save` function to promise based
function save(doc) {
return new Promise((resolve, reject) => {
doc.save((err, saved) => {
if (err) {
reject(err);
}
resolve(saved);
});
});
}
If you can use async/await we can make saveAll function cleaner
async function saveAll() {
const promises = schedules.map(schedule => save(schedule));
const responses = await Promise.all(promises);
res.json(responses);
}
Hope it helps
Use Promises :
doc1.save().exec().then(
data => {
doc1.save().exec().then(
data2 => console.log(data2)
).catch(err2 => console.log(err2))
}
).catch(err1 => console.log(err1))

why does my async function returns undefine while working with mysql2?

I've been trying non-stop to work on this query for my datatables front end.
this is my config.js
var config = {
host : 'localhost',
user : 'root',
password : '',
database : 'ef45db'
}
module.exports = config;
this is the function I want to work with async (wait for the query to return the table's columns name)
async function getColumnNames()
{
try{
aColumns = [];
await connection.query('SHOW COLUMNS FROM '+sTable,
function selectCb(err, results, fields){
console.log("entro a getColumnNames");
if(err){
console.log(err);
}
for(var i in results)
{
aColumns.push(results[i]['Field']);
}
connection.end();
});
}catch (e){
console.log(e);
}
}
and this is the controller code to execute that function:
var mysql = require('mysql2');
var config = require('.././database/config');
var connection = mysql.createConnection(config);
var sIndexColumn = '*';
var sTable = 'users';
var aColumns = [];
module.exports = {
getInfo : async function(req,res,next)
{
var request = req.query;
(async () => await getColumnNames());
console.log(aColumns);
}
I'm trying to get the column's name so I can work with datatable's filtering for backend, since node is async this query was getting executed, but the value was undefined (and still is), I've read hundreds of post regarding promises, bluebird and async methods and trying to make this work, the last I've read a lot thats the best and I choosed it because the code seems cleaner. Any ideas whats happening?
For getColumnNames(), you shouldn't use await because connection.query doesn't return promise. It is a callback function.
However, we can make getColumnNames to return promise.
function getColumnNames() {
const aColumns = [];
return new Promise((resolve, reject) => {
connection.query('SHOW COLUMNS FROM ' + sTable,
function selectCb(err, results, fields) {
console.log("entro a getColumnNames");
if (err) {
console.log(err);
reject(err); // if error happens, reject
}
for (var i in results) {
aColumns.push(results[i]['Field']);
}
connection.end();
resolve(aColumns); // resolve with our database columns
});
});
}
and for your controller we can use async await since getColumnNames returns promise as in
module.exports = {
getInfo: async function (req, res, next) {
var request = req.query;
const aColumns = await getColumnNames();
console.log(aColumns);
}
}
Let me know if it works for you.

Resources