One great feature Firestore is partial update, which is not possible with the older version of Datastore (not sure about the latest version which is based on Firestore).
Partial update
val db = FirebaseFirestore.getInstance()val docRef = db.collection("tests").document("test_partial_update")
set will replace the entire object.
docRef.set(mapOf( "name" to "Replace entire object / Create new object" "age" to 21))
Use update for partial update.
docRef.update(mapOf( "name" to "Partial replace"))
The update will fail if applied to a document that does not exist.
with the following error.
java.util.concurrent.ExecutionException: com.google.firebase.firestore.FirebaseFirestoreException: NOT_FOUND: No document to update
To perform partial update on new document, use set with SetOptions.merge()
docRef.set(mapOf( "name" to "Partial replace"), SetOptions.merge())
Create a kotlin extension for updateOrCreate
.
fun DocumentReference.updateOrCreate(data: Any): Task<Void> { return set(data, SetOptions.merge())}
NOTE: Beware there are some bahavior difference between set with SetOptions.merge()
and update
for update map and updated nested field.
Usage
docRef.updateOrCreate(mapOf( "name" to "Partial replace"))
Partial update: increment value
docRef.update(mapOf( "count" to FieldValue.increment(1)))
NOTE: FieldValue.increment works even when count
field is yet to be created.
If the current field is not an integer or double, or if the field does not yet exist, the transformation will set the field to the given value.
NOTE: Maybe FieldValue.increment(-1)
will work as decrement. I have yet to try this.
Partial update: add or remove from list
Add item to list
docRef.update(mapOf( "items" to FieldValue.arrayUnion("New Item")))
NOTE: FieldValue.arrayUnion works event when items
field is yet to be created.
Each specified element that doesn't already exist in the array will be added to the end. If the field being modified is not already an array it will be overwritten with an array containing exactly the specified elements.
Remove item from list
docRef.update(mapOf( "items" to FieldValue.arrayRemove("Existing Item")))
All instances of each element specified will be removed from the array. If the field being modified is not already an array it will be overwritten with an empty array.
Partial update: update map
Success
This partial update will work: add 3: true
to maps
.
docRef.set(mapOf( "maps" to mapOf( "3" to true )), SetOptions.merge())
This partial update will work.
docRef.update(mapOf( "maps.3" to true))
Fail
This partial update will NOT work: it create a new field maps.6: true
instead of adding 6
to maps
.
docRef.set(mapOf( "maps.6" to true), SetOptions.merge())
This partial update will NOT work: it replace maps
with 3: true
(replace/delete all existing entries in maps
).
docRef.update(mapOf( "maps" to mapOf( "3" to true )))
Partial update: nested field
In essence, partial update for nested field work exactly like update map
.
Success
docRef.set(mapOf( "person" to mapOf( "name" to "Desmond" )), SetOptions.merge())
or
docRef.update(mapOf( "person.name" to "Desmond"))
Fail
This create a new field person.name
instead of adding name
to person
.
docRef.set(mapOf( "person.name" to "Desmond"), SetOptions.merge())
This replace person
with name: Desmond
(replace/delete all existing entries in person
).
docRef.update(mapOf( "person" to mapOf( "name" to "Desmond" )))
Partial update: delete field
docRef.set(mapOf( "name": FieldValue.delete()), SetOptions.merge())
docRef.update(mapOf( "name": FieldValue.delete()))
NOTE: FieldValue.delete() works for update
and set
, even though the docs mentioned Returns a sentinel for use with update() to mark a field for deletion.
.