Reputation: 3133
I'm still struggling to understand how to extract values from a Firestore Query and put them into a global variable. I (now) understand that the asynchronous nature means that code isn't executed in the order that it is written. But, why is user
still undefined in the following code block despite the await
keyword inside of an async
function? I understand (based on this question) that await
should be applied only up to get()
, otherwise there may not be values to iterate over.
My question is similar to this, but I run into the same issue as the comment and it looks like the question was never marked as answered. It is also similar to this, but pushing all gets to a Promise and then resolving that promise still didn't assign the data to a variable. It just printed an empty array.
I can print the variable, so my question is about assignment, I think. This function is a handler for an Intent
in DialogFlow
. I strongly prefer to have the data available outside of the call to the db. Adding to agent responses in a call to firestore doesn't always add the text to the agent, so I'd like to avoid that.
async function loginHandler(agent){
username = agent.parameters.username.name;
password = agent.parameters.password;
const user = await db.collection("users")
.where("name","==",username)
.where("password","==",password)
.limit(1)
.get()
.then(querySnapshot =>{
querySnapshot.forEach(docSnapShot =>{
return docSnapShot.data();
//console.log.(docSnapShot.data()); //prints correct contents, so error is in programming logic
agent.add("Response"); // Would prefer to avoid this, but could refactor if I am fundamentally misunderstanding how to access Firebase data
})
})
.catch(error => console.log);
console.log(user);
console.log("bort");
}
Picture of Firestore to demonstrate that the correct data do exist:
Upvotes: 1
Views: 651
Reputation: 63524
You might be able to split the code up. Return the data from the database first, then map
over the data to extract the details, and then assign that result to the user
variable.
async function loginHandler(agent) {
username = agent.parameters.username.name;
password = agent.parameters.password;
// `await` the promise and assign it to
// `querySnapshot`
const querySnapshot = await db.collection('users')
.where('name', '==', username)
.where('password', '==', password)
.limit(1)
.get()
.catch(error => console.log);
const user = querySnapshot.docs.map(docSnapShot => {
agent.add('Response');
return docSnapShot.data();
});
console.log(user);
}
Upvotes: 2
Reputation: 5840
forEach
iterates the results but doesn't return anything, so your return
inside there isn't doing what you'd expect (forEach
returns void
so you're returning the snapshot to a function that is returning void
). You can create a local variable to hold the results you iterate and then return that:
const user = await db.collection("users")
.where("name","==",username)
.where("password","==",password)
.limit(1)
.get()
.then(querySnapshot =>{
// Set up an empty array to return later
let data = []
querySnapshot.forEach(docSnapShot =>{
// Add each snapshot's data object to the array
data = [...data, ...docSnapShot.data()]
})
// Return `data` which will populate `user`
return data
})
.catch(error => console.log);
Upvotes: 2
Reputation: 9411
Please clarify whether you mean undefined
as you say in the title, or empty array
as you say in the text.
Your code looks correct to me. I would not expect the output of console.log(user)
to be undefined
. However, an empty list [ ]
would be a perfectly reasonable answer.
Perhaps there is nobody in that collection with that username and password?
Have you tried removing the condition of equality of username and password? That should get you an element of the collection, if it is not entirely empty.
Upvotes: 1