Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for decoding legacy BSON map representations into native BSON instances within the Firestore SDK, updating the client classes, helper functions, and DocumentSnapshot to handle the new decode_bson configuration. The review feedback highlights critical issues in the implementation: first, DocumentSnapshot fails to dynamically toggle between BSON-decoded and raw formats when raw_fields is None; second, the system tests incorrectly pass decode_bson to get() methods on document references, which will raise runtime TypeErrors; and finally, the return type annotation of decode_dict should be updated to Any to accurately reflect the potential return of native BSON instances.
| if raw_fields is not None: | ||
| if self._decode_bson: | ||
| self._data_bson = copy.deepcopy(data) | ||
| else: | ||
| self._data_raw = copy.deepcopy(data) | ||
| else: | ||
| self._data_raw = copy.deepcopy(data) if data is not None else None |
There was a problem hiding this comment.
When raw_fields is None (which is currently the case for any DocumentSnapshot created outside of _parse_batch_get, such as query or stream results), the snapshot cannot dynamically toggle between BSON-decoded and raw dict formats because it lacks the original protobuf fields to re-decode.
In the current implementation, if raw_fields is None and self._decode_bson is True, self._data_raw is populated with the decoded BSON data. If a user then calls to_dict(decode_bson=False), it will incorrectly return the BSON-decoded data instead of the raw dict.
To fix this, we should:
- Correctly initialize
self._data_bsonorself._data_rawin__init__based onself._decode_bsonwhenraw_fieldsisNone. - Ensure that all internal paths that instantiate
DocumentSnapshot(such as query and stream result processing inbase_query.py/query.py) are updated to passraw_fieldsso that dynamic BSON decoding works consistently across all read operations.
if raw_fields is not None:
if self._decode_bson:
self._data_bson = copy.deepcopy(data)
else:
self._data_raw = copy.deepcopy(data)
else:
if self._decode_bson:
self._data_bson = copy.deepcopy(data) if data is not None else None
else:
self._data_raw = copy.deepcopy(data) if data is not None else None| snapshot = doc_ref.get(decode_bson=True) | ||
| assert snapshot.exists | ||
| assert snapshot.to_dict(decode_bson=True) == bson_payload |
There was a problem hiding this comment.
DocumentReference.get() does not accept a decode_bson parameter. Calling doc_ref.get(decode_bson=True) will raise a TypeError at runtime.
To test BSON decoding, you should either:
- Set
decode_bson=Trueon the client instance. - Or retrieve the snapshot normally and call
snapshot.to_dict(decode_bson=True)to decode on-demand.
| snapshot = doc_ref.get(decode_bson=True) | |
| assert snapshot.exists | |
| assert snapshot.to_dict(decode_bson=True) == bson_payload | |
| snapshot = doc_ref.get() | |
| assert snapshot.exists | |
| assert snapshot.to_dict(decode_bson=True) == bson_payload |
| snapshot = await doc_ref.get(decode_bson=True) | ||
| assert snapshot.exists | ||
| assert snapshot.to_dict(decode_bson=True) == bson_payload |
There was a problem hiding this comment.
AsyncDocumentReference.get() does not accept a decode_bson parameter. Calling await doc_ref.get(decode_bson=True) will raise a TypeError at runtime.
To test BSON decoding, you should either:
- Set
decode_bson=Trueon the client instance. - Or retrieve the snapshot normally and call
snapshot.to_dict(decode_bson=True)to decode on-demand.
| snapshot = await doc_ref.get(decode_bson=True) | |
| assert snapshot.exists | |
| assert snapshot.to_dict(decode_bson=True) == bson_payload | |
| snapshot = await doc_ref.get() | |
| assert snapshot.exists | |
| assert snapshot.to_dict(decode_bson=True) == bson_payload |
| return None | ||
|
|
||
|
|
||
| def decode_dict(value_fields, client, decode_bson: Optional[bool] = None) -> Union[dict, Vector]: |
There was a problem hiding this comment.
The return type annotation of decode_dict is currently Union[dict, Vector]. However, with the introduction of BSON decoding support, this function can now return native BSON instances (such as BSONObjectID, BSONDecimal128, etc.) when decode_bson is enabled. The type annotation should be updated to Any to accurately reflect this behavior.
| def decode_dict(value_fields, client, decode_bson: Optional[bool] = None) -> Union[dict, Vector]: | |
| def decode_dict(value_fields, client, decode_bson: Optional[bool] = None) -> Any: |
References
- When finding a precise type hint that satisfies both mypy and unit tests is not cost-effective, using a less specific type (e.g., Any) is an acceptable trade-off, especially if it improves upon the previous state.
e507507 to
6c5ea1d
Compare
6c5ea1d to
2bf84e6
Compare
134b979 to
983bcde
Compare
983bcde to
57267de
Compare
57267de to
cacf194
Compare
cacf194 to
e000d4e
Compare
b50ef69 to
e04f8c9
Compare
e04f8c9 to
91ba50f
Compare
…PR 2)
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
Fixes #<issue_number_goes_here> 🦕