How to update multiple Mongo documents manually in NodeJs? - javascript

I have a collection of users in MongoDB. I need to generate a token for each user and save it to the database:
var crypto = require('crypto');
db.users.find({}).exec(function(users){
users.forEach(function(user){
user.token = crypto.randomBytes(32).toString('hex');
user.save();
});
});
I'm always confused about async methods and just can't understand how they work... So this code doesn't work beacuse it exists before the save() calls finish. How would you make it work? How would you wait for all the save()ห™calls and print Done! to console?
Thanks!

Mongoose find function returns a promise, you can use it to create chain. Promise.all produces promises (or a mix of promises and values), iterate over all the values and return a promise that is fulfilled when all the items in the array are fulfilled.
var crypto = require('crypto');
db.users
.find({})
.then(users => {
var ops = users.map(user => {
user.token = crypto.randomBytes(32).toString('hex');
return user.save();
});
return Promise.all(ops);
})
.then(() => console.log('done'))
.catch(err => console.log('error' + err));
});

Related

Pulling a variable out of Promise when querying data from Firestore Firebase database - Javascript

let savedArrayUID = []; let savedArrayEmails = [];
function pullIt(emailSearch) {
db.collection(collectionName).where('email', '==', emailSearch).get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
savedArrayUID.push(doc.id);
savedArrayEmails.push(doc.data());
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
// saved. push(doc.id);
return savedArrayUID;
})
});
}
I can query the data from the database but cannot pull the variable out of the scope of the function.
I want to use this function to pass through emails to find info of their profile saved in my Database.
I really struggle to understand how Promiseses can help here. I have a feeling this is already solved, but I could not find an answer anywhere.
There's two steps to this:
Ensure that your data makes it out of your pullIt (as a promise).
Then call that pullIt correctly, waiting for the promise to resolve.
In code, you're missing a top-level return the pullIt code:
function pullIt(emailSearch) {
// ๐Ÿ‘‡
return db.collection(collectionName).where('email', '==', emailSearch).get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
savedArrayUID.push(doc.id);
savedArrayEmails.push(doc.data());
})
return savedArrayUID; // ๐Ÿ‘ˆ
});
}
And then when calling pullIt, you'll need to use either await or then, to ensure pullIt completed before you try to access the result.
So either:
pullIt("yourSearchTeam").then((results) => {
// TODO: use your results here
})
Or (in an async context):
const results = await pullIt("yourSearchTeam")
// TODO: use your results here

Return Mongoose query result through multiple methods

I'm trying to retrieve data from a MongoDB database using Mongoose to access the data, however I am trying to retrieve the data through a few methods. Here is my retrieveDocument method:
function retrieveDocument(collectionName, schema, _id) {
conn.once('open', async () => {
var model = mongoose.model(collectionName, schema, collectionName)
return await model.findById(_id)
});
}
and how I'm calling the method:
function retrieveUserDocument(_id){
return retrieveDocument("User", some_Schema, _id);
}
console.log(retrieveUserDocument("some_Id"));
However the result isn't being printed out, and instead the code is logging undefined, as the model.findById(_id) method returns a Promise.
How can I print out the result in the structure I have defined above?
I think you should try promise here. It might work and you should try await when you are calling mongoose model. Try the code which is written below
function retrieveDocument(collectionName, schema, _id) {
return new Promise(async(resolve, reject) => {
conn.once('open', async () => {
var model = await mongoose.model(collectionName, schema, collectionName)
resolve(model.findById(_id))
});
});
}

How can i extract values from promise and send them to client via node.js server?

I have a promise that returns data and I want to pass values of the promise as a response to client(web browser). I know i should probably use asynchronous js, but I'm not sure how to do that. Could you please give me some advice?
Here is how it looks like:
if(req.url === "/api/posts"){
res.writeHead(200, {"Content-Type": "application/json"})
let db = new AppDAO('./db/db.sqlite3')
const postsDb = new PostsRepository(db)
let posts = postsDb.getAll()
db.close()
console.log(posts)
res.end()
}
What you need is to build the response when the DB Promise resolves
postsDb.getAll().then(posts => {
console.log(posts)
res.send(posts)
}).finally(() => db.close())
Or if you want to use the modern syntax, and can declare the surrounding function as async:
try {
const posts = await postsDb.getAll()
console.log(posts)
res.send(posts)
} catch(e) {
// Handle database error
} finally {
db.close()
}

Cannot figure out how to wait for Promise

I have an array with user IDs and I need to find out what name belongs to each ID and return them in an array.
I can get the user's name from the database, using knex and push them into an array, but when I try to send the data it is always an empty array.
I am not really good with Promises so can't figure out how to apply to my project.
const userId = [10,11,12,13]
let users = []
userId.map(id => {
db.select('name').from('users').where('user_id', id)
.then(user => {
users.push(user)
})
})
res.json(users)
I want the response to wait until the looping finishes and send the users array.
Your map is creating an array of undefined because your callback function doesn't return anything. If we tweak it slightly, it'll create an array of promises, which conveniently is exactly what Promise.all expects. :-) So:
const userId = [10,11,12,13]
Promise.all(
userId.map(id => db.select('name').from('users').where('user_id', id))
)
.then(users => { // `users` is an array of users, in the same order as the IDs
res.json(users);
})
.catch(error => {
// Render an error response
});
As an alternative answer, here's how you could make 1 trip to the DB for this particular query meaning you don't need to wait for multiple Promises and reduce the load on your DB
knex.raw(
'select name from users where user_id in (' + userId.map(_ => '?').join(',') + ')',
[...userId]
);
First you need to wait for all promises to finish before running res.json(...)
Second, you shouldn't mutate outside variables after promise resolving (the order by which the promises resolve will alter your output and that is not nice.
Something like this should work fine
const userId = [10,11,12,13]
// map userId array to promise array
// Promise.all aggregates a promise array into one big promise that resolves when all promises resolve (and preserves array order)
Promise.all(
userId.map(id =>
db
.select("name")
.from("users")
.where("user_id", id)
)
)
.then(users => res.json(users))
.catch(e => console.error("Error::", e));
/*handle error in the catch block*/
/* visual example of Promise.all.then block
Promise.all([ users = [
getUser(10), -> {userId: 10, ....}
getUser(11), -> {userId: 11, ....}
getUser(12) -> {userId: 12, ....}
]) ]
*/
You want Promise.all(), see here.
Try
const userId = [10,11,12,13]
let users = userId.map(id => new Promise(resolve => {
db.select('name').from('users').where('user_id', id)
.then(user => {
resolve(user)
})
}))
Promise.all(users).then(()=>res.json(users))
Here users is an array of promises. As soon as all of them are resolved, do res.json(users).

using a returned object from asynchronous method

I am trying to do the following, unfortunately to no avail:
1. reading a spreadsheet and get it as a parsed object.
2. use the parsed object to look for a specific value in it. e.g an email.
The spreadsheet serves as a database and the motivation is to determine whether a given email exists there or not.
Apparently, I have misunderstood the use of asynchronous methods as I can't grasp how to return the.
The calling method:
helpers.read('the-spreadsheet-id', 'Sheet1!A:Z');
The reading method # helpers/sheets.js:
exports.read = (spreadsheetId, range) => {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
return sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
.then(_.property('data.values'));
};
what should I do in order to get a json object or similarly parsed data object to keep on working with at the calling method?
Thanks in advance.
For the asynchronous step to work, you have to either declare you are returning a Promise or that the function is async. Async/await is actually using promises under the hood, but it's a cleaner syntax.
For the Promise version, you have to return the value you want with the resolve function, and throw the error with the reject function.
exports.read = (spreadsheetId, range) => {
return new Promise((resolve, reject) => {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
.then(data => {
resolve(data.values);
})
.catch(err => reject(err));
});
};
When you are using async/await, you can wrap the query in a try/catch and throw the error if it fails.
exports.read = async (spreadsheetId, range) => {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
try {
const data = await sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
return data.values;
} catch(err) {
throw err;
}
};
I don't use underscore so I'm not sure what you were trying to return here. I assume that data is the result of the query and you were trying to pass out values?

Categories

Resources