Description
During investigation of iOS SDK bug parse-community/Parse-SDK-iOS-OSX#1067 I've noticed that one of my queries fails when working on 32 bit device. At first, I thought it's due to recent SDK update from 1.13.0 to 1.14.2 but I was able to reproduce it with both SDK versions. After few hours of debugging how requests are made on 32/64 bit SDK I came to conclusion that ParseServer treats differently requests that queries fields with one direct object pointer vs array of those pointers. Because of the way how iOS SDK stores request parameters internally (in NSDictionary which is unordered collection) JSONs producend on 32 bit devices are different then the one from 64 bit devices (presented below) and requests on 32 bit devices is failing.
Steps to reproduce
- Ids known for two objects with type fooo
let fooId = "SLa1pur9QB"
let fooId2 = "zDtZFtdx9X"
- Couple of objects("GameScore") with relation array to "fooo" objects
let foo = PFObject(withoutDataWithClassName: "fooo", objectId: fooId)
let foo2 = PFObject(withoutDataWithClassName: "fooo", objectId: fooId2)
let gameScore = PFObject(className:"GameScore")
gameScore["foooVal"] = [foo, foo2]
- Query to find objects of type GameScore where object foo2 is stored in "fooVal" array
let foo = PFObject(withoutDataWithClassName: "fooo", objectId: fooId)
let gameScoreQuery = PFQuery(className:"GameScore")
gameScoreQuery.whereKey("foooVal", equalTo: foo)
gameScoreQuery.findObjectsInBackground { (objects, error) in
print("error \(error)")
print("objects = \(objects?.count)")
}
I'll also present case where this doesn't matter due to fact that fields containg just one relation objects are stored differently by ParseServer
- Save object of type GameScore2 with direct relation to fooo
let foo2 = PFObject(withoutDataWithClassName: "fooo", objectId: fooId2)
let gameScore = PFObject(className:"GameScore2")
gameScore["foooVal"] = foo2
- Query
let foo2 = PFObject(withoutDataWithClassName: "fooo", objectId: fooId2)
let gameScoreQuery = PFQuery(className:"GameScore2")
gameScoreQuery.whereKey("foooVal", equalTo: foo2)
gameScoreQuery.findObjectsInBackground { (objects, error) in
print("error \(error)")
print("objects = \(objects?.count)")
}
Expected Results
Ordering of parameters in both queries should not influence results.
Actual Outcome
1. Query on array field
Produced JSON on 32 bit devices:
{"where":{"foooVal":{"objectId":"SLa1pur9QB","className":"fooo","__type":"Pointer"}},"_method":"GET"}
Produced JSON on 64 bit devices:
{"where":{"foooVal":{"__type":"Pointer","className":"fooo","objectId":"SLa1pur9QB"}},"_method":"GET"}
Result:
On 64 bit devices all values are returned correctly, on 32 bit empty array is returned(refer to logs section)
2. Query on direct releation field
32 bit device:
{"where":{"foooVal":{"objectId":"zDtZFtdx9X","className":"fooo","__type":"Pointer"}},"_method":"GET"}
64 bit device
{"where":{"foooVal":{"__type":"Pointer","className":"fooo","objectId":"zDtZFtdx9X"}},"_method":"GET"}
Result:
In both cases proper values is returned
Environment Setup
-
Server
- parse-server version: 2.2.25-beta.1 commit: c8823f2
- Operating System: 64bit Amazon Linux 2016.09 v3.1.0 running Node.js
- Hardware: t2.micro instance
- Localhost or remote server?: AWS Cloud
-
Database
- MongoDB version: 3.2.11
- Hardware: mLab Sandbox
- Localhost or remote server?: mLab
Logs/Trace
1. query on relation array
64 bit request/response log from server:
verbose: REQUEST for [GET] /parse/classes/GameScore: {
"where": {
"foooVal": {
"__type": "Pointer",
"className": "fooo",
"objectId": "SLa1pur9QB"
}
}
}
verbose: RESPONSE from [GET] /parse/classes/GameScore: {
"response": {
"results": [
SOME_PROPER_VALUES
]
}
}
32 bit request/response log from server:
verbose: REQUEST for [GET] /parse/classes/GameScore: {
"where": {
"foooVal": {
"objectId": "SLa1pur9QB",
"className": "fooo",
"__type": "Pointer"
}
}
}
verbose: RESPONSE from [GET] /parse/classes/GameScore: {
"response": {
"results": []
}
} results=[]
2. query on direct relation field
64 bit request/response log from server
verbose: REQUEST for [GET] /parse/classes/GameScore2: {
"where": {
"foooVal": {
"__type": "Pointer",
"className": "fooo",
"objectId": "zDtZFtdx9X"
}
}
}
verbose: RESPONSE from [GET] /parse/classes/GameScore2: {
"response": {
"results": [
SOME_PROPER_VALUES
]
}
}
32 bit request/response log from server:
verbose: REQUEST for [GET] /parse/classes/GameScore2: {
"where": {
"foooVal": {
"objectId": "zDtZFtdx9X",
"className": "fooo",
"__type": "Pointer"
}
}
}
verbose: RESPONSE from [GET] /parse/classes/GameScore2: {
"response": {
"results": [
SOME_PROPER_VALUES
]
}
}
I guess that if field contains direct relation to object query is translated from above form to CLASS_NAME$OBJECT_ID before sending it to MongoDB, and in case of field containg array query from above is passed directly to MongoDB which is why it's returning 0 objects. So either such translation should happen in both cases or SDK should create queries in specific order (although I coulnd't find any statement in documentation for REST Api about such requirement) or maybe there's a way to convince MongoDB to not look at parameter order, just "content".