async save multiple document with mongoose - node.js

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))

Related

how to refer to return value of a function from outside in Node.js

I've been making dynamic dropdown box that each option has the table's name of BigQuery and I want to use return value (list) that is made inside .then method in function listTables(). However it seems not to work well . I'm new to Js so could you give any tips ?? Thank you so much.
function listTables() {
const {BigQuery} = require('#google-cloud/bigquery');
const bigquery = new BigQuery({
projectId: 'test_project',
});
const list = [];
bigquery
.dataset("test_table")
.getTables()
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
return console.log(list);←I want to use this list outside a function
})
.catch(err => {
console.error('ERROR:', err);
});
}
listTables();
// select tag
let slt = document.getElementById("slt");
addTables(slt);
// return data
function getList() {
return new Promise(function (onFulliflled, onRejected) {
onFulliflled(list);
});
}
function addTables(slt) {
getList()
.then((list) => {
for (item of list) {
// optionを作成
let option = document.createElement("option");
option.text = item;
option.value = item;
// optionの追加
slt.appendChild(option);
}
})
.catch((err) => {
console.error("error", err);
});
}
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
return console.log(list);
})
RESULT
[ 'test', 'test1', 'test2', 'test3' ]
You can do this in multiple ways.
Using calllback
function listTables(callback) {
//...
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
callback(list);
})
//...
}
listTables(function(list){
});
Using promise or async/await
function listTables() {
return new Promise(function(resolve, reject){
//...
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
resolve(list);
})
//...
});
}
// Promise
listTables().then(function(list){
});
//Async/await
var list = await listTables();
For the await to work you also need to run in within an async function. For example wrap it in async iife.
(async function(){
var list = await listTables();
})();
I don't use await myself so this is just from top of my head and might need some changes.

Can I execute an Firestore query in .then callback of another query?

I am implementing a Cloud Function where I am executing several queries.
let rating_subcollection = await admin.firestore().collection("restaurants_collection").doc(vendor_id).collection('ratings').where('uID', '==', userId)
.get()
.then(async function (data) {
if (data.empty) {
let restaurants_collection = admin.firestore().collection("restaurants_collection").doc(vendor_id);
await admin.firestore().runTransaction(async (transaction) => {
const restDoc = await transaction.get(restaurants_collection);
// Compute new number of ratings
const newNumRatings = restDoc.data().noRat + 1;
// Compute new average rating
const oldRatingTotal = restDoc.data().rat * restDoc.data().noRat;
const newAvgRating = (oldRatingTotal + ratingVal) / newNumRatings;
// Update restaurant info
transaction.update(restaurants_collection, {
rat: newAvgRating,
noRat: newNumRatings
});
})
}
}).catch(error => {
return "Couldnt update the rating: " + error;
})
So, as you can see I am only executing the transaction IF data is empty and I have set async in the then() callback. Is this the right way to do?!
I found this example, where is explained on detail how get a collection using an async function, for example if you want to get a collection:
function getValues(collectionName, docName) {
return db.collection(collectionName).doc(docName).get().then(function (doc) {
if (doc.exists) return doc.data().text;
return Promise.reject("No such document");
}};
}
Or using await it inside an async function in a try/catch block, if you like that better:
async function doSomething() {
try {
let text = await getValues('configuration','helpMessage');
console.log(text);
} catch {
console.log("ERROR:" err);
}
}

Get value out of function in Node.js

I don't know about the correct title for my problem. I just need a value going out of a function, just like return but I think it's not same.
i have code snippet from controller in adonisjs framework:
var nmeadata = "";
jsondata.forEach(element => {
var x = element.nmea
var buff = new Buffer(x, 'base64')
zlib.unzip(buff, (err, res) => {
if(err)
{
//
}
else
{
nmeadata += "greed island"
nmeadata += res.toString()
}
})
});
return view.render('admin.index', {
data: datanmea.toJSON(),
nmea: nmeadata
})
I need the result of unzipped string data that inserted to nmeadata from zlib function then send it to view. But, for this time, even I cannot displaying a simple output like greed island to my view.
thank you.
UPDATE
Still not working after using promises:
class NmeaController {
async index({view})
{
const datanmea = await NmeaModel.all()
const jsondata = datanmea.toJSON()
var promises = [];
var nmeadata = "";
jsondata.forEach(element => {
promises.push(
new Promise(resolve => {
let x = element.nmea
let buff = new Buffer(x, 'base64')
zlib.unzip(buff,
(err, res) => {
if (err) {
//
} else {
nmeadata += "test add text"
// nmeadata += res.toString()
}
//im also try using resolve() and resolve("any text")
resolve(nmeadata);
})
}
)
)
});
await Promise.all(promises);
return view.render('admin.index', {
data: datanmea.toJSON(),
nmea: nmeadata
});
}
UPDATE AUGUST 22 2019
i'm already tried solution from maksbd19 but still not working
class NmeaController {
async index({view})
{
const datanmea = await NmeaModel.all()
const jsondata = datanmea.toJSON()
var promises = [];
var nmeadata = "";
jsondata.forEach(element => {
promises.push(
new Promise(resolve => {
let x = element.nmea
let buff = new Buffer(x, 'base64')
zlib.unzip(buff,
(err, res) => {
if (err) {
// since you are interested in the text only, so no need to reject here
return resolve("");
}
return resolve("greed island")
})
}
)
)
});
const result = await Promise.all(promises); // this will be an array of results of each promises respectively.
nmeadata = result.join(""); // process the array of result
return view.render('admin.index', {
data: datanmea.toJSON(),
nmea: nmeadata
});
}
}
I'd suggest two things-
modify zlib.unzip callback function to resolve properly;
(err, res) => {
if (err) {
// since you are interested in the text only, so no need to reject here
return resolve("");
}
return resolve(res.toString())
}
retrieve the final data from the result of Promise.all
const result = await Promise.all(promises); // this will be an array of results of each promises respectively.
nmeadata = result.join(""); // process the array of result
In this approach every promise will resolve and finally you will get the expected result in array.

Node js, Wait, until get all data from MongoDB

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.

Sending data back to Android from Firebase Cloud Functions

I want to send data back via functions.https.onCall, the code works and returns data to the console but i don't know why I can't send the data back to the device(in return number one, I understand can't why return number 2 is not working because the asynchronous task, so there is a way to to it after its finish?).
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
var posts= new Array();
var finalposts = new Array();
function post (uid,text,user_name,vid,sendtime,vote,tag,pic,lan){
this.uid=uid;
this.text=text;
this.user_name=user_name;
this.vid=vid;
this.sendtime=sendtime;
this.vote=vote;
this.tag=tag;
this.pic=pic;
this.lan=lan;
}
exports.getWarm = functions.https.onCall((data, context) => {
finalposts = [];
posts = [];
db.collection('local uploads/heb/posts').where('vote', '<', 200).where('vote', '>', 100).get()
.then((snapshot) => {
snapshot.forEach((doc) => {
posts.push( new post(doc.data()["uid"],doc.data()["text"],doc.data()["user_name"],doc.data()["vid"],
doc.data()["sendtime"],doc.data()["vote"],doc.data()["tag"],doc.data()["pic"],doc.data()["lan"]));
});
posts.sort(function(a,b) {return (a.sendtime<b.sendtime)? 1:((a.sendtime>b.sendtime)? -1:0);});
for (var i =0; i<data.refresh_num*2; i++) {
finalposts.push(posts[i]);
}
console.log('hey', '=>', finalposts);
// working: the posts are shown in the console
// (1.) return { posts: finalposts };
// returns null
})
.catch((err) => {
console.log('Error getting documents', err);
});
// (2)return { posts: finalposts };
// returns {posts=[]}
return { posts: 'data' };
// returns {posts=data}
});
You need to return a Promise from your Firebase function in order to return data. Try adding return before your db.collection and see if that works.
From the docs: https://firebase.google.com/docs/functions/callable
To return data after an asynchronous operation, return a promise.
EDIT: I am not very familiar with Firebase Firestore, but any calls that generate a promise, add a return statement before it.
Also, from my experience, you will want to move your return { posts: 'data' }; to execute after your last promise by using then.
well it was simpler then I thought, I just needed to return it.
exports.getWarm = functions.https.onCall((data, context) => {
finalposts = [];
posts = [];
return db.collection('local uploads/heb/posts').where('vote', '<', 200).where('vote', '>', 100).get()
.then((snapshot) => {
snapshot.forEach((doc) => {
posts.push( new post(doc.data()["uid"],doc.data()["text"],doc.data()["user_name"],doc.data()["vid"],
doc.data()["sendtime"],doc.data()["vote"],doc.data()["tag"],doc.data()["pic"],doc.data()["lan"]));
});
posts.sort(function(a,b) {return (a.sendtime<b.sendtime)? 1:((a.sendtime>b.sendtime)? -1:0);});
for (var i =0; i<data.refresh_num*2; i++) {
finalposts.push(posts[i]);
}
console.log('hey', '=>', finalposts);
return { posts: finalposts };
})
.catch((err) => {
console.log('Error getting documents', err);
});
});

Resources