Firestore sub collection write with Modular Javascript V9 - javascript

Here is my
users collection, chats is a sub collection in users
users -
-userID1
-chats -
- chatID1
chatDetails
-userID2
-chats -
- chatID2
chatDetails
I want to update a document in chats, here is my code
export const setChatNewMessageAlert = async (user_id, chat_id, bool) => {
const chatRef = doc(db, "users", user_id, "chats", chat_id);
const update = {
new_message: bool,
};
await setDoc(chatRef, update, { merge: true });
};
for some reason is giving me this ERROR
FirebaseError: Invalid document reference. Document references must have an even number of segments, but users/I7Flqla5m9OWStsoWs4PL3ncbQV2/chats has 3
even though I have four segments.

Related

Firebase saying arrayUnion was called with invalid data

Im getting an error with firebase because im trying to update two values when I press handleSelect. Only issue is that the first updateDoc works fine as I'm trying to add an array into the "user" specific userChats database, but when I try to do the opposite and add the user array to the "chat" database, it fails.
const handleSelect = async(chat) =>{
const docRef = doc(db, 'users', user?.uid)
const docSnap = await getDoc(docRef)
const addRef = doc(db, 'userChats', user?.uid)
await updateDoc(addRef, {
userChats: arrayUnion(chat)
})
const addRecieverRef = doc(db, 'userChats', chat?.uid)
await updateDoc(addRecieverRef, {
userChats: arrayUnion(user)
})
console.log(chat.uid)
const concatUID = user.uid > chat.uid ? user.uid + chat.uid : chat.uid + user.uid;
if(!docSnap.exists() && user.uid!=chat.uid){
await setDoc(doc(db, 'messages', concatUID), {
messages: [],
})
}
else{
dispatch({type: 'CHANGE_USER', payload: chat})
console.log(chat)
}
}
Error
Chats.js:53 Uncaught (in promise) FirebaseError:
Function arrayUnion() called with invalid data.
Unsupported field value: a custom UserImpl object (found in document userChats/lJ4u4PqWynXAPthz3FVgYaQQ0Do1)
I already checked and all the reference values are correct, and both "user" and "chat" are plain objects
Firestore can only store values of the types indicated in its documentation on data types. The UserImpl object that you are trying to store is not of a supported type, which is what the error message indicates.
If the user object comes from Firebase Authentication, you can call toJSON() on it to get a JSON serialization of its data.

Better way to get document ID which was auto generated to create the doc or update an array inside the document Firestore / Firebase

I'm creating one document per user to store the id's of the users bookmarks. I'm using addDoc so the ID of the created doc is generated by firebase. I want to check if that document exists to create a new one or update the old. I need to have a document reference to update the doc and I had a horrible time trying to figure out a way to get a hold of that auto generated ID. My code works but it feels really hacky - Is there a better way to accomplish this?
//Add a value to the bookmarks array for individual user
export const addBookmarkForUser = async (userAuth, showId) => {
const bookmarkDocRef = collection(db, 'users', userAuth.uid, 'bookmarks')
const bookmarkSnapshot = await getDocs(bookmarkDocRef)
let userBookmarkDocId
if(bookmarkSnapshot){
bookmarkSnapshot.forEach((doc) => {
if(doc.id){
userBookmarkDocId = doc.id
console.log(userBookmarkDocId)
}
})
}
try {
if(!bookmarkSnapshot) {
await addDoc((collection(db, 'users', userAuth.uid, 'bookmarks'), {
favorites: [{showId: showId}],
}))
}else {
const userBookmarkRef = doc(db, 'users', userAuth.uid, 'bookmarks', userBookmarkDocId)
await updateDoc(userBookmarkRef, {
favorites: arrayUnion({showId: showId})
})}
} catch (error) {
console.log('Error creating bookmark', error.message);
}
};
I know I will only have one document in there for each user - is there a better way to find the doc.id?
Here is the firestore structure -
If you just want to check how many documents are present in a collection then you can use getCountFromServer() that loads the count without the actual document data.
import { collection, getCountFromServer } from 'firebase/firestore';
const bookmarkColRef = collection(db, 'users', userAuth.uid, 'bookmarks');
const bookmarksCount = (await getCountFromServer(bookmarksColRef)).data().count
console.log(`${bookmarksCount} documents found in ${bookmarksColRef.path}`)\
if (!bookmarksCount) {
// no document exists, create new
}
However, since you need the document ID and its data. You can run a simple query:
import { collection, getDocs } from 'firebase/firestore';
const bookmarkColRef = collection(db, 'users', userAuth.uid, 'bookmarks');
const bookmarksSnap = await getDocs(bookmarksColRef);
if (bookmarksSnap.empty()) {
// no document
} else {
const docData = bookmarksSnap.docs[0].data();
console.log(docData)
}

How to create a nested collection when creating a user in Firebase / Firestore where users can save bookmarked items

I want to be able to have a nested collection in firebase/firestore where I can save an authenticated users favorites. I was trying to create the collection when the user is created so I can just read/write to it later but I can't figure out how to create the collection. I have something like this:
//This function creates a new user. If the user already exists, no new document will be created
export const createUserDocumentFromAuth = async (
userAuth,
additionalInfo = {}
) => {
if (!userAuth) return;
const userDocRef = doc(db, 'users', userAuth.uid); //database instance, collection, identifier
const bookmarkRef = doc(db, 'users', userAuth.id, 'bookmarks'); //This triggers error
const userSnapshot = await getDoc(userDocRef);
if (!userSnapshot.exists()) {
//If user snapshot doesn't exist - create userDocRef
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await setDoc(userDocRef, {
displayName,
email,
createdAt,
...additionalInfo,
});
setDoc(bookmarkRef, { //Try to create a bookmarks collection here
favorites: []
})
} catch (error) {
console.log('Error creating user', error.message);
}
}
//if user data exists
return userDocRef;
};
I can create the user just fine but not another collection at the same time. I also tried just creating the collection when a signed-in user clicks on the bookmark button like this but I get a type error in both cases Uncaught (in promise) TypeError: n is undefined every time.
export const addBookmarkForUser = async (userAuth, showId) => {
const bookmarkRef = doc(db, 'users', userAuth.id, 'bookmarks');
try {
await setDoc(bookmarkRef, {
favorites: showId
});
}catch(error){
console.log('error creating bookmark', error.message)
}
};
I'm pretty new to Firebase / Firestore and all I want is to be able to save an item id in an array for an individual user when they click a button. If saving in an array is not ideal or there is any better way to do this, I am open to any suggestions at this point.
I was trying to create the collection when the user is created so I
can just read/write to it later but I can't figure out how to create
the collection.
A (sub)collection is only created when you create the first document in it. There is no way to materialize an empty collection without a document.
And it is normal that you get an error when using the doc() method as follows
const bookmarkRef = doc(db, 'users', userAuth.id, 'bookmarks');
because this method is used to create a DocumentReference and therefore you need to pass a path with an even number of path segments. In you case you pass 3 segments.
You could very well define the CollectionReference for the bookmarks subcollection as follows, using the collection() method and passing the 3 segments
const bookmarkRef = collection(db, 'users', userAuth.id, 'bookmarks');
but, until you add a document in it, it will not exist in the database.
Conclusion: You will automatically create the user's bookmarks subcollection the first time you create a bookmark for the user.
For example:
const bookmarksCollectionRef = collection(db, 'users', userAuth.id, 'bookmarks');
await bookmarksCollectionRef.add({ ... })

Collection references must have an odd number of segments

I am currently encountering this problem in my react native app using Firebase:
Collection references must have an odd number of segments
I've seen similar cases in stackoverflow but wasn't able to solve my problem. Here is my code :
const getData = async () => {
console.log(user.uid + " 🚧🚧🚧")
const col = collection(db, "users", user.uid)
const taskSnapshot = await getDoc(col)
console.log(taskSnapshot)
}
getData()
I am trying to open my document with the document reference (user.uid) but I am getting this error : Collection references must have an odd number of segments
Hope you can help me solve this problem.
The getDoc() takes a DocumentReference as parameter and not a CollectionReference so you must use doc() instead of collection().
import { doc, getDoc } from "firebase/firestore"
const docRef = doc(db, "users", user.uid)
const taskSnapshot = await getDoc(col)
console.log(taskSnapshot.data() || "Doc does not exists")
Also checkout Firestore: What's the pattern for adding new data in Web v9?
replace this
collection(db, "users", user.uid)
with this
collection(db).document("users").collection(user.uid)

add collection and batch add documents with firebase

I have a screen on my app where a user inputs a number {x} and from this number I would like to create a collection in the programs doc and then add {x} documents to the collection.
Only one document gets added to the collection.
const handleContinue = async () => {
const batch = writeBatch(db);
const blockArray = [...Array(blockCount).keys()];
// use the program name as the ID.
const docRef = doc(db, `Users/${userStore.uid}/programs/${programName}`);
const payload = {
title: programName,
units: programUnits,
system: programSystem,
status: programStatus,
days: dayCount,
blocks: blockCount,
};
await setDoc(docRef, payload, { merge: true });
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
const dRef = doc(db, `Users/${userStore.uid}/programs/${programName}`);
const cRef = collection(dRef, "blocks");
blockArray.forEach((index) => {
const insert = doc(cRef, `block_${index}`);
batch.set(insert, { name: `Block ${index}` });
});
await batch.commit();
}
};
Structure I'm expecting starting from programs doc
-programs (doc)
-- programs fields
-- blocks (collection) <-- known collection name
--- block_1 (doc)
--- block_2 (doc)
--- block_3 (doc)
...etc
block_1, block_2 etc would be the document ID.
As far as I can see in the code you're writing multiple documents, but all to the same collection: Users/${userStore.uid}/programs/${programName}/blocks.
If you want to create multiple collections, you'll need to vary one of the odd-indexed parameters in this path, like blocks_1, blocks_2, etc. Note though that this is not recommended in most scenarios, as the client-side SDKs have no way to request a list of the collections under a specific path, so it's typically best to use hard-coded collection names - or collection names that are implicitly known in some other way.
So I found that my array I was looping over wasn't what I expected, nothing to do with Firebase. Fixed it further upstream and now I get the results I was after.

Categories

Resources