Skip to content
9 changes: 8 additions & 1 deletion Sources/FluentKit/Database/Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ extension Database {
public var history: QueryHistory? {
self.context.history
}

public var pageSizeLimit: Int? {
self.context.pageSizeLimit
}
}

public protocol DatabaseDriver {
Expand All @@ -62,17 +66,20 @@ public struct DatabaseContext {
public let logger: Logger
public let eventLoop: EventLoop
public let history: QueryHistory?
public let pageSizeLimit: Int?

public init(
configuration: DatabaseConfiguration,
logger: Logger,
eventLoop: EventLoop,
history: QueryHistory? = nil
history: QueryHistory? = nil,
pageSizeLimit: Int? = nil
) {
self.configuration = configuration
self.logger = logger
self.eventLoop = eventLoop
self.history = history
self.pageSizeLimit = pageSizeLimit
}
}

Expand Down
6 changes: 4 additions & 2 deletions Sources/FluentKit/Database/Databases.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ public final class Databases {
_ id: DatabaseID? = nil,
logger: Logger,
on eventLoop: EventLoop,
history: QueryHistory? = nil
history: QueryHistory? = nil,
pageSizeLimit: Int? = nil
) -> Database? {
self.lock.lock()
defer { self.lock.unlock() }
Expand All @@ -110,7 +111,8 @@ public final class Databases {
configuration: configuration,
logger: logger,
eventLoop: eventLoop,
history: history
history: history,
pageSizeLimit: pageSizeLimit
)
let driver: DatabaseDriver
if let existing = self.drivers[id] {
Expand Down
14 changes: 11 additions & 3 deletions Sources/FluentKit/Query/Builder/QueryBuilder+Paginate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,28 @@ extension QueryBuilder {
/// Returns a single `Page` out of the complete result set according to the supplied `PageRequest`.
///
/// This method will first `count()` the result set, then request a subset of the results using `range()` and `all()`.
///
/// - Parameters:
/// - request: Describes which page should be fetched.
/// - Returns: A single `Page` of the result set containing the requested items and page metadata.
public func paginate(
_ request: PageRequest
) -> EventLoopFuture<Page<Model>> {
let trimmedRequest: PageRequest = {
guard let pageSizeLimit = database.context.pageSizeLimit else { return request }
return .init(
page: request.page,
per: Swift.min(request.per, pageSizeLimit)
)
}()
let count = self.count()
let items = self.copy().range(request.start..<request.end).all()
let items = self.copy().range(trimmedRequest.start..<trimmedRequest.end).all()
return items.and(count).map { (models, total) in
Page(
items: models,
metadata: .init(
page: request.page,
per: request.per,
page: trimmedRequest.page,
per: trimmedRequest.per,
total: total
)
)
Expand Down
49 changes: 49 additions & 0 deletions Tests/FluentKitTests/QueryBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,55 @@ final class QueryBuilderTests: XCTestCase {
XCTAssertEqual(db.history?.queries.first?.schema, Planet.schema)
}

func testPerPageLimit() throws {
let starId = UUID()
let rows = [
TestOutput(["id": UUID(), "name": "a", "star_id": starId]),
TestOutput(["id": UUID(), "name": "b", "star_id": starId]),
TestOutput(["id": UUID(), "name": "c", "star_id": starId]),
TestOutput(["id": UUID(), "name": "d", "star_id": starId]),
TestOutput(["id": UUID(), "name": "e", "star_id": starId]),
]

let test = CallbackTestDatabase { query in
XCTAssertEqual(query.schema, "planets")
let result: [TestOutput]
if
let limit = query.limits.first,
case let DatabaseQuery.Limit.count(limitValue) = limit,
let offset = query.offsets.first,
case let DatabaseQuery.Offset.count(offsetValue) = offset
{
result = [TestOutput](rows[min(offsetValue, rows.count - 1)..<min(offsetValue + limitValue, rows.count)])
} else {
result = rows
}
switch query.action {
case .aggregate(_):
return [TestOutput([.aggregate: rows.count])]
default:
return result
}
}

let pageSizeLimit = 2

let db = test.database(
context: .init(
configuration: test.configuration,
logger: test.db.logger,
eventLoop: test.db.eventLoop,
history: .init(),
pageSizeLimit: pageSizeLimit
)
)

let pageRequest = PageRequest(page: 2, per: 3)
let retrievedPlanets = try Planet.query(on: db).paginate(pageRequest).wait()
XCTAssertEqual(retrievedPlanets.items.count, pageSizeLimit, "Page size limit should be respected.")
XCTAssertEqual(retrievedPlanets.items.first?.name, "c", "Page size limit should determine offset")
}

// https://github.com/vapor/fluent-kit/issues/310
func testJoinOverloads() throws {
var query: DatabaseQuery?
Expand Down