Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.8.0...main)
* _Contributing to this repo? Add info about your change here to be included in the next release_

__Improvements__
- Append instead of replace when using query select, exclude, include, and fields ([#155](https://github.com/parse-community/Parse-Swift/pull/155)), thanks to [Corey Baker](https://github.com/cbaker6).

### 1.8.0
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.7.2...1.8.0)

Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/LiveQuery/Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ struct StandardMessage: LiveQueryable, Codable {
struct SubscribeQuery: Encodable {
let className: String
let `where`: QueryWhere
let fields: [String]?
let fields: Set<String>?
}

struct SubscribeMessage<T: ParseObject>: LiveQueryable, Encodable {
Expand Down
68 changes: 53 additions & 15 deletions Sources/ParseSwift/Types/Query.swift
Original file line number Diff line number Diff line change
Expand Up @@ -633,20 +633,20 @@ public struct Query<T>: Encodable, Equatable where T: ParseObject {
private let method: String = "GET"
internal var limit: Int = 100
internal var skip: Int = 0
internal var keys: [String]?
internal var include: [String]?
internal var keys: Set<String>?
internal var include: Set<String>?
internal var order: [Order]?
internal var isCount: Bool?
internal var explain: Bool?
internal var hint: AnyEncodable?
internal var `where` = QueryWhere()
internal var excludeKeys: [String]?
internal var excludeKeys: Set<String>?
internal var readPreference: String?
internal var includeReadPreference: String?
internal var subqueryReadPreference: String?
internal var distinct: String?
internal var pipeline: [[String: AnyEncodable]]?
internal var fields: [String]?
internal var fields: Set<String>?

/**
An enum that determines the order to sort the results based on a given key.
Expand Down Expand Up @@ -768,21 +768,31 @@ public struct Query<T>: Encodable, Equatable where T: ParseObject {

/**
Make the query include `ParseObject`s that have a reference stored at the provided keys.
If this is called multiple times, then all of the keys specified in each of the calls will be included.
- parameter keys: A variadic list of keys to load child `ParseObject`s for.
*/
public func include(_ keys: String...) -> Query<T> {
var mutableQuery = self
mutableQuery.include = keys
if mutableQuery.include != nil {
mutableQuery.include = mutableQuery.include?.union(keys)
} else {
mutableQuery.include = Set(keys)
}
return mutableQuery
}

/**
Make the query include `ParseObject`s that have a reference stored at the provided keys.
If this is called multiple times, then all of the keys specified in each of the calls will be included.
- parameter keys: An array of keys to load child `ParseObject`s for.
*/
public func include(_ keys: [String]) -> Query<T> {
var mutableQuery = self
mutableQuery.include = keys
if mutableQuery.include != nil {
mutableQuery.include = mutableQuery.include?.union(keys)
} else {
mutableQuery.include = Set(keys)
}
return mutableQuery
}

Expand All @@ -797,24 +807,34 @@ public struct Query<T>: Encodable, Equatable where T: ParseObject {
}

/**
Exclude specific keys for a `ParseObject`. Default is to nil.
Exclude specific keys for a `ParseObject`.
If this is called multiple times, then all of the keys specified in each of the calls will be excluded.
- parameter keys: A variadic list of keys include in the result.
- warning: Requires Parse Server > 4.5.0.
*/
public func exclude(_ keys: String...) -> Query<T> {
var mutableQuery = self
mutableQuery.excludeKeys = keys
if mutableQuery.excludeKeys != nil {
mutableQuery.excludeKeys = mutableQuery.excludeKeys?.union(keys)
} else {
mutableQuery.excludeKeys = Set(keys)
}
return mutableQuery
}

/**
Exclude specific keys for a `ParseObject`. Default is to nil.
- parameter keys: An array of keys to exclude.
Exclude specific keys for a `ParseObject`.
If this is called multiple times, then all of the keys specified in each of the calls will be excluded.
- parameter keys: An array of keys to exclude in the result.
- warning: Requires Parse Server > 4.5.0.
*/
public func exclude(_ keys: [String]) -> Query<T> {
var mutableQuery = self
mutableQuery.excludeKeys = keys
if mutableQuery.excludeKeys != nil {
mutableQuery.excludeKeys = mutableQuery.excludeKeys?.union(keys)
} else {
mutableQuery.excludeKeys = Set(keys)
}
return mutableQuery
}

Expand All @@ -826,7 +846,11 @@ public struct Query<T>: Encodable, Equatable where T: ParseObject {
*/
public func select(_ keys: String...) -> Query<T> {
var mutableQuery = self
mutableQuery.keys = keys
if mutableQuery.keys != nil {
mutableQuery.keys = mutableQuery.keys?.union(keys)
} else {
mutableQuery.keys = Set(keys)
}
return mutableQuery
}

Expand All @@ -838,7 +862,11 @@ public struct Query<T>: Encodable, Equatable where T: ParseObject {
*/
public func select(_ keys: [String]) -> Query<T> {
var mutableQuery = self
mutableQuery.keys = keys
if mutableQuery.keys != nil {
mutableQuery.keys = mutableQuery.keys?.union(keys)
} else {
mutableQuery.keys = Set(keys)
}
return mutableQuery
}

Expand All @@ -859,13 +887,18 @@ public struct Query<T>: Encodable, Equatable where T: ParseObject {
If you are only interested in the change of the name field, you can set `query.fields` to "name".
In this situation, when the change of a Player `ParseObject` fulfills the subscription, only the
name field will be sent to the clients instead of the full Player `ParseObject`.
If this is called multiple times, then all of the keys specified in each of the calls will be received.
- warning: This is only for `ParseLiveQuery`.
- parameter keys: A variadic list of fields to receive back instead of the whole `ParseObject`.
*/
@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
public func fields(_ keys: String...) -> Query<T> {
var mutableQuery = self
mutableQuery.fields = keys
if mutableQuery.fields != nil {
mutableQuery.fields = mutableQuery.fields?.union(keys)
} else {
mutableQuery.fields = Set(keys)
}
return mutableQuery
}

Expand All @@ -876,13 +909,18 @@ public struct Query<T>: Encodable, Equatable where T: ParseObject {
If you are only interested in the change of the name field, you can set `query.fields` to "name".
In this situation, when the change of a Player `ParseObject` fulfills the subscription, only the
name field will be sent to the clients instead of the full Player `ParseObject`.
If this is called multiple times, then all of the keys specified in each of the calls will be received.
- warning: This is only for `ParseLiveQuery`.
- parameter keys: An array of fields to receive back instead of the whole `ParseObject`.
*/
@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
public func fields(_ keys: [String]) -> Query<T> {
var mutableQuery = self
mutableQuery.fields = keys
if mutableQuery.fields != nil {
mutableQuery.fields = mutableQuery.fields?.union(keys)
} else {
mutableQuery.fields = Set(keys)
}
return mutableQuery
}

Expand Down
28 changes: 27 additions & 1 deletion Tests/ParseSwiftTests/ParseLiveQueryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ class ParseLiveQueryTests: XCTestCase {
// swiftlint:disable:next line_length
let expected = "{\"op\":\"subscribe\",\"requestId\":1,\"query\":{\"className\":\"GameScore\",\"where\":{\"score\":{\"$gt\":9}},\"fields\":[\"score\"]}}"
let query = GameScore.query("score" > 9)
.fields("score")
.fields(["score"])
let message = SubscribeMessage(operation: .subscribe,
requestId: RequestId(value: 1),
query: query,
Expand All @@ -204,6 +204,32 @@ class ParseLiveQueryTests: XCTestCase {
XCTAssertEqual(decoded, expected)
}

func testFieldKeys() throws {
let query = GameScore.query()
XCTAssertNil(query.keys)

var query2 = GameScore.query().fields(["yolo"])
XCTAssertEqual(query2.fields?.count, 1)
XCTAssertEqual(query2.fields?.first, "yolo")

query2 = query2.fields(["hello", "wow"])
XCTAssertEqual(query2.fields?.count, 3)
XCTAssertEqual(query2.fields, ["yolo", "hello", "wow"])
}

func testFieldKeysVariadic() throws {
let query = GameScore.query()
XCTAssertNil(query.keys)

var query2 = GameScore.query().fields("yolo")
XCTAssertEqual(query2.fields?.count, 1)
XCTAssertEqual(query2.fields?.first, "yolo")

query2 = query2.fields("hello", "wow")
XCTAssertEqual(query2.fields?.count, 3)
XCTAssertEqual(query2.fields, ["yolo", "hello", "wow"])
}

func testRedirectResponseDecoding() throws {
guard let url = URL(string: "http://parse.org") else {
XCTFail("Should have url")
Expand Down
91 changes: 76 additions & 15 deletions Tests/ParseSwiftTests/ParseQueryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,23 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length
func testIncludeKeys() {
let query = GameScore.query()
XCTAssertNil(query.include)
var query2 = GameScore.query().include("yolo")
var query2 = GameScore.query().include(["yolo"])
XCTAssertEqual(query2.include?.count, 1)
XCTAssertEqual(query2.include?.first, "yolo")
query2 = query2.include("yolo", "wow")
XCTAssertEqual(query2.include?.count, 2)
XCTAssertEqual(query2.include, ["yolo", "wow"])
query2 = query2.include(["yolo"])
query2 = query2.include(["hello", "wow"])
XCTAssertEqual(query2.include?.count, 3)
XCTAssertEqual(query2.include, Set(["yolo", "hello", "wow"]))
}

func testIncludeKeysVariadic() {
let query = GameScore.query()
XCTAssertNil(query.include)
var query2 = GameScore.query().include("yolo")
XCTAssertEqual(query2.include?.count, 1)
XCTAssertEqual(query2.include, ["yolo"])
XCTAssertEqual(query2.include?.first, "yolo")
query2 = query2.include("hello", "wow")
XCTAssertEqual(query2.include?.count, 3)
XCTAssertEqual(query2.include, Set(["yolo", "hello", "wow"]))
}

func testIncludeAllKeys() {
Expand All @@ -179,8 +187,33 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length
func testExcludeKeys() throws {
let query = GameScore.query()
XCTAssertNil(query.excludeKeys)
var query2 = GameScore.query().exclude("yolo")
var query2 = GameScore.query().exclude(["yolo"])
XCTAssertEqual(query2.excludeKeys, ["yolo"])
let encoded = try ParseCoding.jsonEncoder().encode(query2)
let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded)
guard let decodedKeys = decodedDictionary["excludeKeys"],
let decodedValues = decodedKeys.value as? [String] else {
XCTFail("Should have casted")
return
}
XCTAssertEqual(decodedValues, ["yolo"])

query2 = query2.exclude(["hello", "wow"])
XCTAssertEqual(query2.excludeKeys, ["yolo", "hello", "wow"])
let encoded2 = try ParseCoding.jsonEncoder().encode(query2)
let decodedDictionary2 = try JSONDecoder().decode([String: AnyCodable].self, from: encoded2)
guard let decodedKeys2 = decodedDictionary2["excludeKeys"],
let decodedValues2 = decodedKeys2.value as? [String] else {
XCTFail("Should have casted")
return
}
XCTAssertEqual(Set(decodedValues2), Set(["yolo", "hello", "wow"]))
}

func testExcludeKeysVariadic() throws {
let query = GameScore.query()
XCTAssertNil(query.excludeKeys)
var query2 = GameScore.query().exclude("yolo")
XCTAssertEqual(query2.excludeKeys, ["yolo"])
let encoded = try ParseCoding.jsonEncoder().encode(query2)
let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded)
Expand All @@ -191,23 +224,51 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length
}
XCTAssertEqual(decodedValues, ["yolo"])

query2 = GameScore.query().exclude(["yolo", "wow"])
XCTAssertEqual(query2.excludeKeys, ["yolo", "wow"])
XCTAssertEqual(query2.excludeKeys, ["yolo", "wow"])
query2 = query2.exclude("hello", "wow")
XCTAssertEqual(query2.excludeKeys, ["yolo", "hello", "wow"])
let encoded2 = try ParseCoding.jsonEncoder().encode(query2)
let decodedDictionary2 = try JSONDecoder().decode([String: AnyCodable].self, from: encoded2)
guard let decodedKeys2 = decodedDictionary2["excludeKeys"],
let decodedValues2 = decodedKeys2.value as? [String] else {
XCTFail("Should have casted")
return
}
XCTAssertEqual(decodedValues2, ["yolo", "wow"])
XCTAssertEqual(Set(decodedValues2), Set(["yolo", "hello", "wow"]))
}

func testSelectKeys() throws {
let query = GameScore.query()
XCTAssertNil(query.keys)

var query2 = GameScore.query().select(["yolo"])
XCTAssertEqual(query2.keys?.count, 1)
XCTAssertEqual(query2.keys?.first, "yolo")
let encoded = try ParseCoding.jsonEncoder().encode(query2)
let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded)
guard let decodedKeys = decodedDictionary["keys"],
let decodedValues = decodedKeys.value as? [String] else {
XCTFail("Should have casted")
return
}
XCTAssertEqual(decodedValues, ["yolo"])

query2 = query2.select(["hello", "wow"])
XCTAssertEqual(query2.keys?.count, 3)
XCTAssertEqual(query2.keys, ["yolo", "hello", "wow"])
let encoded2 = try ParseCoding.jsonEncoder().encode(query2)
let decodedDictionary2 = try JSONDecoder().decode([String: AnyCodable].self, from: encoded2)
guard let decodedKeys2 = decodedDictionary2["keys"],
let decodedValues2 = decodedKeys2.value as? [String] else {
XCTFail("Should have casted")
return
}
XCTAssertEqual(Set(decodedValues2), Set(["yolo", "hello", "wow"]))
}

func testSelectKeysVariadic() throws {
let query = GameScore.query()
XCTAssertNil(query.keys)

var query2 = GameScore.query().select("yolo")
XCTAssertEqual(query2.keys?.count, 1)
XCTAssertEqual(query2.keys?.first, "yolo")
Expand All @@ -220,17 +281,17 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length
}
XCTAssertEqual(decodedValues, ["yolo"])

query2 = query2.select(["yolo", "wow"])
XCTAssertEqual(query2.keys?.count, 2)
XCTAssertEqual(query2.keys, ["yolo", "wow"])
query2 = query2.select("hello", "wow")
XCTAssertEqual(query2.keys?.count, 3)
XCTAssertEqual(query2.keys, ["yolo", "hello", "wow"])
let encoded2 = try ParseCoding.jsonEncoder().encode(query2)
let decodedDictionary2 = try JSONDecoder().decode([String: AnyCodable].self, from: encoded2)
guard let decodedKeys2 = decodedDictionary2["keys"],
let decodedValues2 = decodedKeys2.value as? [String] else {
XCTFail("Should have casted")
return
}
XCTAssertEqual(decodedValues2, ["yolo", "wow"])
XCTAssertEqual(Set(decodedValues2), Set(["yolo", "hello", "wow"]))
}

func testAddingConstraints() {
Expand Down