I have several models that have a date field with the same name like this:
final class ModelA: Model {
static let schema = "model_a"
@Timestamp(key: "modification_date", on: .update, format: .unix)
var modificationDate: Date?
}
final class ModelB: Model {
static let schema = "model_b"
@Timestamp(key: "modification_date", on: .update, format: .unix)
var modificationDate: Date?
}
final class ModelC: Model {
static let schema = "model_c"
@Timestamp(key: "modification_date", on: .update, format: .unix)
var modificationDate: Date?
}
At some point in my code, I would like to get recently changed models. So I could write something like this:
func getRecentlyChangedModelAs(on db: Database) async throws -> [ModelA] {
return try await ModelA.query(on: db)
.group(.and) { group in
group.filter(\.$modificationDate != nil)
group.filter(\.$modificationDate > Date.now.addingTimeInterval(TimeInterval(-3600)))
}
.all()
}
func getRecentlyChangedModelBs(on db: Database) async throws -> [ModelB] {
return try await ModelB.query(on: db)
.group(.and) { group in
group.filter(\.$modificationDate != nil)
group.filter(\.$modificationDate > Date.now.addingTimeInterval(TimeInterval(-3600)))
}
.all()
}
func getRecentlyChangedModelCs(on db: Database) async throws -> [ModelC] {
return try await ModelC.query(on: db)
.group(.and) { group in
group.filter(\.$modificationDate != nil)
group.filter(\.$modificationDate > Date.now.addingTimeInterval(TimeInterval(-3600)))
}
.all()
}
As you can see, there is a lot of code duplication. In order to avoid this, I tried to use a protocol and a generic query instead:
protocol ModifiableModel: Model, Content {
var modificationDate: Date? { set get }
}
final class ModelA: ModifiableModel {
static let schema = "model_a"
@Timestamp(key: "modification_date", on: .update, format: .unix)
var modificationDate: Date?
}
etc...
func getRecentlyChangedModel<T: ModifiableModel>(on db: Database) async throws -> [T] {
return try await T.query(on: db)
.group(.and) { group in
group.filter(\.$modificationDate != nil) // Errors: 'nil' is not compatible with expected argument type 'KeyPath<Right, RightField>', Generic parameter 'RightField' could not be inferred
group.filter(\.$modificationDate > Date.now.addingTimeInterval(TimeInterval(-3600))) // Errors: Cannot convert value of type 'Date' to expected argument type 'KeyPath<Right, RightField>', No exact matches in call to instance method 'filter'
}
.all()
Unfortunately, this causes errors in the filter of the query. I do not quite understand these errors but I found out that without the filter, everything compiles just fine. My best guess would be that the errors have something to do with the key paths I try to use within the filter.
Does anyone know what the problem is or how to fix it? Or is there another approach that I can use to avoid this kind of code duplication? Any help would be much appreciated!