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

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.

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.

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,
});
}
}

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

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);
});

Firestore document Set with mergeFields when creating a document does not work as expected

Hi I am saving a documemnt with a batch write like so:
batch.set(admin.firestore().collection('suuntoAppWorkoutQueue').doc(generateIDFromParts([serviceToken.userName, payload.workoutKey])), <QueueItemInterface>{
userName: serviceToken.userName,
workoutID: payload.workoutKey,
retryCount: 0,
processed: false,
}, {mergeFields: ['retryCount']});
According to the docs:
Changes the behavior of set() calls to only replace the specified field paths. Any field path that is not specified is ignored and remains untouched.
Above it is said that it will only replace.
However, when I write a new document, eg the doc ID is does not exist the mergeFields only writes the retryCount field.
Is that by design?
Shouldn't then be saying:
Changes the behavior of set() calls to only write
Instead of:
Changes the behavior of set() calls to only replace
I can follow your argumentation about write/replace. I did not check the behavior myself, but if the field is written if the document does not exist 'write' or 'update' would be a better suited description of the behavior than replace as there is nothing to be replaced in the first place.
If you only want to update the field if the document already exists, you can use a transaction to do so. You can use the transaction to get the latest version of the document, check if it exists and if so update it.
let data = {
userName: serviceToken.userName,
workoutID: payload.workoutKey,
retryCount: 0,
processed: false,
}
let db = admin.firestore()
let documentReference = db.collection('suuntoAppWorkoutQueue').doc(...)
db.runTransaction((transaction) =>
transaction.get(documentRef).then((doc) => {
if (doc.exists) {
return t.update(documentRef, {retryCount: data.retryCount});
} else {
return t.set(documentRef, data);
}
}
).then(...).catch(...)
Transactions also have the benefit to be atomic. If the value on the server has changed since you fetched it, the transaction will fail. Firestore actually will re-run your code in this case to re-attempt the update. This eliminates race conditions.

Resources