What is the difference between the merge
and mergeFields
options for the .set() method in Firestore?
This documentation only lists the options and doesn't explain what they do.
As you already noticed, when using DocumentReference's set() function, you are allowed to pass as the second argument either SetOptions's merge:
Changes the behavior of a set() call to only replace the values specified in its data argument. Fields omitted from the set() call remain untouched.
Or SetOptions's mergefields:
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.
Both are optional, but both change set
to act as a Merge/Upsert instead of overwriting all fields not provided in the data parameter. This will create the document from the document reference if it does not exist, and otherwise performs the behavior of update
.
SetOptions.Merge will merge based on the object keys you specify in its data parameter. SetOptions.MergeFields is a bit more awkward. It's easy to skim right over the documentation description, but it states that the array of field paths you specify will be the only ones taken from the data parameter when updating values. Meaning not all the key-values passed in the data parameter are used in the merge operation.
Think of mergeFields
as picking key-values from the provided data and applying those in your operation. It's not really necessary, and just is a shorthand for cleaning up the key values and passing into merge
. Note, it will not delete fields omitted in data that are declared in the field path array, instead you just get a firebase error that a field path is missing from your input data. It seems like explicit FieldValue.delete is the only option for that behavior.
For nested field paths you do get some additional benefit. You can more explicitly control the update operation.
ex. For document name: { first: 'Jon', last: 'Doe' }
set({ name: { first: 'Jane' } }, { merge: true });
becomes
name: { first: 'Jane', last: 'Doe' }
set({ name: { first: 'larry' } }, { mergeFields: ['name.first'] })
also becomes
name: { first: 'Jane', last: 'Doe' }
set({ name: { first: 'larry' } }, { mergeFields: ['name'] })
unlike the others becomes
name: { first: 'Jon' }
where the field path is replaced.