Is it possible to go through documents in cloud firestore to see if a value of a property is equal to a comparing one? - firebase

I have website written in plain javascript to keep daily to-do tasks and the app crashed lately because different tasks of the same date was created on accident. My question is...
how can i write an if statement that checks if a document from a collection has a property (in my case the date) that is equal to the one in the input field of my form. i guess it should check after i click submit? if it exists, creation should be denyed, if not, ok to proceed.
i am using cloud firestore by the way... many thanks in advance for the help!

First, make a query to get a document that has same date:
var query = db.collection("yourCollectionName").where("date", "==", dateInInputfield);
query.get().then(function(querySnapshot) {
if (querySnapshot.empty) {
//empty
} else {
// not empty
}
});
If empty{you can proceed}, if notEmpty{some other task already exist on same date}
If you are making an app like this, a cleaner approach will be to name the id of a document as it's date, for eg. if a task is created at timestamp of 1234567, create a document named 1234567 and inside it, store all the necessary information.
By following this approach, if you create a new task, simply fetch a document by the name in inputfield,
var docRef = db.collection("yourCollectionName").doc("date");
docRef.get().then(function(doc) {
if (doc.exists) {
//this means some other document already exists
} else {
//safe to create a new document by this date.
}
}).catch(function(error) {
console.log("Error:", error);
});

Related

How to create a document if the document doesn't exist or else don't do anything?

I Wanted To Ask If It Is Possible To Make A New Document With A UID If It DOESN'T Exist But if it exists NOT To Do Anything (and if possible return an error) In Firestore. (In Modular Javascript)
And If It Is Possible How?
Note: I Already Read This Question:StackOverFlow 46888701 But It Doesn't Fit My Requirements because after creating the document I want to be able to update it too.
Edit: I Wanted To Know Without Using getDoc because when i use it acts like a read and i don't want to spend lots of my no of reads from my limit.
You should first try to get the document and check if it exists then proceed to your document set/update. See sample code below:
import { doc, getDoc } from "firebase/firestore";
const docRef = doc(db, "<collection>", "<UID>");
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document exist!");
// Throws an error.
throw new Error('Document Exist!');
} else {
await setDoc(docRef, {
// Document Data
});
}
For more relevant information, check out these documentations:
Get a document
Update a document
Set a document
Edit:
If you don' t want to use getDoc then you have the option to use updateDoc, it will produce an error but you can still execute a setDoc method on the catch method. On this approach, you're doing a fail-safe practice that you're responding in the event of failure. See code below:
const docRef = doc(db, "<collection>", "<UID>");
// Produces error log if no document to update
updateDoc(docRef, {
// document data
})
.catch((error) => {
// console.log(error);
setDoc(docRef, {
// document data
});
});
According to the documentation, an update is just a write operation:
Charges for writes and deletes are straightforward. For writes, each set or update operation counts a single write.
We have established that an update is just a write operation (there's no reading involved). A write is a change in a document, since you're not changing anything because the document didn't exist then you won't be charged at all.
In web version 9, the function that can help you create a document is named setDoc(), which creates or overwrites a document at a specific document reference.
How to create a document if the document doesn't exist or else don't do anything?
If you want to achieve that, you have to check if the document already exists. If it doesn't exist, create it using setDoc(), otherwise, take no action, but do not use the updateDoc() function in this case.
Remember that the updateDoc() function helps only when you want to update some fields of a document without overwriting the entire document. If the document doesn't exist, the update operation will fail.
Edit:
According to your edited question, please note that there is no way you can know if a document exists, without checking it explicitly. You can indeed not do that check, but you'll end up overwriting the document over and over again. Please also note, that a write operation is more expensive than a read operation. So that's the best option that you have.

How to only allow a unique document ID to be upload to Firebase Firestore using Rules [duplicate]

I want to create Firestore documents if they don't exist - if they do exist, skip them (don't update).
Here's the flow
var arrayOfRandomIds = [array of 500 random numbers];
for (var id of arrayOfRandomIds)
{
var ref = db.collection("tickets").doc(id);
batch.set(ref, {name: "My name", location: "Somewhere"}, { merge: true });
}
batch.commit();
I just want to know, would this overwrite any existing documents if they exist? I don't want anything overwritten, just skipped.
Thanks.
I think you can use security rules to accomplish that. That way you won't be charged for an additional document read to see if it already exists.
service cloud.firestore {
match /databases/{database}/documents {
match /tickets/{id} {
allow create;
}
}
}
Meanwhile there is a "create but don't overwrite" function.
Assuming you are using JavaScript here is the reference: https://googleapis.dev/nodejs/firestore/latest/DocumentReference.html#create
Here is the corresponding example code from the docs:
let documentRef = firestore.collection('col').doc();
documentRef.create({foo: 'bar'}).then((res) => {
console.log(`Document created at ${res.updateTime}`);
}).catch((err) => {
console.log(`Failed to create document: ${err}`);
});
Using .create() instead of .set() should do the trick for you without relying on security rules for application logic.
Firestore doesn't have a native "create but don't overwrite" operation. Here are the only available operations:
update: only change the contents of an existing document
set without merge: create or overwrite
set with merge: create or update if exists
Instead of a batch, what you can do instead is perform a transaction that checks to see if the document exists, then creates it conditionally if it does not already exist. You will have to write that logic inside your transaction handler.
I want to create Firestore documents if they don't exist - if they do exist, skip them (don't update).
In that case, you should check if a particular document actually exists in a collection, right before the write operation takes place. If it does not exist, create it, otherwise take no action.
So you should simply use set() function, without passing merge: true.

Firestore create documents if they don't exist, skip if they do

I want to create Firestore documents if they don't exist - if they do exist, skip them (don't update).
Here's the flow
var arrayOfRandomIds = [array of 500 random numbers];
for (var id of arrayOfRandomIds)
{
var ref = db.collection("tickets").doc(id);
batch.set(ref, {name: "My name", location: "Somewhere"}, { merge: true });
}
batch.commit();
I just want to know, would this overwrite any existing documents if they exist? I don't want anything overwritten, just skipped.
Thanks.
I think you can use security rules to accomplish that. That way you won't be charged for an additional document read to see if it already exists.
service cloud.firestore {
match /databases/{database}/documents {
match /tickets/{id} {
allow create;
}
}
}
Meanwhile there is a "create but don't overwrite" function.
Assuming you are using JavaScript here is the reference: https://googleapis.dev/nodejs/firestore/latest/DocumentReference.html#create
Here is the corresponding example code from the docs:
let documentRef = firestore.collection('col').doc();
documentRef.create({foo: 'bar'}).then((res) => {
console.log(`Document created at ${res.updateTime}`);
}).catch((err) => {
console.log(`Failed to create document: ${err}`);
});
Using .create() instead of .set() should do the trick for you without relying on security rules for application logic.
Firestore doesn't have a native "create but don't overwrite" operation. Here are the only available operations:
update: only change the contents of an existing document
set without merge: create or overwrite
set with merge: create or update if exists
Instead of a batch, what you can do instead is perform a transaction that checks to see if the document exists, then creates it conditionally if it does not already exist. You will have to write that logic inside your transaction handler.
I want to create Firestore documents if they don't exist - if they do exist, skip them (don't update).
In that case, you should check if a particular document actually exists in a collection, right before the write operation takes place. If it does not exist, create it, otherwise take no action.
So you should simply use set() function, without passing merge: true.

Firebase Cloud Firestore create entry or update if it already exists

I have an issue of knowing when to add or update an entry to a Firebase Firestore database.
Using doc_ref.set will add a document if it does not exist. It will also override all of a documents fields if it already exists and set is called.
Using doc_ref.update will update fields of a document if the document exists. If the document does not exist, nothing happens.
How do I add a new field to a document if the field does not currently exist, or update the field if it does exist? I could read the database and check if the field exists, and then use either set or update, but surely there is an easier way?
What you're looking for is the merge option in the set operation. From the documentation on setting a document:
var cityRef = db.collection('cities').doc('BJ');
var setWithMerge = cityRef.set({
capital: true
}, { merge: true });
If you're not sure whether the document exists, pass the option to merge the new data with any existing document to avoid overwriting entire documents.
I had similar problem, but I wanted to update array field using FieldValue.arrayUnion, FieldValue.arrayRemove. Therefore could not use set/merge.
To avoid checking if document exists before update (costs 1 read), I wrapped update statement with try/catch and check for error code 5 (NOT FOUND). In this case, set value instead of update.
Code example:
try {
await docRef.update({
arrayField: admin.firestore.FieldValue.arrayUnion(...dates),
});
} catch (error) {
if (error.code === 5) {
// Error: 5 NOT_FOUND: no entity to update:
await docRef.set({
arrayField: dates,
});
}
}

How to make a successful batch commit when some of the docs could've been deleted?

Suppose i have a structure like this:
items: {
id1: {
likes: 123
},
id2: {
likes: 456
},
id3: {
sourceId: 'id1',
likes: 123
},
id4: {
sourceId: 'id1',
likes: 123
}
[,...]
}
where any item can either be a source item or an item that references a source item. The purpose of referencing the source item is to keep counters consistent across all items that share the same sourceId.
Therefore, when i change a counter on a source item, i want to batch write that counter's value to all the items that have that item as their sourceId.
My concern
Suppose I read in the docs referencing some sourceId, then i commit the changes in a batch to all of them. What if a very small subset of the docs in the batch were deleted in the small window of time since the documents were read in, or a rare but possible write-rate conflict occurs? Do none of the counters get updated because 1 or 2 documents failed to be updated?
Is it possible to submit a batch that writes the changes to each of its documents independently, such that if one fails, it has no impact on if the others get written?
Or maybe for this scenario it might be better to read in the documents referencing some sourceId and then write the changes to each document in parallel such that write independence is achieved. I don't like this approach since the number of requests would be the size of the batch.
What are your thoughts?
Take a careful read of the API docs for BatchWrite. It will answer your questions. Since you're not showing your batch code (are you using set? update?), we have to look at the API docs to assess the failure cases:
create()
This will fail the batch if a document exists at its location.
It sounds like you're probably not using create(), but this is the failure case.
set()
If the document does not exist yet, it will be created.
So, a set will not fail if the documented was deleted before the batch got committed.
update()
If the document doesn't yet exist, the update fails and the entire
batch will be rejected.
So, if you try to update a nonexistent document, the batch will fail.
If you want to decide what to do with each document, depending on its existence and contents, then control the failure cases, use a transaction.
If I understand your question, I had a similar scenario and here's how I did it. First, I use generated universal ID's, uid's, for all my item keys/id's. Then, what you do is on the grandchildren, simply write the uid of the parent it is associated with. Each grandchild could be associated with more than one parent.
As you create new items, you have to recursively update the parent with the uid of the item so the parent has record of all its associated child items.
fetchPromise = [];
fetchArray = [];
if(!selectIsEmpty("order-panel-one-series-select") || !selectIsUnselected(orderPanelOneSeriesSelect)){
orderPanelOneTitle.innerHTML = "Select " + orderPanelOneSeriesSelect.value.toString().toUpperCase() + " Option";
}
//on change we need to populate option select based on this value
//now we need to get the particular series doc and fill get array then prmise all to get all options
familyuid = getRecordFromList(doc.getElementById("order-panel-one-family-select"),orderPanelOneFamilySelect.selectedIndex);
seriesuid = getRecordFromList(doc.getElementById("order-panel-one-series-select"),orderPanelOneSeriesSelect.selectedIndex);
optionuid = getRecordFromList(doc.getElementById("order-panel-one-option-select"),orderPanelOneOptionsSelect.selectedIndex);
optionRef = db.collection("products").doc(familyuid).collection("option");
itemRef = db.collection("products").doc(familyuid).collection("item");
targetRef = db.collection("products").doc(familyuid).collection("option").doc(optionuid);
try {
targetRef.get().then(function(snap) {
if (snap.exists) {
for (var key in snap.data()) {
if (snap.data().hasOwnProperty(key)) {
fetchPromise = itemRef.doc(key).get();
fetchArray.push(fetchPromise);
}
}
Promise.all(fetchArray).then(function(values) {
populateSelectFromQueryValues(values,"order-panel-one-item-select");
if(!selectIsEmpty("order-panel-one-item-select")){
enableSelect("order-panel-one-item-select");
}
targetRef.get().then(function(snap){
if(snap.data().name){
var str = snap.data().name.toString();
orderAsideInfo.innerHTML = "Select " + capitalizeFirstLetter(str) + " item.";
}
});
});
}
}).catch(function(error) {
toast("error check console");
console.log("Error getting document:", error);
});
}

Resources