Skip to content

Commit 0ac301b

Browse files
cbaker6TomWFox
andauthored
Fix query select and exclude (#88)
* Added select example to Playgrounds * Add testing and playground updates * Switch back to select * Works with a manual join of array * Switch back to original select and exclude. Improve playground select and exclude * Switch back to iOS in playgrounds * Add changelog * Add warning for select/exclude only working on Parse Server > 4.5.0 * Improve codecov * Make fetch include use encoded array instead of join array * Update Sources/ParseSwift/Types/Query.swift Co-authored-by: Tom Fox <[email protected]> Co-authored-by: Tom Fox <[email protected]>
1 parent 65bfd41 commit 0ac301b

File tree

11 files changed

+120
-31
lines changed

11 files changed

+120
-31
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
__New features__
88
- Add modifiers to containsString, hasPrefix, hasSuffix ([#85](https://github.com/parse-community/Parse-Swift/pull/85)), thanks to [Corey Baker](https://github.com/cbaker6).
99

10+
__Improvements__
11+
- Can use a variadic version of exclude. Added examples of select and exclude query in playgrounds ([#88](https://github.com/parse-community/Parse-Swift/pull/88)), thanks to [Corey Baker](https://github.com/cbaker6).
12+
1013
### 1.1.6
1114
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.1.5...1.1.6)
1215

ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift

+40-2
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,22 @@ struct GameScore: ParseObject {
1414
var ACL: ParseACL?
1515

1616
var score: Int?
17+
var oldScore: Int?
1718
}
1819

1920
var score = GameScore()
2021
score.score = 200
21-
try score.save()
22+
score.oldScore = 10
23+
do {
24+
try score.save()
25+
} catch {
26+
print(error)
27+
}
2228

2329
let afterDate = Date().addingTimeInterval(-300)
24-
var query = GameScore.query("score" > 100, "createdAt" > afterDate)
30+
var query = GameScore.query("score" > 50,
31+
"createdAt" > afterDate)
32+
.order([.descending("score")])
2533

2634
// Query asynchronously (preferred way) - Performs work on background
2735
// queue and returns to designated on designated callbackQueue.
@@ -68,6 +76,36 @@ query.first { results in
6876
}
6977
}
7078

79+
let querySelect = query.select("score")
80+
querySelect.first { results in
81+
switch results {
82+
case .success(let score):
83+
84+
guard score.objectId != nil,
85+
let createdAt = score.createdAt else { fatalError() }
86+
assert(createdAt.timeIntervalSince1970 > afterDate.timeIntervalSince1970, "date should be ok")
87+
print("Found score using select: \(score)")
88+
89+
case .failure(let error):
90+
assertionFailure("Error querying: \(error)")
91+
}
92+
}
93+
94+
let queryExclude = query.exclude("score")
95+
queryExclude.first { results in
96+
switch results {
97+
case .success(let score):
98+
99+
guard score.objectId != nil,
100+
let createdAt = score.createdAt else { fatalError() }
101+
assert(createdAt.timeIntervalSince1970 > afterDate.timeIntervalSince1970, "date should be ok")
102+
print("Found score using exclude: \(score)")
103+
104+
case .failure(let error):
105+
assertionFailure("Error querying: \(error)")
106+
}
107+
}
108+
71109
PlaygroundPage.current.finishExecution()
72110

73111
//: [Next](@next)

ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ User.login(username: "hello", password: "world") { results in
100100
User.current?.fetch(includeKeys: ["score"]) { result in
101101
switch result {
102102
case .success:
103-
print("Successfully fetched user with score key: \(User.current)")
103+
print("Successfully fetched user with score key: \(String(describing: User.current))")
104104
case .failure(let error):
105105
print("Error fetching score: \(error)")
106106
}
@@ -111,7 +111,7 @@ User.current?.fetch(includeKeys: ["score"]) { result in
111111
User.current?.fetch(includeKeys: ["*"]) { result in
112112
switch result {
113113
case .success:
114-
print("Successfully fetched user with all keys: \(User.current)")
114+
print("Successfully fetched user with all keys: \(String(describing: User.current))")
115115
case .failure(let error):
116116
print("Error fetching score: \(error)")
117117
}

Sources/ParseSwift/API/API+Commands.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,7 @@ internal extension API.Command {
389389

390390
var params: [String: String]?
391391
if let includeParams = include {
392-
let joined = includeParams.joined(separator: ",")
393-
params = ["include": joined]
392+
params = ["include": "\(includeParams)"]
394393
}
395394

396395
return API.Command<T, T>(

Sources/ParseSwift/Objects/ParseInstallation.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,7 @@ extension ParseInstallation {
413413

414414
var params: [String: String]?
415415
if let includeParams = include {
416-
let joined = includeParams.joined(separator: ",")
417-
params = ["include": joined]
416+
params = ["include": "\(includeParams)"]
418417
}
419418

420419
return API.Command(method: .GET,

Sources/ParseSwift/Objects/ParseUser.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -724,8 +724,7 @@ extension ParseUser {
724724

725725
var params: [String: String]?
726726
if let includeParams = include {
727-
let joined = includeParams.joined(separator: ",")
728-
params = ["include": joined]
727+
params = ["include": "\(includeParams)"]
729728
}
730729

731730
return API.Command(method: .GET,

Sources/ParseSwift/Types/Query.swift

+22-8
Original file line numberDiff line numberDiff line change
@@ -697,9 +697,21 @@ public struct Query<T>: Encodable, Equatable where T: ParseObject {
697697

698698
/**
699699
Exclude specific keys for a `ParseObject`. Default is to nil.
700-
- parameter keys: An arrays of keys to exclude.
700+
- parameter keys: A variadic list of keys include in the result.
701+
- warning: Requires Parse Server > 4.5.0
702+
*/
703+
public func exclude(_ keys: String...) -> Query<T> {
704+
var mutableQuery = self
705+
mutableQuery.excludeKeys = keys
706+
return mutableQuery
707+
}
708+
709+
/**
710+
Exclude specific keys for a `ParseObject`. Default is to nil.
711+
- parameter keys: An array of keys to exclude.
712+
- warning: Requires Parse Server > 4.5.0
701713
*/
702-
public func exclude(_ keys: [String]?) -> Query<T> {
714+
public func exclude(_ keys: [String]) -> Query<T> {
703715
var mutableQuery = self
704716
mutableQuery.excludeKeys = keys
705717
return mutableQuery
@@ -709,6 +721,7 @@ public struct Query<T>: Encodable, Equatable where T: ParseObject {
709721
Make the query restrict the fields of the returned `ParseObject`s to include only the provided keys.
710722
If this is called multiple times, then all of the keys specified in each of the calls will be included.
711723
- parameter keys: A variadic list of keys include in the result.
724+
- warning: Requires Parse Server > 4.5.0
712725
*/
713726
public func select(_ keys: String...) -> Query<T> {
714727
var mutableQuery = self
@@ -720,6 +733,7 @@ public struct Query<T>: Encodable, Equatable where T: ParseObject {
720733
Make the query restrict the fields of the returned `ParseObject`s to include only the provided keys.
721734
If this is called multiple times, then all of the keys specified in each of the calls will be included.
722735
- parameter keys: An array of keys to include in the result.
736+
- warning: Requires Parse Server > 4.5.0
723737
*/
724738
public func select(_ keys: [String]) -> Query<T> {
725739
var mutableQuery = self
@@ -1100,15 +1114,15 @@ extension Query {
11001114

11011115
func findCommand() -> API.NonParseBodyCommand<Query<ResultType>, [ResultType]> {
11021116
let query = self
1103-
return API.NonParseBodyCommand(method: .POST, path: endpoint, body: query) {
1117+
return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) {
11041118
try ParseCoding.jsonDecoder().decode(QueryResponse<T>.self, from: $0).results
11051119
}
11061120
}
11071121

11081122
func firstCommand() -> API.NonParseBodyCommand<Query<ResultType>, ResultType?> {
11091123
var query = self
11101124
query.limit = 1
1111-
return API.NonParseBodyCommand(method: .POST, path: endpoint, body: query) {
1125+
return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) {
11121126
try ParseCoding.jsonDecoder().decode(QueryResponse<T>.self, from: $0).results.first
11131127
}
11141128
}
@@ -1117,7 +1131,7 @@ extension Query {
11171131
var query = self
11181132
query.limit = 1
11191133
query.isCount = true
1120-
return API.NonParseBodyCommand(method: .POST, path: endpoint, body: query) {
1134+
return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) {
11211135
try ParseCoding.jsonDecoder().decode(QueryResponse<T>.self, from: $0).count ?? 0
11221136
}
11231137
}
@@ -1126,7 +1140,7 @@ extension Query {
11261140
var query = self
11271141
query.explain = explain
11281142
query.hint = hint
1129-
return API.NonParseBodyCommand(method: .POST, path: endpoint, body: query) {
1143+
return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) {
11301144
if let results = try JSONDecoder().decode(AnyResultsResponse.self, from: $0).results {
11311145
return results
11321146
}
@@ -1139,7 +1153,7 @@ extension Query {
11391153
query.limit = 1
11401154
query.explain = explain
11411155
query.hint = hint
1142-
return API.NonParseBodyCommand(method: .POST, path: endpoint, body: query) {
1156+
return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) {
11431157
if let results = try JSONDecoder().decode(AnyResultsResponse.self, from: $0).results {
11441158
return results
11451159
}
@@ -1153,7 +1167,7 @@ extension Query {
11531167
query.isCount = true
11541168
query.explain = explain
11551169
query.hint = hint
1156-
return API.NonParseBodyCommand(method: .POST, path: endpoint, body: query) {
1170+
return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) {
11571171
if let results = try JSONDecoder().decode(AnyResultsResponse.self, from: $0).results {
11581172
return results
11591173
}

Tests/ParseSwiftTests/ParseInstallationTests.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ class ParseInstallationTests: XCTestCase { // swiftlint:disable:this type_body_l
468468
var installation = Installation()
469469
let objectId = "yarr"
470470
installation.objectId = objectId
471-
let includeExpected = ["include": "yolo,test"]
471+
let includeExpected = ["include": "[\"yolo\", \"test\"]"]
472472
do {
473473
let command = try installation.fetchCommand(include: ["yolo", "test"])
474474
XCTAssertNotNil(command)
@@ -477,7 +477,8 @@ class ParseInstallationTests: XCTestCase { // swiftlint:disable:this type_body_l
477477
XCTAssertEqual(command.params, includeExpected)
478478
XCTAssertNil(command.body)
479479

480-
guard let urlExpected = URL(string: "http://localhost:1337/1/installations/yarr?include=yolo,test") else {
480+
// swiftlint:disable:next line_length
481+
guard let urlExpected = URL(string: "http://localhost:1337/1/installations/yarr?include=%5B%22yolo%22,%20%22test%22%5D") else {
481482
XCTFail("Should have unwrapped")
482483
return
483484
}

Tests/ParseSwiftTests/ParseObjectTests.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length
244244
let className = score.className
245245
let objectId = "yarr"
246246
score.objectId = objectId
247-
let includeExpected = ["include": "yolo,test"]
247+
let includeExpected = ["include": "[\"yolo\", \"test\"]"]
248248
do {
249249
let command = try score.fetchCommand(include: ["yolo", "test"])
250250
XCTAssertNotNil(command)
@@ -255,7 +255,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length
255255
XCTAssertNil(command.data)
256256

257257
// swiftlint:disable:next line_length
258-
guard let urlExpected = URL(string: "http://localhost:1337/1/classes/GameScore/yarr?include=yolo,test") else {
258+
guard let urlExpected = URL(string: "http://localhost:1337/1/classes/GameScore/yarr?include=%5B%22yolo%22,%20%22test%22%5D") else {
259259
XCTFail("Should have unwrapped")
260260
return
261261
}

Tests/ParseSwiftTests/ParseQueryTests.swift

+42-7
Original file line numberDiff line numberDiff line change
@@ -162,26 +162,61 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length
162162
XCTAssertEqual(query2.include, ["*"])
163163
}
164164

165-
func testExcludeKeys() {
165+
func testExcludeKeys() throws {
166166
let query = GameScore.query()
167167
XCTAssertNil(query.excludeKeys)
168-
let query2 = GameScore.query().exclude(["yolo"])
168+
var query2 = GameScore.query().exclude("yolo")
169169
XCTAssertEqual(query2.excludeKeys, ["yolo"])
170170
XCTAssertEqual(query2.excludeKeys, ["yolo"])
171+
let encoded = try ParseCoding.jsonEncoder().encode(query2)
172+
let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded)
173+
guard let decodedKeys = decodedDictionary["excludeKeys"],
174+
let decodedValues = decodedKeys.value as? [String] else {
175+
XCTFail("Should have casted")
176+
return
177+
}
178+
XCTAssertEqual(decodedValues, ["yolo"])
179+
180+
query2 = GameScore.query().exclude(["yolo", "wow"])
181+
XCTAssertEqual(query2.excludeKeys, ["yolo", "wow"])
182+
XCTAssertEqual(query2.excludeKeys, ["yolo", "wow"])
183+
let encoded2 = try ParseCoding.jsonEncoder().encode(query2)
184+
let decodedDictionary2 = try JSONDecoder().decode([String: AnyCodable].self, from: encoded2)
185+
guard let decodedKeys2 = decodedDictionary2["excludeKeys"],
186+
let decodedValues2 = decodedKeys2.value as? [String] else {
187+
XCTFail("Should have casted")
188+
return
189+
}
190+
XCTAssertEqual(decodedValues2, ["yolo", "wow"])
171191
}
172192

173-
func testSelectKeys() {
193+
func testSelectKeys() throws {
174194
let query = GameScore.query()
175195
XCTAssertNil(query.keys)
196+
176197
var query2 = GameScore.query().select("yolo")
177198
XCTAssertEqual(query2.keys?.count, 1)
178199
XCTAssertEqual(query2.keys?.first, "yolo")
179-
query2 = query2.select("yolo", "wow")
200+
let encoded = try ParseCoding.jsonEncoder().encode(query2)
201+
let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded)
202+
guard let decodedKeys = decodedDictionary["keys"],
203+
let decodedValues = decodedKeys.value as? [String] else {
204+
XCTFail("Should have casted")
205+
return
206+
}
207+
XCTAssertEqual(decodedValues, ["yolo"])
208+
209+
query2 = query2.select(["yolo", "wow"])
180210
XCTAssertEqual(query2.keys?.count, 2)
181211
XCTAssertEqual(query2.keys, ["yolo", "wow"])
182-
query2 = query2.select(["yolo"])
183-
XCTAssertEqual(query2.keys?.count, 1)
184-
XCTAssertEqual(query2.keys, ["yolo"])
212+
let encoded2 = try ParseCoding.jsonEncoder().encode(query2)
213+
let decodedDictionary2 = try JSONDecoder().decode([String: AnyCodable].self, from: encoded2)
214+
guard let decodedKeys2 = decodedDictionary2["keys"],
215+
let decodedValues2 = decodedKeys2.value as? [String] else {
216+
XCTFail("Should have casted")
217+
return
218+
}
219+
XCTAssertEqual(decodedValues2, ["yolo", "wow"])
185220
}
186221

187222
func testAddingConstraints() {

Tests/ParseSwiftTests/ParseUserTests.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length
109109
var user = User()
110110
let objectId = "yarr"
111111
user.objectId = objectId
112-
let includeExpected = ["include": "yolo,test"]
112+
let includeExpected = ["include": "[\"yolo\", \"test\"]"]
113113
do {
114114
let command = try user.fetchCommand(include: ["yolo", "test"])
115115
XCTAssertNotNil(command)
@@ -119,7 +119,8 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length
119119
XCTAssertNil(command.body)
120120
XCTAssertNil(command.data)
121121

122-
guard let urlExpected = URL(string: "http://localhost:1337/1/users/yarr?include=yolo,test") else {
122+
// swiftlint:disable:next line_length
123+
guard let urlExpected = URL(string: "http://localhost:1337/1/users/yarr?include=%5B%22yolo%22,%20%22test%22%5D") else {
123124
XCTFail("Should have unwrapped")
124125
return
125126
}

0 commit comments

Comments
 (0)