How can I add a field to document from Javascript in firebase - javascript

I'm building a basic stackoverflow clone. People can ask questions and comment that post. For that I tried to add a "comments" section in every post. So if someone comment that post comments will be in the document. But I couldn't find a way how can I achieve this. Here is the
I tried this: when I click a button, this function will get the id of that post and add a comment to that specific post. (I get the post ID from useParams). But I'm not sure how to add a field for that specific document.
const createComment = async (author, authorId, comment) => {
const docRef = doc(db, "posts", id);
const docSnap = await getDoc(docRef);
docSnap.data().comments.push(comment);
};

You can use updateDoc() function to update an existing document and arrayUnion() to insert a new value in an array field. Try:
const createComment = async (author, authorId, comment) => {
const docRef = doc(db, "posts", id);
await updateDoc(docRef, {
regions: arrayUnion(comment)
});
};
Checkout the documentation for more information.
arrayUnion() will add the value only if it does not exist in the array already so if multiple users try to add same comment, it'll not work with array of strings. You might have to store an object containing userId e.g. { comment: "test", userId: "1324" }.

Related

Importing User Data from filtered Array (VUE3 + Quasar + Firebase)

I am importing the data from the currently signed in user in order to manage the entire user profile page and all the associated actions.
On one hand I have the auth.currentUser and on the other I have the USERS collection in the db which stores all the additional data related to that particular user.
Now, my question concerns optimization. What would be the ideal way to get this user's data? Currently I am getting the entire users collection and filtering to get the one that matched the uid from the route params, yet I was told that loading the entire users collection and filtering the one I want to display was less than ideal, that I should rather create a function to get a specific user by a property such as name or id. This is what confuses me, is that not essentially what I am doing by filtering the users collection? How else would it be best to get that user's info? By creating this function in the Store and not in the component itself?
Currently it's looking like this:
UserPage.vue
const storeUsers = useUserStore();
const users = storeUsers.users;
const route = useRoute();
const id = route.params.id;
const userData = computed(() => {
return users.find((u) => u.uid == id);
});
Any way to optimize this would be appreciated.
*Adding a screenshot of the Firestore console (data model):
Your code is loading every document from the users collection into your application code, and then choosing there which single document you are actually interested in. Since you pay for every document read from the database, and you (and your users) pay for all bandwidth that is used, this is wasteful - especially as you start adding more users to the collection.
Instead you should use a query to read only the document(s) you are interested in from the database into your application code. Read the documentation for examples for all supported SDK versions.
finally solved it using a query as suggested. I am triggering the getUserInfo action whenever a user signs in and then assigning it to a pinia state called currentUserData:
AUTH STORE
async getUsers() {
onSnapshot(userCollectionRef, (querySnapshot) => {
let users = [];
querySnapshot.forEach((doc) => {
let user = {
did: doc.id,
...doc.data(),
};
this.users.push(user);
});
});
},
getUserInfo(userCredential) {
const q = query(
userCollectionRef,
where("uid", "==", userCredential.user.uid)
);
onSnapshot(q, (snapshot) => {
let currentUserData = [];
snapshot.docs.forEach((doc) => {
currentUserData.push({ ...doc.data(), id: doc.id });
});
this.currentUserData = currentUserData;
});
}

Fetching documents of sub-collections in firestore

I'm trying to fetch sub-collection documents from my firestore database.
Collection Imgs:
My current code:
const fetchHighlight =async()=>{
const Highlight = []
const HighlightDbId = await
db.collection('highlights').doc('2SCS2S0JnzngWEiYkHNk').collection('4C4kd2QnaQhcp9knexkW').get()
console.log(HighlightDbId)
}
React.useEffect(()=>
{
fetchHighlight ()
}, [])
You forgot to fetch your query at the end of your chain, and the subcollection 4C4kd2QnaQhcp9knexkW does not exist, it's the id of a document in the subcollection you're trying to access. The right subcollection ID was hBYWvZ3KN3NLLrucTpryETQZnz2.
To sum up, you could go this way:
const yourDocument = (await db.collection('highlights').doc('2SCS2S0JnzngWEiYkHNk')
.collection('hBYWvZ3KN3NLLrucTpryETQZnz2').doc('YOUR_DOC_ID').get()).data()
or this way:
const yourDocument = (await db.collection('highlights/2SCS2S0JnzngWEiYkHNk/hBYWvZ3KN3NLLrucTpryETQZnz2')
.doc('YOUR_DOC_ID').get()).data()
Edit
If you want to fetch only the first document of the subcollection you can go this way:
const yourDocument = (await db.collection('highlights').doc('2SCS2S0JnzngWEiYkHNk')
.collection('hBYWvZ3KN3NLLrucTpryETQZnz2') // subcollection ref
.orderBy("createdAt", "asc") // index
.limit(1) // limit the size of your response
.get()) // send the request and wait for it (you could also use '.then()' here)
.docs[0] // get the first doc of the array
.data() // retrieve the doc's data use `.id` instead if you want its id
And if you want to get the first subcollection of a doc you should go this way with the listCollections method:
const subcollectionId = (await db
.doc('highlights/2SCS2S0JnzngWEiYkHNk')
.listCollections())[0] // retrieve the first subcollection `.id`
Note that this only works with the node.js library, if you're attempting to do your query fore the front-end, it will fail. Then you should simply put a reference of your subcollection inside your parent doc by an update when creating your subcollection in the first place:
// const HighlightDbId = creating you subcollection
db.collection('highlights').doc('2SCS2S0JnzngWEiYkHNk').update({
subcollection: HighlightDbId
});
And simply retrieve the field subcollection when you need to fetch data from its subcollection.
I fixed this problem by adding the last doc :
db.collection('highlights').doc('2SCS2S0JnzngWEiYkHNk').collection('4C4kd2QnaQhcp9knexkW').doc('XXXXXXXX').get()

Add a new field in mongoDB from another collection independently?

I have two collections: profiles and contents
The profiles collection looks like this:
{
_id: ObjectId('618ef65e5295ba3132c11111'),
blacklist: [ObjectId('618ef65e5295ba3132c33333'), ObjectId('618ef65e5295ba3132c22222')],
//more fields
}
The contents collection looks like this:
{
_id: ObjectId('618ef65e5295ba3132c00000'),
owner: ObjectId('618ef65e5295ba3132c22222'),
//more fields
}
What I need is to get those contents where owner is not included in the blacklist. I thought about to put the blacklist field into the contents documents. I could get the profile by id separately (in another query) and set it manually in the aggregation where I get the contents, but this requires one extra connection.
So my question is: Is there a way to add my profile into each document of another collection? keep in mind that I have the profile ID.
Here is some psuedo code on how it "should" look like, First fetching the blacklists owners, then using that variable as a parameter in the pipeline.
const profileId = input;
const blackListIds = await db.getCollection('profiles').distinct('blacklist', { _id: profileId });
const aggregationResults = await db.getCollection('contents').aggregate([
{
$match: {
owner: {$nin: blackListIds}
}
}
... continuation of pipeline ...
])

How do I access another user's data by a specific field?

I'm trying to make a user send their num to another user.
I created a random keycode for every user to send each other a num.
I tried accessing the data by querying them.
const sendNum = async(e) => {
const userCol = collection(db, "users")
e.preventDefault();
const targetQuery = query(userCol, where("keycode", "==", target))
const targetSnapshot = await getDocs(targetQuery)
targetSnapshot.forEach((doc) => {
console.log(doc.data().num);
})
console.log(targetSnapshot);
But it returns an object rather than the another user's num field
NuĀ {_firestore: xc, _userDataWriter: ah, _snapshot: Oo, metadata: Su, query: Ic}
Here's what the data looks like:
Posting the solution suggested by Doug Stevenson as a Community Wiki for visibility.
From the description it's not possible to tell what value the target holds.
In this case, hard coding the value worked to access the user field.

Firebase Query, get multiple documents from a single collection where each doc.id is equal to the id in a seperate list

I am building a simple react-native app using firebase, in which users can create posts, see lists of other users posts, and posts can be saved as a "saved posts" as a sub-collection within my users collection.
I am new to react-native and firebase so this is just for educational purposes.
I am getting stuck on querying the posts for my 'saved' list. Currently, when a user clicks the 'save' button, a sub-collection of 'saved posts' is added to (or created) of the post id. I do not want to rewrite the entire post data to the users saved list, only to create a reference to the post via the post id.
I am able to save the list, but have not been able to query firestore for the documents associated with each post id.
Currently I can query based on the users id to retrieve the list of their own posts, as the authorID matches the user uid, and I thought getting the saved posts would be similar.
Here is my code for getting a users own list of posts:
userId is a single string representing user.uid
const getUserPosts = async (userId) => {
const snapshot = await firebase
.firestore()
.collection('posts')
.where('authorID', '==', userId)
.get();
let data = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
return data;
};
This function is exported and then wrapped in a try catch statement within the component.
The difference between the above and getting the users list of saved posts is that I have multiple different "postId's" which I need to input (vs a single userID), but the .get() method will not accept an array for the first argument, it requires a single string.
To quickly summarize I need a method or function in which I can take an array of postId's and query my posts collection to return those posts in which the doc.id matches the postId's provided.
I apologize if this is unclear or confusing, I am getting a bit lost on this one.
Any suggestions would be very welcomed.
Thank you so much for your time!
You can do a query for multiple document IDs at once using an "in" query and using FieldPath.documentId():
const array = [...];
const snapshot = await firebase
.firestore()
.collection('posts')
.where(firebase.firestore.FieldPath.documentId(), 'in', array)
.get();
But this only works if the array is of less then 10 items long (Firestore limitation). If you need more, you'll have to either batch the IDs, into smaller arrays and perform multiple queries, or simply iterate the IDs, and get() each document individually (which is just fine, really, don't worry about performance on that).
It's working :)
Big Thank you to Doug Stevenson for your help!!
Here is the function:
postedId is an array of id's referring to the id of saved posts from the 'posts' collection
export const getSaveData = async (postedId) => {
const array = [postedId];
const snapshot = await db.collection('posts').get();
const data = snapshot.docs.map((doc) => ({ postedId: doc.id,
...doc.data() }));
return data;
};

Categories