Parse-SDK-JS: Parse.query on local datastore objects returns other local object attributes or null
Issue Description
Performing a query for objects located in the local datastore sometimes returns other local object attributes or no attributes when there should be attributes for the local object.
Steps to reproduce
Add multiple items with attributes of the same class to the local datastore. Quit and restart the RN app and perform a query for a subset of objects.
Expected Results
The expectation is that the query results contain the matching objects for the query parameters and each object contains the attributes for the object when it was pinned to the local datastore.
Actual Outcome
The query will return fewer objects than it should due to ParseQuery._handleOfflineQuery setting the object._localId after ParseObject.fromJSON. This causes the static cache reference for the object to now point to null or the attributes of another object.
Environment Setup
- JS SDK
- JS SDK version: 2.6.0
- Application? (Browser, Node, React-Native, etc): React-Native
More Info on Solution
I was able to fix this with two changes and I’m asking for feedback or commentary on what other side-effects this may introduce:
Change 1:
In ParseObject._finishFetch the first thing this function does is to set the id of the object if it isn’t set already. There’s already a check here for the serverData.objectId. I added an additional check for the _localId and set the object’s id to the localId of the object if it doesn’t have an objectId in the case of locally stored objects only.
if (!this.id && serverData.objectId) {
this.id = serverData.objectId;
} else if (!this.id && serverData._localId) {
this.id = serverData._localId;
}
This seems to be the right place to do this. Without this set here, AFTER the parse object is created using ParseObject.fromJSON during the _handleOfflineQuery operation the next step will then check the object’s localId and objectId and reset the _localId to match the attribute of _localId. However, by doing this outside of the .fromJSON call, this has a side effect of changing the reference to the cache attributes and thus returns another object’s attributes or empty object for attributes.
Change 2:
The second issue is how attribute values are getting encoded into the cache.
In ObjectStateMutations.js the commitServerChanges function was limiting the attributes to only objects. Thus, if an attribute was a string, it wouldn’t be saved to the cache. I changed the check on the val to encode objects and just store the rest as-is. This I’m not clear on what other side-effects this may introduce, but it is now working in my app for strings and bools for attributes.
export function commitServerChanges(serverData
/*: AttributeMap*/
, objectCache
/*: ObjectCache*/
, changes
/*: AttributeMap*/
) {
for (const attr in changes) {
const val = changes[attr];
serverData[attr] = val;
if (val && !(val instanceof ParseObject) && !(val instanceof ParseFile) && !(val instanceof ParseRelation)) {
if (typeof val === 'object') {
const json = encode(val, false, true);
objectCache[attr] = JSON.stringify(json);
} else {
objectCache[attr] = val;
}
}
}
}
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 18 (8 by maintainers)
This problem may be related to the issue I’m reporting.
When you
Parse.Object.unPinAllObjects();this will clear the references under_defaultbut it doesn’t remove the objects from the top flat list.Next, when you perform a query, the query will return any object that meets the query params including any object that was created under the
_defaultkey but was removed later since it still exists at the top level.I think this may be the root of the issue. Here’s what I think is happening in my case. My app does queries and updates to the local datastore. In this process, objects are pinned and unpinned and updated. Older objects may have attributes that no longer exist as time moves forward, however, there’s a series of orphaned objects at the top level of the localDatastore. Then, when performing a query on the localdatastore, the query INCLUDES these orphaned objects as part of the query response. Then, during fromJSON the localIDs are swapped which causes the local object to point to an older (possibly missing) attribute cache that was removed in the past.
I suspect that if the object at the top-level local database store is actually removed when unpinning an object so that
_defaulttracks with the stored objects then this issue might be solved.Hi @dplewis
I’m putting together a demo of the problem, but I’m running into another issue with local storage.
Parse.Object.unPinAllObjects();Doesn’t seems like this is clearing out the local database.
If I do:
local always has the local database objects. I can’t seem to find a way to actually clear out the local database to finish this demo and get over to you.
Wes