Perform Firestore query in a JavaScript function - javascript

I want to perform a Firestore query in a JavaScript function, but I'm having some difficulties with promises.
Let's say I want to get the document ID from a user. So I have created this JavaScript function:
function getUid(email) {
db.collection("users").where("email", "==", email)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
return doc.id;
});
})
.catch(function(error) {
return error;
});
}
Now when I call the function res.send(getUid("user#example.com")), it returns undefined.
Which is the correct syntax to wait until the Firestore query finsished?

get() is an async function, so you need to wrap it into an async function. Also, you are not returning anything from the getUid function - you are just returning inside a forEach parameter. If you want to get all id from the snapshot, you can use the map function.
async function getUids(email) {
const db = admin.firestore();
const querySnapshot = await db.collection("users").where("email", "==", email).get();
const uids = querySnapshot.docs.map((doc) => { return doc.id });
return uids;
}
exports.yourFunction = functions.http.onRequest(async (req, res) => {
const email = // ...
res.send(await getUids(email));
});

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

Firestore, retrieve specific fields as JSON?

I'm trying to retrieve specific fields from a given firestore collection (inside of an HTTPSCallable) but I'm getting "nulls" for fields that I know exist and have data. Any ideas what I'm doing wrong?
var docs;
await db
.collection(mycollection)
.where("flag", "==", "true")
.get()
.then((querySnapshot) => {
docs = querySnapshot.docs.map((doc) => {
doc.data();
});
});
return JSON.stringify({ mydocs: docs });
You should avoid using promise chaining and async-await at the same time. The return statement will execute without checking if the promise returned by Firestore is resolved. Try this:
const querySnapshot = await db
.collection(mycollection)
.where("flag", "==", "true")
.get()
const docs = querySnapshot.docs.map((doc) => doc.data());
return JSON.stringify({ mydocs: docs });
If you are using promise chaining then anything that should happen after the promise is resolved should ideally be inside of the then() block:
const myQuery = db.collection(mycollection).where("flag", "==", "true")
return myQuery.get().then((querySnapshot) => {
const docs = querySnapshot.docs.map((doc) => doc.data());
return JSON.stringify({ mydocs: docs });
})

Calling async / await function and receiving a value back

I have got a function in an API library that calls firestore and gets data back. This part works fine:
export const getUserByReferrerId = async id => {
let doc = await firestore
.collection(FIRESTORE_COLLECTIONS.USERS)
.where('grsfId', '==', id)
.get()
.then(querySnapshot => {
if (!querySnapshot.empty) {
console.log ("we found a doc");
// use only the first document, but there could be more
const snapshot = querySnapshot.docs[0];
console.log ("snapshot", snapshot.id);
return snapshot.id // uid of the user
}
});
}
I am calling this library from a component. I have another function that runs on a button click. I cannot figure out how to get the value from the async api call.
I have tried this - a promise appears when I console.log the return:
testCreditFunction = (id) => {
let uid = getUserByReferrerId(id).then(doc => {
console.log("uid1", doc);
});
}
I have also tried this - log shows null for uid.
testCreditFunction = (id) => {
let uid = '';
(async () => {
uid = await getUserByReferrerId(id);
console.log ("uid in the function", uid);
})();
}
I have seen this question asked a few times and I have tried several of the answers and none are working for me. The odd thing is that I have done this same thing in other areas and I cannot figure out what the difference is.
Change your funtion to this.
export const getUserByReferrerId = async id => {
return await firestore
.collection(FIRESTORE_COLLECTIONS.USERS)
.where('grsfId', '==', id)
.get();
}
Try getting data this way.
testCreditFunction = (id) => {
let querySnapshot = '';
(async () => {
querySnapshot = await getUserByReferrerId(id);
const snapshot = querySnapshot.docs[0];
console.log ("snapshot", snapshot.id);
})();
One thing I notice right off the bat is that you're mixing async/await & .then/.error. While it's not strictly forbidden it definitely makes things more difficult to follow.
As others have mentioned in the comments, you need to make a return for the promise to resolve (complete). Here's how you might write getUserByReferrerId (using async/await).
const getUserByReferrerId = async id => {
const usersRef = firestore.collection('users');
const query = usersRef.where('grsfId', '==', id);
const snapshot = await query.get();
if (snapshot.empty) {
console.log('no doc found');
return;
}
console.log('doc found');
const doc = snapshot.docs[0]; // use first result only (there might be more though)
return doc.id
}
You'll notice how I split up the query building steps from the snapshot request. This was intentional as the only promised function in this is the get request.
Using this getUserByReferrerID can be done with a simple await. Just be sure to check that the result isn't undefined.
const userID = await getUserByReferrerId('someid')
if (!userID) {
console.log('user not found');
}
console.log(`User ID is ${userID}`);
p.s - I would recommend renaming the function to getUserIdByReferrerId to more accurately reflect the fact that you're returning an ID and not a user doc. Alternatively, you can return the user object and leave the name as is.

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 do I iterate through the results of a firebase get

I am trying to all the users from a firebase doc. I suspect my problem is a limitation with my understanding with javascript though.
I've tried this with and without the async/dispatch pattern with no luck
const getUsers = () => {
database.collection('users').onSnapshot(docs => {
const users = [];
docs.forEach(doc => {
users.push({
...doc.data(),
id: doc.id,
ref: doc.ref,
});
});
return users;
});
};
let users = getUsers();
users &&
users.map(user => {
console.log('name: ', user.displayName);
});
Ultimately, I'd like to loop through each user
As explained by #GazihanAlankus, since the query to the Firestore database is an asynchronous operation, you need to return a Promise in your function, by using the get() method, as follows:
const getUsers = () => {
return //Note the return here
database
.collection('users')
.get()
.then(querySnapshot => {
const users = [];
querySnapshot.forEach(doc => {
users.push({
...doc.data(),
id: doc.id,
ref: doc.ref
});
});
return users;
});
};
Then you need to call it as follows:
getUsers().then(usersArray => {
console.log(usersArray);
usersArray.map(user => {
console.log('name: ', user.displayName);
});
});
because the function returns a Promise, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then.
You can do the following test and see in which order the console.log()s are executed and what the second console.log() prints in the console:
getUsers().then(usersArray => {
console.log(usersArray);
usersArray.map(user => {
console.log('name: ', user.name);
});
});
console.log(getUsers());
You can't have a non-async function that gets users from Firebase. You're trying to make impossible happen.
Here's what's wrong with your code: the return users; is returning from the closure that you give to onSnapshot. It's not returning from getUsers. There is no return in getUsers, so it actually returns undefined.
The closure you give to onSnapshot runs in the future but getUsers has to return in the past. There is no way to pass that data in the past when you don't have it. You have to return a promise and deal with it in the calling code. You cannot write a getUsers that directly returns the users instantly.

Categories

Resources