Firestore Partial Update

May 17, 2019
Support Field, List, Map, Nested Field, Delete

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..

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.