How can I update a nested field in a firestore database? - reactjs

I am creating a clothing e-commerce application integrated with woocommerce and Firestore.
I currently am trying to build the 'add to wishlist part, here I am struggling with trying to update the items 'favourite' field in the database.
I present my Firestore"
my Firestore database
I have access to the item on my react native side
I need to be able to iterate through the objects, compare the nested object items ID against the idea of the item I am currently clicking on and change the favorite field to true.
Currently, I have tried to do the following, but to no avail.
const like = (item) => {
// db.collection("users").doc(user).collection("wishlist").doc(random).set({
// id:item.id,
// name:item.name,
// })
db.collection("users")
.doc(user)
.collection("products")
.doc("0")
.get()
.then((data) => {
const info = data.data();
});
};

In order to perform an update to an object that exists in an array-type field, you need to find that particular object first. Unfortunately, there is no way you can query a Firestore collection based on a value that exists in an object that is contained in an array. This kind of filtering cannot be achieved using partial data. To solve this, you have to read the array, find the desired elements that need to be updated, perform the update and then write the document back to Firestore.
I have also written an article called:
How to update an array of objects in Firestore?

so at the end I came up with a solution which I thought I would share here:
const like = (item) => {
const newData = { ...Data };
if (newData !== null) {
let index = newData.Products.findIndex((e) => {
return e.id === item.id;
});
console.log(index);
newData.Products[index].favourite = true;
db.collection("users")
.doc(userID)
.collection("products")
.doc("0")
.set(newData)
.then((data) => setData(data))
.catch((error) => console.log(error));
} else {
alert("An error has occured");
}
};
so this takes the data, saves it, adds the particular change and sets it again

Related

cloud functions to delete an item from basket

have a cloud functions that run on call
the function utility is to remove an item from an array so to do so I'm doing an update on the fields that correspond to the doc id
(with firebase.firestore.FieldValue.arrayRemove(item)).
The problem is that all working fine it delete and update the state and the ui ,but its not deleting the last item (ui) and when i check in the firestore i see that the array is empty.
const remove_from_bucket = (my_id, item) => {
firebase
.firestore()
.collection("bucket")
.doc(my_id)
.update({
item_array: firebase.firestore.FieldValue.arrayRemove(item)
}).then((scc) => {
update_item_status_available(item)
console.log("success");
}).catch((err) => {
console.log(err.message);
});
};

How to sort createdAt field from firestore v9

Im new to firebase. I want to retrieve all the data via userId then sort the createdAt field to desc order but it doesnt work for me.
Any help is greatly appreciated.
useEffect(() => {
const user = JSON.parse(localStorage.getItem('user'));
const q = query(collectionRef, where('userId', '==',user.uid),orderBy('createdAt','desc'));
onSnapshot(q, (snapshot) => {
const documents = snapshot.docs.map((doc) => {
return {
...doc.data(),
doc_id: doc.id
};
});
setIsLoading(false);
setTodos(documents);
});
}, []);
Your problem most probably comes from the fact that Firestore misses the composite index corresponding to your query.
Since you didn't add an error callback in your onSnapshot() method call, you cannot see the error. Adding a callback will firstly allow you to confirm that it is a problem of missing composite index and, secondly, show you an URL (in the error message) that will allow you to automatically build this index.

Writing to Firebase Firestore React

so I have a problem right now. I'm entering the users dates into cloud firestore like this:
so this is a user collection, with a document by user's id's and then the dates are entered as a list. But whenever I refresh the page and enter new data, all the previous data disappears.
So I'm wondering how do I enter data so that it goes like collection(userCalendar).doc(USERID).collection(dates) and then it has all the user's data entered as strings rather than an array like I've been doing.
My code for the way it's behaving right now is below. Thank you! :)
export const allEvents = [];
const Calendar = () => {
const [date, setData] = useState([]);
const handleDateClick = async (DateClickArg) => {
if (DateClickArg.jsEvent.altKey) {
const title = prompt("Enter title", DateClickArg.dateStr); // allows user to put a title in
// making object
const event = {
title: title ? title : DateClickArg.dateStr,
start: DateClickArg.date,
allDay: true
}
allEvents.push(event)
const db = fire.firestore();
let currentUserUID = fire.auth().currentUser.uid
const doc = await fire
.firestore()
.collection("userCalendar")
.doc(currentUserUID)
.get()
db.collection("userCalendar")
.doc(currentUserUID)
.set({
activites: allEvents
})
}
}
You can use arrayUnion() to add new items to an array however it'll be difficult for you to query activities of a user.
For example, you cannot fetch a single activity from that array but you'll have to fetch all of them get the required one. Additionally, you cannot update an object in an array directly in Firestore.
Also a document has a max size limit of 1 MB so if a user can have many activities, it'll be best to create sub-collection.
I would recommend restructuring the following way:
users -> { userId } -> activities-> { activityId }
(col) (doc) (col) (doc)
All of user's activities/events are now in a sub-collection "activities" and each activity would be a document instead of an array item. With this you can easily read/update/delete a single activity.
Also checkout: Firestore subcollection vs array
Not sure whether this meets your requirement, but from my understanding you just want to update the activities with the allEvents which contains all the updated activities.
db.collection("userCalendar")
.doc(currentUserUID)
.set({
activites: allEvents
})
should become
db.collection("userCalendar")
.doc(currentUserUID)
.set({
activites: allEvents
}, { merge: true })
Or you can use the update method
db.collection("userCalendar")
.doc(currentUserUID)
.update({
activites: allEvents
})
From the docs
To update some fields of a document without overwriting the entire document, use the update() method:
import { doc, setDoc } from "firebase/firestore";
const cityRef = doc(db, 'cities', 'BJ');
setDoc(cityRef, { capital: true }, { merge: true });
It looks like you're overwriting your collection with every code execution:
db.collection("userCalendar")
.doc(currentUserUID)
.set({
activites: allEvents
})
You should consider to make an array union, so that the values are added to your collection instead of overwriting them:
db.collection("userCalendar")
.doc(currentUserUID)
.update({
activites: firebase.firestore.FieldValue.arrayUnion(
{
allEvents
}),
})
Also some examples from firestore docu:
https://cloud.google.com/firestore/docs/samples/firestore-data-set-array-operations

Firestore where filter and add data to the result

i got the following code:
const db = firebase.firestore();
var duettsRef = db.collection("duetts");
export const keyExists = (setKey)=>{
duettsRef.where("key", "==", setKey).where("player2", "==", "").get().then(querySnapshot => {
console.log(querySnapshot);
if (!querySnapshot.empty) {
this.db.collection('duett').set
({
player2: firebase.auth().currentUser.uid,
});
}
})
}
I logged the snapshot, and its working good untill this point.
The "key" is unique, so the snapshot is either empty or finding one document with this specific properties.
Screenshot of my firestore structure.
What is not working, that I want if the snapshot is not empty, to edit the "player2" field in the found document, and set it to the current user id.
So for example, if I search for the key:2jskmd21, it would fill the User ID in the player2 field: Like this.
How do I do this correctly?
For this situation, you will want to use .update() rather than .set(). This is because the update method is designed to update values in a document while set requires a merge: true flag however its primary function is to create a document if it doesn't exist which can be counterproductive.
In this code, I am setting the value as you described and by setting the limit to 1, will retrieve only 1 document.
const db = firebase.firestore();
var duettsRef = db.collection("duetts");
export const keyExists = (setKey)=>{
duettsRef.where("key", "==", setKey)
.where("player2", "==", "").limit(1).get()
.then(querySnapshot => {
console.log(querySnapshot);
if (!querySnapshot.empty) {
querySnapshot.docs[0].ref.update({
player2:firebase.auth().currentUser.uid});
}
})
.catch(e => console.log(e));
}

React Redux FireStore - Changes in FireStore collection does not reflect on my screen unless I refresh

I use React with Redux and Firebase. Here is one of the functions from my Action.js
export const loadItemsInCategory = (categoryId) => {
return (dispatch) => {
let itemsArray = [];
firestoreService.getItemsInCategory(categoryId)
.then(updatedGroceryList => {
itemsArray = updatedGroceryList;
console.log(`category id is ${categoryId}`)
dispatch(loadItemsInCategoryHelper(categoryId, itemsArray))
})
.catch((error) => console.log(error));
}
}
It's a normal FireStore query. Here is what happens in firestoreService.getItemsInCategory(categoryId)
export const getItemsInCategory = async (categoryId) => {
console.log(`firebase category id is ${categoryId}`)
const snapshot = await db.collection('Item').where('category', '==', categoryId).get()
return snapshot.docs.map(doc => {console.log("called");return {id: doc.id, ...doc.data()}});
}
Right now, my application shows the list of items in the given Category. However, the list does not get updated when a new Item is added to the category by someone else. In other words, additions in FireStore collection does not reflect on my screen unless I refresh the page.
How can I code my webapp in such a way that any change on the FireStore end gets reflected on my webapp?
Thanks in advance!
Your code is doing a one-time query with get(). Queries made like this are not realtime. They don't refresh.
If you want to receive updates to your query in realtime, you should follow the documentation for realtime queries. Instead of using get(), you will use onSnapshot(). And instead of getting a promise, you will attach a listener callback that will be invoked whenever there is a change in the results of the query. Because of these differences, your code will look drastically different.

Resources