Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.select() in finds and firsts does not seem to exclude any fields #86

Closed
pmmlo opened this issue Mar 2, 2021 · 10 comments · Fixed by #88
Closed

.select() in finds and firsts does not seem to exclude any fields #86

pmmlo opened this issue Mar 2, 2021 · 10 comments · Fixed by #88
Labels
type:bug Impaired feature or lacking behavior that is likely assumed

Comments

@pmmlo
Copy link
Contributor

pmmlo commented Mar 2, 2021

Hey @cbaker6 - Do you think this could be an encoding problem or a potential parse-server bug?

struct GameScore: ParseObject {
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    var score: Int?
}

var score = GameScore()
score.score = 200
try score.save()

let afterDate = Date().addingTimeInterval(-300)
var query = GameScore.query("createdAt" > afterDate).select(["score"]).limit(1)

query.find() { results in
    print(results) /// <<< I would expect only the GameScore objects with createdAt after afterDate to only return the "score" field. (Yes, this is lazy.)
}

/// Alternatively
query.first() { results in
    print(results)
}

Anybody have any thoughts? Will dig deeper into the source, if not. Thanks!

EDIT:
.select() does seem to work, as of late. Reference: https://stackoverflow.com/questions/61100282/parse-server-select-a-few-fields-from-included-object

@pmmlo
Copy link
Contributor Author

pmmlo commented Mar 2, 2021

I could be wrong, but I think this test may be incorrectly written:

func testSelectKeys() {
        let query = GameScore.query()
        XCTAssertNil(query.keys)
        var query2 = GameScore.query().select("yolo")
        XCTAssertEqual(query2.keys?.count, 1)
        XCTAssertEqual(query2.keys?.first, "yolo")
        query2 = query2.select("yolo", "wow")
        XCTAssertEqual(query2.keys?.count, 2)
        XCTAssertEqual(query2.keys, ["yolo", "wow"])
        query2 = query2.select(["yolo"])
        XCTAssertEqual(query2.keys?.count, 1)
        XCTAssertEqual(query2.keys, ["yolo"])
}

I would imagine it could be something like:

func testSelectKeys() {
///  Poorly assuming .select() is equally effective on .first() and .find()
        let query = GameScore.query()
        let results = try? query.first()
        XCTAssertNotEqual(results?.updatedAt, nil)

        let query2 = GameScore.query().select("score")
        let results2 = try? query2.first()
        XCTAssertEqual(results2?.createdAt, nil)
        XCTAssertEqual(results2?.updatedAt, nil)
        XCTAssertNotEqual(results2?.score, nil)

        let query3 = GameScore.query().select(["score", "createdAt"])
        let results3 = try? query3.first()
        XCTAssertNotEqual(results3?.createdAt, nil)
        XCTAssertEqual(results3?.updatedAt, nil)
        XCTAssertNotEqual(results3?.score, nil)
}

@cbaker6
Copy link
Contributor

cbaker6 commented Mar 2, 2021

What error are you getting when trying select?

@cbaker6
Copy link
Contributor

cbaker6 commented Mar 2, 2021

I could be wrong, but I think this test may be incorrectly written:

The test looks okay from what I see. An improvement could be to check that the selected keys are encoded because if keys is accidentally removed from CodingKeys then the current test would miss this while it should fail. The improved test would be similar to

func testWhereKeyExists() {
let expected: [String: AnyCodable] = [
"yolo": ["$exists": true]
]
let constraint = exists(key: "yolo")
let query = GameScore.query(constraint)
let queryWhere = query.`where`
do {
let encoded = try ParseCoding.jsonEncoder().encode(queryWhere)
let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded)
XCTAssertEqual(expected.keys, decodedDictionary.keys)
guard let expectedValues = expected.values.first?.value as? [String: Bool],
let decodedValues = decodedDictionary.values.first?.value as? [String: Bool] else {
XCTFail("Should have casted")
return
}
XCTAssertEqual(expectedValues, decodedValues)
} catch {
XCTFail(error.localizedDescription)
return
}
}

let results2 = try? query2.first()

There's no need to actually perform the query as 1) you will need to mock the network (like some of the other tests) 2) Testing to see if the keys array is populated/encoded properly is good enough for a client side test since the Parse Server does the rest.

@cbaker6
Copy link
Contributor

cbaker6 commented Mar 2, 2021

@cbaker6
Copy link
Contributor

cbaker6 commented Mar 2, 2021

I've tested this by adding to the playgrounds file I mentioned above and it seems to work fine.

struct GameScore: ParseObject {
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    var score: Int?
    var oldScore: Int?
}

var score = GameScore()
score.score = 200
score.oldScore = 10
do {
    try score.save()
} catch {
    print(error)
}

let afterDate = Date().addingTimeInterval(-300)
var query = GameScore.query("score" > 100, "createdAt" > afterDate)

// Query first asynchronously (preferred way) - Performs work on background
// queue and returns to designated on designated callbackQueue.
// If no callbackQueue is specified it returns to main queue.
query.first { results in
    switch results {
    case .success(let score):

        guard score.objectId != nil,
            let createdAt = score.createdAt else { fatalError() }
        assert(createdAt.timeIntervalSince1970 > afterDate.timeIntervalSince1970, "date should be ok")
        print("Found score: \(score)")

    case .failure(let error):
        assertionFailure("Error querying: \(error)")
    }
}

//The above outputs:
// Found score: GameScore ({"objectId":"oBdHL84zSZ9qCWXFrB4RkhR5AOJd0a5U","updatedAt":{"__type":"Date","iso":"2021-03-02T18:30:07.596Z"},"score":200,"createdAt":{"__type":"Date","iso":"2021-03-02T18:30:07.482Z"}})

let querySelect = query.select("score")
querySelect.first { results in
    switch results {
    case .success(let score):

        guard score.objectId != nil,
            let createdAt = score.createdAt else { fatalError() }
        assert(createdAt.timeIntervalSince1970 > afterDate.timeIntervalSince1970, "date should be ok")
        print("Found score using select: \(score)")

    case .failure(let error):
        assertionFailure("Error querying: \(error)")
    }
}

//Outputs:
//Found score using select: GameScore ({"objectId":"oBdHL84zSZ9qCWXFrB4RkhR5AOJd0a5U","updatedAt":{"__type":"Date","iso":"2021-03-02T18:30:07.596Z"},"score":200,"createdAt":{"__type":"Date","iso":"2021-03-02T18:30:07.482Z"}})

In the last output, oldScore is missing because it wasn't selected.

@cbaker6
Copy link
Contributor

cbaker6 commented Mar 2, 2021

Note that if you add a property to the your type that isn't optional (added highestScore):

struct GameScore: ParseObject {
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    var score: Int?
    var oldScore: Int?
    var highestScore: Int //New property
}

highestScore must be one of the keys selected or else the compiler can't decode GameScore. This would be considered a user error, because you are telling the compiler that GameScore requires a value for highestScore

@cbaker6 cbaker6 added the type:question Support or code-level question label Mar 2, 2021
@cbaker6
Copy link
Contributor

cbaker6 commented Mar 2, 2021

There may be something up with this...

@cbaker6 cbaker6 removed the type:question Support or code-level question label Mar 2, 2021
@pmmlo
Copy link
Contributor Author

pmmlo commented Mar 2, 2021

Thanks @cbaker6 Glad it's working! I can't seem to reproduce your results, so I'll play around and see if I'm bugging out.

What error are you getting when trying select?

No error, but I'm getting all the fields back.

An improvement could be to check that the selected keys are encoded because if keys is accidentally removed from CodingKeys then the current test would miss this while it should fail.

Agree.

EDIT: I'm using Parse-Swift 1.1.6 and Parse-Server 4.5.0.

@cbaker6 cbaker6 mentioned this issue Mar 2, 2021
5 tasks
@cbaker6 cbaker6 added the type:bug Impaired feature or lacking behavior that is likely assumed label Mar 2, 2021
@cbaker6
Copy link
Contributor

cbaker6 commented Mar 5, 2021

Noting that this has been classified as a server-side bug as oppose to a Parse-Swift bug. Check the linked PR and issue for details.

@cbaker6
Copy link
Contributor

cbaker6 commented Jun 3, 2021

The fix on the server side has been merged to the master. If you would like to use select and and exclude with the ParseSwift client, please use the latest master branch of the parse-server.

@cbaker6 cbaker6 closed this as completed Jun 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:bug Impaired feature or lacking behavior that is likely assumed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants