Reputation: 2411
I am using Node.js (which I am very new at) and Google Cloud Firestore database to save documents according to:
One Users document, i.e. a User, has many Tweets in a subcollection 'Tweets'. I am interested in retrieving a User together with the last Tweet in the subcollection so I get a JSON-file like this. In other words, this is what I want to get:
users: {
{
name:'john',
twitter_username:'johnny',
tweets: {
text: "I am called johnny, this is my tweet",
created_at: "2021-06-29 12:00:00"
},
},
{
name:'anne',
twitter_username:'anne',
tweets: {
text: "I am called anne, this is another tweet",
created_at: "2019-06-28 12:00:00"
},
}
}
I have this function:
function getUserData() {
return db.collection('users').get()
.then((querySnapshot) => {
var docs = querySnapshot.docs.map(doc => [doc.data(), doc.id]);
//console.log(docs);
return docs
which, if I could fetch and replace doc.id (i.e. the User document ID) with the last tweet, would solve it I guess. But how can I do that?
Any other solution, possibly with for loops, would be fine as well. I have spent hours on this seemingly easy problem but can't get it to return both the User-data and the tweet-data.
Edit 2:
I realized I could do this:
function getUserData() {
return db.collection('users').get()
.then((querySnapshot) => {
var docs = querySnapshot.docs.map(doc => [doc.data(), doc.id, getStuff(doc.id)])
console.log(docs)
return docs
});
}
function getStuff(doc_id) {
return db.collection('users').doc(doc_id).collection('tweets').limit(1).get()
.then((querySnapshot) => {
var docs = querySnapshot.docs.map(doc => doc.data());
console.log("TWEETS", doc_id, docs[0]['text']);
return docs[0]['text']
});
}
which produces a log result as:
TWEETS DAU86mxIhmD6qQQpH4F God’s country!
TWEETS JQHTO0jUjAodMQMR6wI I’m almost there Danny!
from the getStuff-function.
The only issue now is that I can't get the map function to wait for getStuff so the return docs
return a Promise { <pending> }
for getStuff(doc.id)
.
I am not to familiar with Promises and await/async and I can't get that to work. Solving this Promise pending -> twitter text would then solve my problem. How do I do that?
Upvotes: 0
Views: 169
Reputation: 7388
If you want to get the data of a single user you could write the code like this:
const getUserData = async (userUid) => {
const userSnap = await db.collection("users").doc(userUid).get();
const tweetSnaps = await db
.collection("tweets")
.orderBy("created_at", "desc")
.limit(1)
.get();
let tweet = {};
tweetSnaps.forEach((doc) => {
tweet = doc.data();
});
return {
...userSnap.data(),
...tweet,
};
};
We first get the user and then query for the last tweet and get that. We sort the tweets
collection by created_at
and limit it for a single doc.
If you want to get the same data for all users at once we would need to change the code a little bit but the logic would be the same.
If the data is saved in separate collections you can't get them in a single database request.
UPDATE for Edit 2
Here your code how it should look like with correct async/await
:
const getUserData = async () => {
const querySnapshot = await db.collection("users").get();
const docs = querySnapshot.docs;
for (let i = 0; i < docs.length; i++) {
const element = docs[i];
doc.data(),
doc.id,
await getStuff(doc.id),
}
console.log(docs);
return docs;
};
const getStuff = async (doc_id) => {
const querySnapshot = await db
.collection("users")
.doc(doc_id)
.collection("tweets")
.limit(1)
.get();
var docs = querySnapshot.docs.map((doc) => doc.data());
console.log("TWEETS", doc_id, docs[0]["text"]);
return docs[0]["text"];
};
Upvotes: 1