This project now contains: - A generic JVM document library (with Kotlin extensions on the JDBC `Connection` object) - A Groovy library which adds extension methods to the `Connection` object - A Scala library, which uses native Scala collections and adds Scala-style extension methods to the `Connection` object - A Kotlin library which uses `kotlinx.serialization` (no reflection, reified generic types) along with `Connection` extensions Reviewed-on: #1
83 lines
2.9 KiB
Kotlin
83 lines
2.9 KiB
Kotlin
@file:JvmName("QueryUtils")
|
|
package solutions.bitbadger.documents.query
|
|
|
|
import solutions.bitbadger.documents.*
|
|
import kotlin.jvm.Throws
|
|
|
|
// ~~~ TOP-LEVEL FUNCTIONS FOR THE QUERY PACKAGE ~~~
|
|
|
|
/**
|
|
* Combine a query (`SELECT`, `UPDATE`, etc.) and a `WHERE` clause
|
|
*
|
|
* @param statement The first part of the statement
|
|
* @param where The `WHERE` clause for the statement
|
|
* @return The two parts of the query combined with `WHERE`
|
|
*/
|
|
fun statementWhere(statement: String, where: String) =
|
|
"$statement WHERE $where"
|
|
|
|
/**
|
|
* Create a query by a document's ID
|
|
*
|
|
* @param statement The SQL statement to be run against a document by its ID
|
|
* @param docId The ID of the document targeted
|
|
* @returns A query addressing a document by its ID
|
|
* @throws DocumentException If the dialect has not been set
|
|
*/
|
|
@Throws(DocumentException::class)
|
|
fun <TKey> byId(statement: String, docId: TKey) =
|
|
statementWhere(statement, Where.byId(docId = docId))
|
|
|
|
/**
|
|
* Create a query on JSON fields
|
|
*
|
|
* @param statement The SQL statement to be run against matching fields
|
|
* @param fields The field conditions to be matched
|
|
* @param howMatched Whether to match any or all of the field conditions (optional; default ALL)
|
|
* @return A query addressing documents by field matching conditions
|
|
*/
|
|
@Throws(DocumentException::class)
|
|
@JvmOverloads
|
|
fun byFields(statement: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
|
|
statementWhere(statement, Where.byFields(fields, howMatched))
|
|
|
|
/**
|
|
* Create an `ORDER BY` clause for the given fields
|
|
*
|
|
* @param fields One or more fields by which to order
|
|
* @param dialect The SQL dialect for the generated clause
|
|
* @return An `ORDER BY` clause for the given fields
|
|
*/
|
|
@Throws(DocumentException::class)
|
|
@JvmOverloads
|
|
fun orderBy(fields: Collection<Field<*>>, dialect: Dialect? = null): String {
|
|
val mode = dialect ?: Configuration.dialect("generate ORDER BY clause")
|
|
if (fields.isEmpty()) return ""
|
|
val orderFields = fields.joinToString(", ") {
|
|
val (field, direction) =
|
|
if (it.name.indexOf(' ') > -1) {
|
|
val parts = it.name.split(' ')
|
|
Pair(Field.named(parts[0]), " " + parts.drop(1).joinToString(" "))
|
|
} else {
|
|
Pair<Field<*>, String?>(it, null)
|
|
}
|
|
val path = when {
|
|
field.name.startsWith("n:") -> Field.named(field.name.substring(2)).let { fld ->
|
|
when (mode) {
|
|
Dialect.POSTGRESQL -> "(${fld.path(mode)})::numeric"
|
|
Dialect.SQLITE -> fld.path(mode)
|
|
}
|
|
}
|
|
field.name.startsWith("i:") -> Field.named(field.name.substring(2)).path(mode).let { p ->
|
|
when (mode) {
|
|
Dialect.POSTGRESQL -> "LOWER($p)"
|
|
Dialect.SQLITE -> "$p COLLATE NOCASE"
|
|
}
|
|
}
|
|
else -> field.path(mode)
|
|
}
|
|
"$path${direction ?: ""}"
|
|
}
|
|
return " ORDER BY $orderFields"
|
|
}
|