Hildebrandt Tobias
06/29/2025, 6:06 PMvalue: String
comes from an API call over HTTP.
So I want to always compare against a given String
in the where clause.
I can't filter after the fact in Kotlin, because it's part of a paging scheme.
I tried poking AI about a better way, but they were no help in this case.
fun <T : Any, V: T?> Column<V>.compareWithString(value: String): Op<Boolean> {
if(columnType is EntityIDColumnType<*>){
val checkType = (columnType as EntityIDColumnType<T>).idColumn.columnType
val typedValue = when (checkType) {
is IntegerColumnType -> value.toInt() as T
is LongColumnType -> value.toLong() as T
is UUIDColumnType -> UUID.fromString(value) as T
is StringColumnType -> value as T
is BooleanColumnType -> value.toBooleanStrictOrNull() as T
else -> error("Unsupported column type for comparison: $columnType")
}
return (this as Column<EntityID<T>>) eq typedValue
} else {
val typedValue = when (columnType) {
is IntegerColumnType -> value.toInt() as V
is LongColumnType -> value.toLong() as V
is UUIDColumnType -> UUID.fromString(value) as V
is StringColumnType -> value as V
is BooleanColumnType -> value.toBooleanStrictOrNull() as V
else -> error("Unsupported column type for comparison: $columnType")
}
return this eq typedValue
}
}
Hildebrandt Tobias
06/29/2025, 10:20 PMdata class ExposedMapper<T>(
val column: Column<T>,
val castFn: (String) -> Any?
)
val exposedMapper = mapOf(
TableField.DEVICE_DEVICE_ID to ExposedMapper(DeviceIdentificationTable.deviceId) { it.toIntOrNull() }, // assuming String
TableField.DEVICE_SERIAL_NUMBER to ExposedMapper(DeviceIdentificationTable.serialNumber) { it.toIntOrNull() },
TableField.DEVICE_CPU_ID to ExposedMapper(DeviceIdentificationTable.cpuId) { it },
TableField.DEVICE_HARDWARE_ID to ExposedMapper(DeviceHardwareIdTable.hardwareId) { it.toIntOrNull() },
TableField.DEVICE_VEHICLE_ID to ExposedMapper(DeviceIdentificationTable.vehicleId) { it },
TableField.DEVICE_COMPANY_ID to ExposedMapper(DeviceIdentificationTable.companyId) { it },
TableField.DEVICE_PRODUCT to ExposedMapper(DeviceIdentificationTable.product) { Product.findNameIncomplete(it) },
)
fun <T : Any, E: EntityID<T>, V: T?>ExposedMapper<V>.compareToString(value: String): Op<Boolean> {
val castResult = this.castFn(value) ?: return Op.FALSE
return if(this.column.columnType is EntityIDColumnType<*>){
(this.column as Column<E>) eq castResult as T
} else {
this.column eq castResult as V
}
}
But then I hit a snag that I couldn't really search for incomplete filter with like
So I looked again and found .castTo
and rewrote it to this:
data class ExposedMapper<T>(
val column: Column<T>,
val filterTransform: (String) -> String?
)
val exposedMapper = mapOf(
TableField.DEVICE_DEVICE_ID to ExposedMapper(DeviceIdentificationTable.deviceId) { it }, // assuming String
TableField.DEVICE_SERIAL_NUMBER to ExposedMapper(DeviceIdentificationTable.serialNumber) { it },
TableField.DEVICE_CPU_ID to ExposedMapper(DeviceIdentificationTable.cpuId) { it },
TableField.DEVICE_HARDWARE_ID to ExposedMapper(DeviceHardwareIdTable.hardwareId) { it },
TableField.DEVICE_VEHICLE_ID to ExposedMapper(DeviceIdentificationTable.vehicleId) { it },
TableField.DEVICE_COMPANY_ID to ExposedMapper(DeviceIdentificationTable.companyId) { it },
TableField.DEVICE_PRODUCT to ExposedMapper(DeviceIdentificationTable.product) { Product.findNameIncomplete(it)?.name },
)
// usage with surrounding code omitted:
window.filtering.forEach {
exposedMapper[it.filterField]?.let { (keySelector, transform) ->
val filter = transform(it.filterValue) ?: return@forEach
query = try {
query.andWhere { keySelector.castTo(TextColumnType()) like "%$filter%" }
} catch (e: SQLException) {
query
}
}
}
Which works quite well.