Package rearrangement; build succeeds
This commit is contained in:
@@ -30,14 +30,6 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>solutions.bitbadger.documents</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>4.0.0-alpha1-SNAPSHOT</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${project.basedir}/../common/target/common-4.0.0-alpha1-SNAPSHOT.jar</systemPath>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
|
||||
85
src/jvm/src/main/kotlin/AutoId.kt
Normal file
85
src/jvm/src/main/kotlin/AutoId.kt
Normal file
@@ -0,0 +1,85 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
import kotlin.jvm.Throws
|
||||
import kotlin.reflect.full.*
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
|
||||
/**
|
||||
* Strategies for automatic document IDs
|
||||
*/
|
||||
enum class AutoId {
|
||||
/** No automatic IDs will be generated */
|
||||
DISABLED,
|
||||
|
||||
/** Generate a `MAX`-plus-1 numeric ID */
|
||||
NUMBER,
|
||||
|
||||
/** Generate a `UUID` string ID */
|
||||
UUID,
|
||||
|
||||
/** Generate a random hex character string ID */
|
||||
RANDOM_STRING;
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Generate a `UUID` string
|
||||
*
|
||||
* @return A `UUID` string
|
||||
*/
|
||||
@JvmStatic
|
||||
fun generateUUID(): String =
|
||||
java.util.UUID.randomUUID().toString().replace("-", "")
|
||||
|
||||
/**
|
||||
* Generate a string of random hex characters
|
||||
*
|
||||
* @param length The length of the string (optional; defaults to configured length)
|
||||
* @return A string of random hex characters of the requested length
|
||||
*/
|
||||
@JvmStatic
|
||||
fun generateRandomString(length: Int? = null): String =
|
||||
(length ?: Configuration.idStringLength).let { len ->
|
||||
kotlin.random.Random.nextBytes((len + 2) / 2)
|
||||
.joinToString("") { String.format("%02x", it) }
|
||||
.substring(0, len)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a document needs an automatic ID applied
|
||||
*
|
||||
* @param strategy The auto ID strategy for which the document is evaluated
|
||||
* @param document The document whose need of an automatic ID should be determined
|
||||
* @param idProp The name of the document property containing the ID
|
||||
* @return `true` if the document needs an automatic ID, `false` if not
|
||||
* @throws DocumentException If bad input prevents the determination
|
||||
*/
|
||||
@Throws(DocumentException::class)
|
||||
@JvmStatic
|
||||
fun <T> needsAutoId(strategy: AutoId, document: T, idProp: String): Boolean {
|
||||
if (document == null) throw DocumentException("document cannot be null")
|
||||
|
||||
if (strategy == DISABLED) return false
|
||||
|
||||
val id = document!!::class.memberProperties.find { it.name == idProp }?.apply { isAccessible = true }
|
||||
if (id == null) throw DocumentException("$idProp not found in document")
|
||||
|
||||
if (strategy == NUMBER) {
|
||||
return when (id.returnType) {
|
||||
Byte::class.createType() -> id.call(document) == 0.toByte()
|
||||
Short::class.createType() -> id.call(document) == 0.toShort()
|
||||
Int::class.createType() -> id.call(document) == 0
|
||||
Long::class.createType() -> id.call(document) == 0.toLong()
|
||||
else -> throw DocumentException("$idProp was not a number; cannot auto-generate number ID")
|
||||
}
|
||||
}
|
||||
|
||||
val typ = id.returnType.toString()
|
||||
if (typ.endsWith("String") || typ.endsWith("String!")) {
|
||||
return id.call(document) == ""
|
||||
}
|
||||
|
||||
throw DocumentException("$idProp was not a string ($typ); cannot auto-generate UUID or random string")
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/jvm/src/main/kotlin/Comparison.kt
Normal file
68
src/jvm/src/main/kotlin/Comparison.kt
Normal file
@@ -0,0 +1,68 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* Information required to generate a JSON field comparison
|
||||
*/
|
||||
interface Comparison<T> {
|
||||
|
||||
/** The operation for the field comparison */
|
||||
val op: Op
|
||||
|
||||
/** The value against which the comparison will be made */
|
||||
val value: T
|
||||
|
||||
/** Whether the value should be considered numeric */
|
||||
val isNumeric: Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to determine if a value is numeric
|
||||
*
|
||||
* @param it The value in question
|
||||
* @return True if it is a numeric type, false if not
|
||||
*/
|
||||
private fun <T> isNumeric(it: T) =
|
||||
it is Byte || it is Short || it is Int || it is Long
|
||||
|
||||
/**
|
||||
* A single-value comparison against a field in a JSON document
|
||||
*/
|
||||
class ComparisonSingle<T>(override val op: Op, override val value: T) : Comparison<T> {
|
||||
|
||||
init {
|
||||
when (op) {
|
||||
Op.BETWEEN, Op.IN, Op.IN_ARRAY ->
|
||||
throw DocumentException("Cannot use single comparison for multiple values")
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
|
||||
override val isNumeric = isNumeric(value)
|
||||
|
||||
override fun toString() =
|
||||
"$op $value"
|
||||
}
|
||||
|
||||
/**
|
||||
* A range comparison against a field in a JSON document
|
||||
*/
|
||||
class ComparisonBetween<T>(override val value: Pair<T, T>) : Comparison<Pair<T, T>> {
|
||||
override val op = Op.BETWEEN
|
||||
override val isNumeric = isNumeric(value.first)
|
||||
}
|
||||
|
||||
/**
|
||||
* A check within a collection of values
|
||||
*/
|
||||
class ComparisonIn<T>(override val value: Collection<T>) : Comparison<Collection<T>> {
|
||||
override val op = Op.IN
|
||||
override val isNumeric = !value.isEmpty() && isNumeric(value.elementAt(0))
|
||||
}
|
||||
|
||||
/**
|
||||
* A check within a collection of values against an array in a document
|
||||
*/
|
||||
class ComparisonInArray<T>(override val value: Pair<String, Collection<T>>) : Comparison<Pair<String, Collection<T>>> {
|
||||
override val op = Op.IN_ARRAY
|
||||
override val isNumeric = false
|
||||
}
|
||||
62
src/jvm/src/main/kotlin/Configuration.kt
Normal file
62
src/jvm/src/main/kotlin/Configuration.kt
Normal file
@@ -0,0 +1,62 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
import java.sql.Connection
|
||||
import java.sql.DriverManager
|
||||
|
||||
/**
|
||||
* Configuration for the document library
|
||||
*/
|
||||
object Configuration {
|
||||
|
||||
/** The field in which a document's ID is stored */
|
||||
@JvmField
|
||||
var idField = "id"
|
||||
|
||||
/** The automatic ID strategy to use */
|
||||
@JvmField
|
||||
var autoIdStrategy = AutoId.DISABLED
|
||||
|
||||
/** The length of automatic random hex character string */
|
||||
@JvmField
|
||||
var idStringLength = 16
|
||||
|
||||
/** The derived dialect value from the connection string */
|
||||
internal var dialectValue: Dialect? = null
|
||||
|
||||
/** The connection string for the JDBC connection */
|
||||
@JvmStatic
|
||||
var connectionString: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
dialectValue = if (value.isNullOrBlank()) null else Dialect.deriveFromConnectionString(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a new connection to the configured database
|
||||
*
|
||||
* @return A new connection to the configured database
|
||||
* @throws IllegalArgumentException If the connection string is not set before calling this
|
||||
*/
|
||||
@JvmStatic
|
||||
fun dbConn(): Connection {
|
||||
if (connectionString == null) {
|
||||
throw IllegalArgumentException("Please provide a connection string before attempting data access")
|
||||
}
|
||||
return DriverManager.getConnection(connectionString)
|
||||
}
|
||||
|
||||
/**
|
||||
* The dialect in use
|
||||
*
|
||||
* @param process The process being attempted
|
||||
* @return The dialect for the current connection
|
||||
* @throws DocumentException If the dialect has not been set
|
||||
*/
|
||||
@Throws(DocumentException::class)
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun dialect(process: String? = null): Dialect =
|
||||
dialectValue ?: throw DocumentException(
|
||||
"Database mode not set" + if (process == null) "" else "; cannot $process"
|
||||
)
|
||||
}
|
||||
28
src/jvm/src/main/kotlin/Dialect.kt
Normal file
28
src/jvm/src/main/kotlin/Dialect.kt
Normal file
@@ -0,0 +1,28 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* The SQL dialect to use when building queries
|
||||
*/
|
||||
enum class Dialect {
|
||||
/** PostgreSQL */
|
||||
POSTGRESQL,
|
||||
/** SQLite */
|
||||
SQLITE;
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Derive the dialect from the given connection string
|
||||
*
|
||||
* @param connectionString The connection string from which the dialect will be derived
|
||||
* @return The dialect for the connection string
|
||||
* @throws DocumentException If the dialect cannot be determined
|
||||
*/
|
||||
fun deriveFromConnectionString(connectionString: String): Dialect =
|
||||
when {
|
||||
connectionString.contains(":sqlite:") -> SQLITE
|
||||
connectionString.contains(":postgresql:") -> POSTGRESQL
|
||||
else -> throw DocumentException("Cannot determine dialect from [$connectionString]")
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/jvm/src/main/kotlin/DocumentException.kt
Normal file
9
src/jvm/src/main/kotlin/DocumentException.kt
Normal file
@@ -0,0 +1,9 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* An exception caused by invalid operations in the document library
|
||||
*
|
||||
* @param message The message for the exception
|
||||
* @param cause The underlying exception (optional)
|
||||
*/
|
||||
class DocumentException(message: String, cause: Throwable? = null) : Exception(message, cause)
|
||||
13
src/jvm/src/main/kotlin/DocumentIndex.kt
Normal file
13
src/jvm/src/main/kotlin/DocumentIndex.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* The type of index to generate for the document
|
||||
*/
|
||||
enum class DocumentIndex(val sql: String) {
|
||||
|
||||
/** A GIN index with standard operations (all operators supported) */
|
||||
FULL(""),
|
||||
|
||||
/** A GIN index with JSONPath operations (optimized for @>, @?, @@ operators) */
|
||||
OPTIMIZED(" jsonb_path_ops")
|
||||
}
|
||||
24
src/jvm/src/main/kotlin/DocumentSerializer.kt
Normal file
24
src/jvm/src/main/kotlin/DocumentSerializer.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* The interface for a document serializer/deserializer
|
||||
*/
|
||||
interface DocumentSerializer {
|
||||
|
||||
/**
|
||||
* Serialize a document to its JSON representation
|
||||
*
|
||||
* @param document The document to be serialized
|
||||
* @return The JSON representation of the document
|
||||
*/
|
||||
fun <TDoc> serialize(document: TDoc): String
|
||||
|
||||
/**
|
||||
* Deserialize a document from its JSON representation
|
||||
*
|
||||
* @param json The JSON representation of the document
|
||||
* @param clazz The class of the document to be deserialized
|
||||
* @return The document instance represented by the given JSON string
|
||||
*/
|
||||
fun <TDoc> deserialize(json: String, clazz: Class<TDoc>): TDoc
|
||||
}
|
||||
317
src/jvm/src/main/kotlin/Field.kt
Normal file
317
src/jvm/src/main/kotlin/Field.kt
Normal file
@@ -0,0 +1,317 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* A field and its comparison
|
||||
*
|
||||
* @property name The name of the field in the JSON document
|
||||
* @property comparison The comparison to apply against the field
|
||||
* @property parameterName The name of the parameter to use in the query (optional, generated if missing)
|
||||
* @property qualifier A table qualifier to use to address the `data` field (useful for multi-table queries)
|
||||
*/
|
||||
class Field<T> private constructor(
|
||||
val name: String,
|
||||
val comparison: Comparison<T>,
|
||||
val parameterName: String? = null,
|
||||
val qualifier: String? = null) {
|
||||
|
||||
init {
|
||||
if (parameterName != null && !parameterName.startsWith(':') && !parameterName.startsWith('@'))
|
||||
throw DocumentException("Parameter Name must start with : or @ ($name)")
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the parameter name for the field
|
||||
*
|
||||
* @param paramName The parameter name to use for this field
|
||||
* @return A new `Field` with the parameter name specified
|
||||
*/
|
||||
fun withParameterName(paramName: String) =
|
||||
Field(name, comparison, paramName, qualifier)
|
||||
|
||||
/**
|
||||
* Specify a qualifier (alias) for the document table
|
||||
*
|
||||
* @param alias The table alias for this field
|
||||
* @return A new `Field` with the table qualifier specified
|
||||
*/
|
||||
fun withQualifier(alias: String) =
|
||||
Field(name, comparison, parameterName, alias)
|
||||
|
||||
/**
|
||||
* Get the path for this field
|
||||
*
|
||||
* @param dialect The SQL dialect to use for the path to the JSON field
|
||||
* @param format Whether the value should be retrieved as JSON or SQL (optional, default SQL)
|
||||
* @return The path for the field
|
||||
*/
|
||||
fun path(dialect: Dialect, format: FieldFormat = FieldFormat.SQL): String =
|
||||
(if (qualifier == null) "" else "${qualifier}.") + nameToPath(name, dialect, format)
|
||||
|
||||
/** Parameters to bind each value of `IN` and `IN_ARRAY` operations */
|
||||
private val inParameterNames: String
|
||||
get() {
|
||||
val values = if (comparison.op == Op.IN) {
|
||||
comparison.value as Collection<*>
|
||||
} else {
|
||||
val parts = comparison.value as Pair<*, *>
|
||||
parts.second as Collection<*>
|
||||
}
|
||||
return List(values.size) { idx -> "${parameterName}_$idx" }.joinToString(", ")
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `WHERE` clause fragment for this field
|
||||
*
|
||||
* @return The `WHERE` clause for this field
|
||||
* @throws DocumentException If the field has no parameter name or the database dialect has not been set
|
||||
*/
|
||||
fun toWhere(): String {
|
||||
if (parameterName == null && !listOf(Op.EXISTS, Op.NOT_EXISTS).contains(comparison.op))
|
||||
throw DocumentException("Parameter for $name must be specified")
|
||||
|
||||
val dialect = Configuration.dialect("make field WHERE clause")
|
||||
val fieldName = path(dialect, if (comparison.op == Op.IN_ARRAY) FieldFormat.JSON else FieldFormat.SQL)
|
||||
val fieldPath = when (dialect) {
|
||||
Dialect.POSTGRESQL -> if (comparison.isNumeric) "($fieldName)::numeric" else fieldName
|
||||
Dialect.SQLITE -> fieldName
|
||||
}
|
||||
val criteria = when (comparison.op) {
|
||||
in listOf(Op.EXISTS, Op.NOT_EXISTS) -> ""
|
||||
Op.BETWEEN -> " ${parameterName}min AND ${parameterName}max"
|
||||
Op.IN -> " ($inParameterNames)"
|
||||
Op.IN_ARRAY -> if (dialect == Dialect.POSTGRESQL) " ARRAY[$inParameterNames]" else ""
|
||||
else -> " $parameterName"
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return if (dialect == Dialect.SQLITE && comparison.op == Op.IN_ARRAY) {
|
||||
val (table, _) = comparison.value as? Pair<String, *> ?: throw DocumentException("InArray field invalid")
|
||||
"EXISTS (SELECT 1 FROM json_each($table.data, '$.$name') WHERE value IN ($inParameterNames))"
|
||||
} else {
|
||||
"$fieldPath ${comparison.op.sql}$criteria"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the parameters required for this field
|
||||
*
|
||||
* @param existing The existing parameters
|
||||
* @return The collection with the necessary parameters appended
|
||||
*/
|
||||
fun appendParameter(existing: MutableCollection<Parameter<*>>): MutableCollection<Parameter<*>> {
|
||||
val typ = if (comparison.isNumeric) ParameterType.NUMBER else ParameterType.STRING
|
||||
when (comparison) {
|
||||
is ComparisonBetween<*> -> {
|
||||
existing.add(Parameter("${parameterName}min", typ, comparison.value.first))
|
||||
existing.add(Parameter("${parameterName}max", typ, comparison.value.second))
|
||||
}
|
||||
|
||||
is ComparisonIn<*> -> {
|
||||
comparison.value.forEachIndexed { index, item ->
|
||||
existing.add(Parameter("${parameterName}_$index", typ, item))
|
||||
}
|
||||
}
|
||||
|
||||
is ComparisonInArray<*> -> {
|
||||
val mkString = Configuration.dialect("append parameters for InArray") == Dialect.POSTGRESQL
|
||||
// TODO: I think this is actually Pair<String, Collection<*>>
|
||||
comparison.value.second.forEachIndexed { index, item ->
|
||||
if (mkString) {
|
||||
existing.add(Parameter("${parameterName}_$index", ParameterType.STRING, "$item"))
|
||||
} else {
|
||||
existing.add(Parameter("${parameterName}_$index", typ, item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (comparison.op != Op.EXISTS && comparison.op != Op.NOT_EXISTS) {
|
||||
existing.add(Parameter(parameterName!!, typ, comparison.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
return existing
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
"Field ${parameterName ?: "<unnamed>"} $comparison${qualifier?.let { " (qualifier $it)" } ?: ""}"
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Create a field equality comparison
|
||||
*
|
||||
* @param name The name of the field to be compared
|
||||
* @param value The value for the comparison
|
||||
* @param paramName The parameter name for the field (optional, defaults to auto-generated)
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <T> equal(name: String, value: T, paramName: String? = null) =
|
||||
Field(name, ComparisonSingle(Op.EQUAL, value), paramName)
|
||||
|
||||
/**
|
||||
* Create a field greater-than comparison
|
||||
*
|
||||
* @param name The name of the field to be compared
|
||||
* @param value The value for the comparison
|
||||
* @param paramName The parameter name for the field (optional, defaults to auto-generated)
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <T> greater(name: String, value: T, paramName: String? = null) =
|
||||
Field(name, ComparisonSingle(Op.GREATER, value), paramName)
|
||||
|
||||
/**
|
||||
* Create a field greater-than-or-equal-to comparison
|
||||
*
|
||||
* @param name The name of the field to be compared
|
||||
* @param value The value for the comparison
|
||||
* @param paramName The parameter name for the field (optional, defaults to auto-generated)
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <T> greaterOrEqual(name: String, value: T, paramName: String? = null) =
|
||||
Field(name, ComparisonSingle(Op.GREATER_OR_EQUAL, value), paramName)
|
||||
|
||||
/**
|
||||
* Create a field less-than comparison
|
||||
*
|
||||
* @param name The name of the field to be compared
|
||||
* @param value The value for the comparison
|
||||
* @param paramName The parameter name for the field (optional, defaults to auto-generated)
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <T> less(name: String, value: T, paramName: String? = null) =
|
||||
Field(name, ComparisonSingle(Op.LESS, value), paramName)
|
||||
|
||||
/**
|
||||
* Create a field less-than-or-equal-to comparison
|
||||
*
|
||||
* @param name The name of the field to be compared
|
||||
* @param value The value for the comparison
|
||||
* @param paramName The parameter name for the field (optional, defaults to auto-generated)
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <T> lessOrEqual(name: String, value: T, paramName: String? = null) =
|
||||
Field(name, ComparisonSingle(Op.LESS_OR_EQUAL, value), paramName)
|
||||
|
||||
/**
|
||||
* Create a field inequality comparison
|
||||
*
|
||||
* @param name The name of the field to be compared
|
||||
* @param value The value for the comparison
|
||||
* @param paramName The parameter name for the field (optional, defaults to auto-generated)
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <T> notEqual(name: String, value: T, paramName: String? = null) =
|
||||
Field(name, ComparisonSingle(Op.NOT_EQUAL, value), paramName)
|
||||
|
||||
/**
|
||||
* Create a field range comparison
|
||||
*
|
||||
* @param name The name of the field to be compared
|
||||
* @param minValue The lower value for the comparison
|
||||
* @param maxValue The upper value for the comparison
|
||||
* @param paramName The parameter name for the field (optional, defaults to auto-generated)
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <T> between(name: String, minValue: T, maxValue: T, paramName: String? = null) =
|
||||
Field(name, ComparisonBetween(Pair(minValue, maxValue)), paramName)
|
||||
|
||||
/**
|
||||
* Create a field where any values match (SQL `IN`)
|
||||
*
|
||||
* @param name The name of the field to be compared
|
||||
* @param values The values for the comparison
|
||||
* @param paramName The parameter name for the field (optional, defaults to auto-generated)
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <T> any(name: String, values: Collection<T>, paramName: String? = null) =
|
||||
Field(name, ComparisonIn(values), paramName)
|
||||
|
||||
/**
|
||||
* Create a field where values should exist in a document's array
|
||||
*
|
||||
* @param name The name of the field to be compared
|
||||
* @param tableName The name of the document table
|
||||
* @param values The values for the comparison
|
||||
* @param paramName The parameter name for the field (optional, defaults to auto-generated)
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <T> inArray(name: String, tableName: String, values: Collection<T>, paramName: String? = null) =
|
||||
Field(name, ComparisonInArray(Pair(tableName, values)), paramName)
|
||||
|
||||
/**
|
||||
* Create a field where a document field should exist
|
||||
*
|
||||
* @param name The name of the field whose existence should be checked
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
fun exists(name: String) =
|
||||
Field(name, ComparisonSingle(Op.EXISTS, ""))
|
||||
|
||||
/**
|
||||
* Create a field where a document field should not exist
|
||||
*
|
||||
* @param name The name of the field whose existence should be checked
|
||||
* @return A `Field` with the given comparison
|
||||
*/
|
||||
@JvmStatic
|
||||
fun notExists(name: String) =
|
||||
Field(name, ComparisonSingle(Op.NOT_EXISTS, ""))
|
||||
|
||||
/**
|
||||
* Create a field with a given named comparison (useful for ordering fields)
|
||||
*
|
||||
* @param name The name of the field
|
||||
* @return A `Field` with the given name (comparison equal to an empty string)
|
||||
*/
|
||||
@JvmStatic
|
||||
fun named(name: String) =
|
||||
Field(name, ComparisonSingle(Op.EQUAL, ""))
|
||||
|
||||
/**
|
||||
* Convert a name to the SQL path for the given dialect
|
||||
*
|
||||
* @param name The field name to be translated
|
||||
* @param dialect The database for which the path should be created
|
||||
* @param format Whether the field should be retrieved as a JSON value or a SQL value
|
||||
* @return The path to the JSON field
|
||||
*/
|
||||
@JvmStatic
|
||||
fun nameToPath(name: String, dialect: Dialect, format: FieldFormat): String {
|
||||
val path = StringBuilder("data")
|
||||
val extra = if (format == FieldFormat.SQL) ">" else ""
|
||||
if (name.indexOf('.') > -1) {
|
||||
if (dialect == Dialect.POSTGRESQL) {
|
||||
path.append("#>", extra, "'{", name.replace('.', ','), "}'")
|
||||
} else {
|
||||
val names = name.split('.').toMutableList()
|
||||
val last = names.removeLast()
|
||||
names.forEach { path.append("->'", it, "'") }
|
||||
path.append("->", extra, "'", last, "'")
|
||||
}
|
||||
} else {
|
||||
path.append("->", extra, "'", name, "'")
|
||||
}
|
||||
return path.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/jvm/src/main/kotlin/FieldFormat.kt
Normal file
11
src/jvm/src/main/kotlin/FieldFormat.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* The data format for a document field retrieval
|
||||
*/
|
||||
enum class FieldFormat {
|
||||
/** Retrieve the field as a SQL value (string in PostgreSQL, best guess in SQLite */
|
||||
SQL,
|
||||
/** Retrieve the field as a JSON value */
|
||||
JSON
|
||||
}
|
||||
11
src/jvm/src/main/kotlin/FieldMatch.kt
Normal file
11
src/jvm/src/main/kotlin/FieldMatch.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* How fields should be matched in by-field queries
|
||||
*/
|
||||
enum class FieldMatch(val sql: String) {
|
||||
/** Match any of the field criteria (`OR`) */
|
||||
ANY("OR"),
|
||||
/** Match all the field criteria (`AND`) */
|
||||
ALL("AND"),
|
||||
}
|
||||
29
src/jvm/src/main/kotlin/Op.kt
Normal file
29
src/jvm/src/main/kotlin/Op.kt
Normal file
@@ -0,0 +1,29 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* A comparison operator used for fields
|
||||
*/
|
||||
enum class Op(val sql: String) {
|
||||
/** Compare using equality */
|
||||
EQUAL("="),
|
||||
/** Compare using greater-than */
|
||||
GREATER(">"),
|
||||
/** Compare using greater-than-or-equal-to */
|
||||
GREATER_OR_EQUAL(">="),
|
||||
/** Compare using less-than */
|
||||
LESS("<"),
|
||||
/** Compare using less-than-or-equal-to */
|
||||
LESS_OR_EQUAL("<="),
|
||||
/** Compare using inequality */
|
||||
NOT_EQUAL("<>"),
|
||||
/** Compare between two values */
|
||||
BETWEEN("BETWEEN"),
|
||||
/** Compare existence in a list of values */
|
||||
IN("IN"),
|
||||
/** Compare overlap between an array and a list of values */
|
||||
IN_ARRAY("??|"),
|
||||
/** Compare existence */
|
||||
EXISTS("IS NOT NULL"),
|
||||
/** Compare nonexistence */
|
||||
NOT_EXISTS("IS NULL")
|
||||
}
|
||||
55
src/jvm/src/main/kotlin/Parameter.kt
Normal file
55
src/jvm/src/main/kotlin/Parameter.kt
Normal file
@@ -0,0 +1,55 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
import java.sql.PreparedStatement
|
||||
import java.sql.Types
|
||||
|
||||
/**
|
||||
* A parameter to use for a query
|
||||
*
|
||||
* @property name The name of the parameter (prefixed with a colon)
|
||||
* @property type The type of this parameter
|
||||
* @property value The value of the parameter
|
||||
*/
|
||||
class Parameter<T>(val name: String, val type: ParameterType, val value: T) {
|
||||
|
||||
init {
|
||||
if (!name.startsWith(':') && !name.startsWith('@'))
|
||||
throw DocumentException("Name must start with : or @ ($name)")
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind this parameter to a prepared statement at the given index
|
||||
*
|
||||
* @param stmt The prepared statement to which this parameter should be bound
|
||||
* @param index The index (1-based) to which the parameter should be bound
|
||||
*/
|
||||
fun bind(stmt: PreparedStatement, index: Int) {
|
||||
when (type) {
|
||||
ParameterType.NUMBER -> {
|
||||
when (value) {
|
||||
null -> stmt.setNull(index, Types.NULL)
|
||||
is Byte -> stmt.setByte(index, value)
|
||||
is Short -> stmt.setShort(index, value)
|
||||
is Int -> stmt.setInt(index, value)
|
||||
is Long -> stmt.setLong(index, value)
|
||||
else -> throw DocumentException(
|
||||
"Number parameter must be Byte, Short, Int, or Long (${value!!::class.simpleName})"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ParameterType.STRING -> {
|
||||
when (value) {
|
||||
null -> stmt.setNull(index, Types.NULL)
|
||||
is String -> stmt.setString(index, value)
|
||||
else -> stmt.setString(index, value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
ParameterType.JSON -> stmt.setObject(index, value as String, Types.OTHER)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
"$type[$name] = $value"
|
||||
}
|
||||
18
src/jvm/src/main/kotlin/ParameterName.kt
Normal file
18
src/jvm/src/main/kotlin/ParameterName.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* Derive parameter names; each instance wraps a counter to provide names for anonymous fields
|
||||
*/
|
||||
class ParameterName {
|
||||
|
||||
private var currentIdx = 0
|
||||
|
||||
/**
|
||||
* Derive the parameter name from the current possibly-null string
|
||||
*
|
||||
* @param paramName The name of the parameter as specified by the field
|
||||
* @return The name from the field, if present, or a derived name if missing
|
||||
*/
|
||||
fun derive(paramName: String?): String =
|
||||
paramName ?: ":field${currentIdx++}"
|
||||
}
|
||||
15
src/jvm/src/main/kotlin/ParameterType.kt
Normal file
15
src/jvm/src/main/kotlin/ParameterType.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package solutions.bitbadger.documents
|
||||
|
||||
/**
|
||||
* The types of parameters supported by the document library
|
||||
*/
|
||||
enum class ParameterType {
|
||||
/** The parameter value is some sort of number (`Byte`, `Short`, `Int`, or `Long`) */
|
||||
NUMBER,
|
||||
|
||||
/** The parameter value is a string */
|
||||
STRING,
|
||||
|
||||
/** The parameter should be JSON-encoded */
|
||||
JSON,
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
@file:JvmName("ConnExt")
|
||||
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.extensions
|
||||
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.*
|
||||
import solutions.bitbadger.documents.jvm.*
|
||||
import java.sql.Connection
|
||||
import java.sql.ResultSet
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.common.query.Count
|
||||
import solutions.bitbadger.documents.*
|
||||
import solutions.bitbadger.documents.query.Count
|
||||
import solutions.bitbadger.documents.extensions.customScalar
|
||||
import java.sql.Connection
|
||||
|
||||
/**
|
||||
@@ -1,7 +1,7 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.Configuration
|
||||
import solutions.bitbadger.documents.common.Parameter
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Parameter
|
||||
import java.sql.Connection
|
||||
import java.sql.ResultSet
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.Configuration
|
||||
import solutions.bitbadger.documents.common.DocumentException
|
||||
import solutions.bitbadger.documents.common.DocumentIndex
|
||||
import solutions.bitbadger.documents.common.query.Definition
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.DocumentIndex
|
||||
import solutions.bitbadger.documents.extensions.customNonQuery
|
||||
import solutions.bitbadger.documents.query.Definition
|
||||
import java.sql.Connection
|
||||
|
||||
/**
|
||||
@@ -1,7 +1,8 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.common.query.Delete
|
||||
import solutions.bitbadger.documents.*
|
||||
import solutions.bitbadger.documents.extensions.customNonQuery
|
||||
import solutions.bitbadger.documents.query.Delete
|
||||
import java.sql.Connection
|
||||
|
||||
/**
|
||||
@@ -1,13 +1,14 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.AutoId
|
||||
import solutions.bitbadger.documents.common.Configuration
|
||||
import solutions.bitbadger.documents.common.Dialect
|
||||
import solutions.bitbadger.documents.common.Field
|
||||
import solutions.bitbadger.documents.AutoId
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Dialect
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.extensions.customNonQuery
|
||||
import solutions.bitbadger.documents.query.Document
|
||||
import solutions.bitbadger.documents.query.Where
|
||||
import solutions.bitbadger.documents.query.statementWhere
|
||||
import java.sql.Connection
|
||||
import solutions.bitbadger.documents.common.query.Document
|
||||
import solutions.bitbadger.documents.common.query.Where
|
||||
import solutions.bitbadger.documents.common.query.statementWhere
|
||||
|
||||
/**
|
||||
* Functions for manipulating documents
|
||||
@@ -1,6 +1,6 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.DocumentSerializer
|
||||
import solutions.bitbadger.documents.DocumentSerializer
|
||||
|
||||
/**
|
||||
* Configuration for document serialization
|
||||
@@ -1,7 +1,8 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.common.query.Exists
|
||||
import solutions.bitbadger.documents.*
|
||||
import solutions.bitbadger.documents.extensions.customScalar
|
||||
import solutions.bitbadger.documents.query.Exists
|
||||
import java.sql.Connection
|
||||
|
||||
/**
|
||||
@@ -1,8 +1,10 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.common.query.Find
|
||||
import solutions.bitbadger.documents.common.query.orderBy
|
||||
import solutions.bitbadger.documents.*
|
||||
import solutions.bitbadger.documents.extensions.customList
|
||||
import solutions.bitbadger.documents.extensions.customSingle
|
||||
import solutions.bitbadger.documents.query.Find
|
||||
import solutions.bitbadger.documents.query.orderBy
|
||||
import java.sql.Connection
|
||||
|
||||
/**
|
||||
@@ -1,6 +1,6 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.DocumentSerializer
|
||||
import solutions.bitbadger.documents.DocumentSerializer
|
||||
|
||||
/**
|
||||
* A serializer that tells the user to implement another one
|
||||
@@ -1,6 +1,7 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.*
|
||||
import solutions.bitbadger.documents.ParameterName
|
||||
import java.sql.Connection
|
||||
import java.sql.PreparedStatement
|
||||
import java.sql.SQLException
|
||||
@@ -1,7 +1,8 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.common.query.Patch
|
||||
import solutions.bitbadger.documents.*
|
||||
import solutions.bitbadger.documents.extensions.customNonQuery
|
||||
import solutions.bitbadger.documents.query.Patch
|
||||
import java.sql.Connection
|
||||
|
||||
/**
|
||||
@@ -1,7 +1,8 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.common.query.RemoveFields
|
||||
import solutions.bitbadger.documents.*
|
||||
import solutions.bitbadger.documents.extensions.customNonQuery
|
||||
import solutions.bitbadger.documents.query.RemoveFields
|
||||
import java.sql.Connection
|
||||
|
||||
/**
|
||||
@@ -1,8 +1,8 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
package solutions.bitbadger.documents.jvm
|
||||
|
||||
import solutions.bitbadger.documents.common.Configuration
|
||||
import solutions.bitbadger.documents.common.Dialect
|
||||
import solutions.bitbadger.documents.common.DocumentException
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Dialect
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import java.sql.PreparedStatement
|
||||
import java.sql.ResultSet
|
||||
import java.sql.SQLException
|
||||
49
src/jvm/src/main/kotlin/query/Count.kt
Normal file
49
src/jvm/src/main/kotlin/query/Count.kt
Normal file
@@ -0,0 +1,49 @@
|
||||
package solutions.bitbadger.documents.query
|
||||
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.FieldMatch
|
||||
import solutions.bitbadger.documents.query.byFields as byFieldsBase;
|
||||
|
||||
/**
|
||||
* Functions to count documents
|
||||
*/
|
||||
object Count {
|
||||
|
||||
/**
|
||||
* Query to count all documents in a table
|
||||
*
|
||||
* @param tableName The table in which to count documents (may include schema)
|
||||
* @return A query to count documents
|
||||
*/
|
||||
fun all(tableName: String) =
|
||||
"SELECT COUNT(*) AS it FROM $tableName"
|
||||
|
||||
/**
|
||||
* Query to count documents matching the given fields
|
||||
*
|
||||
* @param tableName The table in which to count documents (may include schema)
|
||||
* @param fields The field comparisons for the count
|
||||
* @param howMatched How fields should be compared (optional, defaults to ALL)
|
||||
* @return A query to count documents matching the given fields
|
||||
*/
|
||||
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
|
||||
byFieldsBase(all(tableName), fields, howMatched)
|
||||
|
||||
/**
|
||||
* Query to count documents via JSON containment (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table in which to count documents (may include schema)
|
||||
* @return A query to count documents via JSON containment
|
||||
*/
|
||||
fun byContains(tableName: String) =
|
||||
statementWhere(all(tableName), Where.jsonContains())
|
||||
|
||||
/**
|
||||
* Query to count documents via a JSON path match (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table in which to count documents (may include schema)
|
||||
* @return A query to count documents via a JSON path match
|
||||
*/
|
||||
fun byJsonPath(tableName: String) =
|
||||
statementWhere(all(tableName), Where.jsonPathMatches())
|
||||
}
|
||||
92
src/jvm/src/main/kotlin/query/Definition.kt
Normal file
92
src/jvm/src/main/kotlin/query/Definition.kt
Normal file
@@ -0,0 +1,92 @@
|
||||
package solutions.bitbadger.documents.query
|
||||
|
||||
import solutions.bitbadger.documents.*
|
||||
|
||||
/**
|
||||
* Functions to create queries to define tables and indexes
|
||||
*/
|
||||
object Definition {
|
||||
|
||||
/**
|
||||
* SQL statement to create a document table
|
||||
*
|
||||
* @param tableName The name of the table to create (may include schema)
|
||||
* @param dataType The type of data for the column (`JSON`, `JSONB`, etc.)
|
||||
* @return A query to create a document table
|
||||
*/
|
||||
fun ensureTableFor(tableName: String, dataType: String) =
|
||||
"CREATE TABLE IF NOT EXISTS $tableName (data $dataType NOT NULL)"
|
||||
|
||||
/**
|
||||
* SQL statement to create a document table in the current dialect
|
||||
*
|
||||
* @param tableName The name of the table to create (may include schema)
|
||||
* @param dialect The dialect to generate (optional, used in place of current)
|
||||
* @return A query to create a document table
|
||||
*/
|
||||
fun ensureTable(tableName: String, dialect: Dialect? = null) =
|
||||
when (dialect ?: Configuration.dialect("create table creation query")) {
|
||||
Dialect.POSTGRESQL -> ensureTableFor(tableName, "JSONB")
|
||||
Dialect.SQLITE -> ensureTableFor(tableName, "TEXT")
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a schema and table name
|
||||
*
|
||||
* @param tableName The name of the table, possibly with a schema
|
||||
* @return A pair with the first item as the schema and the second as the table name
|
||||
*/
|
||||
private fun splitSchemaAndTable(tableName: String) =
|
||||
tableName.split('.').let { if (it.size == 1) Pair("", tableName) else Pair(it[0], it[1]) }
|
||||
|
||||
/**
|
||||
* SQL statement to create an index on one or more fields in a JSON document
|
||||
*
|
||||
* @param tableName The table on which an index should be created (may include schema)
|
||||
* @param indexName The name of the index to be created
|
||||
* @param fields One or more fields to include in the index
|
||||
* @param dialect The SQL dialect to use when creating this index (optional, used in place of current)
|
||||
* @return A query to create the field index
|
||||
*/
|
||||
fun ensureIndexOn(
|
||||
tableName: String,
|
||||
indexName: String,
|
||||
fields: Collection<String>,
|
||||
dialect: Dialect? = null
|
||||
): String {
|
||||
val (_, tbl) = splitSchemaAndTable(tableName)
|
||||
val mode = dialect ?: Configuration.dialect("create index $tbl.$indexName")
|
||||
val jsonFields = fields.joinToString(", ") {
|
||||
val parts = it.split(' ')
|
||||
val direction = if (parts.size > 1) " ${parts[1]}" else ""
|
||||
"(" + Field.nameToPath(parts[0], mode, FieldFormat.SQL) + ")$direction"
|
||||
}
|
||||
return "CREATE INDEX IF NOT EXISTS idx_${tbl}_$indexName ON $tableName ($jsonFields)"
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL statement to create a key index for a document table
|
||||
*
|
||||
* @param tableName The table on which a key index should be created (may include schema)
|
||||
* @param dialect The SQL dialect to use when creating this index (optional, used in place of current)
|
||||
* @return A query to create the key index
|
||||
*/
|
||||
fun ensureKey(tableName: String, dialect: Dialect? = null) =
|
||||
ensureIndexOn(tableName, "key", listOf(Configuration.idField), dialect).replace("INDEX", "UNIQUE INDEX")
|
||||
|
||||
/**
|
||||
* Create a document-wide index on a table (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table on which the document index should be created
|
||||
* @param indexType The type of index to be created
|
||||
* @return The SQL statement to create an index on JSON documents in the specified table
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
fun ensureDocumentIndexOn(tableName: String, indexType: DocumentIndex): String {
|
||||
if (Configuration.dialect("create document index query") != Dialect.POSTGRESQL) {
|
||||
throw DocumentException("'Document indexes are only supported on PostgreSQL")
|
||||
}
|
||||
val (_, tbl) = splitSchemaAndTable(tableName)
|
||||
return "CREATE INDEX IF NOT EXISTS idx_${tbl}_document ON $tableName USING GIN (data${indexType.sql})"
|
||||
}
|
||||
}
|
||||
60
src/jvm/src/main/kotlin/query/Delete.kt
Normal file
60
src/jvm/src/main/kotlin/query/Delete.kt
Normal file
@@ -0,0 +1,60 @@
|
||||
package solutions.bitbadger.documents.query
|
||||
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.FieldMatch
|
||||
import solutions.bitbadger.documents.query.byFields as byFieldsBase
|
||||
import solutions.bitbadger.documents.query.byId as byIdBase
|
||||
|
||||
/**
|
||||
* Functions to delete documents
|
||||
*/
|
||||
object Delete {
|
||||
|
||||
/**
|
||||
* Query to delete documents from a table
|
||||
*
|
||||
* @param tableName The table in which documents should be deleted (may include schema)
|
||||
* @return A query to delete documents
|
||||
*/
|
||||
private fun delete(tableName: String) =
|
||||
"DELETE FROM $tableName"
|
||||
|
||||
/**
|
||||
* Query to delete a document by its ID
|
||||
*
|
||||
* @param tableName The table from which documents should be deleted (may include schema)
|
||||
* @param docId The ID of the document (optional, used for type checking)
|
||||
* @return A query to delete a document by its ID
|
||||
*/
|
||||
fun <TKey> byId(tableName: String, docId: TKey? = null) =
|
||||
byIdBase(delete(tableName), docId)
|
||||
|
||||
/**
|
||||
* Query to delete documents matching the given fields
|
||||
*
|
||||
* @param tableName The table from which documents should be deleted (may include schema)
|
||||
* @param fields The field comparisons for documents to be deleted
|
||||
* @param howMatched How fields should be compared (optional, defaults to ALL)
|
||||
* @return A query to delete documents matching for the given fields
|
||||
*/
|
||||
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
|
||||
byFieldsBase(delete(tableName), fields, howMatched)
|
||||
|
||||
/**
|
||||
* Query to delete documents via JSON containment (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be deleted (may include schema)
|
||||
* @return A query to delete documents via JSON containment
|
||||
*/
|
||||
fun byContains(tableName: String) =
|
||||
statementWhere(delete(tableName), Where.jsonContains())
|
||||
|
||||
/**
|
||||
* Query to delete documents via a JSON path match (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be deleted (may include schema)
|
||||
* @return A query to delete documents via a JSON path match
|
||||
*/
|
||||
fun byJsonPath(tableName: String) =
|
||||
statementWhere(delete(tableName), Where.jsonPathMatches())
|
||||
}
|
||||
58
src/jvm/src/main/kotlin/query/Document.kt
Normal file
58
src/jvm/src/main/kotlin/query/Document.kt
Normal file
@@ -0,0 +1,58 @@
|
||||
package solutions.bitbadger.documents.query
|
||||
|
||||
import solutions.bitbadger.documents.AutoId
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Dialect
|
||||
|
||||
/**
|
||||
* Functions for document-level operations
|
||||
*/
|
||||
object Document {
|
||||
|
||||
/**
|
||||
* Query to insert a document
|
||||
*
|
||||
* @param tableName The table into which to insert (may include schema)
|
||||
* @return A query to insert a document
|
||||
*/
|
||||
fun insert(tableName: String, autoId: AutoId? = null): String {
|
||||
val id = Configuration.idField
|
||||
val values = when (Configuration.dialect("create INSERT statement")) {
|
||||
Dialect.POSTGRESQL -> when (autoId ?: AutoId.DISABLED) {
|
||||
AutoId.DISABLED -> ":data"
|
||||
AutoId.NUMBER -> ":data::jsonb || ('{\"$id\":' || " +
|
||||
"(SELECT COALESCE(MAX((data->>'$id')::numeric), 0) + 1 " +
|
||||
"FROM $tableName) || '}')::jsonb"
|
||||
AutoId.UUID -> ":data::jsonb || '{\"$id\":\"${AutoId.generateUUID()}\"}'"
|
||||
AutoId.RANDOM_STRING -> ":data::jsonb || '{\"$id\":\"${AutoId.generateRandomString()}\"}'"
|
||||
}
|
||||
Dialect.SQLITE -> when (autoId ?: AutoId.DISABLED) {
|
||||
AutoId.DISABLED -> ":data"
|
||||
AutoId.NUMBER -> "json_set(:data, '$.$id', " +
|
||||
"(SELECT coalesce(max(data->>'$id'), 0) + 1 FROM $tableName))"
|
||||
AutoId.UUID -> "json_set(:data, '$.$id', '${AutoId.generateUUID()}')"
|
||||
AutoId.RANDOM_STRING -> "json_set(:data, '$.$id', '${AutoId.generateRandomString()}')"
|
||||
}
|
||||
}
|
||||
return "INSERT INTO $tableName VALUES ($values)"
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
|
||||
*
|
||||
* @param tableName The table into which to save (may include schema)
|
||||
* @return A query to save a document
|
||||
*/
|
||||
fun save(tableName: String) =
|
||||
insert(tableName, AutoId.DISABLED) +
|
||||
" ON CONFLICT ((data->>'${Configuration.idField}')) DO UPDATE SET data = EXCLUDED.data"
|
||||
|
||||
/**
|
||||
* Query to update (replace) a document (this query has no `WHERE` clause)
|
||||
*
|
||||
* @param tableName The table in which documents should be replaced (may include schema)
|
||||
* @return A query to update documents
|
||||
*/
|
||||
fun update(tableName: String) =
|
||||
"UPDATE $tableName SET data = :data"
|
||||
}
|
||||
59
src/jvm/src/main/kotlin/query/Exists.kt
Normal file
59
src/jvm/src/main/kotlin/query/Exists.kt
Normal file
@@ -0,0 +1,59 @@
|
||||
package solutions.bitbadger.documents.query
|
||||
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.FieldMatch
|
||||
|
||||
/**
|
||||
* Functions to check for document existence
|
||||
*/
|
||||
object Exists {
|
||||
|
||||
/**
|
||||
* Query to check for document existence in a table
|
||||
*
|
||||
* @param tableName The table in which existence should be checked (may include schema)
|
||||
* @param where The `WHERE` clause with the existence criteria
|
||||
* @return A query to check document existence
|
||||
*/
|
||||
private fun exists(tableName: String, where: String) =
|
||||
"SELECT EXISTS (SELECT 1 FROM $tableName WHERE $where) AS it"
|
||||
|
||||
/**
|
||||
* Query to check for document existence by ID
|
||||
*
|
||||
* @param tableName The table in which existence should be checked (may include schema)
|
||||
* @param docId The ID of the document (optional, used for type checking)
|
||||
* @return A query to determine document existence by ID
|
||||
*/
|
||||
fun <TKey> byId(tableName: String, docId: TKey? = null) =
|
||||
exists(tableName, Where.byId(docId = docId))
|
||||
|
||||
/**
|
||||
* Query to check for document existence matching the given fields
|
||||
*
|
||||
* @param tableName The table in which existence should be checked (may include schema)
|
||||
* @param fields The field comparisons for the existence check
|
||||
* @param howMatched How fields should be compared (optional, defaults to ALL)
|
||||
* @return A query to determine document existence for the given fields
|
||||
*/
|
||||
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
|
||||
exists(tableName, Where.byFields(fields, howMatched))
|
||||
|
||||
/**
|
||||
* Query to check for document existence via JSON containment (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table in which existence should be checked (may include schema)
|
||||
* @return A query to determine document existence via JSON containment
|
||||
*/
|
||||
fun byContains(tableName: String) =
|
||||
exists(tableName, Where.jsonContains())
|
||||
|
||||
/**
|
||||
* Query to check for document existence via a JSON path match (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table in which existence should be checked (may include schema)
|
||||
* @return A query to determine document existence via a JSON path match
|
||||
*/
|
||||
fun byJsonPath(tableName: String) =
|
||||
exists(tableName, Where.jsonPathMatches())
|
||||
}
|
||||
60
src/jvm/src/main/kotlin/query/Find.kt
Normal file
60
src/jvm/src/main/kotlin/query/Find.kt
Normal file
@@ -0,0 +1,60 @@
|
||||
package solutions.bitbadger.documents.query
|
||||
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.FieldMatch
|
||||
import solutions.bitbadger.documents.query.byId as byIdBase
|
||||
import solutions.bitbadger.documents.query.byFields as byFieldsBase
|
||||
|
||||
/**
|
||||
* Functions to retrieve documents
|
||||
*/
|
||||
object Find {
|
||||
|
||||
/**
|
||||
* Query to retrieve all documents from a table
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved (may include schema)
|
||||
* @return A query to retrieve documents
|
||||
*/
|
||||
fun all(tableName: String) =
|
||||
"SELECT data FROM $tableName"
|
||||
|
||||
/**
|
||||
* Query to retrieve a document by its ID
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved (may include schema)
|
||||
* @param docId The ID of the document (optional, used for type checking)
|
||||
* @return A query to retrieve a document by its ID
|
||||
*/
|
||||
fun <TKey> byId(tableName: String, docId: TKey? = null) =
|
||||
byIdBase(all(tableName), docId)
|
||||
|
||||
/**
|
||||
* Query to retrieve documents matching the given fields
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved (may include schema)
|
||||
* @param fields The field comparisons for matching documents to retrieve
|
||||
* @param howMatched How fields should be compared (optional, defaults to ALL)
|
||||
* @return A query to retrieve documents matching the given fields
|
||||
*/
|
||||
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
|
||||
byFieldsBase(all(tableName), fields, howMatched)
|
||||
|
||||
/**
|
||||
* Query to retrieve documents via JSON containment (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved (may include schema)
|
||||
* @return A query to retrieve documents via JSON containment
|
||||
*/
|
||||
fun byContains(tableName: String) =
|
||||
statementWhere(all(tableName), Where.jsonContains())
|
||||
|
||||
/**
|
||||
* Query to retrieve documents via a JSON path match (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved (may include schema)
|
||||
* @return A query to retrieve documents via a JSON path match
|
||||
*/
|
||||
fun byJsonPath(tableName: String) =
|
||||
statementWhere(all(tableName), Where.jsonPathMatches())
|
||||
}
|
||||
65
src/jvm/src/main/kotlin/query/Patch.kt
Normal file
65
src/jvm/src/main/kotlin/query/Patch.kt
Normal file
@@ -0,0 +1,65 @@
|
||||
package solutions.bitbadger.documents.query
|
||||
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Dialect
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.FieldMatch
|
||||
import solutions.bitbadger.documents.query.byFields as byFieldsBase
|
||||
import solutions.bitbadger.documents.query.byId as byIdBase
|
||||
|
||||
/**
|
||||
* Functions to create queries to patch (partially update) JSON documents
|
||||
*/
|
||||
object Patch {
|
||||
|
||||
/**
|
||||
* Create an `UPDATE` statement to patch documents
|
||||
*
|
||||
* @param tableName The table to be updated
|
||||
* @return A query to patch documents
|
||||
*/
|
||||
private fun patch(tableName: String) =
|
||||
when (Configuration.dialect("create patch query")) {
|
||||
Dialect.POSTGRESQL -> "data || :data"
|
||||
Dialect.SQLITE -> "json_patch(data, json(:data))"
|
||||
}.let { "UPDATE $tableName SET data = $it" }
|
||||
|
||||
/**
|
||||
* A query to patch (partially update) a JSON document by its ID
|
||||
*
|
||||
* @param tableName The name of the table where the document is stored
|
||||
* @param docId The ID of the document to be updated (optional, used for type checking)
|
||||
* @return A query to patch a JSON document by its ID
|
||||
*/
|
||||
fun <TKey> byId(tableName: String, docId: TKey? = null) =
|
||||
byIdBase(patch(tableName), docId)
|
||||
|
||||
/**
|
||||
* A query to patch (partially update) a JSON document using field match criteria
|
||||
*
|
||||
* @param tableName The name of the table where the documents are stored
|
||||
* @param fields The field criteria
|
||||
* @param howMatched How the fields should be matched (optional, defaults to `ALL`)
|
||||
* @return A query to patch JSON documents by field match criteria
|
||||
*/
|
||||
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
|
||||
byFieldsBase(patch(tableName), fields, howMatched)
|
||||
|
||||
/**
|
||||
* A query to patch (partially update) a JSON document by JSON containment (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table where the document is stored
|
||||
* @return A query to patch JSON documents by JSON containment
|
||||
*/
|
||||
fun byContains(tableName: String) =
|
||||
statementWhere(patch(tableName), Where.jsonContains())
|
||||
|
||||
/**
|
||||
* A query to patch (partially update) a JSON document by JSON path match (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table where the document is stored
|
||||
* @return A query to patch JSON documents by JSON path match
|
||||
*/
|
||||
fun byJsonPath(tableName: String) =
|
||||
statementWhere(patch(tableName), Where.jsonPathMatches())
|
||||
}
|
||||
77
src/jvm/src/main/kotlin/query/Query.kt
Normal file
77
src/jvm/src/main/kotlin/query/Query.kt
Normal file
@@ -0,0 +1,77 @@
|
||||
package solutions.bitbadger.documents.query
|
||||
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Dialect
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.FieldMatch
|
||||
|
||||
// ~~~ 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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"
|
||||
}
|
||||
74
src/jvm/src/main/kotlin/query/RemoveFields.kt
Normal file
74
src/jvm/src/main/kotlin/query/RemoveFields.kt
Normal file
@@ -0,0 +1,74 @@
|
||||
package solutions.bitbadger.documents.query
|
||||
|
||||
import solutions.bitbadger.documents.*
|
||||
import solutions.bitbadger.documents.query.byFields as byFieldsBase
|
||||
import solutions.bitbadger.documents.query.byId as byIdBase
|
||||
|
||||
/**
|
||||
* Functions to create queries to remove fields from documents
|
||||
*/
|
||||
object RemoveFields {
|
||||
|
||||
/**
|
||||
* Create a query to remove fields based on the given parameters
|
||||
*
|
||||
* @param tableName The name of the table in which documents should have fields removed
|
||||
* @param toRemove The parameters for the fields to be removed
|
||||
* @return A query to remove fields from documents in the given table
|
||||
*/
|
||||
private fun removeFields(tableName: String, toRemove: Collection<Parameter<*>>) =
|
||||
when (Configuration.dialect("generate field removal query")) {
|
||||
Dialect.POSTGRESQL -> "UPDATE $tableName SET data = data - ${toRemove.elementAt(0).name}::text[]"
|
||||
Dialect.SQLITE -> toRemove.joinToString(", ") { it.name }.let {
|
||||
"UPDATE $tableName SET data = json_remove(data, $it)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A query to patch (partially update) a JSON document by its ID
|
||||
*
|
||||
* @param tableName The name of the table where the document is stored
|
||||
* @param toRemove The parameters for the fields to be removed
|
||||
* @param docId The ID of the document to be updated (optional, used for type checking)
|
||||
* @return A query to patch a JSON document by its ID
|
||||
*/
|
||||
fun <TKey> byId(tableName: String, toRemove: Collection<Parameter<*>>, docId: TKey? = null) =
|
||||
byIdBase(removeFields(tableName, toRemove), docId)
|
||||
|
||||
/**
|
||||
* A query to patch (partially update) a JSON document using field match criteria
|
||||
*
|
||||
* @param tableName The name of the table where the documents are stored
|
||||
* @param toRemove The parameters for the fields to be removed
|
||||
* @param fields The field criteria
|
||||
* @param howMatched How the fields should be matched (optional, defaults to `ALL`)
|
||||
* @return A query to patch JSON documents by field match criteria
|
||||
*/
|
||||
fun byFields(
|
||||
tableName: String,
|
||||
toRemove: Collection<Parameter<*>>,
|
||||
fields: Collection<Field<*>>,
|
||||
howMatched: FieldMatch? = null
|
||||
) =
|
||||
byFieldsBase(removeFields(tableName, toRemove), fields, howMatched)
|
||||
|
||||
/**
|
||||
* A query to patch (partially update) a JSON document by JSON containment (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table where the document is stored
|
||||
* @param toRemove The parameters for the fields to be removed
|
||||
* @return A query to patch JSON documents by JSON containment
|
||||
*/
|
||||
fun byContains(tableName: String, toRemove: Collection<Parameter<*>>) =
|
||||
statementWhere(removeFields(tableName, toRemove), Where.jsonContains())
|
||||
|
||||
/**
|
||||
* A query to patch (partially update) a JSON document by JSON path match (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table where the document is stored
|
||||
* @param toRemove The parameters for the fields to be removed
|
||||
* @return A query to patch JSON documents by JSON path match
|
||||
*/
|
||||
fun byJsonPath(tableName: String, toRemove: Collection<Parameter<*>>) =
|
||||
statementWhere(removeFields(tableName, toRemove), Where.jsonPathMatches())
|
||||
}
|
||||
58
src/jvm/src/main/kotlin/query/Where.kt
Normal file
58
src/jvm/src/main/kotlin/query/Where.kt
Normal file
@@ -0,0 +1,58 @@
|
||||
package solutions.bitbadger.documents.query
|
||||
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Dialect
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.FieldMatch
|
||||
|
||||
/**
|
||||
* Functions to create `WHERE` clause fragments
|
||||
*/
|
||||
object Where {
|
||||
|
||||
/**
|
||||
* Create a `WHERE` clause fragment to query by one or more fields
|
||||
*
|
||||
* @param fields The fields to be queried
|
||||
* @param howMatched How the fields should be matched (optional, defaults to `ALL`)
|
||||
* @return A `WHERE` clause fragment to match the given fields
|
||||
*/
|
||||
fun byFields(fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
|
||||
fields.joinToString(" ${(howMatched ?: FieldMatch.ALL).sql} ") { it.toWhere() }
|
||||
|
||||
/**
|
||||
* Create a `WHERE` clause fragment to retrieve a document by its ID
|
||||
*
|
||||
* @param parameterName The parameter name to use for the ID placeholder (optional, defaults to ":id")
|
||||
* @param docId The ID value (optional; used for type determinations, string assumed if not provided)
|
||||
*/
|
||||
fun <TKey> byId(parameterName: String = ":id", docId: TKey? = null) =
|
||||
byFields(listOf(Field.equal(Configuration.idField, docId ?: "", parameterName)))
|
||||
|
||||
/**
|
||||
* Create a `WHERE` clause fragment to implement a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param parameterName The parameter name to use for the JSON placeholder (optional, defaults to ":criteria")
|
||||
* @return A `WHERE` clause fragment to implement a JSON containment criterion
|
||||
* @throws DocumentException If called against a SQLite database
|
||||
*/
|
||||
fun jsonContains(parameterName: String = ":criteria") =
|
||||
when (Configuration.dialect("create containment WHERE clause")) {
|
||||
Dialect.POSTGRESQL -> "data @> $parameterName"
|
||||
Dialect.SQLITE -> throw DocumentException("JSON containment is not supported")
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `WHERE` clause fragment to implement a JSON path match query (PostgreSQL only)
|
||||
*
|
||||
* @param parameterName The parameter name to use for the placeholder (optional, defaults to ":path")
|
||||
* @return A `WHERE` clause fragment to implement a JSON path match criterion
|
||||
* @throws DocumentException If called against a SQLite database
|
||||
*/
|
||||
fun jsonPathMatches(parameterName: String = ":path") =
|
||||
when (Configuration.dialect("create JSON path match WHERE clause")) {
|
||||
Dialect.POSTGRESQL -> "jsonb_path_exists(data, $parameterName::jsonpath)"
|
||||
Dialect.SQLITE -> throw DocumentException("JSON path match is not supported")
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package solutions.bitbadger.documents.java.java;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import solutions.bitbadger.documents.common.*;
|
||||
import solutions.bitbadger.documents.java.Parameters;
|
||||
import solutions.bitbadger.documents.*;
|
||||
import solutions.bitbadger.documents.java.jvm.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package solutions.bitbadger.documents.java.java.integration.common;
|
||||
|
||||
import solutions.bitbadger.documents.common.Field;
|
||||
import solutions.bitbadger.documents.java.Count;
|
||||
import solutions.bitbadger.documents.Field;
|
||||
import solutions.bitbadger.documents.java.jvm.Count;
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase;
|
||||
import solutions.bitbadger.documents.java.java.testDocs.JsonDocument;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package solutions.bitbadger.documents.java.java.integration.sqlite;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import solutions.bitbadger.documents.common.DocumentException;
|
||||
import solutions.bitbadger.documents.DocumentException;
|
||||
import solutions.bitbadger.documents.java.integration.sqlite.SQLiteDB;
|
||||
import solutions.bitbadger.documents.java.java.integration.common.CountFunctions;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package solutions.bitbadger.documents.java.java.testDocs;
|
||||
|
||||
import kotlinx.serialization.Serializable;
|
||||
import solutions.bitbadger.documents.java.Document;
|
||||
import solutions.bitbadger.documents.java.jvm.Document;
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package solutions.bitbadger.documents.java
|
||||
|
||||
import solutions.bitbadger.documents.common.DocumentSerializer
|
||||
import solutions.bitbadger.documents.DocumentSerializer
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@ package solutions.bitbadger.documents.java
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotSame
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package solutions.bitbadger.documents.java.integration
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import solutions.bitbadger.documents.java.insert
|
||||
import solutions.bitbadger.documents.java.extensions.insert
|
||||
|
||||
/** The test table name to use for integration tests */
|
||||
const val TEST_TABLE = "test_table"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package solutions.bitbadger.documents.java.integration.common
|
||||
|
||||
import solutions.bitbadger.documents.common.Field
|
||||
import solutions.bitbadger.documents.java.countAll
|
||||
import solutions.bitbadger.documents.java.countByContains
|
||||
import solutions.bitbadger.documents.java.countByFields
|
||||
import solutions.bitbadger.documents.java.countByJsonPath
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.java.extensions.countAll
|
||||
import solutions.bitbadger.documents.java.extensions.countByContains
|
||||
import solutions.bitbadger.documents.java.extensions.countByFields
|
||||
import solutions.bitbadger.documents.java.extensions.countByJsonPath
|
||||
import solutions.bitbadger.documents.java.integration.JsonDocument
|
||||
import solutions.bitbadger.documents.java.integration.TEST_TABLE
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package solutions.bitbadger.documents.java.integration.common
|
||||
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.Parameter
|
||||
import solutions.bitbadger.documents.ParameterType
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.common.query.Count
|
||||
import solutions.bitbadger.documents.common.query.Delete
|
||||
import solutions.bitbadger.documents.common.query.Find
|
||||
import solutions.bitbadger.documents.java.*
|
||||
import solutions.bitbadger.documents.java.extensions.*
|
||||
import solutions.bitbadger.documents.java.integration.JsonDocument
|
||||
import solutions.bitbadger.documents.java.integration.TEST_TABLE
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
|
||||
import solutions.bitbadger.documents.java.jvm.Results
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package solutions.bitbadger.documents.java.integration.common
|
||||
|
||||
import solutions.bitbadger.documents.common.DocumentIndex
|
||||
import solutions.bitbadger.documents.java.ensureDocumentIndex
|
||||
import solutions.bitbadger.documents.java.ensureFieldIndex
|
||||
import solutions.bitbadger.documents.java.ensureTable
|
||||
import solutions.bitbadger.documents.DocumentIndex
|
||||
import solutions.bitbadger.documents.java.extensions.ensureDocumentIndex
|
||||
import solutions.bitbadger.documents.java.extensions.ensureFieldIndex
|
||||
import solutions.bitbadger.documents.java.extensions.ensureTable
|
||||
import solutions.bitbadger.documents.java.integration.TEST_TABLE
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package solutions.bitbadger.documents.java.integration.common
|
||||
|
||||
import solutions.bitbadger.documents.common.Field
|
||||
import solutions.bitbadger.documents.java.*
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.java.extensions.*
|
||||
import solutions.bitbadger.documents.java.integration.JsonDocument
|
||||
import solutions.bitbadger.documents.java.integration.TEST_TABLE
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package solutions.bitbadger.documents.java.integration.common
|
||||
|
||||
import AutoId
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.java.*
|
||||
import solutions.bitbadger.documents.java.extensions.*
|
||||
import solutions.bitbadger.documents.java.integration.*
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package solutions.bitbadger.documents.java.integration.common
|
||||
|
||||
import solutions.bitbadger.documents.common.Field
|
||||
import solutions.bitbadger.documents.java.existsByContains
|
||||
import solutions.bitbadger.documents.java.existsByFields
|
||||
import solutions.bitbadger.documents.java.existsById
|
||||
import solutions.bitbadger.documents.java.existsByJsonPath
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.java.extensions.existsByContains
|
||||
import solutions.bitbadger.documents.java.extensions.existsByFields
|
||||
import solutions.bitbadger.documents.java.extensions.existsById
|
||||
import solutions.bitbadger.documents.java.extensions.existsByJsonPath
|
||||
import solutions.bitbadger.documents.java.integration.JsonDocument
|
||||
import solutions.bitbadger.documents.java.integration.TEST_TABLE
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package solutions.bitbadger.documents.java.integration.common
|
||||
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.FieldMatch
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.java.*
|
||||
import solutions.bitbadger.documents.java.extensions.*
|
||||
import solutions.bitbadger.documents.java.integration.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package solutions.bitbadger.documents.java.integration.common
|
||||
|
||||
import solutions.bitbadger.documents.common.Field
|
||||
import solutions.bitbadger.documents.java.*
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.java.extensions.*
|
||||
import solutions.bitbadger.documents.java.integration.JsonDocument
|
||||
import solutions.bitbadger.documents.java.integration.TEST_TABLE
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package solutions.bitbadger.documents.java.integration.common
|
||||
|
||||
import solutions.bitbadger.documents.common.Field
|
||||
import solutions.bitbadger.documents.java.*
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.java.extensions.*
|
||||
import solutions.bitbadger.documents.java.integration.JsonDocument
|
||||
import solutions.bitbadger.documents.java.integration.TEST_TABLE
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
package solutions.bitbadger.documents.java.integration.postgresql
|
||||
|
||||
import AutoId
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Parameter
|
||||
import solutions.bitbadger.documents.ParameterType
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.java.*
|
||||
import solutions.bitbadger.documents.java.extensions.customNonQuery
|
||||
import solutions.bitbadger.documents.java.extensions.customScalar
|
||||
import solutions.bitbadger.documents.java.extensions.ensureTable
|
||||
import solutions.bitbadger.documents.java.integration.TEST_TABLE
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
|
||||
import solutions.bitbadger.documents.java.jvm.DocumentConfig
|
||||
import solutions.bitbadger.documents.java.jvm.Results
|
||||
|
||||
/**
|
||||
* A wrapper for a throwaway PostgreSQL database
|
||||
|
||||
@@ -2,7 +2,7 @@ package solutions.bitbadger.documents.java.integration.sqlite
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import solutions.bitbadger.documents.common.DocumentException
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.java.integration.common.Count
|
||||
import kotlin.test.Test
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package solutions.bitbadger.documents.java.integration.sqlite
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import solutions.bitbadger.documents.common.DocumentException
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.java.integration.common.Definition
|
||||
import kotlin.test.Test
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package solutions.bitbadger.documents.java.integration.sqlite
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import solutions.bitbadger.documents.common.DocumentException
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.java.integration.common.Delete
|
||||
import kotlin.test.Test
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package solutions.bitbadger.documents.java.integration.sqlite
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import solutions.bitbadger.documents.common.DocumentException
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.java.integration.common.Exists
|
||||
import kotlin.test.Test
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package solutions.bitbadger.documents.java.integration.sqlite
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import solutions.bitbadger.documents.common.DocumentException
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.java.integration.common.Find
|
||||
import kotlin.test.Test
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package solutions.bitbadger.documents.java.integration.sqlite
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import solutions.bitbadger.documents.common.DocumentException
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.java.integration.common.Patch
|
||||
import kotlin.test.Test
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package solutions.bitbadger.documents.java.integration.sqlite
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import solutions.bitbadger.documents.common.DocumentException
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.java.integration.common.RemoveFields
|
||||
import kotlin.test.Test
|
||||
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
package solutions.bitbadger.documents.java.integration.sqlite
|
||||
|
||||
import AutoId
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
import solutions.bitbadger.documents.Parameter
|
||||
import solutions.bitbadger.documents.ParameterType
|
||||
import solutions.bitbadger.documents.common.*
|
||||
import solutions.bitbadger.documents.java.*
|
||||
import solutions.bitbadger.documents.java.extensions.customScalar
|
||||
import solutions.bitbadger.documents.java.extensions.ensureTable
|
||||
import solutions.bitbadger.documents.java.integration.TEST_TABLE
|
||||
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
|
||||
import solutions.bitbadger.documents.java.jvm.DocumentConfig
|
||||
import solutions.bitbadger.documents.java.jvm.Results
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user