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

Add more options in findAll #38

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
92 changes: 66 additions & 26 deletions Sources/SwiftKueryORM/Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ public protocol Model: Codable {

/// Call to find all the models in the database that accepts a completion
/// handler. The callback is passed an array of models or an error
static func findAll(using db: Database?, _ onCompletion: @escaping ([Self]?, RequestError?) -> Void)
static func findAll(using db: Database?, order: Order..., offset: Int?, limit: Int?, _ onCompletion: @escaping ([Self]?, RequestError?) -> Void)

/// Call to find all the models in the database that accepts a completion
/// handler. The callback is passed an array of tuples (id, model) or an error
static func findAll<I: Identifier>(using db: Database?, _ onCompletion: @escaping ([(I, Self)]?, RequestError?) -> Void)
static func findAll<I: Identifier>(using db: Database?, order: Order..., offset: Int?, limit: Int?, _ onCompletion: @escaping ([(I, Self)]?, RequestError?) -> Void)

/// Call to find all the models in the database that accepts a completion
/// handler. The callback is passed a dictionary [id: model] or an error
static func findAll<I: Identifier>(using db: Database?, _ onCompletion: @escaping ([I: Self]?, RequestError?) -> Void)
static func findAll<I: Identifier>(using db: Database?, order: Order..., offset: Int?, limit: Int?, _ onCompletion: @escaping ([I: Self]?, RequestError?) -> Void)

/// Call to find all the models in the database matching the QueryParams that accepts a completion
/// handler. The callback is passed an array of models or an error
Expand Down Expand Up @@ -895,25 +895,32 @@ public extension Model {
}

/**
This function constructs an array of OrderBy from the QueryParameters values
This function constructs an array of OrderBy from the QueryParameters or the Order values
*/
private static func getOrderBy(values: [String: Any], table: Table) -> [OrderBy] {
private static func getOrderBy(values: [String: Any]? = nil, order: [Order]? = nil, table: Table) -> [OrderBy] {
var orderByArray: [OrderBy] = []
for (_, value) in values {
if let orderValue = value as? Ordering {
let columnDictionary = table.columns.reduce(into: [String: Column]()) { dict, value in
dict[value.name] = value
}
let orders = orderValue.getValues()
for order in orders where columnDictionary[order.value] != nil {
let column = columnDictionary[order.value]!
if case .asc(_) = order {
orderByArray.append(.ASC(column))
} else {
orderByArray.append(.DESC(column))
}
}
}
var orders: [Order] = []
let columnDictionary = table.columns.reduce(into: [String: Column]()) { dict, value in
dict[value.name] = value
}

if let order = order {
orders = order
} else if let values = values {
for (_, value) in values {
if let orderValue = value as? Ordering {
orders = orderValue.getValues()
}
}
}

for order in orders where columnDictionary[order.value] != nil {
let column = columnDictionary[order.value]!
if case .asc(_) = order {
orderByArray.append(.ASC(column))
} else {
orderByArray.append(.DESC(column))
}
}

return orderByArray
Expand Down Expand Up @@ -967,46 +974,79 @@ public extension Model {


///
static func findAll(using db: Database? = nil, _ onCompletion: @escaping ([Self]?, RequestError?) -> Void) {
static func findAll(using db: Database? = nil, order: Order..., offset: Int? = nil, limit: Int? = nil, _ onCompletion: @escaping ([Self]?, RequestError?) -> Void) {
var table: Table
do {
table = try Self.getTable()
} catch let error {
onCompletion(nil, Self.convertError(error))
return
}
let orderBy: [OrderBy] = Self.getOrderBy(order: order, table: table)

var query = Select(from: table)
if orderBy.count > 0 {
query = query.order(by: orderBy)
}
if let offset = offset {
query = query.offset(offset)
}
if let limit = limit {
query = query.limit(to: limit)
}

let query = Select(from: table)
Self.executeQuery(query: query, using: db, onCompletion)
}

/// Find all the models
/// - Parameter using: Optional Database to use
/// - Returns: An array of tuples (id, model)
static func findAll<I: Identifier>(using db: Database? = nil, _ onCompletion: @escaping ([(I, Self)]?, RequestError?) -> Void) {
static func findAll<I: Identifier>(using db: Database? = nil, order: Order..., offset: Int? = nil, limit: Int? = nil, _ onCompletion: @escaping ([(I, Self)]?, RequestError?) -> Void) {
var table: Table
do {
table = try Self.getTable()
} catch let error {
onCompletion(nil, Self.convertError(error))
return
}
let orderBy: [OrderBy] = Self.getOrderBy(order: order, table: table)

var query = Select(from: table)
if orderBy.count > 0 {
query = query.order(by: orderBy)
}
if let offset = offset {
query = query.offset(offset)
}
if let limit = limit {
query = query.limit(to: limit)
}

let query = Select(from: table)
Self.executeQuery(query: query, using: db, onCompletion)
}

/// :nodoc:
static func findAll<I: Identifier>(using db: Database? = nil, _ onCompletion: @escaping ([I: Self]?, RequestError?) -> Void) {
static func findAll<I: Identifier>(using db: Database? = nil, order: Order..., offset: Int? = nil, limit: Int? = nil, _ onCompletion: @escaping ([I: Self]?, RequestError?) -> Void) {
var table: Table
do {
table = try Self.getTable()
} catch let error {
onCompletion(nil, Self.convertError(error))
return
}
let orderBy: [OrderBy] = Self.getOrderBy(order: order, table: table)

var query = Select(from: table)
if orderBy.count > 0 {
query = query.order(by: orderBy)
}
if let offset = offset {
query = query.offset(offset)
}
if let limit = limit {
query = query.limit(to: limit)
}

let query = Select(from: table)
Self.executeQuery(query: query, using: db) { (tuples: [(I, Self)]?, error: RequestError?) in
if let error = error {
onCompletion(nil, error)
Expand Down
17 changes: 15 additions & 2 deletions Tests/SwiftKueryORMTests/CommonUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class TestConnection: Connection {
case returnEmpty
case returnOneRow
case returnThreeRows
case returnThreeRowsSortedAscending
case returnThreeRowsSortedDescending
case returnError
case returnValue
}
Expand Down Expand Up @@ -100,6 +102,10 @@ class TestConnection: Connection {
onCompletion(.resultSet(ResultSet(TestResultFetcher(numberOfRows: 1))))
case .returnThreeRows:
onCompletion(.resultSet(ResultSet(TestResultFetcher(numberOfRows: 3))))
case .returnThreeRowsSortedAscending:
onCompletion(.resultSet(ResultSet(TestResultFetcher(numberOfRows: 3, sortedByAge: "ascending"))))
case .returnThreeRowsSortedDescending:
onCompletion(.resultSet(ResultSet(TestResultFetcher(numberOfRows: 3, sortedByAge: "descending"))))
case .returnError:
onCompletion(.error(QueryError.noResult("Error in query execution.")))
case .returnValue:
Expand Down Expand Up @@ -136,12 +142,19 @@ class TestConnection: Connection {

class TestResultFetcher: ResultFetcher {
let numberOfRows: Int
let rows = [[1, "Joe", Int32(38)], [2, "Adam", Int32(28)], [3, "Chris", Int32(36)]]
var rows = [[1, "Joe", Int32(38)], [2, "Adam", Int32(28)], [3, "Chris", Int32(36)]]
let titles = ["id", "name", "age"]
var fetched = 0

init(numberOfRows: Int) {
init(numberOfRows: Int, sortedByAge: String? = nil) {
self.numberOfRows = numberOfRows
if let sortedByAge = sortedByAge {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we sort the array programatically? By using array.sort? In the case of having to change the rows we would also have to update these two lines.

if sortedByAge == "descending" {
rows.sort {($0[2] as! Int32) > ($1[2] as! Int32)}
} else if sortedByAge == "ascending" {
rows.sort {($0[2] as! Int32) < ($1[2] as! Int32)}
}
}
}

func fetchNext() -> [Any?]? {
Expand Down
117 changes: 117 additions & 0 deletions Tests/SwiftKueryORMTests/TestFind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class TestFind: XCTestCase {
("testFind", testFind),
("testFindAll", testFindAll),
("testFindAllMatching", testFindAllMatching),
("testFindAllLimit", testFindAllLimit),
("testFindAllLimitAndOffset", testFindAllLimitAndOffset),
("testFindAllOrderByDescending", testFindAllOrderByDescending),
("testFindAllOrderByAscending", testFindAllOrderByAscending),
]
}

Expand Down Expand Up @@ -113,4 +117,117 @@ class TestFind: XCTestCase {
}
})
}


/**
Testing that the correct SQL Query is created to retrieve a specific model.
Testing that the model can be retrieved
*/
func testFindAllLimit() {
let connection: TestConnection = createConnection(.returnOneRow)
Database.default = Database(single: connection)
performTest(asyncTasks: { expectation in
Person.findAll(limit: 1) { array, error in
XCTAssertNil(error, "Find Failed: \(String(describing: error))")
XCTAssertNotNil(connection.query, "Find Failed: Query is nil")
if let query = connection.query {
let expectedQuery = "SELECT * FROM \"People\" LIMIT 1"
let resultQuery = connection.descriptionOf(query: query)
XCTAssertEqual(resultQuery, expectedQuery, "Find Failed: Invalid query")
}
XCTAssertNotNil(array, "Find Failed: No array of models returned")
if let array = array {
XCTAssertEqual(array.count, 1, "Find Failed: \(String(describing: array.count)) is not equal to 1")
}
expectation.fulfill()
}
})
}

/**
Testing that the correct SQL Query is created to retrieve a specific model.
Testing that the model can be retrieved
*/
func testFindAllLimitAndOffset() {
let connection: TestConnection = createConnection(.returnOneRow)
Database.default = Database(single: connection)
performTest(asyncTasks: { expectation in
Person.findAll(offset: 2, limit: 1) { array, error in
XCTAssertNil(error, "Find Failed: \(String(describing: error))")
XCTAssertNotNil(connection.query, "Find Failed: Query is nil")
if let query = connection.query {
let expectedQuery = "SELECT * FROM \"People\" LIMIT 1 OFFSET 2"
let resultQuery = connection.descriptionOf(query: query)
XCTAssertEqual(resultQuery, expectedQuery, "Find Failed: Invalid query")
}
XCTAssertNotNil(array, "Find Failed: No array of models returned")
if let array = array {
XCTAssertEqual(array.count, 1, "Find Failed: \(String(describing: array.count)) is not equal to 1")
}
expectation.fulfill()
}
})
}

/**
Testing that the correct SQL Query is created to retrieve a specific model.
Testing that correct amount of models are retrieved
Testing that models are sorted by age in descending order
*/
func testFindAllOrderByDescending() {
let connection: TestConnection = createConnection(.returnThreeRowsSortedDescending)
Database.default = Database(single: connection)
performTest(asyncTasks: { expectation in
Person.findAll(order: Order.desc("age")) { array, error in
XCTAssertNil(error, "Find Failed: \(String(describing: error))")
XCTAssertNotNil(connection.query, "Find Failed: Query is nil")
if let query = connection.query {
let expectedQuery = "SELECT * FROM \"People\" ORDER BY \"People\".\"age\" DESC"
let resultQuery = connection.descriptionOf(query: query)
XCTAssertEqual(resultQuery, expectedQuery, "Find Failed: Invalid query")
}
XCTAssertNotNil(array, "Find Failed: No array of models returned")
if let array = array {
for (index, person) in array.enumerated() {
if index + 1 < array.count {
XCTAssertGreaterThanOrEqual(person.age, array[index + 1].age, "Find Failed: Age of person: \(String(describing: person.age)) is not greater than or equal to age of next person: \(String(describing: array[index + 1].age))")
}
}
XCTAssertEqual(array.count, 3, "Find Failed: \(String(describing: array.count)) is not equal to 3")
}
expectation.fulfill()
}
})
}

/**
Testing that the correct SQL Query is created to retrieve a specific model.
Testing that correct amount of models are retrieved
Testing that models are sorted by age in ascending order
*/
func testFindAllOrderByAscending() {
let connection: TestConnection = createConnection(.returnThreeRowsSortedAscending)
Database.default = Database(single: connection)
performTest(asyncTasks: { expectation in
Person.findAll(order: Order.asc("age")) { array, error in
XCTAssertNil(error, "Find Failed: \(String(describing: error))")
XCTAssertNotNil(connection.query, "Find Failed: Query is nil")
if let query = connection.query {
let expectedQuery = "SELECT * FROM \"People\" ORDER BY \"People\".\"age\" ASC"
let resultQuery = connection.descriptionOf(query: query)
XCTAssertEqual(resultQuery, expectedQuery, "Find Failed: Invalid query")
}
XCTAssertNotNil(array, "Find Failed: No array of models returned")
if let array = array {
for (index, person) in array.enumerated() {
if index + 1 < array.count {
XCTAssertLessThanOrEqual(person.age, array[index + 1].age, "Find Failed: Age of person: \(String(describing: person.age)) is not less than or equal to age of next person: \(String(describing: array[index + 1].age))")
}
}
XCTAssertEqual(array.count, 3, "Find Failed: \(String(describing: array.count)) is not equal to 3")
}
expectation.fulfill()
}
})
}
}