SnapshotChanges subscription doesn't detect final deletion of sub-collection item - javascript

I have the following code to retrieve some user information from Cloud Firestore - it grabs the list of challenges a user is part of from a subcollection on their user document and then uses those ids to collect the full challenge information from the top level challenge document.
When I delete a challenge I remove it from the main challenges document and then remove the id reference in each users challenges subcollection. The UI updates nicely for each delete as snapshotchanges is an active subscription, until I delete the final one. The final one deletes from the database but stays in the UI until I refresh or make a new challenge.
My theory is that becuase deleting the last document in the challenges subcollection also deletes the subcollection this means snapshotChanges doesn't run as there is no longer a collection in the database to monitor the changes on.
Any thoughts on how to workaround this would be great.
getChallenges() {
return this.getCurrentUserId().pipe(switchMap(userId => {
return this.db.collection(`users/${userId}/challenges`).snapshotChanges().pipe(
map(actions => actions.map(a => {
const data = a.payload.doc.data();
console.log('in get challenges: ', data);
const user_challenge_key = a.payload.doc.id;
return this.getOneChallenge(data['id'], user_challenge_key);
}))
);
}));
}
getOneChallenge(id, user_challenge_key = null): Observable<Challenge> {
return this.db.doc(`challenges/${id}`).snapshotChanges().pipe(
take(1),
map(changes => {
const data = new Challenge(changes.payload.data());
const challengeId = changes.payload.id;
data.user_challenge_key = user_challenge_key;
data.id = id;
console.log('in get One challenge: ', data);
return data;
})
);
}

Maybe maintain a dummy document in order to keep the sub-collection
For example, validate if you going to delete the last element of that sub-collection add the dummy and later delete the last element
for the first insertion check if the dummy object is the only document, after that add your first entry and delete the dummy
I found a similar question with similar approach on the Firestore's Google Group

Related

Firestore is deleting the last document not the one I selected

I am trying to impelement a delete function in Firestore database and the problem is that when I click delete, it deletes the last document created, not the one that I want to delete.
This is how I get the data from the database:
db.collection("flights").get().then((snapshot) =>{
snapshot.docs.forEach(doc => {
var data = doc.data();
var docID = doc.id;
And this is how I am trying to delete it:
function deleteFlight(docID){
firebase.firestore()
.collection("flights")
.doc(docID)
.delete()
.then(() => console.log("Document deleted")) // Document deleted
.catch((error) => console.error("Error deleting document", error));
}
I want to specify that after I create a new document, the website is refreshed, so I think it loses somehow the document id.
In the comment section you have specified that you are calling the deleteFlight() function from a html button. As you are using forEach() loop after getting the collection of documents and within that populating docID, by the time the html button is clicked the forEach() loop would have been completed and then docID will have the document id of the document which comes last in ascending order because by default the documents are sorted in ascending order of document id as mentioned in this document. In your case I suspect the document id for the recently added document comes last when sorted in ascending order. This is the reason the recently added document is getting deleted.
I am not sure of your use case. But I would suggest you to declare a variable outside of the db.collection() method and implement some logic according to your use case to populate it with the document id which you want to delete. Then use that variable in the deleteFlight() function.

Firebase Firestore: How to update or access and update a field value, in a map, in an array, in a document, that is in a collection

Sorry for the long title. Visually and more precise, I would like to update the stock value after a payment is made. However, I get stuck after querying the entire document (e.g. the selected one with title sneakers). Is there a way to actually query and update for example the Timberlands stock value to its value -1. Or do you have to get all data from the entire document. Then modify the desired part in javascript and update the entire document?
Here is a little snippet of a solution I came up with so far. However, this approach hurts my soul as it seems very inefficient.
const updateFirebaseStock = (orders) => {
orders.forEach( async (order) => {
try {
collRef = db.doc(`collections/${order.collectionid}`);
doc = await collRef.get();
data = doc.data();
//Here:const newItems = data.items.map(if it's corr name, update value, else just return object), results in desired new Array of objects.
//Then Update entire document by collRef.update({items: newItems})
} catch (error) {
console.error(error)
};
});
}
You don't need to get the document at all for that, all you have to do is use FieldValue.increment(), using your code as a starting point it could look like this:
collRef = db.doc(`collections/${order.collectionid}`);
collRef.update({
Price: firebase.firestore.FieldValue.increment(-1)
});
You can increment/decrement with any numeric value using that function.

Conditinal database record delete firebase

I want to clean some records of a firestore database. I have a function that receives the records I would like to keep, I check in all the records if id is not in the docsToKeep:firebase documents argument.
I use a foreach for all the records, and I user filter to find the matching id as shown in the working snippet below:
async function checkForNotifClean(docsToKeep:firebase.firestore.QuerySnapshot<firebase.firestore.DocumentData>) {
const db = firebase.firestore();
const notifCollection = db.collection('notifications')
const allData = await notifCollection.get();
allData.docs.forEach(doc => {
const filtered = docsToKeep.docs.filter(entry => entry.id === doc.id);
const needsErase = filtered.length === 0;
if (needsErase) {
const id = doc.id;
notifCollection.doc(id).delete();
}
})
}
Is there a cleaner way to erase all the documents that are not in docsToKeep ? Would be kind of function that get all the objects that are not in both arrays and delete those. I would be happy on improvements on the javascript side to filter out the records to delete, and in the firebase side, improvements regarding the chance of deleting some set of records at once instead of looping through all and deleting each.
To delete (or write to) a document in Firestore you need to know the complete path to that document, including its document ID. If you don't have the IDs of the documents to delete, then your only option is to determine those IDs as you're doing now.
It does lead to the question how docsToKeep is populated though. My guess it that the user already selects some documents from a full list. If that's the case, might you be able to pass the inverted list: docsToDelete?

How to get a Firestore document ID based on its content?

I am trying to get a way to delete firestore document by a click event.
here is what i have so far. x is the document id i get from firestore. But how to get the ID change dynamically based on the content show in the page? SO that user can delete the content they wanted.
var deleteContent = document.getElementById('delete');
deleteContent.addEventListener('click', function(e) {
// get current document ID
x = 'poOjcQce2iiKzp2FaFVA'
var getId = db.collection("checkin").doc(x);
getId.delete().then(function() {
console.log(" successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
});
Thanks
Firebase Cloud Firestore allows you to perform simple and compounded queries. Essentially, you can provide a condition in the db.collection(<COLLECTION>).where() clause which will filter all documents that match that certain condition.
For illustrative purposes, suppose documents in the a collection follow the following structure: { id, '123', name: 'test', color: 'red' }. If you want to get all documents with the color red, you can simply call db.collection('a').where('color', '==', 'red'), which you can then iterate into to process each document that matches that condition.
Moreover, in your specific case, suppose your document structure is { id, content }you can try something like this:
var deleteContent = document.getElementById('delete');
deleteContent.addEventListener('click', function(e) {
// get current document ID
let docToDelete = db.collection("checkin").where('content', '==', 'CONTENT_GOES_HERE')
x = docToDelete.id;
var getId = db.collection("checkin").doc(x);
getId.delete().then(function() {
console.log(" successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
});
However, a much simpler way would be to store the document id somewhere within your application whenever a specific document is read, local storage or session storage for instance. As such, you can refer to the document id whenever you need. For example, suppose that you fetch all of a user's documents in an onLoad event handler --- suppose that you have HTML elements with the id 'content1', 'content2', 'content3', etc. and that it is where the contents of your documents are shown. You can try something like this:
let userId = 123;
[...].onload = () => {
// Get all of the documents belonging to this user
let i = 0;
let docs = [];
let documents = db.collection('posts').where('userId', '==', userId);
documents.forEach(doc => {
document.getElementById(`content${i}`).innerHTML = JSON.stringify(doc.data()); // Just stringifying to be certain
/**
* Extra step to add the document id in the sessionStorage so that we can refer to it later
*/
htmlId = 'content' + i;
sessionStorage.setItem(htmlID, doc.id );
})
}
In doing so, you can simply refer to the document id like such sessionStorage.getItem('content2').
Note that in any of the examples I listed, there is heavy preparation that needs to be done in order for this to be as smooth sailing as possible. However, wouldn't you agree that a programmer merely is a person that spends hours on things that takes minutes for the sake of making it easier in subsequent uses? :)

Saving a field from firestore that is an array

I am currently working on a mobile app on React and I am having trouble understanding how to save a field from fire store that is an array.
Since I can't post images my database structure is all strings such as username, first name, etc but I have a field called follow list that is an array.
What I want to do is save the usernames from the following list into an array to later search fire store for the username's in the array, this is basically what I want to do so I can render my app's social Feed. I do know that I can probably create another subcollection and write something familiar to what I did to search for users but that was a QuerySnapShot which was overall documents not a specific one and I also know firebase creates an ID for arrays and it increases as the array gets bigger.
I do not want to end up making two more subcollections one for followers and following which I think not ideal right? My current approach is this
export const fetchUserFollowing = async (username) => {
const ref = firebase.firestore().collection('users').doc(username)
let results = []
ref
.get()
.then( doc => {
let data = doc.data()
results = data
})
.catch((err) => {
return 'an error has occurred ', err
})
}
From what I understand is that a DocumentSnapShot .get() function returns an object but what I want is to store follow list into results and then return that but I am not sure how to manipulate the object return to just give me follow List which is an array
https://rnfirebase.io/docs/v5.x.x/firestore/reference/DocumentSnapshot
link to docs

Categories

Resources