[Firestore] API Feedback – Empty Object Merge Behavior
See original GitHub issueEnvironment
- Operating System version: macOS Catalina 10.15.2
- Firebase SDK version: 7.17.2
- Firebase Product: Firestore
- Node.js version: 12.14.1
- NPM version: 6.14.7
Problem
It’s been discussed in more than a few other places, but the current behavior of firestore.set('any/document'. { property: {} }, { merge: true })
is unintuitive and dangerous!
ref: https://github.com/firebase/firebase-js-sdk/issues/1371#issuecomment-445066500 ref: https://github.com/firebase/firebase-admin-node/issues/365 ref: https://github.com/mesqueeb/vuex-easy-firestore/issues/73 ref: https://github.com/firebase/firebase-js-sdk/issues/1371 ref: https://github.com/firebase/firebase-js-sdk/issues/1168
We nearly had a production error because of this database API quirk – we caught it through sheer luck. I wanted to flag it again and make the case for changing the behavior in a future major release.
Steps to reproduce:
The Good
firestore.set('any/document'. { property: { foo: 'bar' } }, { merge: true });
firestore.set('any/document'. { property: { biz: 'baz' } }, { merge: true });
console.log(await firestore.doc('any/document').get()).data());
// Expected Log: { foo: 'bar', biz: 'baz' } 👍
// Actual Log: { foo: 'bar', biz: 'baz' } 😎
The Bad
firestore.set('any/document'. { property: { foo: 'bar' } }, { merge: true });
firestore.set('any/document'. { property: {} }, { merge: true });
console.log(await firestore.doc('any/document').get()).data());
// Expected Log: { foo: 'bar' } 👍
// Actual Log: { } 😱
This is a very easy mistake to make, especially since it it not clearly called out in the docs, and depending on the setup a system may remove keys to patch update based on user input. This behavior forces extra (expensive!) input validation on deep objects that should not be necessary.
I would expect that any destructive operations with { merge: true }
must be explicitly set using FieldValue.delete()
.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:7 (4 by maintainers)
New firebase user chiming in: I came here because I just tripped over this behavior when updating a document with data from another query that could potentially be empty. I think it definitely makes sense to document the behavior more explicitly. However, I would agree with @amiller-gh that is behavior is really not intuitive and I think it is preferable to provide an intuitive API than to document an unintuitive one.
I would also like to bring algebraic argument: I think most people (including me) think of merging as a kind of addition operation and expect
{}
to behave as a neutral element. This also makes sense, since merging objects is basically a union of sets, which can be seen as equivalent to addition. In the current API{}
is treated as an absorbing element which apparently trips many people off.@amiller-gh Thanks for filing this. We have had some lengthy discussions on how to best implement this. While we know that the current “drop on empty” can be surprising, we found it less surprising that simply ignoring empty objects. Since changing the behavior here would be a major breaking change, I don’t think this is realistically going to happen.
We can certainly improve our documentation here. I will use this issue to do so.