Create and update documents
strict document creation
Strictly create a document with a controlled ID. The operation aborts if a document exists. (admin SDK only)
docRef.create(data)
The client SDK wants to be offline friendly. As such, It doesn't support strict document creation with a controlled ID because it requires a server roundtrip to green-light it. It does support random ID creation because the document won't exist by design:
addDoc(collectionRef, data)
db.collection("message").add(data)
To get a controlled, strict document creation, we must use a two-steps transaction where we first read then write and throw if a document exists.
upsert
An upsert works regardless if a document exists or not, and has the same result (idempotent). It is destructive, aka override any existing document: It has the effect of a creation:
setDoc(docRef, data)
docRef.set(data)
partial update
We assume the document already exists: we use the update pattern or the set merge pattern.
The update pattern is a strict update: it correctly fails if the document doesn't exist.
Both update and set merge expect a change object.
For update, the change fields replace the existing ones as-provided, the other fields are unchanged.
If we want to mutate a single property within an object field (aka mutate a sub-field), we target the sub-field directly, with a dot notation field:
const change = { displayName: "Johnny Appleseed" }
updateDoc(docRef, data)
docRef.update(data)
// sub-field
const change = { "address.city": "Lyon" }
updateDoc(docRef, data)
Note: We type the change as a Partial or a Pick of the document. If TypeScript complains about the dot notation, we use a separate version of updateDoc():
updateDoc(docRef, new FieldPath("address", "city"), "Lyon")
partial update with set
set comes with a merge option that changes its meaning: we are now providing a change object. The risk is to forget the merge option and override the document with a change object.
We provide the sub-fields we want to change. The other ones are preserved (deep merge):
const change = { address: { city: "Lyon" } } // it preserves the country field
setDoc(docRef, data, { merge: true })
docRef.set(data, { merge: true })
blind increment
We ask the server to increment the field by n, which may be negative for decrement. We skip a preemptive read since we don't care about the absolute value:
const partialUserDoc = {
activityScore: increment(1),
}
docRef.update({
count: FieldValue.increment(1),
})
delete field
We ask the server to delete a field. This shortcuts the need to fetch the document first and store it second omitting the given field:
updateDoc(docRef, {
fleet: deleteField(),
})
docRef.update({
fleet: FieldValue.delete(),
})
server timestamp field
Ask the server to generate a Firestore timestamp value.
updateDoc(docRef, {
count: serverTimestamp(),
})
docRef.update({
count: FieldValue.serverTimestamp(),
})
delete document
docRef.delete()
deleteDoc(docRef)