Initial Development (#1)

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
This commit was merged in pull request #1.
This commit is contained in:
2025-04-16 01:29:19 +00:00
parent 995f565255
commit d202c002b5
385 changed files with 41134 additions and 1 deletions

186
src/kotlinx/pom.xml Normal file
View File

@@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>solutions.bitbadger</groupId>
<artifactId>documents</artifactId>
<version>1.0.0-RC1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<groupId>solutions.bitbadger.documents</groupId>
<artifactId>kotlinx</artifactId>
<name>${project.groupId}:${project.artifactId}</name>
<description>Expose a document store interface for PostgreSQL and SQLite (KotlinX Serialization Library)</description>
<url>https://relationaldocs.bitbadger.solutions/jvm/</url>
<licenses>
<license>
<name>MIT License</name>
<url>https://www.opensource.org/licenses/mit-license.php</url>
</license>
</licenses>
<developers>
<developer>
<name>Daniel J. Summers</name>
<email>daniel@bitbadger.solutions</email>
<organization>Bit Badger Solutions</organization>
<organizationUrl>https://bitbadger.solutions</organizationUrl>
</developer>
</developers>
<scm>
<connection>scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git</connection>
<developerConnection>scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git</developerConnection>
<url>https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents</url>
</scm>
<dependencies>
<dependency>
<groupId>solutions.bitbadger.documents</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-json-jvm</artifactId>
<version>${serialization.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/java</sourceDir>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<phase>process-test-sources</phase>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/java</sourceDir>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
<configuration>
<compilerPlugins>
<plugin>kotlinx-serialization</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-serialization</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${failsafe.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>${buildHelperPlugin.version}</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/kotlin</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${sourcePlugin.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jetbrains.dokka</groupId>
<artifactId>dokka-maven-plugin</artifactId>
<version>2.0.0</version>
<configuration>
<reportUndocumented>true</reportUndocumented>
<includes>${project.basedir}/src/main/module-info.md</includes>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>javadocJar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.7.0</version>
<extensions>true</extensions>
<configuration>
<deploymentName>Deployment-kotlinx-${project.version}</deploymentName>
<publishingServerId>central</publishingServerId>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,10 @@
module solutions.bitbadger.documents.kotlinx {
requires solutions.bitbadger.documents.core;
requires kotlin.stdlib;
requires kotlin.reflect;
requires kotlinx.serialization.json;
requires java.sql;
exports solutions.bitbadger.documents.kotlinx;
exports solutions.bitbadger.documents.kotlinx.extensions;
}

View File

@@ -0,0 +1,120 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.query.CountQuery
import java.sql.Connection
/**
* Functions to count documents
*/
object Count {
/**
* Count all documents in the table
*
* @param tableName The name of the table in which documents should be counted
* @param conn The connection over which documents should be counted
* @return A count of the documents in the table
*/
fun all(tableName: String, conn: Connection) =
conn.customScalar(CountQuery.all(tableName), mapFunc = Results::toCount)
/**
* Count all documents in the table
*
* @param tableName The name of the table in which documents should be counted
* @return A count of the documents in the table
*/
fun all(tableName: String) =
Configuration.dbConn().use { all(tableName, it) }
/**
* Count documents using a field comparison
*
* @param tableName The name of the table in which documents should be counted
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param conn The connection on which the deletion should be executed
* @return A count of the matching documents in the table
*/
fun byFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
conn: Connection
): Long {
val named = Parameters.nameFields(fields)
return conn.customScalar(
CountQuery.byFields(tableName, named, howMatched),
Parameters.addFields(named),
Results::toCount
)
}
/**
* Count documents using a field comparison
*
* @param tableName The name of the table in which documents should be counted
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @return A count of the matching documents in the table
*/
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
Configuration.dbConn().use { byFields(tableName, fields, howMatched, it) }
/**
* Count documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be counted
* @param criteria The object for which JSON containment should be checked
* @param conn The connection on which the count should be executed
* @return A count of the matching documents in the table
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
conn.customScalar<Long>(
CountQuery.byContains(tableName),
listOf(Parameters.json<TContains>(":criteria", criteria)),
Results::toCount
)
/**
* Count documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be counted
* @param criteria The object for which JSON containment should be checked
* @return A count of the matching documents in the table
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(tableName: String, criteria: TContains) =
Configuration.dbConn().use { byContains(tableName, criteria, it) }
/**
* Count documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be counted
* @param path The JSON path comparison to match
* @param conn The connection on which the count should be executed
* @return A count of the matching documents in the table
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, conn: Connection) =
conn.customScalar(
CountQuery.byJsonPath(tableName),
listOf(Parameter(":path", ParameterType.STRING, path)),
Results::toCount
)
/**
* Count documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be counted
* @param path The JSON path comparison to match
* @return A count of the matching documents in the table
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String) =
Configuration.dbConn().use { byJsonPath(tableName, path, it) }
}

View File

@@ -0,0 +1,218 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.Configuration
import java.io.PrintWriter
import solutions.bitbadger.documents.java.Custom as CoreCustom
import java.sql.Connection
import java.sql.ResultSet
/**
* Custom query execution functions
*/
object Custom {
/**
* Execute a query that returns a list of results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param conn The connection over which the query should be executed
* @param mapFunc The mapping function between the document and the domain item
* @return A list of results for the given query
*/
inline fun <reified TDoc : Any> list(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
conn: Connection,
mapFunc: (ResultSet) -> TDoc
) = Parameters.apply(conn, query, parameters).use { Results.toCustomList(it, mapFunc) }
/**
* Execute a query that returns a list of results (creates connection)
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return A list of results for the given query
*/
inline fun <reified TDoc : Any> list(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
mapFunc: (ResultSet) -> TDoc
) = Configuration.dbConn().use { list(query, parameters, it, mapFunc) }
/**
* Execute a query that returns a JSON array of results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param conn The connection over which the query should be executed
* @param mapFunc The mapping function to extract the JSON from the query
* @return A JSON array of results for the given query
* @throws DocumentException If parameters are invalid
*/
fun jsonArray(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
conn: Connection,
mapFunc: (ResultSet) -> String
) = CoreCustom.jsonArray(query, parameters, conn, mapFunc)
/**
* Execute a query that returns a JSON array of results (creates connection)
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function to extract the JSON from the query
* @return A JSON array of results for the given query
* @throws DocumentException If parameters are invalid
*/
fun jsonArray(query: String, parameters: Collection<Parameter<*>> = listOf(), mapFunc: (ResultSet) -> String) =
CoreCustom.jsonArray(query, parameters, mapFunc)
/**
* Execute a query, writing its JSON array of results to the given `PrintWriter`
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param writer The writer to which the results should be written
* @param conn The connection over which the query should be executed
* @param mapFunc The mapping function to extract the JSON from the query
* @throws DocumentException If parameters are invalid
*/
fun writeJsonArray(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
writer: PrintWriter,
conn: Connection,
mapFunc: (ResultSet) -> String
) = CoreCustom.writeJsonArray(query, parameters, writer, conn, mapFunc)
/**
* Execute a query, writing its JSON array of results to the given `PrintWriter` (creates connection)
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param writer The writer to which the results should be written
* @param mapFunc The mapping function to extract the JSON from the query
* @throws DocumentException If parameters are invalid
*/
fun writeJsonArray(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
writer: PrintWriter,
mapFunc: (ResultSet) -> String
) = CoreCustom.writeJsonArray(query, parameters, writer, mapFunc)
/**
* Execute a query that returns one or no results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param conn The connection over which the query should be executed
* @param mapFunc The mapping function between the document and the domain item
* @return The document if one matches the query, `null` otherwise
*/
inline fun <reified TDoc : Any> single(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
conn: Connection,
mapFunc: (ResultSet) -> TDoc
) = list("$query LIMIT 1", parameters, conn, mapFunc).singleOrNull()
/**
* Execute a query that returns one or no results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return The document if one matches the query, `null` otherwise
*/
inline fun <reified TDoc : Any> single(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
noinline mapFunc: (ResultSet) -> TDoc
) = Configuration.dbConn().use { single(query, parameters, it, mapFunc) }
/**
* Execute a query that returns JSON for one or no documents
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param conn The connection over which the query should be executed
* @param mapFunc The mapping function between the document and the domain item
* @return The JSON for the document if found, an empty object (`{}`) if not
* @throws DocumentException If parameters are invalid
*/
fun jsonSingle(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
conn: Connection,
mapFunc: (ResultSet) -> String
) = CoreCustom.jsonSingle(query, parameters, conn, mapFunc)
/**
* Execute a query that returns JSON for one or no documents (creates connection)
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return The JSON for the document if found, an empty object (`{}`) if not
* @throws DocumentException If parameters are invalid
*/
fun jsonSingle(query: String, parameters: Collection<Parameter<*>> = listOf(), mapFunc: (ResultSet) -> String) =
CoreCustom.jsonSingle(query, parameters, mapFunc)
/**
* Execute a query that returns no results
*
* @param query The query to retrieve the results
* @param conn The connection over which the query should be executed
* @param parameters Parameters to use for the query
*/
fun nonQuery(query: String, parameters: Collection<Parameter<*>> = listOf(), conn: Connection) =
CoreCustom.nonQuery(query, parameters, conn)
/**
* Execute a query that returns no results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
*/
fun nonQuery(query: String, parameters: Collection<Parameter<*>> = listOf()) =
Configuration.dbConn().use { nonQuery(query, parameters, it) }
/**
* Execute a query that returns a scalar result
*
* @param query The query to retrieve the result
* @param parameters Parameters to use for the query
* @param conn The connection over which the query should be executed
* @param mapFunc The mapping function between the document and the domain item
* @return The scalar value from the query
*/
inline fun <reified T : Any> scalar(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
conn: Connection,
mapFunc: (ResultSet) -> T
) = Parameters.apply(conn, query, parameters).use { stmt ->
stmt.executeQuery().use { rs ->
rs.next()
mapFunc(rs)
}
}
/**
* Execute a query that returns a scalar result
*
* @param query The query to retrieve the result
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return The scalar value from the query
*/
inline fun <reified T : Any> scalar(
query: String, parameters: Collection<Parameter<*>> = listOf(), mapFunc: (ResultSet) -> T
) = Configuration.dbConn().use { scalar(query, parameters, it, mapFunc) }
}

View File

@@ -0,0 +1,71 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.DocumentIndex
import solutions.bitbadger.documents.java.Definition as JvmDefinition
import java.sql.Connection
/**
* Functions to define tables and indexes
*/
object Definition {
/**
* Create a document table if necessary
*
* @param tableName The table whose existence should be ensured (may include schema)
* @param conn The connection on which the query should be executed
*/
fun ensureTable(tableName: String, conn: Connection) =
JvmDefinition.ensureTable(tableName, conn)
/**
* Create a document table if necessary
*
* @param tableName The table whose existence should be ensured (may include schema)
*/
fun ensureTable(tableName: String) =
JvmDefinition.ensureTable(tableName)
/**
* Create an index on field(s) within documents in the specified table if necessary
*
* @param tableName The table to be indexed (may include schema)
* @param indexName The name of the index to create
* @param fields One or more fields to be indexed<
* @param conn The connection on which the query should be executed
*/
fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>, conn: Connection) =
JvmDefinition.ensureFieldIndex(tableName, indexName, fields, conn)
/**
* Create an index on field(s) within documents in the specified table if necessary
*
* @param tableName The table to be indexed (may include schema)
* @param indexName The name of the index to create
* @param fields One or more fields to be indexed<
*/
fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>) =
JvmDefinition.ensureFieldIndex(tableName, indexName, fields)
/**
* Create a document index on a table (PostgreSQL only)
*
* @param tableName The table to be indexed (may include schema)
* @param indexType The type of index to ensure
* @param conn The connection on which the query should be executed
* @throws DocumentException If called on a SQLite connection
*/
fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex, conn: Connection) =
JvmDefinition.ensureDocumentIndex(tableName, indexType, conn)
/**
* Create a document index on a table (PostgreSQL only)
*
* @param tableName The table to be indexed (may include schema)
* @param indexType The type of index to ensure
* @throws DocumentException If called on a SQLite connection
*/
fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex) =
JvmDefinition.ensureDocumentIndex(tableName, indexType)
}

View File

@@ -0,0 +1,95 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.java.Delete as JvmDelete
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.query.DeleteQuery
import java.sql.Connection
/**
* Functions to delete documents
*/
object Delete {
/**
* Delete a document by its ID
*
* @param tableName The name of the table from which documents should be deleted
* @param docId The ID of the document to be deleted
* @param conn The connection on which the deletion should be executed
*/
fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) =
JvmDelete.byId(tableName, docId, conn)
/**
* Delete a document by its ID
*
* @param tableName The name of the table from which documents should be deleted
* @param docId The ID of the document to be deleted
*/
fun <TKey> byId(tableName: String, docId: TKey) =
JvmDelete.byId(tableName, docId)
/**
* Delete documents using a field comparison
*
* @param tableName The name of the table from which documents should be deleted
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param conn The connection on which the deletion should be executed
*/
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null, conn: Connection) =
JvmDelete.byFields(tableName, fields, howMatched, conn)
/**
* Delete documents using a field comparison
*
* @param tableName The name of the table from which documents should be deleted
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
*/
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
JvmDelete.byFields(tableName, fields, howMatched)
/**
* Delete documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table from which documents should be deleted
* @param criteria The object for which JSON containment should be checked
* @param conn The connection on which the deletion should be executed
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
conn.customNonQuery(DeleteQuery.byContains(tableName), listOf(Parameters.json<TContains>(":criteria", criteria)))
/**
* Delete documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table from which documents should be deleted
* @param criteria The object for which JSON containment should be checked
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(tableName: String, criteria: TContains) =
Configuration.dbConn().use { byContains(tableName, criteria, it) }
/**
* Delete documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table from which documents should be deleted
* @param path The JSON path comparison to match
* @param conn The connection on which the deletion should be executed
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, conn: Connection) =
JvmDelete.byJsonPath(tableName, path, conn)
/**
* Delete documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table from which documents should be deleted
* @param path The JSON path comparison to match
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String) =
JvmDelete.byJsonPath(tableName, path)
}

View File

@@ -0,0 +1,114 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.AutoId
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.kotlinx.extensions.customNonQuery
import solutions.bitbadger.documents.query.DocumentQuery
import solutions.bitbadger.documents.query.Where
import solutions.bitbadger.documents.query.statementWhere
import java.sql.Connection
/**
* Functions for manipulating documents
*/
object Document {
/**
* Insert a new document
*
* @param tableName The table into which the document should be inserted (may include schema)
* @param document The document to be inserted
* @param conn The connection on which the query should be executed
*/
inline fun <reified TDoc> insert(tableName: String, document: TDoc, conn: Connection) {
val strategy = Configuration.autoIdStrategy
val query = if (strategy == AutoId.DISABLED) {
DocumentQuery.insert(tableName)
} else {
val idField = Configuration.idField
val dialect = Configuration.dialect("Create auto-ID insert query")
val dataParam = if (AutoId.needsAutoId(strategy, document, idField)) {
when (dialect) {
Dialect.POSTGRESQL ->
when (strategy) {
AutoId.NUMBER -> "' || (SELECT coalesce(max(data->>'$idField')::numeric, 0) + 1 " +
"FROM $tableName) || '"
AutoId.UUID -> "\"${AutoId.generateUUID()}\""
AutoId.RANDOM_STRING -> "\"${AutoId.generateRandomString()}\""
else -> "\"' || (:data)->>'$idField' || '\""
}.let { ":data::jsonb || ('{\"$idField\":$it}')::jsonb" }
Dialect.SQLITE ->
when (strategy) {
AutoId.NUMBER -> "(SELECT coalesce(max(data->>'$idField'), 0) + 1 FROM $tableName)"
AutoId.UUID -> "'${AutoId.generateUUID()}'"
AutoId.RANDOM_STRING -> "'${AutoId.generateRandomString()}'"
else -> "(:data)->>'$idField'"
}.let { "json_set(:data, '$.$idField', $it)" }
}
} else {
":data"
}
DocumentQuery.insert(tableName).replace(":data", dataParam)
}
conn.customNonQuery(query, listOf(Parameters.json(":data", document)))
}
/**
* Insert a new document
*
* @param tableName The table into which the document should be inserted (may include schema)
* @param document The document to be inserted
*/
inline fun <reified TDoc> insert(tableName: String, document: TDoc) =
Configuration.dbConn().use { insert(tableName, document, it) }
/**
* Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
*
* @param tableName The table in which the document should be saved (may include schema)
* @param document The document to be saved
* @param conn The connection on which the query should be executed
*/
inline fun <reified TDoc> save(tableName: String, document: TDoc, conn: Connection) =
conn.customNonQuery(DocumentQuery.save(tableName), listOf(Parameters.json(":data", document)))
/**
* Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
*
* @param tableName The table in which the document should be saved (may include schema)
* @param document The document to be saved
*/
inline fun <reified TDoc> save(tableName: String, document: TDoc) =
Configuration.dbConn().use { save(tableName, document, it) }
/**
* Update (replace) a document by its ID
*
* @param tableName The table in which the document should be replaced (may include schema)
* @param docId The ID of the document to be replaced
* @param document The document to be replaced
* @param conn The connection on which the query should be executed
*/
inline fun <TKey, reified TDoc> update(tableName: String, docId: TKey, document: TDoc, conn: Connection) =
conn.customNonQuery(
statementWhere(DocumentQuery.update(tableName), Where.byId(":id", docId)),
Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")),
mutableListOf(Parameters.json(":data", document))
)
)
/**
* Update (replace) a document by its ID
*
* @param tableName The table in which the document should be replaced (may include schema)
* @param docId The ID of the document to be replaced
* @param document The document to be replaced
*/
inline fun <TKey, reified TDoc> update(tableName: String, docId: TKey, document: TDoc) =
Configuration.dbConn().use { update(tableName, docId, document, it) }
}

View File

@@ -0,0 +1,33 @@
package solutions.bitbadger.documents.kotlinx
import kotlinx.serialization.json.Json
/**
* Configuration for document serialization
*/
object DocumentConfig {
val options = Json {
coerceInputValues = true
encodeDefaults = true
explicitNulls = false
}
/**
* Serialize a document to JSON
*
* @param document The document to be serialized
* @return The JSON string with the serialized document
*/
inline fun <reified TDoc> serialize(document: TDoc) =
options.encodeToString(document)
/**
* Deserialize a document from JSON
*
* @param json The JSON string with the serialized document
* @return The document created from the given JSON
*/
inline fun <reified TDoc> deserialize(json: String) =
options.decodeFromString<TDoc>(json)
}

View File

@@ -0,0 +1,107 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.java.Exists as JvmExists
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.query.ExistsQuery
import java.sql.Connection
/**
* Functions to determine whether documents exist
*/
object Exists {
/**
* Determine a document's existence by its ID
*
* @param tableName The name of the table in which document existence should be checked
* @param docId The ID of the document to be checked
* @param conn The connection on which the existence check should be executed
* @return True if the document exists, false if not
*/
fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) =
JvmExists.byId(tableName, docId, conn)
/**
* Determine a document's existence by its ID
*
* @param tableName The name of the table in which document existence should be checked
* @param docId The ID of the document to be checked
* @return True if the document exists, false if not
*/
fun <TKey> byId(tableName: String, docId: TKey) =
JvmExists.byId(tableName, docId)
/**
* Determine document existence using a field comparison
*
* @param tableName The name of the table in which document existence should be checked
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param conn The connection on which the existence check should be executed
* @return True if any matching documents exist, false if not
*/
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null, conn: Connection) =
JvmExists.byFields(tableName, fields, howMatched, conn)
/**
* Determine document existence using a field comparison
*
* @param tableName The name of the table in which document existence should be checked
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @return True if any matching documents exist, false if not
*/
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
JvmExists.byFields(tableName, fields, howMatched)
/**
* Determine document existence using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which document existence should be checked
* @param criteria The object for which JSON containment should be checked
* @param conn The connection on which the existence check should be executed
* @return True if any matching documents exist, false if not
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
conn.customScalar(
ExistsQuery.byContains(tableName),
listOf(Parameters.json(":criteria", criteria)),
Results::toExists
)
/**
* Determine document existence using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which document existence should be checked
* @param criteria The object for which JSON containment should be checked
* @return True if any matching documents exist, false if not
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(tableName: String, criteria: TContains) =
Configuration.dbConn().use { byContains(tableName, criteria, it) }
/**
* Determine document existence using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which document existence should be checked
* @param path The JSON path comparison to match
* @param conn The connection on which the existence check should be executed
* @return True if any matching documents exist, false if not
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, conn: Connection) =
JvmExists.byJsonPath(tableName, path, conn)
/**
* Determine document existence using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which document existence should be checked
* @param path The JSON path comparison to match
* @return True if any matching documents exist, false if not
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String) =
JvmExists.byJsonPath(tableName, path)
}

View File

@@ -0,0 +1,417 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.query.FindQuery
import solutions.bitbadger.documents.query.orderBy
import java.sql.Connection
/**
* Functions to find and retrieve documents
*/
object Find {
/**
* Retrieve all documents in the given table, ordering results by the optional given fields
*
* @param tableName The table from which documents should be retrieved
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return A list of documents from the given table
*/
inline fun <reified TDoc : Any> all(tableName: String, orderBy: Collection<Field<*>>? = null, conn: Connection) =
conn.customList<TDoc>(FindQuery.all(tableName) + (orderBy?.let(::orderBy) ?: ""), mapFunc = Results::fromData)
/**
* Retrieve all documents in the given table
*
* @param tableName The table from which documents should be retrieved
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents from the given table
*/
inline fun <reified TDoc : Any> all(tableName: String, orderBy: Collection<Field<*>>? = null) =
Configuration.dbConn().use { all<TDoc>(tableName, orderBy, it) }
/**
* Retrieve all documents in the given table
*
* @param tableName The table from which documents should be retrieved
* @param conn The connection over which documents should be retrieved
* @return A list of documents from the given table
*/
inline fun <reified TDoc : Any> all(tableName: String, conn: Connection) =
all<TDoc>(tableName, null, conn)
/**
* Retrieve a document by its ID
*
* @param tableName The table from which the document should be retrieved
* @param docId The ID of the document to retrieve
* @param conn The connection over which documents should be retrieved
* @return The document if it is found, `null` otherwise
*/
inline fun <TKey, reified TDoc : Any> byId(tableName: String, docId: TKey, conn: Connection) =
conn.customSingle<TDoc>(
FindQuery.byId(tableName, docId),
Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))),
Results::fromData
)
/**
* Retrieve a document by its ID
*
* @param tableName The table from which the document should be retrieved
* @param docId The ID of the document to retrieve
* @return The document if it is found, `null` otherwise
*/
inline fun <TKey, reified TDoc : Any> byId(tableName: String, docId: TKey) =
Configuration.dbConn().use { byId<TKey, TDoc>(tableName, docId, it) }
/**
* Retrieve documents using a field comparison, ordering results by the given fields
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return A list of documents matching the field comparison
*/
inline fun <reified TDoc : Any> byFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null,
conn: Connection
): List<TDoc> {
val named = Parameters.nameFields(fields)
return conn.customList<TDoc>(
FindQuery.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""),
Parameters.addFields(named),
Results::fromData
)
}
/**
* Retrieve documents using a field comparison, ordering results by the given fields
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents matching the field comparison
*/
inline fun <reified TDoc : Any> byFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) =
Configuration.dbConn().use { byFields<TDoc>(tableName, fields, howMatched, orderBy, it) }
/**
* Retrieve documents using a field comparison
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param conn The connection over which documents should be retrieved
* @return A list of documents matching the field comparison
*/
inline fun <reified TDoc : Any> byFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
conn: Connection
) =
byFields<TDoc>(tableName, fields, howMatched, null, conn)
/**
* Retrieve documents using a field comparison
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @return A list of documents matching the field comparison
*/
inline fun <reified TDoc : Any> byFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null
) =
Configuration.dbConn().use { byFields<TDoc>(tableName, fields, howMatched, null, it) }
/**
* Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any, reified TContains> byContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) =
conn.customList<TDoc>(
FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameters.json(":criteria", criteria)),
Results::fromData
)
/**
* Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any, reified TContains> byContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) =
Configuration.dbConn().use { byContains<TDoc, TContains>(tableName, criteria, orderBy, it) }
/**
* Retrieve documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param conn The connection over which documents should be retrieved
* @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any, reified TContains> byContains(
tableName: String,
criteria: TContains,
conn: Connection
) =
byContains<TDoc, TContains>(tableName, criteria, null, conn)
/**
* Retrieve documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any, reified TContains> byContains(tableName: String, criteria: TContains) =
Configuration.dbConn().use { byContains<TDoc, TContains>(tableName, criteria, it) }
/**
* Retrieve documents using a JSON Path match query, ordering results by the given fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return A list of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any> byJsonPath(
tableName: String,
path: String,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) =
conn.customList<TDoc>(
FindQuery.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameter(":path", ParameterType.STRING, path)),
Results::fromData
)
/**
* Retrieve documents using a JSON Path match query, ordering results by the given fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any> byJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null) =
Configuration.dbConn().use { byJsonPath<TDoc>(tableName, path, orderBy, it) }
/**
* Retrieve documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param conn The connection over which documents should be retrieved
* @return A list of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any> byJsonPath(tableName: String, path: String, conn: Connection) =
byJsonPath<TDoc>(tableName, path, null, conn)
/**
* Retrieve the first document using a field comparison and optional ordering fields
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return The first document matching the field comparison, or `null` if no matches are found
*/
inline fun <reified TDoc : Any> firstByFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null,
conn: Connection
): TDoc? {
val named = Parameters.nameFields(fields)
return conn.customSingle<TDoc>(
FindQuery.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""),
Parameters.addFields(named),
Results::fromData
)
}
/**
* Retrieve the first document using a field comparison and optional ordering fields
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first document matching the field comparison, or `null` if no matches are found
*/
inline fun <reified TDoc : Any> firstByFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) =
Configuration.dbConn().use { firstByFields<TDoc>(tableName, fields, howMatched, orderBy, it) }
/**
* Retrieve the first document using a field comparison
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param conn The connection over which documents should be retrieved
* @return The first document matching the field comparison, or `null` if no matches are found
*/
inline fun <reified TDoc : Any> firstByFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
conn: Connection
) =
firstByFields<TDoc>(tableName, fields, howMatched, null, conn)
/**
* Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return The first document matching the JSON containment query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any, reified TContains> firstByContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) =
conn.customSingle<TDoc>(
FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameters.json(":criteria", criteria)),
Results::fromData
)
/**
* Retrieve the first document using a JSON containment query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param conn The connection over which documents should be retrieved
* @return The first document matching the JSON containment query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any, reified TContains> firstByContains(
tableName: String,
criteria: TContains,
conn: Connection
) =
firstByContains<TDoc, TContains>(tableName, criteria, null, conn)
/**
* Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first document matching the JSON containment query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any, reified TContains> firstByContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) =
Configuration.dbConn().use { firstByContains<TDoc, TContains>(tableName, criteria, orderBy, it) }
/**
* Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return The first document matching the JSON Path match query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any> firstByJsonPath(
tableName: String,
path: String,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) =
conn.customSingle<TDoc>(
FindQuery.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameter(":path", ParameterType.STRING, path)),
Results::fromData
)
/**
* Retrieve the first document using a JSON Path match query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param conn The connection over which documents should be retrieved
* @return The first document matching the JSON Path match query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any> firstByJsonPath(tableName: String, path: String, conn: Connection) =
firstByJsonPath<TDoc>(tableName, path, null, conn)
/**
* Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first document matching the JSON Path match query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any> firstByJsonPath(
tableName: String,
path: String,
orderBy: Collection<Field<*>>? = null
) =
Configuration.dbConn().use { firstByJsonPath<TDoc>(tableName, path, orderBy, it) }
}

View File

@@ -0,0 +1,731 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.query.FindQuery
import solutions.bitbadger.documents.query.orderBy
import java.io.PrintWriter
import solutions.bitbadger.documents.java.Json as CoreJson
import java.sql.Connection
/**
* Functions to find and retrieve documents, returning them as JSON strings
*/
object Json {
/**
* Retrieve all documents in the given table, ordering results by the optional given fields
*
* @param tableName The table from which documents should be retrieved
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return A JSON array of documents from the given table
* @throws DocumentException If query execution fails
*/
fun all(tableName: String, orderBy: Collection<Field<*>>? = null, conn: Connection) =
CoreJson.all(tableName, orderBy, conn)
/**
* Retrieve all documents in the given table (creates connection)
*
* @param tableName The table from which documents should be retrieved
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A JSON array of documents from the given table
* @throws DocumentException If no connection string has been set, or if query execution fails
*/
fun all(tableName: String, orderBy: Collection<Field<*>>? = null) =
CoreJson.all(tableName, orderBy)
/**
* Retrieve all documents in the given table
*
* @param tableName The table from which documents should be retrieved
* @param conn The connection over which documents should be retrieved
* @return A JSON array of documents from the given table
* @throws DocumentException If query execution fails
*/
fun all(tableName: String, conn: Connection) =
CoreJson.all(tableName, conn)
/**
* Write all documents in the given table to the given `PrintWriter`, ordering results by the optional given fields
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If query execution fails
*/
fun writeAll(tableName: String, writer: PrintWriter, orderBy: Collection<Field<*>>? = null, conn: Connection) =
CoreJson.writeAll(tableName, writer, orderBy, conn)
/**
* Write all documents in the given table to the given `PrintWriter`, ordering results by the optional given fields
* (creates connection)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If query execution fails
*/
fun writeAll(tableName: String, writer: PrintWriter, orderBy: Collection<Field<*>>? = null) =
CoreJson.writeAll(tableName, writer, orderBy)
/**
* Write all documents in the given table to the given `PrintWriter`
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If query execution fails
*/
fun writeAll(tableName: String, writer: PrintWriter, conn: Connection) =
CoreJson.writeAll(tableName, writer, conn)
/**
* Retrieve a document by its ID
*
* @param tableName The table from which the document should be retrieved
* @param docId The ID of the document to retrieve
* @param conn The connection over which documents should be retrieved
* @return A JSON document if found, an empty JSON object if not found
* @throws DocumentException If no dialect has been configured
*/
fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) =
CoreJson.byId(tableName, docId, conn)
/**
* Retrieve a document by its ID (creates connection)
*
* @param tableName The table from which the document should be retrieved
* @param docId The ID of the document to retrieve
* @return A JSON document if found, an empty JSON object if not found
* @throws DocumentException If no connection string has been set
*/
fun <TKey> byId(tableName: String, docId: TKey) =
CoreJson.byId(tableName, docId)
/**
* Write a document to the given `PrintWriter` by its ID
*
* @param tableName The table from which the document should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param docId The ID of the document to retrieve
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If no dialect has been configured
*/
fun <TKey> writeById(tableName: String, writer: PrintWriter, docId: TKey, conn: Connection) =
CoreJson.writeById(tableName, writer, docId, conn)
/**
* Write a document to the given `PrintWriter` by its ID (creates connection)
*
* @param tableName The table from which the document should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param docId The ID of the document to retrieve
* @throws DocumentException If no dialect has been configured
*/
fun <TKey> writeById(tableName: String, writer: PrintWriter, docId: TKey) =
CoreJson.writeById(tableName, writer, docId)
/**
* Retrieve documents using a field comparison, ordering results by the given fields
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return A JSON array of documents matching the field comparison
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
*/
fun byFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) = CoreJson.byFields(tableName, fields, howMatched, orderBy, conn)
/**
* Retrieve documents using a field comparison, ordering results by the given fields (creates connection)
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A JSON array of documents matching the field comparison
* @throws DocumentException If no connection string has been set, or if parameters are invalid
*/
fun byFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) = CoreJson.byFields(tableName, fields, howMatched, orderBy)
/**
* Retrieve documents using a field comparison
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param conn The connection over which documents should be retrieved
* @return A JSON array of documents matching the field comparison
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
*/
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null, conn: Connection) =
CoreJson.byFields(tableName, fields, howMatched, conn)
/**
* Write documents to the given `PrintWriter` using a field comparison, ordering results by the given fields
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
*/
fun writeByFields(
tableName: String,
writer: PrintWriter,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) = CoreJson.writeByFields(tableName, writer, fields, howMatched, orderBy, conn)
/**
* Write documents to the given `PrintWriter` using a field comparison, ordering results by the given fields
* (creates connection)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
*/
fun writeByFields(
tableName: String,
writer: PrintWriter,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) = CoreJson.writeByFields(tableName, writer, fields, howMatched, orderBy)
/**
* Write documents to the given `PrintWriter` using a field comparison
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
*/
fun writeByFields(
tableName: String,
writer: PrintWriter,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
conn: Connection
) = CoreJson.writeByFields(tableName, writer, fields, howMatched, conn)
/**
* Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return A JSON array of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) = Custom.jsonArray(
FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameters.json(":criteria", criteria)),
conn,
Results::jsonFromData
)
/**
* Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only; creates
* connection)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A JSON array of documents matching the JSON containment query
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
inline fun <reified TContains> byContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) = Configuration.dbConn().use { byContains(tableName, criteria, orderBy, it) }
/**
* Retrieve documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param conn The connection over which documents should be retrieved
* @return A JSON array of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
byContains(tableName, criteria, null, conn)
/**
* Write documents to the given `PrintWriter` using a JSON containment query, ordering results by the given fields
* (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> writeByContains(
tableName: String,
writer: PrintWriter,
criteria: TContains,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) = Custom.writeJsonArray(
FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameters.json(":criteria", criteria)),
writer,
conn,
Results::jsonFromData
)
/**
* Write documents to the given `PrintWriter` using a JSON containment query, ordering results by the given fields
* (PostgreSQL only; creates connection)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> writeByContains(
tableName: String,
writer: PrintWriter,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) = Configuration.dbConn().use { writeByContains(tableName, writer, criteria, orderBy, it) }
/**
* Write documents to the given `PrintWriter` using a JSON containment query, ordering results by the given fields
* (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param criteria The object for which JSON containment should be checked
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> writeByContains(
tableName: String,
writer: PrintWriter,
criteria: TContains,
conn: Connection
) = writeByContains(tableName, writer, criteria, null, conn)
/**
* Retrieve documents using a JSON Path match query, ordering results by the given fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return A JSON array of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null, conn: Connection) =
CoreJson.byJsonPath(tableName, path, orderBy, conn)
/**
* Retrieve documents using a JSON Path match query, ordering results by the given fields (PostgreSQL only; creates
* connection)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A JSON array of documents matching the JSON Path match query
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null) =
CoreJson.byJsonPath(tableName, path, orderBy)
/**
* Retrieve documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param conn The connection over which documents should be retrieved
* @return A JSON array of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, conn: Connection) =
CoreJson.byJsonPath(tableName, path, conn)
/**
* Write documents to the given `PrintWriter` using a JSON Path match query, ordering results by the given fields
* (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If called on a SQLite connection
*/
fun writeByJsonPath(
tableName: String,
writer: PrintWriter,
path: String,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) = CoreJson.writeByJsonPath(tableName, writer, path, orderBy, conn)
/**
* Write documents to the given `PrintWriter` using a JSON Path match query, ordering results by the given fields
* (PostgreSQL only; creates connection)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If called on a SQLite connection
*/
fun writeByJsonPath(tableName: String, writer: PrintWriter, path: String, orderBy: Collection<Field<*>>? = null) =
CoreJson.writeByJsonPath(tableName, writer, path, orderBy)
/**
* Write documents to the given `PrintWriter` using a JSON Path match query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param path The JSON path comparison to match
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If called on a SQLite connection
*/
fun writeByJsonPath(tableName: String, writer: PrintWriter, path: String, conn: Connection) =
CoreJson.writeByJsonPath(tableName, writer, path, conn)
/**
* Retrieve the first document using a field comparison and optional ordering fields
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return The first JSON document matching the field comparison if found, an empty JSON object otherwise
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
*/
fun firstByFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) = CoreJson.firstByFields(tableName, fields, howMatched, orderBy, conn)
/**
* Retrieve the first document using a field comparison and optional ordering fields (creates connection)
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first JSON document matching the field comparison if found, an empty JSON object otherwise
* @throws DocumentException If no connection string has been set, or if parameters are invalid
*/
fun firstByFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) = CoreJson.firstByFields(tableName, fields, howMatched, orderBy)
/**
* Retrieve the first document using a field comparison
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param conn The connection over which documents should be retrieved
* @return The first JSON document matching the field comparison if found, an empty JSON object otherwise
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
*/
fun firstByFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
conn: Connection
) = CoreJson.firstByFields(tableName, fields, howMatched, conn)
/**
* Write the first document to the given `PrintWriter` using a field comparison and optional ordering fields
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
*/
fun writeFirstByFields(
tableName: String,
writer: PrintWriter,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) = CoreJson.writeFirstByFields(tableName, writer, fields, howMatched, orderBy, conn)
/**
* Write the first document to the given `PrintWriter` using a field comparison and optional ordering fields
* (creates connection)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
*/
fun writeFirstByFields(
tableName: String,
writer: PrintWriter,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) = CoreJson.writeFirstByFields(tableName, writer, fields, howMatched, orderBy)
/**
* Write the first document to the given `PrintWriter` using a field comparison
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
*/
fun writeFirstByFields(
tableName: String,
writer: PrintWriter,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
conn: Connection
) = CoreJson.writeFirstByFields(tableName, writer, fields, howMatched, conn)
/**
* Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return The first JSON document matching the JSON containment query if found, an empty JSON object otherwise
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> firstByContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) = Custom.jsonSingle(
FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameters.json(":criteria", criteria)),
conn,
Results::jsonFromData
)
/**
* Retrieve the first document using a JSON containment query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param conn The connection over which documents should be retrieved
* @return The first JSON document matching the JSON containment query if found, an empty JSON object otherwise
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> firstByContains(tableName: String, criteria: TContains, conn: Connection) =
firstByContains(tableName, criteria, null, conn)
/**
* Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only; creates
* connection)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first JSON document matching the JSON containment query if found, an empty JSON object otherwise
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
inline fun <reified TContains> firstByContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) = Configuration.dbConn().use { firstByContains(tableName, criteria, orderBy, it) }
/**
* Write the first document to the given `PrintWriter` using a JSON containment query and optional ordering fields
* (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> writeFirstByContains(
tableName: String,
writer: PrintWriter,
criteria: TContains,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) = writer.write(
Custom.jsonSingle(
FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameters.json(":criteria", criteria)),
conn,
Results::jsonFromData
)
)
/**
* Write the first document to the given `PrintWriter` using a JSON containment query and optional ordering fields
* (PostgreSQL only; creates connection)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> writeFirstByContains(
tableName: String,
writer: PrintWriter,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) = Configuration.dbConn().use { writeFirstByContains<TContains>(tableName, writer, criteria, orderBy, it) }
/**
* Write the first document to the given `PrintWriter` using a JSON containment query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param criteria The object for which JSON containment should be checked
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> writeFirstByContains(
tableName: String,
writer: PrintWriter,
criteria: TContains,
conn: Connection
) = writeFirstByContains<TContains>(tableName, writer, criteria, null, conn)
/**
* Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @return The first JSON document matching the JSON Path match query if found, an empty JSON object otherwise
* @throws DocumentException If called on a SQLite connection
*/
fun firstByJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null, conn: Connection) =
CoreJson.firstByJsonPath(tableName, path, orderBy, conn)
/**
* Retrieve the first document using a JSON Path match query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param conn The connection over which documents should be retrieved
* @return The first JSON document matching the JSON Path match query if found, an empty JSON object otherwise
* @throws DocumentException If called on a SQLite connection
*/
fun firstByJsonPath(tableName: String, path: String, conn: Connection) =
CoreJson.firstByJsonPath(tableName, path, conn)
/**
* Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only; creates
* connection)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first JSON document matching the JSON Path match query if found, an empty JSON object otherwise
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
fun firstByJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null) =
CoreJson.firstByJsonPath(tableName, path, orderBy)
/**
* Write the first document to the given `PrintWriter` using a JSON Path match query and optional ordering fields
* (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If called on a SQLite connection
*/
fun writeFirstByJsonPath(
tableName: String,
writer: PrintWriter,
path: String,
orderBy: Collection<Field<*>>? = null,
conn: Connection
) = CoreJson.writeFirstByJsonPath(tableName, writer, path, orderBy, conn)
/**
* Write the first document to the given `PrintWriter` using a JSON Path match query and optional ordering fields
* (PostgreSQL only; creates connection)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If called on a SQLite connection
*/
fun writeFirstByJsonPath(
tableName: String,
writer: PrintWriter,
path: String,
orderBy: Collection<Field<*>>? = null
) = CoreJson.writeFirstByJsonPath(tableName, writer, path, orderBy)
/**
* Write the first document to the given `PrintWriter` using a JSON Path match query (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param path The JSON path comparison to match
* @param conn The connection over which documents should be retrieved
* @throws DocumentException If called on a SQLite connection
*/
fun writeFirstByJsonPath(tableName: String, writer: PrintWriter, path: String, conn: Connection) =
CoreJson.writeFirstByJsonPath(tableName, writer, path, conn)
}

View File

@@ -0,0 +1,77 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.Parameter
import solutions.bitbadger.documents.ParameterType
import solutions.bitbadger.documents.java.Parameters as JvmParameters
import java.sql.Connection
/**
* Functions to assist with the creation and implementation of parameters for SQL queries
*
* @author Daniel J. Summers <daniel@bitbadger.solutions>
*/
object Parameters {
/**
* Assign parameter names to any fields that do not have them assigned
*
* @param fields The collection of fields to be named
* @return The collection of fields with parameter names assigned
*/
fun nameFields(fields: Collection<Field<*>>): Collection<Field<*>> =
JvmParameters.nameFields(fields)
/**
* Create a parameter by encoding a JSON object
*
* @param name The parameter name
* @param value The object to be encoded as JSON
* @return A parameter with the value encoded
*/
inline fun <reified T> json(name: String, value: T) =
Parameter(name, ParameterType.JSON, DocumentConfig.serialize<T>(value))
/**
* Add field parameters to the given set of parameters
*
* @param fields The fields being compared in the query
* @param existing Any existing parameters for the query (optional, defaults to empty collection)
* @return A collection of parameters for the query
*/
fun addFields(fields: Collection<Field<*>>, existing: MutableCollection<Parameter<*>> = mutableListOf()) =
JvmParameters.addFields(fields, existing)
/**
* Replace the parameter names in the query with question marks
*
* @param query The query with named placeholders
* @param parameters The parameters for the query
* @return The query, with name parameters changed to `?`s
*/
fun replaceNamesInQuery(query: String, parameters: Collection<Parameter<*>>) =
JvmParameters.replaceNamesInQuery(query, parameters)
/**
* Apply the given parameters to the given query, returning a prepared statement
*
* @param conn The active JDBC connection
* @param query The query
* @param parameters The parameters for the query
* @return A `PreparedStatement` with the parameter names replaced with `?` and parameter values bound
* @throws DocumentException If parameter names are invalid or number value types are invalid
*/
fun apply(conn: Connection, query: String, parameters: Collection<Parameter<*>>) =
JvmParameters.apply(conn, query, parameters)
/**
* Create parameters for field names to be removed from a document
*
* @param names The names of the fields to be removed
* @param parameterName The parameter name to use for the query
* @return A list of parameters to use for building the query
* @throws DocumentException If the dialect has not been set
*/
fun fieldNames(names: Collection<String>, parameterName: String = ":name") =
JvmParameters.fieldNames(names, parameterName)
}

View File

@@ -0,0 +1,137 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.query.PatchQuery
import java.sql.Connection
/**
* Functions to patch (partially update) documents
*/
object Patch {
/**
* Patch a document by its ID
*
* @param tableName The name of the table in which a document should be patched
* @param docId The ID of the document to be patched
* @param patch The object whose properties should be replaced in the document
* @param conn The connection on which the update should be executed
*/
inline fun <TKey, reified TPatch> byId(tableName: String, docId: TKey, patch: TPatch, conn: Connection) =
conn.customNonQuery(
PatchQuery.byId(tableName, docId),
Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")),
mutableListOf(Parameters.json(":data", patch))
)
)
/**
* Patch a document by its ID
*
* @param tableName The name of the table in which a document should be patched
* @param docId The ID of the document to be patched
* @param patch The object whose properties should be replaced in the document
*/
inline fun <TKey, reified TPatch> byId(tableName: String, docId: TKey, patch: TPatch) =
Configuration.dbConn().use { byId(tableName, docId, patch, it) }
/**
* Patch documents using a field comparison
*
* @param tableName The name of the table in which documents should be patched
* @param fields The fields which should be compared
* @param patch The object whose properties should be replaced in the document
* @param howMatched How the fields should be matched
* @param conn The connection on which the update should be executed
*/
inline fun <reified TPatch> byFields(
tableName: String,
fields: Collection<Field<*>>,
patch: TPatch,
howMatched: FieldMatch? = null,
conn: Connection
) {
val named = Parameters.nameFields(fields)
conn.customNonQuery(
PatchQuery.byFields(tableName, named, howMatched), Parameters.addFields(
named,
mutableListOf(Parameters.json(":data", patch))
)
)
}
/**
* Patch documents using a field comparison
*
* @param tableName The name of the table in which documents should be patched
* @param fields The fields which should be compared
* @param patch The object whose properties should be replaced in the document
* @param howMatched How the fields should be matched
*/
inline fun <reified TPatch> byFields(
tableName: String,
fields: Collection<Field<*>>,
patch: TPatch,
howMatched: FieldMatch? = null
) =
Configuration.dbConn().use { byFields(tableName, fields, patch, howMatched, it) }
/**
* Patch documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be patched
* @param criteria The object against which JSON containment should be checked
* @param patch The object whose properties should be replaced in the document
* @param conn The connection on which the update should be executed
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains, reified TPatch> byContains(
tableName: String,
criteria: TContains,
patch: TPatch,
conn: Connection
) =
conn.customNonQuery(
PatchQuery.byContains(tableName),
listOf(Parameters.json(":criteria", criteria), Parameters.json(":data", patch))
)
/**
* Patch documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be patched
* @param criteria The object against which JSON containment should be checked
* @param patch The object whose properties should be replaced in the document
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains, reified TPatch> byContains(tableName: String, criteria: TContains, patch: TPatch) =
Configuration.dbConn().use { byContains(tableName, criteria, patch, it) }
/**
* Patch documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be patched
* @param path The JSON path comparison to match
* @param patch The object whose properties should be replaced in the document
* @param conn The connection on which the update should be executed
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TPatch> byJsonPath(tableName: String, path: String, patch: TPatch, conn: Connection) =
conn.customNonQuery(
PatchQuery.byJsonPath(tableName),
listOf(Parameter(":path", ParameterType.STRING, path), Parameters.json(":data", patch))
)
/**
* Patch documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be patched
* @param path The JSON path comparison to match
* @param patch The object whose properties should be replaced in the document
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TPatch> byJsonPath(tableName: String, path: String, patch: TPatch) =
Configuration.dbConn().use { byJsonPath(tableName, path, patch, it) }
}

View File

@@ -0,0 +1,124 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.java.RemoveFields as JvmRemoveFields
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.query.RemoveFieldsQuery
import java.sql.Connection
/**
* Functions to remove fields from documents
*/
object RemoveFields {
/**
* Remove fields from a document by its ID
*
* @param tableName The name of the table in which the document's fields should be removed
* @param docId The ID of the document to have fields removed
* @param toRemove The names of the fields to be removed
* @param conn The connection on which the update should be executed
*/
fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>, conn: Connection) =
JvmRemoveFields.byId(tableName, docId, toRemove, conn)
/**
* Remove fields from a document by its ID
*
* @param tableName The name of the table in which the document's fields should be removed
* @param docId The ID of the document to have fields removed
* @param toRemove The names of the fields to be removed
*/
fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>) =
JvmRemoveFields.byId(tableName, docId, toRemove)
/**
* Remove fields from documents using a field comparison
*
* @param tableName The name of the table in which document fields should be removed
* @param fields The fields which should be compared
* @param toRemove The names of the fields to be removed
* @param howMatched How the fields should be matched
* @param conn The connection on which the update should be executed
*/
fun byFields(
tableName: String,
fields: Collection<Field<*>>,
toRemove: Collection<String>,
howMatched: FieldMatch? = null,
conn: Connection
) =
JvmRemoveFields.byFields(tableName, fields, toRemove, howMatched, conn)
/**
* Remove fields from documents using a field comparison
*
* @param tableName The name of the table in which document fields should be removed
* @param fields The fields which should be compared
* @param toRemove The names of the fields to be removed
* @param howMatched How the fields should be matched
*/
fun byFields(
tableName: String,
fields: Collection<Field<*>>,
toRemove: Collection<String>,
howMatched: FieldMatch? = null
) =
JvmRemoveFields.byFields(tableName, fields, toRemove, howMatched)
/**
* Remove fields from documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which document fields should be removed
* @param criteria The object against which JSON containment should be checked
* @param toRemove The names of the fields to be removed
* @param conn The connection on which the update should be executed
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(
tableName: String,
criteria: TContains,
toRemove: Collection<String>,
conn: Connection
) {
val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery(
RemoveFieldsQuery.byContains(tableName, nameParams),
listOf(Parameters.json(":criteria", criteria), *nameParams.toTypedArray())
)
}
/**
* Remove fields from documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which document fields should be removed
* @param criteria The object against which JSON containment should be checked
* @param toRemove The names of the fields to be removed
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, toRemove: Collection<String>) =
Configuration.dbConn().use { byContains(tableName, criteria, toRemove, it) }
/**
* Remove fields from documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which document fields should be removed
* @param path The JSON path comparison to match
* @param toRemove The names of the fields to be removed
* @param conn The connection on which the update should be executed
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>, conn: Connection) =
JvmRemoveFields.byJsonPath(tableName, path, toRemove, conn)
/**
* Remove fields from documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which document fields should be removed
* @param path The JSON path comparison to match
* @param toRemove The names of the fields to be removed
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>) =
JvmRemoveFields.byJsonPath(tableName, path, toRemove)
}

View File

@@ -0,0 +1,107 @@
package solutions.bitbadger.documents.kotlinx
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.java.Results as CoreResults
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
/**
* Helper functions for handling results
*/
object Results {
/**
* Create a domain item from a document, specifying the field in which the document is found
*
* @param field The field name containing the JSON document
* @return A function to create the constructed domain item
*/
inline fun <reified TDoc> fromDocument(field: String): (ResultSet) -> TDoc =
{ rs -> DocumentConfig.deserialize<TDoc>(rs.getString(field)) }
/**
* Create a domain item from a document
*
* @return The constructed domain item
*/
inline fun <reified TDoc> fromData(rs: ResultSet) =
fromDocument<TDoc>("data")(rs)
/**
* Create a list of items for the results of the given command, using the specified mapping function
*
* @param stmt The prepared statement to execute
* @param mapFunc The mapping function from data reader to domain class instance
* @return A list of items from the query's result
* @throws DocumentException If there is a problem executing the query
*/
inline fun <reified TDoc : Any> toCustomList(stmt: PreparedStatement, mapFunc: (ResultSet) -> TDoc) =
try {
stmt.executeQuery().use {
val results = mutableListOf<TDoc>()
while (it.next()) {
results.add(mapFunc(it))
}
results.toList()
}
} catch (ex: SQLException) {
throw DocumentException("Error retrieving documents from query: ${ex.message}", ex)
}
/**
* Extract a count from the first column
*
* @param rs A `ResultSet` set to the row with the count to retrieve
* @return The count from the row
*/
fun toCount(rs: ResultSet) =
when (Configuration.dialect()) {
Dialect.POSTGRESQL -> rs.getInt("it").toLong()
Dialect.SQLITE -> rs.getLong("it")
}
/**
* Extract a true/false value from the first column
*
* @param rs A `ResultSet` set to the row with the true/false value to retrieve
* @return The true/false value from the row
*/
fun toExists(rs: ResultSet) =
when (Configuration.dialect()) {
Dialect.POSTGRESQL -> rs.getBoolean("it")
Dialect.SQLITE -> toCount(rs) > 0L
}
/**
* Retrieve the JSON text of a document, specifying the field in which the document is found
*
* @param field The field name containing the JSON document
* @param rs A `ResultSet` set to the row with the document to be constructed
* @return The JSON text of the document
*/
fun jsonFromDocument(field: String, rs: ResultSet) =
CoreResults.jsonFromDocument(field, rs)
/**
* Retrieve the JSON text of a document, specifying the field in which the document is found
*
* @param rs A `ResultSet` set to the row with the document to be constructed
* @return The JSON text of the document
*/
fun jsonFromData(rs: ResultSet) =
CoreResults.jsonFromData(rs)
/**
* Create a JSON array of items for the results of the given command, using the specified mapping function
*
* @param stmt The prepared statement to execute
* @param mapFunc The mapping function from data reader to JSON text
* @return A string with a JSON array of documents from the query's result
* @throws DocumentException If there is a problem executing the query (unchecked)
*/
fun toJsonArray(stmt: PreparedStatement, mapFunc: (ResultSet) -> String) =
CoreResults.toJsonArray(stmt, mapFunc)
}

View File

@@ -0,0 +1,750 @@
package solutions.bitbadger.documents.kotlinx.extensions
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.*
import java.io.PrintWriter
import java.sql.Connection
import java.sql.ResultSet
// ~~~ CUSTOM QUERIES ~~~
/**
* Execute a query that returns a list of results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return A list of results for the given query
*/
inline fun <reified TDoc : Any> Connection.customList(
query: String, parameters: Collection<Parameter<*>> = listOf(), mapFunc: (ResultSet) -> TDoc
) = Custom.list(query, parameters, this, mapFunc)
/**
* Execute a query that returns a JSON array of results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function to extract the JSON from the query
* @return A JSON array of results for the given query
* @throws DocumentException If parameters are invalid
*/
fun Connection.customJsonArray(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
mapFunc: (ResultSet) -> String
) = Custom.jsonArray(query, parameters, this, mapFunc)
/**
* Execute a query, writing its JSON array of results to the given `PrintWriter` (creates connection)
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param writer The writer to which the results should be written
* @param mapFunc The mapping function to extract the JSON from the query
* @throws DocumentException If parameters are invalid
*/
fun Connection.writeCustomJsonArray(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
writer: PrintWriter,
mapFunc: (ResultSet) -> String
) = Custom.writeJsonArray(query, parameters, writer, this, mapFunc)
/**
* Execute a query that returns one or no results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return The document if one matches the query, `null` otherwise
*/
inline fun <reified TDoc : Any> Connection.customSingle(
query: String, parameters: Collection<Parameter<*>> = listOf(), mapFunc: (ResultSet) -> TDoc
) = Custom.single(query, parameters, this, mapFunc)
/**
* Execute a query that returns JSON for one or no documents (creates connection)
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return The JSON for the document if found, an empty object (`{}`) if not
* @throws DocumentException If parameters are invalid
*/
fun Connection.customJsonSingle(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
mapFunc: (ResultSet) -> String
) = Custom.jsonSingle(query, parameters, this, mapFunc)
/**
* Execute a query that returns no results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
*/
fun Connection.customNonQuery(query: String, parameters: Collection<Parameter<*>> = listOf()) =
Custom.nonQuery(query, parameters, this)
/**
* Execute a query that returns a scalar result
*
* @param query The query to retrieve the result
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return The scalar value from the query
*/
inline fun <reified T : Any> Connection.customScalar(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
mapFunc: (ResultSet) -> T
) = Custom.scalar(query, parameters, this, mapFunc)
// ~~~ DEFINITION QUERIES ~~~
/**
* Create a document table if necessary
*
* @param tableName The table whose existence should be ensured (may include schema)
*/
fun Connection.ensureTable(tableName: String) =
Definition.ensureTable(tableName, this)
/**
* Create an index on field(s) within documents in the specified table if necessary
*
* @param tableName The table to be indexed (may include schema)
* @param indexName The name of the index to create
* @param fields One or more fields to be indexed<
*/
fun Connection.ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>) =
Definition.ensureFieldIndex(tableName, indexName, fields, this)
/**
* Create a document index on a table (PostgreSQL only)
*
* @param tableName The table to be indexed (may include schema)
* @param indexType The type of index to ensure
* @throws DocumentException If called on a SQLite connection
*/
fun Connection.ensureDocumentIndex(tableName: String, indexType: DocumentIndex) =
Definition.ensureDocumentIndex(tableName, indexType, this)
// ~~~ DOCUMENT MANIPULATION QUERIES ~~~
/**
* Insert a new document
*
* @param tableName The table into which the document should be inserted (may include schema)
* @param document The document to be inserted
*/
inline fun <reified TDoc> Connection.insert(tableName: String, document: TDoc) =
Document.insert(tableName, document, this)
/**
* Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
*
* @param tableName The table in which the document should be saved (may include schema)
* @param document The document to be saved
*/
inline fun <reified TDoc> Connection.save(tableName: String, document: TDoc) =
Document.save(tableName, document, this)
/**
* Update (replace) a document by its ID
*
* @param tableName The table in which the document should be replaced (may include schema)
* @param docId The ID of the document to be replaced
* @param document The document to be replaced
*/
inline fun <TKey, reified TDoc> Connection.update(tableName: String, docId: TKey, document: TDoc) =
Document.update(tableName, docId, document, this)
// ~~~ DOCUMENT COUNT QUERIES ~~~
/**
* Count all documents in the table
*
* @param tableName The name of the table in which documents should be counted
* @return A count of the documents in the table
*/
fun Connection.countAll(tableName: String) =
Count.all(tableName, this)
/**
* Count documents using a field comparison
*
* @param tableName The name of the table in which documents should be counted
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @return A count of the matching documents in the table
*/
fun Connection.countByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
Count.byFields(tableName, fields, howMatched, this)
/**
* Count documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be counted
* @param criteria The object for which JSON containment should be checked
* @return A count of the matching documents in the table
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> Connection.countByContains(tableName: String, criteria: TContains) =
Count.byContains(tableName, criteria, this)
/**
* Count documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be counted
* @param path The JSON path comparison to match
* @return A count of the matching documents in the table
* @throws DocumentException If called on a SQLite connection
*/
fun Connection.countByJsonPath(tableName: String, path: String) =
Count.byJsonPath(tableName, path, this)
// ~~~ DOCUMENT EXISTENCE QUERIES ~~~
/**
* Determine a document's existence by its ID
*
* @param tableName The name of the table in which document existence should be checked
* @param docId The ID of the document to be checked
* @return True if the document exists, false if not
*/
fun <TKey> Connection.existsById(tableName: String, docId: TKey) =
Exists.byId(tableName, docId, this)
/**
* Determine document existence using a field comparison
*
* @param tableName The name of the table in which document existence should be checked
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @return True if any matching documents exist, false if not
*/
fun Connection.existsByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
Exists.byFields(tableName, fields, howMatched, this)
/**
* Determine document existence using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which document existence should be checked
* @param criteria The object for which JSON containment should be checked
* @return True if any matching documents exist, false if not
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> Connection.existsByContains(tableName: String, criteria: TContains) =
Exists.byContains(tableName, criteria, this)
/**
* Determine document existence using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which document existence should be checked
* @param path The JSON path comparison to match
* @return True if any matching documents exist, false if not
* @throws DocumentException If called on a SQLite connection
*/
fun Connection.existsByJsonPath(tableName: String, path: String) =
Exists.byJsonPath(tableName, path, this)
// ~~~ DOCUMENT RETRIEVAL QUERIES (Domain Objects) ~~~
/**
* Retrieve all documents in the given table, ordering results by the optional given fields
*
* @param tableName The table from which documents should be retrieved
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents from the given table
*/
inline fun <reified TDoc : Any> Connection.findAll(tableName: String, orderBy: Collection<Field<*>>? = null) =
Find.all<TDoc>(tableName, orderBy, this)
/**
* Retrieve a document by its ID
*
* @param tableName The table from which the document should be retrieved
* @param docId The ID of the document to retrieve
* @return The document if it is found, `null` otherwise
*/
inline fun <TKey, reified TDoc : Any> Connection.findById(tableName: String, docId: TKey) =
Find.byId<TKey, TDoc>(tableName, docId, this)
/**
* Retrieve documents using a field comparison, ordering results by the optional given fields
*
* @param tableName The table from which the document should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents matching the field comparison
*/
inline fun <reified TDoc : Any> Connection.findByFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) = Find.byFields<TDoc>(tableName, fields, howMatched, orderBy, this)
/**
* Retrieve documents using a JSON containment query, ordering results by the optional given fields (PostgreSQL only)
*
* @param tableName The name of the table in which document existence should be checked
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any, reified TContains> Connection.findByContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) = Find.byContains<TDoc, TContains>(tableName, criteria, orderBy, this)
/**
* Retrieve documents using a JSON Path match query, ordering results by the optional given fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any> Connection.findByJsonPath(
tableName: String,
path: String,
orderBy: Collection<Field<*>>? = null
) = Find.byJsonPath<TDoc>(tableName, path, orderBy, this)
/**
* Retrieve the first document using a field comparison and optional ordering fields
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first document matching the field comparison, or `null` if no matches are found
*/
inline fun <reified TDoc : Any> Connection.findFirstByFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) = Find.firstByFields<TDoc>(tableName, fields, howMatched, orderBy, this)
/**
* Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first document matching the JSON containment query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any, reified TContains> Connection.findFirstByContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) = Find.firstByContains<TDoc, TContains>(tableName, criteria, orderBy, this)
/**
* Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first document matching the JSON Path match query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TDoc : Any> Connection.findFirstByJsonPath(
tableName: String,
path: String,
orderBy: Collection<Field<*>>? = null
) = Find.firstByJsonPath<TDoc>(tableName, path, orderBy, this)
// ~~~ DOCUMENT RETRIEVAL QUERIES (Raw JSON) ~~~
/**
* Retrieve all documents in the given table
*
* @param tableName The table from which documents should be retrieved
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A JSON array of documents from the given table
* @throws DocumentException If no connection string has been set, or if query execution fails
*/
fun Connection.jsonAll(tableName: String, orderBy: Collection<Field<*>>? = null) =
Json.all(tableName, orderBy, this)
/**
* Retrieve a document by its ID
*
* @param tableName The table from which the document should be retrieved
* @param docId The ID of the document to retrieve
* @return A JSON document if found, an empty JSON object if not found
* @throws DocumentException If no connection string has been set
*/
fun <TKey> Connection.jsonById(tableName: String, docId: TKey) =
Json.byId(tableName, docId, this)
/**
* Retrieve documents using a field comparison, ordering results by the given fields
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A JSON array of documents matching the field comparison
* @throws DocumentException If no connection string has been set, or if parameters are invalid
*/
fun Connection.jsonByFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) = Json.byFields(tableName, fields, howMatched, orderBy, this)
/**
* Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A JSON array of documents matching the JSON containment query
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
inline fun <reified TContains> Connection.jsonByContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) = Json.byContains(tableName, criteria, orderBy, this)
/**
* Retrieve documents using a JSON Path match query, ordering results by the given fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A JSON array of documents matching the JSON Path match query
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
fun Connection.jsonByJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null) =
Json.byJsonPath(tableName, path, orderBy, this)
/**
* Retrieve the first document using a field comparison and optional ordering fields
*
* @param tableName The table from which documents should be retrieved
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first JSON document matching the field comparison if found, an empty JSON object otherwise
* @throws DocumentException If no connection string has been set, or if parameters are invalid
*/
fun Connection.jsonFirstByFields(
tableName: String,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) = Json.firstByFields(tableName, fields, howMatched, orderBy, this)
/**
* Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first JSON document matching the JSON containment query if found, an empty JSON object otherwise
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
inline fun <reified TContains> Connection.jsonFirstByContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) = Json.firstByContains(tableName, criteria, orderBy, this)
/**
* Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return The first JSON document matching the JSON Path match query if found, an empty JSON object otherwise
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
fun Connection.jsonFirstByJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null) =
Json.firstByJsonPath(tableName, path, orderBy, this)
// ~~~ DOCUMENT RETRIEVAL QUERIES (Write raw JSON to PrintWriter) ~~~
/**
* Write all documents in the given table to the given `PrintWriter`, ordering results by the optional given fields
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If no connection string has been set, or if query execution fails
*/
fun Connection.writeJsonAll(tableName: String, writer: PrintWriter, orderBy: Collection<Field<*>>? = null) =
Json.writeAll(tableName, writer, orderBy, this)
/**
* Write a document to the given `PrintWriter` by its ID
*
* @param tableName The table from which the document should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param docId The ID of the document to retrieve
* @throws DocumentException If no connection string has been set
*/
fun <TKey> Connection.writeJsonById(tableName: String, writer: PrintWriter, docId: TKey) =
Json.writeById(tableName, writer, docId, this)
/**
* Write documents to the given `PrintWriter` using a field comparison, ordering results by the given fields
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If no connection string has been set, or if parameters are invalid
*/
fun Connection.writeJsonByFields(
tableName: String,
writer: PrintWriter,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) = Json.writeByFields(tableName, writer, fields, howMatched, orderBy, this)
/**
* Write documents to the given `PrintWriter` using a JSON containment query, ordering results by the given fields
* (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
inline fun <reified TContains> Connection.writeJsonByContains(
tableName: String,
writer: PrintWriter,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) = Json.writeByContains(tableName, writer, criteria, orderBy, this)
/**
* Write documents to the given `PrintWriter` using a JSON Path match query, ordering results by the given fields
* (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
fun Connection.writeJsonByJsonPath(
tableName: String,
writer: PrintWriter,
path: String,
orderBy: Collection<Field<*>>? = null
) = Json.writeByJsonPath(tableName, writer, path, orderBy, this)
/**
* Write the first document to the given `PrintWriter` using a field comparison and optional ordering fields
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If no connection string has been set, or if parameters are invalid
*/
fun Connection.writeJsonFirstByFields(
tableName: String,
writer: PrintWriter,
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
orderBy: Collection<Field<*>>? = null
) = Json.writeFirstByFields(tableName, writer, fields, howMatched, orderBy, this)
/**
* Write the first document to the given `PrintWriter` using a JSON containment query and optional ordering fields
* (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param criteria The object for which JSON containment should be checked
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
inline fun <reified TContains> Connection.writeJsonFirstByContains(
tableName: String,
writer: PrintWriter,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) = Json.writeFirstByContains(tableName, writer, criteria, orderBy, this)
/**
* Write the first document to the given `PrintWriter` using a JSON Path match query and optional ordering fields
* (PostgreSQL only)
*
* @param tableName The table from which documents should be retrieved
* @param writer The `PrintWriter` to which the results should be written
* @param path The JSON path comparison to match
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
*/
fun Connection.writeJsonFirstByJsonPath(
tableName: String,
writer: PrintWriter,
path: String,
orderBy: Collection<Field<*>>? = null
) = Json.writeFirstByJsonPath(tableName, writer, path, orderBy, this)
// ~~~ DOCUMENT PATCH (PARTIAL UPDATE) QUERIES ~~~
/**
* Patch a document by its ID
*
* @param tableName The name of the table in which a document should be patched
* @param docId The ID of the document to be patched
* @param patch The object whose properties should be replaced in the document
*/
inline fun <TKey, reified TPatch> Connection.patchById(tableName: String, docId: TKey, patch: TPatch) =
Patch.byId(tableName, docId, patch, this)
/**
* Patch documents using a field comparison
*
* @param tableName The name of the table in which documents should be patched
* @param fields The fields which should be compared
* @param patch The object whose properties should be replaced in the document
* @param howMatched How the fields should be matched
*/
inline fun <reified TPatch> Connection.patchByFields(
tableName: String,
fields: Collection<Field<*>>,
patch: TPatch,
howMatched: FieldMatch? = null
) = Patch.byFields(tableName, fields, patch, howMatched, this)
/**
* Patch documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be patched
* @param criteria The object against which JSON containment should be checked
* @param patch The object whose properties should be replaced in the document
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains, reified TPatch> Connection.patchByContains(
tableName: String,
criteria: TContains,
patch: TPatch
) = Patch.byContains(tableName, criteria, patch, this)
/**
* Patch documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which documents should be patched
* @param path The JSON path comparison to match
* @param patch The object whose properties should be replaced in the document
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TPatch> Connection.patchByJsonPath(tableName: String, path: String, patch: TPatch) =
Patch.byJsonPath(tableName, path, patch, this)
// ~~~ DOCUMENT FIELD REMOVAL QUERIES ~~~
/**
* Remove fields from a document by its ID
*
* @param tableName The name of the table in which the document's fields should be removed
* @param docId The ID of the document to have fields removed
* @param toRemove The names of the fields to be removed
*/
fun <TKey> Connection.removeFieldsById(tableName: String, docId: TKey, toRemove: Collection<String>) =
RemoveFields.byId(tableName, docId, toRemove, this)
/**
* Remove fields from documents using a field comparison
*
* @param tableName The name of the table in which document fields should be removed
* @param fields The fields which should be compared
* @param toRemove The names of the fields to be removed
* @param howMatched How the fields should be matched
*/
fun Connection.removeFieldsByFields(
tableName: String,
fields: Collection<Field<*>>,
toRemove: Collection<String>,
howMatched: FieldMatch? = null
) = RemoveFields.byFields(tableName, fields, toRemove, howMatched, this)
/**
* Remove fields from documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which document fields should be removed
* @param criteria The object against which JSON containment should be checked
* @param toRemove The names of the fields to be removed
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> Connection.removeFieldsByContains(
tableName: String,
criteria: TContains,
toRemove: Collection<String>
) = RemoveFields.byContains(tableName, criteria, toRemove, this)
/**
* Remove fields from documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which document fields should be removed
* @param path The JSON path comparison to match
* @param toRemove The names of the fields to be removed
* @throws DocumentException If called on a SQLite connection
*/
fun Connection.removeFieldsByJsonPath(tableName: String, path: String, toRemove: Collection<String>) =
RemoveFields.byJsonPath(tableName, path, toRemove, this)
// ~~~ DOCUMENT DELETION QUERIES ~~~
/**
* Delete a document by its ID
*
* @param tableName The name of the table from which documents should be deleted
* @param docId The ID of the document to be deleted
*/
fun <TKey> Connection.deleteById(tableName: String, docId: TKey) =
Delete.byId(tableName, docId, this)
/**
* Delete documents using a field comparison
*
* @param tableName The name of the table from which documents should be deleted
* @param fields The fields which should be compared
* @param howMatched How the fields should be matched
*/
fun Connection.deleteByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
Delete.byFields(tableName, fields, howMatched, this)
/**
* Delete documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table from which documents should be deleted
* @param criteria The object for which JSON containment should be checked
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> Connection.deleteByContains(tableName: String, criteria: TContains) =
Delete.byContains(tableName, criteria, this)
/**
* Delete documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table from which documents should be deleted
* @param path The JSON path comparison to match
* @throws DocumentException If called on a SQLite connection
*/
fun Connection.deleteByJsonPath(tableName: String, path: String) =
Delete.byJsonPath(tableName, path, this)

View File

@@ -0,0 +1,11 @@
# Module kotlinx
This module contains an implementation of the document store API which uses a `kotlinx.serialization`-based serializer (relies on reified generics)
# Package solutions.bitbadger.documents.kotlinx
The document store API based on `kotlinx.serialization`
# Package solutions.bitbadger.documents.kotlinx.extensions
Extensions on the Java `Connection` object for document manipulation

View File

@@ -0,0 +1,14 @@
module solutions.bitbadger.documents.kotlinx.tests {
requires solutions.bitbadger.documents.core;
requires solutions.bitbadger.documents.kotlinx;
requires java.sql;
requires kotlin.stdlib;
requires kotlinx.serialization.json;
requires kotlin.test.junit5;
exports solutions.bitbadger.documents.kotlinx.tests;
exports solutions.bitbadger.documents.kotlinx.tests.integration;
opens solutions.bitbadger.documents.kotlinx.tests;
opens solutions.bitbadger.documents.kotlinx.tests.integration;
}

View File

@@ -0,0 +1,22 @@
package solutions.bitbadger.documents.kotlinx.tests
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.kotlinx.DocumentConfig
import kotlin.test.assertFalse
import kotlin.test.assertTrue
/**
* Unit tests for the `Configuration` object
*/
@DisplayName("KotlinX | DocumentConfig")
class DocumentConfigTest {
@Test
@DisplayName("Default JSON options are as expected")
fun defaultJsonOptions() {
assertTrue(DocumentConfig.options.configuration.encodeDefaults, "Encode Defaults should have been set")
assertFalse(DocumentConfig.options.configuration.explicitNulls, "Explicit Nulls should not have been set")
assertTrue(DocumentConfig.options.configuration.coerceInputValues, "Coerce Input Values should have been set")
}
}

View File

@@ -0,0 +1,68 @@
package solutions.bitbadger.documents.kotlinx.tests
import kotlinx.serialization.Serializable
import solutions.bitbadger.documents.kotlinx.extensions.insert
import solutions.bitbadger.documents.kotlinx.tests.integration.ThrowawayDatabase
/** The test table name to use for integration tests */
const val TEST_TABLE = "test_table"
@Serializable
data class NumIdDocument(val key: Int, val text: String) {
constructor() : this(0, "")
}
@Serializable
data class SubDocument(val foo: String, val bar: String) {
constructor() : this("", "")
}
@Serializable
data class ArrayDocument(val id: String, val values: List<String>) {
constructor() : this("", listOf())
companion object {
/** A set of documents used for integration tests */
val testDocuments = listOf(
ArrayDocument("first", listOf("a", "b", "c")),
ArrayDocument("second", listOf("c", "d", "e")),
ArrayDocument("third", listOf("x", "y", "z"))
)
}
}
@Serializable
data class JsonDocument(val id: String, val value: String = "", val numValue: Int = 0, val sub: SubDocument? = null) {
constructor() : this("")
companion object {
/** Documents to use for testing */
private val testDocuments = listOf(
JsonDocument("one", "FIRST!", 0, null),
JsonDocument("two", "another", 10, SubDocument("green", "blue")),
JsonDocument("three", "", 4, null),
JsonDocument("four", "purple", 17, SubDocument("green", "red")),
JsonDocument("five", "purple", 18, null)
)
fun load(db: ThrowawayDatabase, tableName: String = TEST_TABLE) =
testDocuments.forEach { db.conn.insert(tableName, it) }
/** Document ID `one` as a JSON string */
val one = """{"id":"one","value":"FIRST!","numValue":0}"""
/** Document ID `two` as a JSON string */
val two = """{"id":"two","value":"another","numValue":10,"sub":{"foo":"green","bar":"blue"}}"""
/** Document ID `three` as a JSON string */
val three = """{"id":"three","value":"","numValue":4}"""
/** Document ID `four` as a JSON string */
val four = """{"id":"four","value":"purple","numValue":17,"sub":{"foo":"green","bar":"red"}}"""
/** Document ID `five` as a JSON string */
val five = """{"id":"five","value":"purple","numValue":18}"""
}
}

View File

@@ -0,0 +1,72 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.JsonDocument
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
import kotlin.test.assertEquals
/**
* Integration tests for the `Count` object
*/
object CountFunctions {
fun all(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should have been 5 documents in the table")
}
fun byFieldsNumeric(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
3L,
db.conn.countByFields(TEST_TABLE, listOf(Field.between("numValue", 10, 20))),
"There should have been 3 matching documents"
)
}
fun byFieldsAlpha(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
1L,
db.conn.countByFields(TEST_TABLE, listOf(Field.between("value", "aardvark", "apple"))),
"There should have been 1 matching document"
)
}
fun byContainsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
2L,
db.conn.countByContains(TEST_TABLE, mapOf("value" to "purple")),
"There should have been 2 matching documents"
)
}
fun byContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
0L,
db.conn.countByContains(TEST_TABLE, mapOf("value" to "magenta")),
"There should have been no matching documents"
)
}
fun byJsonPathMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
2L,
db.conn.countByJsonPath(TEST_TABLE, "$.numValue ? (@ < 5)"),
"There should have been 2 matching documents"
)
}
fun byJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
0L,
db.conn.countByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)"),
"There should have been no matching documents"
)
}
}

View File

@@ -0,0 +1,162 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.Results
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.ArrayDocument
import solutions.bitbadger.documents.kotlinx.tests.JsonDocument
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
import solutions.bitbadger.documents.query.*
import java.io.PrintWriter
import java.io.StringWriter
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
/**
* Integration tests for the `Custom` object
*/
object CustomFunctions {
fun listEmpty(db: ThrowawayDatabase) {
JsonDocument.load(db)
db.conn.deleteByFields(TEST_TABLE, listOf(Field.exists(Configuration.idField)))
val result = db.conn.customList<JsonDocument>(FindQuery.all(TEST_TABLE), mapFunc = Results::fromData)
assertEquals(0, result.size, "There should have been no results")
}
fun listAll(db: ThrowawayDatabase) {
JsonDocument.load(db)
val result = db.conn.customList<JsonDocument>(FindQuery.all(TEST_TABLE), mapFunc = Results::fromData)
assertEquals(5, result.size, "There should have been 5 results")
}
fun jsonArrayEmpty(db: ThrowawayDatabase) {
assertEquals(0L, db.conn.countAll(TEST_TABLE), "The test table should be empty")
assertEquals(
"[]",
db.conn.customJsonArray(FindQuery.all(TEST_TABLE), listOf(), Results::jsonFromData),
"An empty list was not represented correctly"
)
}
fun jsonArraySingle(db: ThrowawayDatabase) {
db.conn.insert(TEST_TABLE, ArrayDocument("one", listOf("2", "3")))
assertEquals(
JsonFunctions.maybeJsonB("""[{"id":"one","values":["2","3"]}]"""),
db.conn.customJsonArray(FindQuery.all(TEST_TABLE), listOf(), Results::jsonFromData),
"A single document list was not represented correctly"
)
}
fun jsonArrayMany(db: ThrowawayDatabase) {
ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) }
assertEquals(
JsonFunctions.maybeJsonB("""[{"id":"first","values":["a","b","c"]},"""
+ """{"id":"second","values":["c","d","e"]},{"id":"third","values":["x","y","z"]}]"""),
db.conn.customJsonArray(FindQuery.all(TEST_TABLE) + orderBy(listOf(Field.named("id"))), listOf(),
Results::jsonFromData),
"A multiple document list was not represented correctly"
)
}
fun writeJsonArrayEmpty(db: ThrowawayDatabase) {
assertEquals(0L, db.conn.countAll(TEST_TABLE), "The test table should be empty")
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeCustomJsonArray(FindQuery.all(TEST_TABLE), listOf(), writer, Results::jsonFromData)
assertEquals("[]", output.toString(), "An empty list was not represented correctly")
}
fun writeJsonArraySingle(db: ThrowawayDatabase) {
db.conn.insert(TEST_TABLE, ArrayDocument("one", listOf("2", "3")))
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeCustomJsonArray(FindQuery.all(TEST_TABLE), listOf(), writer, Results::jsonFromData)
assertEquals(
JsonFunctions.maybeJsonB("""[{"id":"one","values":["2","3"]}]"""),
output.toString(),
"A single document list was not represented correctly"
)
}
fun writeJsonArrayMany(db: ThrowawayDatabase) {
ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) }
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeCustomJsonArray(FindQuery.all(TEST_TABLE) + orderBy(listOf(Field.named("id"))), listOf(), writer,
Results::jsonFromData)
assertEquals(
JsonFunctions.maybeJsonB("""[{"id":"first","values":["a","b","c"]},"""
+ """{"id":"second","values":["c","d","e"]},{"id":"third","values":["x","y","z"]}]"""),
output.toString(),
"A multiple document list was not represented correctly"
)
}
fun singleNone(db: ThrowawayDatabase) =
assertNull(
db.conn.customSingle(FindQuery.all(TEST_TABLE), mapFunc = Results::fromData),
"There should not have been a document returned"
)
fun singleOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertNotNull(
db.conn.customSingle<JsonDocument>(FindQuery.all(TEST_TABLE), mapFunc = Results::fromData),
"There should not have been a document returned"
)
}
fun jsonSingleNone(db: ThrowawayDatabase) =
assertEquals("{}", db.conn.customJsonSingle(FindQuery.all(TEST_TABLE), listOf(), Results::jsonFromData),
"An empty document was not represented correctly")
fun jsonSingleOne(db: ThrowawayDatabase) {
db.conn.insert(TEST_TABLE, ArrayDocument("me", listOf("myself", "i")))
assertEquals(
JsonFunctions.maybeJsonB("{\"id\":\"me\",\"values\":[\"myself\",\"i\"]}"),
db.conn.customJsonSingle(FindQuery.all(TEST_TABLE), listOf(), Results::jsonFromData),
"A single document was not represented correctly"
)
}
fun nonQueryChanges(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), mapFunc = Results::toCount),
"There should have been 5 documents in the table"
)
db.conn.customNonQuery("DELETE FROM $TEST_TABLE")
assertEquals(
0L, db.conn.customScalar(CountQuery.all(TEST_TABLE), mapFunc = Results::toCount),
"There should have been no documents in the table"
)
}
fun nonQueryNoChanges(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), mapFunc = Results::toCount),
"There should have been 5 documents in the table"
)
db.conn.customNonQuery(
DeleteQuery.byId(TEST_TABLE, "eighty-two"),
listOf(Parameter(":id", ParameterType.STRING, "eighty-two"))
)
assertEquals(
5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), mapFunc = Results::toCount),
"There should still have been 5 documents in the table"
)
}
fun scalar(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
3L,
db.conn.customScalar("SELECT 3 AS it FROM $TEST_TABLE LIMIT 1", mapFunc = Results::toCount),
"The number 3 should have been returned"
)
}
}

View File

@@ -0,0 +1,45 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
import kotlin.test.assertFalse
import kotlin.test.assertTrue
/**
* Integration tests for the `Definition` object / `ensure*` connection extension functions
*/
object DefinitionFunctions {
fun ensureTable(db: ThrowawayDatabase) {
assertFalse(db.dbObjectExists("ensured"), "The 'ensured' table should not exist")
assertFalse(db.dbObjectExists("idx_ensured_key"), "The PK index for the 'ensured' table should not exist")
db.conn.ensureTable("ensured")
assertTrue(db.dbObjectExists("ensured"), "The 'ensured' table should exist")
assertTrue(db.dbObjectExists("idx_ensured_key"), "The PK index for the 'ensured' table should now exist")
}
fun ensureFieldIndex(db: ThrowawayDatabase) {
assertFalse(db.dbObjectExists("idx_${TEST_TABLE}_test"), "The test index should not exist")
db.conn.ensureFieldIndex(TEST_TABLE, "test", listOf("id", "category"))
assertTrue(db.dbObjectExists("idx_${TEST_TABLE}_test"), "The test index should now exist")
}
fun ensureDocumentIndexFull(db: ThrowawayDatabase) {
assertFalse(db.dbObjectExists("doc_table"), "The 'doc_table' table should not exist")
db.conn.ensureTable("doc_table")
assertTrue(db.dbObjectExists("doc_table"), "The 'doc_table' table should exist")
assertFalse(db.dbObjectExists("idx_doc_table_document"), "The document index should not exist")
db.conn.ensureDocumentIndex("doc_table", DocumentIndex.FULL)
assertTrue(db.dbObjectExists("idx_doc_table_document"), "The document index should exist")
}
fun ensureDocumentIndexOptimized(db: ThrowawayDatabase) {
assertFalse(db.dbObjectExists("doc_table"), "The 'doc_table' table should not exist")
db.conn.ensureTable("doc_table")
assertTrue(db.dbObjectExists("doc_table"), "The 'doc_table' table should exist")
assertFalse(db.dbObjectExists("idx_doc_table_document"), "The document index should not exist")
db.conn.ensureDocumentIndex("doc_table", DocumentIndex.OPTIMIZED)
assertTrue(db.dbObjectExists("idx_doc_table_document"), "The document index should exist")
}
}

View File

@@ -0,0 +1,69 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.JsonDocument
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
import kotlin.test.assertEquals
/**
* Integration tests for the `Delete` object
*/
object DeleteFunctions {
fun byIdMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
db.conn.deleteById(TEST_TABLE, "four")
assertEquals(4, db.conn.countAll(TEST_TABLE), "There should now be 4 documents in the table")
}
fun byIdNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
db.conn.deleteById(TEST_TABLE, "negative four")
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table")
}
fun byFieldsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
db.conn.deleteByFields(TEST_TABLE, listOf(Field.notEqual("value", "purple")))
assertEquals(2, db.conn.countAll(TEST_TABLE), "There should now be 2 documents in the table")
}
fun byFieldsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
db.conn.deleteByFields(TEST_TABLE, listOf(Field.equal("value", "crimson")))
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table")
}
fun byContainsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
db.conn.deleteByContains(TEST_TABLE, mapOf("value" to "purple"))
assertEquals(3, db.conn.countAll(TEST_TABLE), "There should now be 3 documents in the table")
}
fun byContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
db.conn.deleteByContains(TEST_TABLE, mapOf("target" to "acquired"))
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table")
}
fun byJsonPathMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
db.conn.deleteByJsonPath(TEST_TABLE, "$.value ? (@ == \"purple\")")
assertEquals(3, db.conn.countAll(TEST_TABLE), "There should now be 3 documents in the table")
}
fun byJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
db.conn.deleteByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)")
assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table")
}
}

View File

@@ -0,0 +1,130 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.JsonDocument
import solutions.bitbadger.documents.kotlinx.tests.NumIdDocument
import solutions.bitbadger.documents.kotlinx.tests.SubDocument
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
import kotlin.test.*
/**
* Integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
*/
object DocumentFunctions {
fun insertDefault(db: ThrowawayDatabase) {
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
val doc = JsonDocument("turkey", "", 0, SubDocument("gobble", "gobble"))
db.conn.insert(TEST_TABLE, doc)
val after = db.conn.findAll<JsonDocument>(TEST_TABLE)
assertEquals(1, after.size, "There should be one document in the table")
assertEquals(doc, after[0], "The document should be what was inserted")
}
fun insertDupe(db: ThrowawayDatabase) {
db.conn.insert(TEST_TABLE, JsonDocument("a", "", 0, null))
assertThrows<DocumentException>("Inserting a document with a duplicate key should have thrown an exception") {
db.conn.insert(TEST_TABLE, JsonDocument("a", "b", 22, null))
}
}
fun insertNumAutoId(db: ThrowawayDatabase) {
try {
Configuration.autoIdStrategy = AutoId.NUMBER
Configuration.idField = "key"
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
db.conn.insert(TEST_TABLE, NumIdDocument(0, "one"))
db.conn.insert(TEST_TABLE, NumIdDocument(0, "two"))
db.conn.insert(TEST_TABLE, NumIdDocument(77, "three"))
db.conn.insert(TEST_TABLE, NumIdDocument(0, "four"))
val after = db.conn.findAll<NumIdDocument>(TEST_TABLE, listOf(Field.named("key")))
assertEquals(4, after.size, "There should have been 4 documents returned")
assertEquals(
"1|2|77|78", after.joinToString("|") { it.key.toString() },
"The IDs were not generated correctly"
)
} finally {
Configuration.autoIdStrategy = AutoId.DISABLED
Configuration.idField = "id"
}
}
fun insertUUIDAutoId(db: ThrowawayDatabase) {
try {
Configuration.autoIdStrategy = AutoId.UUID
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
db.conn.insert(TEST_TABLE, JsonDocument(""))
val after = db.conn.findAll<JsonDocument>(TEST_TABLE)
assertEquals(1, after.size, "There should have been 1 document returned")
assertEquals(32, after[0].id.length, "The ID was not generated correctly")
} finally {
Configuration.autoIdStrategy = AutoId.DISABLED
}
}
fun insertStringAutoId(db: ThrowawayDatabase) {
try {
Configuration.autoIdStrategy = AutoId.RANDOM_STRING
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
db.conn.insert(TEST_TABLE, JsonDocument(""))
Configuration.idStringLength = 21
db.conn.insert(TEST_TABLE, JsonDocument(""))
val after = db.conn.findAll<JsonDocument>(TEST_TABLE)
assertEquals(2, after.size, "There should have been 2 documents returned")
assertEquals(16, after[0].id.length, "The first document's ID was not generated correctly")
assertEquals(21, after[1].id.length, "The second document's ID was not generated correctly")
} finally {
Configuration.autoIdStrategy = AutoId.DISABLED
Configuration.idStringLength = 16
}
}
fun saveMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
db.conn.save(TEST_TABLE, JsonDocument("two", numValue = 44))
val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "two")
assertNotNull(doc, "There should have been a document returned")
assertEquals("two", doc.id, "An incorrect document was returned")
assertEquals("", doc.value, "The \"value\" field was not updated")
assertEquals(44, doc.numValue, "The \"numValue\" field was not updated")
assertNull(doc.sub, "The \"sub\" field was not updated")
}
fun saveNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
db.conn.save(TEST_TABLE, JsonDocument("test", sub = SubDocument("a", "b")))
assertNotNull(
db.conn.findById<String, JsonDocument>(TEST_TABLE, "test"),
"The test document should have been saved"
)
}
fun updateMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
db.conn.update(TEST_TABLE, "one", JsonDocument("one", "howdy", 8, SubDocument("y", "z")))
val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "one")
assertNotNull(doc, "There should have been a document returned")
assertEquals("one", doc.id, "An incorrect document was returned")
assertEquals("howdy", doc.value, "The \"value\" field was not updated")
assertEquals(8, doc.numValue, "The \"numValue\" field was not updated")
assertNotNull(doc.sub, "The sub-document should not be null")
assertEquals("y", doc.sub.foo, "The sub-document \"foo\" field was not updated")
assertEquals("z", doc.sub.bar, "The sub-document \"bar\" field was not updated")
}
fun updateNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse { db.conn.existsById(TEST_TABLE, "two-hundred") }
db.conn.update(TEST_TABLE, "two-hundred", JsonDocument("two-hundred", numValue = 200))
assertFalse { db.conn.existsById(TEST_TABLE, "two-hundred") }
}
}

View File

@@ -0,0 +1,66 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.JsonDocument
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
import kotlin.test.assertFalse
import kotlin.test.assertTrue
/**
* Integration tests for the `Exists` object
*/
object ExistsFunctions {
fun byIdMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertTrue("The document with ID \"three\" should exist") { db.conn.existsById(TEST_TABLE, "three") }
}
fun byIdNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse("The document with ID \"seven\" should not exist") { db.conn.existsById(TEST_TABLE, "seven") }
}
fun byFieldsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertTrue("Matching documents should have been found") {
db.conn.existsByFields(TEST_TABLE, listOf(Field.equal("numValue", 10)))
}
}
fun byFieldsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse("No matching documents should have been found") {
db.conn.existsByFields(TEST_TABLE, listOf(Field.equal("nothing", "none")))
}
}
fun byContainsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertTrue("Matching documents should have been found") {
db.conn.existsByContains(TEST_TABLE, mapOf("value" to "purple"))
}
}
fun byContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse("Matching documents should not have been found") {
db.conn.existsByContains(TEST_TABLE, mapOf("value" to "violet"))
}
}
fun byJsonPathMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertTrue("Matching documents should have been found") {
db.conn.existsByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10)")
}
}
fun byJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse("Matching documents should not have been found") {
db.conn.existsByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10.1)")
}
}
}

View File

@@ -0,0 +1,300 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.ArrayDocument
import solutions.bitbadger.documents.kotlinx.tests.JsonDocument
import solutions.bitbadger.documents.kotlinx.tests.NumIdDocument
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
/**
* Integration tests for the `Find` object
*/
object FindFunctions {
fun allDefault(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(5, db.conn.findAll<JsonDocument>(TEST_TABLE).size, "There should have been 5 documents returned")
}
fun allAscending(db: ThrowawayDatabase) {
JsonDocument.load(db)
val docs = db.conn.findAll<JsonDocument>(TEST_TABLE, listOf(Field.named("id")))
assertEquals(5, docs.size, "There should have been 5 documents returned")
assertEquals(
"five|four|one|three|two",
docs.joinToString("|") { it.id },
"The documents were not ordered correctly"
)
}
fun allDescending(db: ThrowawayDatabase) {
JsonDocument.load(db)
val docs = db.conn.findAll<JsonDocument>(TEST_TABLE, listOf(Field.named("id DESC")))
assertEquals(5, docs.size, "There should have been 5 documents returned")
assertEquals(
"two|three|one|four|five",
docs.joinToString("|") { it.id },
"The documents were not ordered correctly"
)
}
fun allNumOrder(db: ThrowawayDatabase) {
JsonDocument.load(db)
val docs = db.conn.findAll<JsonDocument>(
TEST_TABLE,
listOf(Field.named("sub.foo NULLS LAST"), Field.named("n:numValue"))
)
assertEquals(5, docs.size, "There should have been 5 documents returned")
assertEquals(
"two|four|one|three|five",
docs.joinToString("|") { it.id },
"The documents were not ordered correctly"
)
}
fun allEmpty(db: ThrowawayDatabase) =
assertEquals(0, db.conn.findAll<JsonDocument>(TEST_TABLE).size, "There should have been no documents returned")
fun byIdString(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "two")
assertNotNull(doc, "The document should have been returned")
assertEquals("two", doc.id, "An incorrect document was returned")
}
fun byIdNumber(db: ThrowawayDatabase) {
Configuration.idField = "key"
try {
db.conn.insert(TEST_TABLE, NumIdDocument(18, "howdy"))
val doc = db.conn.findById<Int, NumIdDocument>(TEST_TABLE, 18)
assertNotNull(doc, "The document should have been returned")
} finally {
Configuration.idField = "id"
}
}
fun byIdNotFound(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertNull(
db.conn.findById<String, JsonDocument>(TEST_TABLE, "x"),
"There should have been no document returned"
)
}
fun byFieldsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val docs = db.conn.findByFields<JsonDocument>(
TEST_TABLE,
listOf(Field.any("value", listOf("blue", "purple")), Field.exists("sub")),
FieldMatch.ALL
)
assertEquals(1, docs.size, "There should have been a document returned")
assertEquals("four", docs[0].id, "The incorrect document was returned")
}
fun byFieldsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val docs = db.conn.findByFields<JsonDocument>(
TEST_TABLE,
listOf(Field.equal("value", "purple")),
orderBy = listOf(Field.named("id"))
)
assertEquals(2, docs.size, "There should have been 2 documents returned")
assertEquals("five|four", docs.joinToString("|") { it.id }, "The documents were not ordered correctly")
}
fun byFieldsMatchNumIn(db: ThrowawayDatabase) {
JsonDocument.load(db)
val docs = db.conn.findByFields<JsonDocument>(TEST_TABLE, listOf(Field.any("numValue", listOf(2, 4, 6, 8))))
assertEquals(1, docs.size, "There should have been a document returned")
assertEquals("three", docs[0].id, "The incorrect document was returned")
}
fun byFieldsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
0,
db.conn.findByFields<JsonDocument>(TEST_TABLE, listOf(Field.greater("numValue", 100))).size,
"There should have been no documents returned"
)
}
fun byFieldsMatchInArray(db: ThrowawayDatabase) {
ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) }
val docs =
db.conn.findByFields<ArrayDocument>(TEST_TABLE, listOf(Field.inArray("values", TEST_TABLE, listOf("c"))))
assertEquals(2, docs.size, "There should have been two documents returned")
assertTrue(listOf("first", "second").contains(docs[0].id), "An incorrect document was returned (${docs[0].id})")
assertTrue(listOf("first", "second").contains(docs[1].id), "An incorrect document was returned (${docs[1].id})")
}
fun byFieldsNoMatchInArray(db: ThrowawayDatabase) {
ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) }
assertEquals(
0,
db.conn.findByFields<ArrayDocument>(
TEST_TABLE,
listOf(Field.inArray("values", TEST_TABLE, listOf("j")))
).size,
"There should have been no documents returned"
)
}
fun byContainsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val docs = db.conn.findByContains<JsonDocument, Map<String, String>>(TEST_TABLE, mapOf("value" to "purple"))
assertEquals(2, docs.size, "There should have been 2 documents returned")
assertTrue(listOf("four", "five").contains(docs[0].id), "An incorrect document was returned (${docs[0].id})")
assertTrue(listOf("four", "five").contains(docs[1].id), "An incorrect document was returned (${docs[1].id})")
}
fun byContainsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val docs = db.conn.findByContains<JsonDocument, Map<String, Map<String, String>>>(
TEST_TABLE,
mapOf("sub" to mapOf("foo" to "green")),
listOf(Field.named("value"))
)
assertEquals(2, docs.size, "There should have been 2 documents returned")
assertEquals("two|four", docs.joinToString("|") { it.id }, "The documents were not ordered correctly")
}
fun byContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
0,
db.conn.findByContains<JsonDocument, Map<String, String>>(TEST_TABLE, mapOf("value" to "indigo")).size,
"There should have been no documents returned"
)
}
fun byJsonPathMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val docs = db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ > 10)")
assertEquals(2, docs.size, "There should have been 2 documents returned")
assertTrue(listOf("four", "five").contains(docs[0].id), "An incorrect document was returned (${docs[0].id})")
assertTrue(listOf("four", "five").contains(docs[1].id), "An incorrect document was returned (${docs[1].id})")
}
fun byJsonPathMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val docs = db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ > 10)", listOf(Field.named("id")))
assertEquals(2, docs.size, "There should have been 2 documents returned")
assertEquals("five|four", docs.joinToString("|") { it.id }, "The documents were not ordered correctly")
}
fun byJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertEquals(
0,
db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ > 100)").size,
"There should have been no documents returned"
)
}
fun firstByFieldsMatchOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findFirstByFields<JsonDocument>(TEST_TABLE, listOf(Field.equal("value", "another")))
assertNotNull(doc, "There should have been a document returned")
assertEquals("two", doc.id, "The incorrect document was returned")
}
fun firstByFieldsMatchMany(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findFirstByFields<JsonDocument>(TEST_TABLE, listOf(Field.equal("sub.foo", "green")))
assertNotNull(doc, "There should have been a document returned")
assertTrue(listOf("two", "four").contains(doc.id), "An incorrect document was returned (${doc.id})")
}
fun firstByFieldsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findFirstByFields<JsonDocument>(
TEST_TABLE,
listOf(Field.equal("sub.foo", "green")),
orderBy = listOf(Field.named("n:numValue DESC"))
)
assertNotNull(doc, "There should have been a document returned")
assertEquals("four", doc.id, "An incorrect document was returned")
}
fun firstByFieldsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertNull(
db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("value", "absent"))),
"There should have been no document returned"
)
}
fun firstByContainsMatchOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findFirstByContains<JsonDocument, Map<String, String>>(TEST_TABLE, mapOf("value" to "FIRST!"))
assertNotNull(doc, "There should have been a document returned")
assertEquals("one", doc.id, "An incorrect document was returned")
}
fun firstByContainsMatchMany(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findFirstByContains<JsonDocument, Map<String, String>>(TEST_TABLE, mapOf("value" to "purple"))
assertNotNull(doc, "There should have been a document returned")
assertTrue(listOf("four", "five").contains(doc.id), "An incorrect document was returned (${doc.id})")
}
fun firstByContainsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findFirstByContains<JsonDocument, Map<String, String>>(
TEST_TABLE,
mapOf("value" to "purple"),
listOf(Field.named("sub.bar NULLS FIRST"))
)
assertNotNull(doc, "There should have been a document returned")
assertEquals("five", doc.id, "An incorrect document was returned")
}
fun firstByContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertNull(
db.conn.findFirstByContains<JsonDocument, Map<String, String>>(
TEST_TABLE,
mapOf("value" to "indigo")
), "There should have been no document returned"
)
}
fun firstByJsonPathMatchOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findFirstByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ == 10)")
assertNotNull(doc, "There should have been a document returned")
assertEquals("two", doc.id, "An incorrect document was returned")
}
fun firstByJsonPathMatchMany(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findFirstByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ > 10)")
assertNotNull(doc, "There should have been a document returned")
assertTrue(listOf("four", "five").contains(doc.id), "An incorrect document was returned (${doc.id})")
}
fun firstByJsonPathMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findFirstByJsonPath<JsonDocument>(
TEST_TABLE,
"$.numValue ? (@ > 10)",
listOf(Field.named("id DESC"))
)
assertNotNull(doc, "There should have been a document returned")
assertEquals("four", doc.id, "An incorrect document was returned")
}
fun firstByJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertNull(
db.conn.findFirstByJsonPath<JsonDocument>(TEST_TABLE, "$.numValue ? (@ > 100)"),
"There should have been no document returned"
)
}
}

View File

@@ -0,0 +1,719 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.*
import java.io.PrintWriter
import java.io.StringWriter
import kotlin.test.assertEquals
import kotlin.test.assertTrue
/**
* Tests for the JSON-returning functions
*
* NOTE: PostgreSQL JSONB columns do not preserve the original JSON with which a document was stored. These tests are
* the most complex within the library, as they have split testing based on the backing data store. The PostgreSQL tests
* check IDs (and, in the case of ordered queries, which ones occur before which others) vs. the entire JSON string.
* Meanwhile, SQLite stores JSON as text, and will return exactly the JSON it was given when it was originally written.
* These tests can ensure the expected round-trip of the entire JSON string.
*/
object JsonFunctions {
/**
* PostgreSQL, when returning JSONB as a string, has spaces after commas and colons delineating fields and values.
* This function will do a crude string replacement to match the target string based on the dialect being tested.
*
* @param json The JSON which should be returned
* @return The actual expected JSON based on the database being tested
*/
fun maybeJsonB(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> json
Dialect.POSTGRESQL -> json.replace("\":", "\": ").replace(",\"", ", \"")
}
/**
* Create a snippet of JSON to find a document ID
*
* @param id The ID of the document
* @return A connection-aware ID to check for presence and positioning
*/
private fun docId(id: String) =
maybeJsonB("{\"id\":\"$id\"")
private fun checkAllDefault(json: String) {
assertTrue(json.startsWith("["), "JSON should start with '[' ($json)")
when (Configuration.dialect()) {
Dialect.SQLITE -> {
assertTrue(json.contains(JsonDocument.one), "Document 'one' not found in JSON ($json)")
assertTrue(json.contains(JsonDocument.two), "Document 'two' not found in JSON ($json)")
assertTrue(json.contains(JsonDocument.three), "Document 'three' not found in JSON ($json)")
assertTrue(json.contains(JsonDocument.four), "Document 'four' not found in JSON ($json)")
assertTrue(json.contains(JsonDocument.five), "Document 'five' not found in JSON ($json)")
}
Dialect.POSTGRESQL -> {
assertTrue(json.contains(docId("one")), "Document 'one' not found in JSON ($json)")
assertTrue(json.contains(docId("two")), "Document 'two' not found in JSON ($json)")
assertTrue(json.contains(docId("three")), "Document 'three' not found in JSON ($json)")
assertTrue(json.contains(docId("four")), "Document 'four' not found in JSON ($json)")
assertTrue(json.contains(docId("five")), "Document 'five' not found in JSON ($json)")
}
}
assertTrue(json.endsWith("]"), "JSON should end with ']' ($json)")
}
fun allDefault(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkAllDefault(db.conn.jsonAll(TEST_TABLE))
}
fun writeAllDefault(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonAll(TEST_TABLE, writer)
checkAllDefault(output.toString())
}
private fun checkAllEmpty(json: String) =
assertEquals("[]", json, "There should have been no documents returned")
fun allEmpty(db: ThrowawayDatabase) =
checkAllEmpty(db.conn.jsonAll(TEST_TABLE))
fun writeAllEmpty(db: ThrowawayDatabase) {
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonAll(TEST_TABLE, writer)
checkAllEmpty(output.toString())
}
private fun checkByIdString(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals(JsonDocument.two, json, "An incorrect document was returned")
Dialect.POSTGRESQL -> assertTrue(json.contains(docId("two")), "An incorrect document was returned ($json)")
}
fun byIdString(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByIdString(db.conn.jsonById(TEST_TABLE, "two"))
}
fun writeByIdString(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonById(TEST_TABLE, writer, "two")
checkByIdString(output.toString())
}
private fun checkByIdNumber(json: String) =
assertEquals(
maybeJsonB("""{"key":18,"text":"howdy"}"""),
json,
"The document should have been found by numeric ID"
)
fun byIdNumber(db: ThrowawayDatabase) {
Configuration.idField = "key"
try {
db.conn.insert(TEST_TABLE, NumIdDocument(18, "howdy"))
checkByIdNumber(db.conn.jsonById(TEST_TABLE, 18))
} finally {
Configuration.idField = "id"
}
}
fun writeByIdNumber(db: ThrowawayDatabase) {
Configuration.idField = "key"
try {
db.conn.insert(TEST_TABLE, NumIdDocument(18, "howdy"))
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonById(TEST_TABLE, writer, 18)
checkByIdNumber(output.toString())
} finally {
Configuration.idField = "id"
}
}
private fun checkByIdNotFound(json: String) =
assertEquals("{}", json, "There should have been no document returned")
fun byIdNotFound(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByIdNotFound(db.conn.jsonById(TEST_TABLE, "x"))
}
fun writeByIdNotFound(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonById(TEST_TABLE, writer, "x")
checkByIdNotFound(output.toString())
}
private fun checkByFieldsMatch(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals("[${JsonDocument.four}]", json, "The incorrect document was returned")
Dialect.POSTGRESQL -> {
assertTrue(json.startsWith("["), "JSON should start with '[' ($json)")
assertTrue(json.contains(docId("four")),"The incorrect document was returned ($json)")
assertTrue(json.endsWith("]"), "JSON should end with ']' ($json)")
}
}
fun byFieldsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByFieldsMatch(
db.conn.jsonByFields(
TEST_TABLE, listOf(Field.any("value", listOf("blue", "purple")), Field.exists("sub")), FieldMatch.ALL
)
)
}
fun writeByFieldsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByFields(
TEST_TABLE,
writer,
listOf(Field.any("value", listOf("blue", "purple")), Field.exists("sub")),
FieldMatch.ALL
)
checkByFieldsMatch(output.toString())
}
private fun checkByFieldsMatchOrdered(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals(
"[${JsonDocument.five},${JsonDocument.four}]", json, "The documents were not ordered correctly"
)
Dialect.POSTGRESQL -> {
val fiveIdx = json.indexOf(docId("five"))
val fourIdx = json.indexOf(docId("four"))
assertTrue(json.startsWith("["), "JSON should start with '[' ($json)")
assertTrue(fiveIdx >= 0, "Document 'five' not found ($json)")
assertTrue(fourIdx >= 0, "Document 'four' not found ($json)")
assertTrue(fiveIdx < fourIdx, "Document 'five' should have been before 'four' ($json)")
assertTrue(json.endsWith("]"), "JSON should end with ']' ($json)")
}
}
fun byFieldsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByFieldsMatchOrdered(
db.conn.jsonByFields(
TEST_TABLE, listOf(Field.equal("value", "purple")), orderBy = listOf(Field.named("id"))
)
)
}
fun writeByFieldsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByFields(
TEST_TABLE, writer, listOf(Field.equal("value", "purple")), orderBy = listOf(Field.named("id"))
)
checkByFieldsMatchOrdered(output.toString())
}
private fun checkByFieldsMatchNumIn(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals("[${JsonDocument.three}]", json, "The incorrect document was returned")
Dialect.POSTGRESQL -> {
assertTrue(json.startsWith("["), "JSON should start with '[' ($json)")
assertTrue(json.contains(docId("three")), "The incorrect document was returned ($json)")
assertTrue(json.endsWith("]"), "JSON should end with ']' ($json)")
}
}
fun byFieldsMatchNumIn(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByFieldsMatchNumIn(db.conn.jsonByFields(TEST_TABLE, listOf(Field.any("numValue", listOf(2, 4, 6, 8)))))
}
fun writeByFieldsMatchNumIn(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByFields(TEST_TABLE, writer, listOf(Field.any("numValue", listOf(2, 4, 6, 8))))
checkByFieldsMatchNumIn(output.toString())
}
private fun checkByFieldsNoMatch(json: String) =
assertEquals("[]", json, "There should have been no documents returned")
fun byFieldsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByFieldsNoMatch(db.conn.jsonByFields(TEST_TABLE, listOf(Field.greater("numValue", 100))))
}
fun writeByFieldsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByFields(TEST_TABLE, writer, listOf(Field.greater("numValue", 100)))
checkByFieldsNoMatch(output.toString())
}
private fun checkByFieldsMatchInArray(json: String) {
assertTrue(json.startsWith("["), "JSON should start with '[' ($json)")
assertTrue(json.contains(docId("first")), "The 'first' document was not found ($json)")
assertTrue(json.contains(docId("second")), "The 'second' document was not found ($json)")
assertTrue(json.endsWith("]"), "JSON should end with ']' ($json)")
}
fun byFieldsMatchInArray(db: ThrowawayDatabase) {
ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) }
checkByFieldsMatchInArray(
db.conn.jsonByFields(TEST_TABLE, listOf(Field.inArray("values", TEST_TABLE, listOf("c"))))
)
}
fun writeByFieldsMatchInArray(db: ThrowawayDatabase) {
ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) }
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByFields(TEST_TABLE, writer, listOf(Field.inArray("values", TEST_TABLE, listOf("c"))))
checkByFieldsMatchInArray(output.toString())
}
private fun checkByFieldsNoMatchInArray(json: String) =
assertEquals("[]", json, "There should have been no documents returned")
fun byFieldsNoMatchInArray(db: ThrowawayDatabase) {
ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) }
checkByFieldsNoMatchInArray(
db.conn.jsonByFields(TEST_TABLE, listOf(Field.inArray("values", TEST_TABLE, listOf("j"))))
)
}
fun writeByFieldsNoMatchInArray(db: ThrowawayDatabase) {
ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) }
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByFields(TEST_TABLE, writer, listOf(Field.inArray("values", TEST_TABLE, listOf("j"))))
checkByFieldsNoMatchInArray(output.toString())
}
private fun checkByContainsMatch(json: String) {
assertTrue(json.startsWith("["), "JSON should start with '[' ($json)")
when (Configuration.dialect()) {
Dialect.SQLITE -> {
assertTrue(json.contains(JsonDocument.four), "Document 'four' not found ($json)")
assertTrue(json.contains(JsonDocument.five), "Document 'five' not found ($json)")
}
Dialect.POSTGRESQL -> {
assertTrue(json.contains(docId("four")), "Document 'four' not found ($json)")
assertTrue(json.contains(docId("five")), "Document 'five' not found ($json)")
}
}
assertTrue(json.endsWith("]"), "JSON should end with ']' ($json)")
}
fun byContainsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByContainsMatch(db.conn.jsonByContains<Map<String, String>>(TEST_TABLE, mapOf("value" to "purple")))
}
fun writeByContainsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByContains<Map<String, String>>(TEST_TABLE, writer, mapOf("value" to "purple"))
checkByContainsMatch(output.toString())
}
private fun checkByContainsMatchOrdered(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals(
"[${JsonDocument.two},${JsonDocument.four}]", json, "The documents were not ordered correctly"
)
Dialect.POSTGRESQL -> {
val twoIdx = json.indexOf(docId("two"))
val fourIdx = json.indexOf(docId("four"))
assertTrue(json.startsWith("["), "JSON should start with '[' ($json)")
assertTrue(twoIdx >= 0, "Document 'two' not found ($json)")
assertTrue(fourIdx >= 0, "Document 'four' not found ($json)")
assertTrue(twoIdx < fourIdx, "Document 'two' should have been before 'four' ($json)")
assertTrue(json.endsWith("]"), "JSON should end with ']' ($json)")
}
}
fun byContainsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByContainsMatchOrdered(
db.conn.jsonByContains<Map<String, Map<String, String>>>(
TEST_TABLE, mapOf("sub" to mapOf("foo" to "green")), listOf(Field.named("value"))
)
)
}
fun writeByContainsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByContains<Map<String, Map<String, String>>>(
TEST_TABLE, writer, mapOf("sub" to mapOf("foo" to "green")), listOf(Field.named("value"))
)
checkByContainsMatchOrdered(output.toString())
}
private fun checkByContainsNoMatch(json: String) =
assertEquals("[]", json, "There should have been no documents returned")
fun byContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByContainsNoMatch(db.conn.jsonByContains<Map<String, String>>(TEST_TABLE, mapOf("value" to "indigo")))
}
fun writeByContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByContains<Map<String, String>>(TEST_TABLE, writer, mapOf("value" to "indigo"))
checkByContainsNoMatch(output.toString())
}
private fun checkByJsonPathMatch(json: String) {
assertTrue(json.startsWith("["), "JSON should start with '[' ($json)")
when (Configuration.dialect()) {
Dialect.SQLITE -> {
assertTrue(json.contains(JsonDocument.four), "Document 'four' not found ($json)")
assertTrue(json.contains(JsonDocument.five), "Document 'five' not found ($json)")
}
Dialect.POSTGRESQL -> {
assertTrue(json.contains(docId("four")), "Document 'four' not found ($json)")
assertTrue(json.contains(docId("five")), "Document 'five' not found ($json)")
}
}
assertTrue(json.endsWith("]"), "JSON should end with ']' ($json)")
}
fun byJsonPathMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByJsonPathMatch(db.conn.jsonByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)"))
}
fun writeByJsonPathMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByJsonPath(TEST_TABLE, writer, "$.numValue ? (@ > 10)")
checkByJsonPathMatch(output.toString())
}
private fun checkByJsonPathMatchOrdered(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals(
"[${JsonDocument.five},${JsonDocument.four}]", json, "The documents were not ordered correctly"
)
Dialect.POSTGRESQL -> {
val fiveIdx = json.indexOf(docId("five"))
val fourIdx = json.indexOf(docId("four"))
assertTrue(json.startsWith("["), "JSON should start with '[' ($json)")
assertTrue(fiveIdx >= 0, "Document 'five' not found ($json)")
assertTrue(fourIdx >= 0, "Document 'four' not found ($json)")
assertTrue(fiveIdx < fourIdx, "Document 'five' should have been before 'four' ($json)")
assertTrue(json.endsWith("]"), "JSON should end with ']' ($json)")
}
}
fun byJsonPathMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByJsonPathMatchOrdered(
db.conn.jsonByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)", listOf(Field.named("id")))
)
}
fun writeByJsonPathMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByJsonPath(TEST_TABLE, writer, "$.numValue ? (@ > 10)", listOf(Field.named("id")))
checkByJsonPathMatchOrdered(output.toString())
}
private fun checkByJsonPathNoMatch(json: String) =
assertEquals("[]", json, "There should have been no documents returned")
fun byJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkByJsonPathNoMatch(db.conn.jsonByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)"))
}
fun writeByJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonByJsonPath(TEST_TABLE, writer, "$.numValue ? (@ > 100)")
checkByJsonPathNoMatch(output.toString())
}
private fun checkFirstByFieldsMatchOne(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals(JsonDocument.two, json, "The incorrect document was returned")
Dialect.POSTGRESQL -> assertTrue(json.contains(docId("two")), "The incorrect document was returned ($json)")
}
fun firstByFieldsMatchOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByFieldsMatchOne(db.conn.jsonFirstByFields(TEST_TABLE, listOf(Field.equal("value", "another"))))
}
fun writeFirstByFieldsMatchOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByFields(TEST_TABLE, writer, listOf(Field.equal("value", "another")))
checkFirstByFieldsMatchOne(output.toString())
}
private fun checkFirstByFieldsMatchMany(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertTrue(
json.contains(JsonDocument.two) || json.contains(JsonDocument.four),
"Expected document 'two' or 'four' ($json)"
)
Dialect.POSTGRESQL -> assertTrue(
json.contains(docId("two")) || json.contains(docId("four")),
"Expected document 'two' or 'four' ($json)"
)
}
fun firstByFieldsMatchMany(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByFieldsMatchMany(db.conn.jsonFirstByFields(TEST_TABLE, listOf(Field.equal("sub.foo", "green"))))
}
fun writeFirstByFieldsMatchMany(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByFields(TEST_TABLE, writer, listOf(Field.equal("sub.foo", "green")))
checkFirstByFieldsMatchMany(output.toString())
}
private fun checkFirstByFieldsMatchOrdered(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals(JsonDocument.four, json, "An incorrect document was returned")
Dialect.POSTGRESQL -> assertTrue(json.contains(docId("four")), "An incorrect document was returned ($json)")
}
fun firstByFieldsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByFieldsMatchOrdered(
db.conn.jsonFirstByFields(
TEST_TABLE, listOf(Field.equal("sub.foo", "green")), orderBy = listOf(Field.named("n:numValue DESC"))
)
)
}
fun writeFirstByFieldsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByFields(
TEST_TABLE,
writer,
listOf(Field.equal("sub.foo", "green")),
orderBy = listOf(Field.named("n:numValue DESC"))
)
checkFirstByFieldsMatchOrdered(output.toString())
}
private fun checkFirstByFieldsNoMatch(json: String) =
assertEquals("{}", json, "There should have been no document returned")
fun firstByFieldsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByFieldsNoMatch(db.conn.jsonFirstByFields(TEST_TABLE, listOf(Field.equal("value", "absent"))))
}
fun writeFirstByFieldsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByFields(TEST_TABLE, writer, listOf(Field.equal("value", "absent")))
checkFirstByFieldsNoMatch(output.toString())
}
private fun checkFirstByContainsMatchOne(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals(JsonDocument.one, json, "An incorrect document was returned")
Dialect.POSTGRESQL -> assertTrue(json.contains(docId("one")), "An incorrect document was returned ($json)")
}
fun firstByContainsMatchOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByContainsMatchOne(
db.conn.jsonFirstByContains<Map<String, String>>(TEST_TABLE, mapOf("value" to "FIRST!"))
)
}
fun writeFirstByContainsMatchOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByContains<Map<String, String>>(TEST_TABLE, writer, mapOf("value" to "FIRST!"))
}
private fun checkFirstByContainsMatchMany(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertTrue(
json.contains(JsonDocument.four) || json.contains(JsonDocument.five),
"Expected document 'four' or 'five' ($json)"
)
Dialect.POSTGRESQL -> assertTrue(
json.contains(docId("four")) || json.contains(docId("five")),
"Expected document 'four' or 'five' ($json)"
)
}
fun firstByContainsMatchMany(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByContainsMatchMany(
db.conn.jsonFirstByContains<Map<String, String>>(TEST_TABLE, mapOf("value" to "purple"))
)
}
fun writeFirstByContainsMatchMany(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByContains<Map<String, String>>(TEST_TABLE, writer, mapOf("value" to "purple"))
checkFirstByContainsMatchMany(output.toString())
}
private fun checkFirstByContainsMatchOrdered(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals(JsonDocument.five, json, "An incorrect document was returned")
Dialect.POSTGRESQL -> assertTrue(json.contains(docId("five")), "An incorrect document was returned ($json)")
}
fun firstByContainsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByContainsMatchOrdered(
db.conn.jsonFirstByContains<Map<String, String>>(
TEST_TABLE, mapOf("value" to "purple"), listOf(Field.named("sub.bar NULLS FIRST"))
)
)
}
fun writeFirstByContainsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByContains<Map<String, String>>(
TEST_TABLE, writer, mapOf("value" to "purple"), listOf(Field.named("sub.bar NULLS FIRST"))
)
checkFirstByContainsMatchOrdered(output.toString())
}
private fun checkFirstByContainsNoMatch(json: String) =
assertEquals("{}", json, "There should have been no document returned")
fun firstByContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByContainsNoMatch(
db.conn.jsonFirstByContains<Map<String, String>>(TEST_TABLE, mapOf("value" to "indigo"))
)
}
fun writeFirstByContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByContains<Map<String, String>>(TEST_TABLE, writer, mapOf("value" to "indigo"))
checkFirstByContainsNoMatch(output.toString())
}
private fun checkFirstByJsonPathMatchOne(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals(JsonDocument.two, json, "An incorrect document was returned")
Dialect.POSTGRESQL -> assertTrue(json.contains(docId("two")), "An incorrect document was returned ($json)")
}
fun firstByJsonPathMatchOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByJsonPathMatchOne(db.conn.jsonFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10)"))
}
fun writeFirstByJsonPathMatchOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByJsonPath(TEST_TABLE, writer, "$.numValue ? (@ == 10)")
checkFirstByJsonPathMatchOne(output.toString())
}
private fun checkFirstByJsonPathMatchMany(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertTrue(
json.contains(JsonDocument.four) || json.contains(JsonDocument.five),
"Expected document 'four' or 'five' ($json)"
)
Dialect.POSTGRESQL -> assertTrue(
json.contains(docId("four")) || json.contains(docId("five")),
"Expected document 'four' or 'five' ($json)"
)
}
fun firstByJsonPathMatchMany(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByJsonPathMatchMany(db.conn.jsonFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)"))
}
fun writeFirstByJsonPathMatchMany(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByJsonPath(TEST_TABLE, writer, "$.numValue ? (@ > 10)")
checkFirstByJsonPathMatchMany(output.toString())
}
private fun checkFirstByJsonPathMatchOrdered(json: String) =
when (Configuration.dialect()) {
Dialect.SQLITE -> assertEquals(JsonDocument.four, json, "An incorrect document was returned")
Dialect.POSTGRESQL -> assertTrue(json.contains(docId("four")), "An incorrect document was returned ($json)")
}
fun firstByJsonPathMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByJsonPathMatchOrdered(
db.conn.jsonFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)", listOf(Field.named("id DESC")))
)
}
fun writeFirstByJsonPathMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByJsonPath(TEST_TABLE, writer, "$.numValue ? (@ > 10)", listOf(Field.named("id DESC")))
checkFirstByJsonPathMatchOrdered(output.toString())
}
private fun checkFirstByJsonPathNoMatch(json: String) =
assertEquals("{}", json, "There should have been no document returned")
fun firstByJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
checkFirstByJsonPathNoMatch(db.conn.jsonFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)"))
}
fun writeFirstByJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val output = StringWriter()
val writer = PrintWriter(output)
db.conn.writeJsonFirstByJsonPath(TEST_TABLE, writer, "$.numValue ? (@ > 100)")
checkFirstByJsonPathNoMatch(output.toString())
}
}

View File

@@ -0,0 +1,90 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.JsonDocument
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
/**
* Integration tests for the `Patch` object
*/
object PatchFunctions {
fun byIdMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
db.conn.patchById(TEST_TABLE, "one", mapOf("numValue" to 44))
val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "one")
assertNotNull(doc, "There should have been a document returned")
assertEquals("one", doc.id, "An incorrect document was returned")
assertEquals(44, doc.numValue, "The document was not patched")
}
fun byIdNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse("Document with ID \"forty-seven\" should not exist") {
db.conn.existsById(TEST_TABLE, "forty-seven")
}
db.conn.patchById(TEST_TABLE, "forty-seven", mapOf("foo" to "green")) // no exception = pass
}
fun byFieldsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
db.conn.patchByFields(TEST_TABLE, listOf(Field.equal("value", "purple")), mapOf("numValue" to 77))
assertEquals(
2,
db.conn.countByFields(TEST_TABLE, listOf(Field.equal("numValue", 77))),
"There should have been 2 documents with numeric value 77"
)
}
fun byFieldsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val fields = listOf(Field.equal("value", "burgundy"))
assertFalse("There should be no documents with value of \"burgundy\"") {
db.conn.existsByFields(TEST_TABLE, fields)
}
db.conn.patchByFields(TEST_TABLE, fields, mapOf("foo" to "green")) // no exception = pass
}
fun byContainsMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val contains = mapOf("value" to "another")
db.conn.patchByContains(TEST_TABLE, contains, mapOf("numValue" to 12))
val doc = db.conn.findFirstByContains<JsonDocument, Map<String, String>>(TEST_TABLE, contains)
assertNotNull(doc, "There should have been a document returned")
assertEquals("two", doc.id, "The incorrect document was returned")
assertEquals(12, doc.numValue, "The document was not updated")
}
fun byContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val contains = mapOf("value" to "updated")
assertFalse("There should be no matching documents") { db.conn.existsByContains(TEST_TABLE, contains) }
db.conn.patchByContains(TEST_TABLE, contains, mapOf("sub.foo" to "green")) // no exception = pass
}
fun byJsonPathMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val path = "$.numValue ? (@ > 10)"
db.conn.patchByJsonPath(TEST_TABLE, path, mapOf("value" to "blue"))
val docs = db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, path)
assertEquals(2, docs.size, "There should have been two documents returned")
docs.forEach {
assertTrue(listOf("four", "five").contains(it.id), "An incorrect document was returned (${it.id})")
assertEquals("blue", it.value, "The value for ID ${it.id} was incorrect")
}
}
fun byJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val path = "$.numValue ? (@ > 100)"
assertFalse("There should be no documents with numeric values over 100") {
db.conn.existsByJsonPath(TEST_TABLE, path)
}
db.conn.patchByJsonPath(TEST_TABLE, path, mapOf("value" to "blue")) // no exception = pass
}
}

View File

@@ -0,0 +1,47 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.Results
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
/**
* A wrapper for a throwaway PostgreSQL database
*/
class PgDB : ThrowawayDatabase() {
init {
Configuration.connectionString = connString("postgres")
Configuration.dbConn().use { it.customNonQuery("CREATE DATABASE $dbName") }
Configuration.connectionString = connString(dbName)
}
override val conn = Configuration.dbConn()
init {
conn.ensureTable(TEST_TABLE)
}
override fun close() {
conn.close()
Configuration.connectionString = connString("postgres")
Configuration.dbConn().use { it.customNonQuery("DROP DATABASE $dbName") }
Configuration.connectionString = null
}
override fun dbObjectExists(name: String) =
conn.customScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = :name) AS it",
listOf(Parameter(":name", ParameterType.STRING, name)), Results::toExists)
companion object {
/**
* Create a connection string for the given database
*
* @param database The database to which the library should connect
* @return The connection string for the database
*/
private fun connString(database: String) =
"jdbc:postgresql://localhost/$database?user=postgres&password=postgres"
}
}

View File

@@ -0,0 +1,46 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* PostgreSQL integration tests for the `Count` object / `count*` connection extension functions
*/
@DisplayName("KotlinX | PostgreSQL: Count")
class PostgreSQLCountIT {
@Test
@DisplayName("all counts all documents")
fun all() =
PgDB().use(CountFunctions::all)
@Test
@DisplayName("byFields counts documents by a numeric value")
fun byFieldsNumeric() =
PgDB().use(CountFunctions::byFieldsNumeric)
@Test
@DisplayName("byFields counts documents by a alphanumeric value")
fun byFieldsAlpha() =
PgDB().use(CountFunctions::byFieldsAlpha)
@Test
@DisplayName("byContains counts documents when matches are found")
fun byContainsMatch() =
PgDB().use(CountFunctions::byContainsMatch)
@Test
@DisplayName("byContains counts documents when no matches are found")
fun byContainsNoMatch() =
PgDB().use(CountFunctions::byContainsNoMatch)
@Test
@DisplayName("byJsonPath counts documents when matches are found")
fun byJsonPathMatch() =
PgDB().use(CountFunctions::byJsonPathMatch)
@Test
@DisplayName("byJsonPath counts documents when no matches are found")
fun byJsonPathNoMatch() =
PgDB().use(CountFunctions::byJsonPathNoMatch)
}

View File

@@ -0,0 +1,87 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* PostgreSQL integration tests for the `Custom` object / `custom*` connection extension functions
*/
@DisplayName("KotlinX | PostgreSQL: Custom")
class PostgreSQLCustomIT {
@Test
@DisplayName("list succeeds with empty list")
fun listEmpty() =
PgDB().use(CustomFunctions::listEmpty)
@Test
@DisplayName("list succeeds with a non-empty list")
fun listAll() =
PgDB().use(CustomFunctions::listAll)
@Test
@DisplayName("jsonArray succeeds with empty array")
fun jsonArrayEmpty() =
PgDB().use(CustomFunctions::jsonArrayEmpty)
@Test
@DisplayName("jsonArray succeeds with a single-item array")
fun jsonArraySingle() =
PgDB().use(CustomFunctions::jsonArraySingle)
@Test
@DisplayName("jsonArray succeeds with a multi-item array")
fun jsonArrayMany() =
PgDB().use(CustomFunctions::jsonArrayMany)
@Test
@DisplayName("writeJsonArray succeeds with empty array")
fun writeJsonArrayEmpty() =
PgDB().use(CustomFunctions::writeJsonArrayEmpty)
@Test
@DisplayName("writeJsonArray succeeds with a single-item array")
fun writeJsonArraySingle() =
PgDB().use(CustomFunctions::writeJsonArraySingle)
@Test
@DisplayName("writeJsonArray succeeds with a multi-item array")
fun writeJsonArrayMany() =
PgDB().use(CustomFunctions::writeJsonArrayMany)
@Test
@DisplayName("single succeeds when document not found")
fun singleNone() =
PgDB().use(CustomFunctions::singleNone)
@Test
@DisplayName("single succeeds when a document is found")
fun singleOne() =
PgDB().use(CustomFunctions::singleOne)
@Test
@DisplayName("jsonSingle succeeds when document not found")
fun jsonSingleNone() =
PgDB().use(CustomFunctions::jsonSingleNone)
@Test
@DisplayName("jsonSingle succeeds when a document is found")
fun jsonSingleOne() =
PgDB().use(CustomFunctions::jsonSingleOne)
@Test
@DisplayName("nonQuery makes changes")
fun nonQueryChanges() =
PgDB().use(CustomFunctions::nonQueryChanges)
@Test
@DisplayName("nonQuery makes no changes when where clause matches nothing")
fun nonQueryNoChanges() =
PgDB().use(CustomFunctions::nonQueryNoChanges)
@Test
@DisplayName("scalar succeeds")
fun scalar() =
PgDB().use(CustomFunctions::scalar)
}

View File

@@ -0,0 +1,31 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions
*/
@DisplayName("KotlinX | PostgreSQL: Definition")
class PostgreSQLDefinitionIT {
@Test
@DisplayName("ensureTable creates table and index")
fun ensureTable() =
PgDB().use(DefinitionFunctions::ensureTable)
@Test
@DisplayName("ensureFieldIndex creates an index")
fun ensureFieldIndex() =
PgDB().use(DefinitionFunctions::ensureFieldIndex)
@Test
@DisplayName("ensureDocumentIndex creates a full index")
fun ensureDocumentIndexFull() =
PgDB().use(DefinitionFunctions::ensureDocumentIndexFull)
@Test
@DisplayName("ensureDocumentIndex creates an optimized index")
fun ensureDocumentIndexOptimized() =
PgDB().use(DefinitionFunctions::ensureDocumentIndexOptimized)
}

View File

@@ -0,0 +1,51 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions
*/
@DisplayName("KotlinX | PostgreSQL: Delete")
class PostgreSQLDeleteIT {
@Test
@DisplayName("byId deletes a matching ID")
fun byIdMatch() =
PgDB().use(DeleteFunctions::byIdMatch)
@Test
@DisplayName("byId succeeds when no ID matches")
fun byIdNoMatch() =
PgDB().use(DeleteFunctions::byIdNoMatch)
@Test
@DisplayName("byFields deletes matching documents")
fun byFieldsMatch() =
PgDB().use(DeleteFunctions::byFieldsMatch)
@Test
@DisplayName("byFields succeeds when no documents match")
fun byFieldsNoMatch() =
PgDB().use(DeleteFunctions::byFieldsNoMatch)
@Test
@DisplayName("byContains deletes matching documents")
fun byContainsMatch() =
PgDB().use(DeleteFunctions::byContainsMatch)
@Test
@DisplayName("byContains succeeds when no documents match")
fun byContainsNoMatch() =
PgDB().use(DeleteFunctions::byContainsNoMatch)
@Test
@DisplayName("byJsonPath deletes matching documents")
fun byJsonPathMatch() =
PgDB().use(DeleteFunctions::byJsonPathMatch)
@Test
@DisplayName("byJsonPath succeeds when no documents match")
fun byJsonPathNoMatch() =
PgDB().use(DeleteFunctions::byJsonPathNoMatch)
}

View File

@@ -0,0 +1,56 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
*/
@DisplayName("KotlinX | PostgreSQL: Document")
class PostgreSQLDocumentIT {
@Test
@DisplayName("insert works with default values")
fun insertDefault() =
PgDB().use(DocumentFunctions::insertDefault)
@Test
@DisplayName("insert fails with duplicate key")
fun insertDupe() =
PgDB().use(DocumentFunctions::insertDupe)
@Test
@DisplayName("insert succeeds with numeric auto IDs")
fun insertNumAutoId() =
PgDB().use(DocumentFunctions::insertNumAutoId)
@Test
@DisplayName("insert succeeds with UUID auto ID")
fun insertUUIDAutoId() =
PgDB().use(DocumentFunctions::insertUUIDAutoId)
@Test
@DisplayName("insert succeeds with random string auto ID")
fun insertStringAutoId() =
PgDB().use(DocumentFunctions::insertStringAutoId)
@Test
@DisplayName("save updates an existing document")
fun saveMatch() =
PgDB().use(DocumentFunctions::saveMatch)
@Test
@DisplayName("save inserts a new document")
fun saveNoMatch() =
PgDB().use(DocumentFunctions::saveNoMatch)
@Test
@DisplayName("update replaces an existing document")
fun updateMatch() =
PgDB().use(DocumentFunctions::updateMatch)
@Test
@DisplayName("update succeeds when no document exists")
fun updateNoMatch() =
PgDB().use(DocumentFunctions::updateNoMatch)
}

View File

@@ -0,0 +1,51 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions
*/
@DisplayName("KotlinX | PostgreSQL: Exists")
class PostgreSQLExistsIT {
@Test
@DisplayName("byId returns true when a document matches the ID")
fun byIdMatch() =
PgDB().use(ExistsFunctions::byIdMatch)
@Test
@DisplayName("byId returns false when no document matches the ID")
fun byIdNoMatch() =
PgDB().use(ExistsFunctions::byIdNoMatch)
@Test
@DisplayName("byFields returns true when documents match")
fun byFieldsMatch() =
PgDB().use(ExistsFunctions::byFieldsMatch)
@Test
@DisplayName("byFields returns false when no documents match")
fun byFieldsNoMatch() =
PgDB().use(ExistsFunctions::byFieldsNoMatch)
@Test
@DisplayName("byContains returns true when documents match")
fun byContainsMatch() =
PgDB().use(ExistsFunctions::byContainsMatch)
@Test
@DisplayName("byContains returns false when no documents match")
fun byContainsNoMatch() =
PgDB().use(ExistsFunctions::byContainsNoMatch)
@Test
@DisplayName("byJsonPath returns true when documents match")
fun byJsonPathMatch() =
PgDB().use(ExistsFunctions::byJsonPathMatch)
@Test
@DisplayName("byJsonPath returns false when no documents match")
fun byJsonPathNoMatch() =
PgDB().use(ExistsFunctions::byJsonPathNoMatch)
}

View File

@@ -0,0 +1,171 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* PostgreSQL integration tests for the `Find` object / `find*` connection extension functions
*/
@DisplayName("KotlinX | PostgreSQL: Find")
class PostgreSQLFindIT {
@Test
@DisplayName("all retrieves all documents")
fun allDefault() =
PgDB().use(FindFunctions::allDefault)
@Test
@DisplayName("all sorts data ascending")
fun allAscending() =
PgDB().use(FindFunctions::allAscending)
@Test
@DisplayName("all sorts data descending")
fun allDescending() =
PgDB().use(FindFunctions::allDescending)
@Test
@DisplayName("all sorts data numerically")
fun allNumOrder() =
PgDB().use(FindFunctions::allNumOrder)
@Test
@DisplayName("all succeeds with an empty table")
fun allEmpty() =
PgDB().use(FindFunctions::allEmpty)
@Test
@DisplayName("byId retrieves a document via a string ID")
fun byIdString() =
PgDB().use(FindFunctions::byIdString)
@Test
@DisplayName("byId retrieves a document via a numeric ID")
fun byIdNumber() =
PgDB().use(FindFunctions::byIdNumber)
@Test
@DisplayName("byId returns null when a matching ID is not found")
fun byIdNotFound() =
PgDB().use(FindFunctions::byIdNotFound)
@Test
@DisplayName("byFields retrieves matching documents")
fun byFieldsMatch() =
PgDB().use(FindFunctions::byFieldsMatch)
@Test
@DisplayName("byFields retrieves ordered matching documents")
fun byFieldsMatchOrdered() =
PgDB().use(FindFunctions::byFieldsMatchOrdered)
@Test
@DisplayName("byFields retrieves matching documents with a numeric IN clause")
fun byFieldsMatchNumIn() =
PgDB().use(FindFunctions::byFieldsMatchNumIn)
@Test
@DisplayName("byFields succeeds when no documents match")
fun byFieldsNoMatch() =
PgDB().use(FindFunctions::byFieldsNoMatch)
@Test
@DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison")
fun byFieldsMatchInArray() =
PgDB().use(FindFunctions::byFieldsMatchInArray)
@Test
@DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison")
fun byFieldsNoMatchInArray() =
PgDB().use(FindFunctions::byFieldsNoMatchInArray)
@Test
@DisplayName("byContains retrieves matching documents")
fun byContainsMatch() =
PgDB().use(FindFunctions::byContainsMatch)
@Test
@DisplayName("byContains retrieves ordered matching documents")
fun byContainsMatchOrdered() =
PgDB().use(FindFunctions::byContainsMatchOrdered)
@Test
@DisplayName("byContains succeeds when no documents match")
fun byContainsNoMatch() =
PgDB().use(FindFunctions::byContainsNoMatch)
@Test
@DisplayName("byJsonPath retrieves matching documents")
fun byJsonPathMatch() =
PgDB().use(FindFunctions::byJsonPathMatch)
@Test
@DisplayName("byJsonPath retrieves ordered matching documents")
fun byJsonPathMatchOrdered() =
PgDB().use(FindFunctions::byJsonPathMatchOrdered)
@Test
@DisplayName("byJsonPath succeeds when no documents match")
fun byJsonPathNoMatch() =
PgDB().use(FindFunctions::byJsonPathNoMatch)
@Test
@DisplayName("firstByFields retrieves a matching document")
fun firstByFieldsMatchOne() =
PgDB().use(FindFunctions::firstByFieldsMatchOne)
@Test
@DisplayName("firstByFields retrieves a matching document among many")
fun firstByFieldsMatchMany() =
PgDB().use(FindFunctions::firstByFieldsMatchMany)
@Test
@DisplayName("firstByFields retrieves a matching document among many (ordered)")
fun firstByFieldsMatchOrdered() =
PgDB().use(FindFunctions::firstByFieldsMatchOrdered)
@Test
@DisplayName("firstByFields returns null when no document matches")
fun firstByFieldsNoMatch() =
PgDB().use(FindFunctions::firstByFieldsNoMatch)
@Test
@DisplayName("firstByContains retrieves a matching document")
fun firstByContainsMatchOne() =
PgDB().use(FindFunctions::firstByContainsMatchOne)
@Test
@DisplayName("firstByContains retrieves a matching document among many")
fun firstByContainsMatchMany() =
PgDB().use(FindFunctions::firstByContainsMatchMany)
@Test
@DisplayName("firstByContains retrieves a matching document among many (ordered)")
fun firstByContainsMatchOrdered() =
PgDB().use(FindFunctions::firstByContainsMatchOrdered)
@Test
@DisplayName("firstByContains returns null when no document matches")
fun firstByContainsNoMatch() =
PgDB().use(FindFunctions::firstByContainsNoMatch)
@Test
@DisplayName("firstByJsonPath retrieves a matching document")
fun firstByJsonPathMatchOne() =
PgDB().use(FindFunctions::firstByJsonPathMatchOne)
@Test
@DisplayName("firstByJsonPath retrieves a matching document among many")
fun firstByJsonPathMatchMany() =
PgDB().use(FindFunctions::firstByJsonPathMatchMany)
@Test
@DisplayName("firstByJsonPath retrieves a matching document among many (ordered)")
fun firstByJsonPathMatchOrdered() =
PgDB().use(FindFunctions::firstByJsonPathMatchOrdered)
@Test
@DisplayName("firstByJsonPath returns null when no document matches")
fun firstByJsonPathNoMatch() =
PgDB().use(FindFunctions::firstByJsonPathNoMatch)
}

View File

@@ -0,0 +1,301 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* PostgreSQL integration tests for the `Json` object / `json*` connection extension functions
*/
@DisplayName("KotlinX | PostgreSQL: Json")
class PostgreSQLJsonIT {
@Test
@DisplayName("all retrieves all documents")
fun allDefault() =
PgDB().use(JsonFunctions::allDefault)
@Test
@DisplayName("all succeeds with an empty table")
fun allEmpty() =
PgDB().use(JsonFunctions::allEmpty)
@Test
@DisplayName("byId retrieves a document via a string ID")
fun byIdString() =
PgDB().use(JsonFunctions::byIdString)
@Test
@DisplayName("byId retrieves a document via a numeric ID")
fun byIdNumber() =
PgDB().use(JsonFunctions::byIdNumber)
@Test
@DisplayName("byId returns null when a matching ID is not found")
fun byIdNotFound() =
PgDB().use(JsonFunctions::byIdNotFound)
@Test
@DisplayName("byFields retrieves matching documents")
fun byFieldsMatch() =
PgDB().use(JsonFunctions::byFieldsMatch)
@Test
@DisplayName("byFields retrieves ordered matching documents")
fun byFieldsMatchOrdered() =
PgDB().use(JsonFunctions::byFieldsMatchOrdered)
@Test
@DisplayName("byFields retrieves matching documents with a numeric IN clause")
fun byFieldsMatchNumIn() =
PgDB().use(JsonFunctions::byFieldsMatchNumIn)
@Test
@DisplayName("byFields succeeds when no documents match")
fun byFieldsNoMatch() =
PgDB().use(JsonFunctions::byFieldsNoMatch)
@Test
@DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison")
fun byFieldsMatchInArray() =
PgDB().use(JsonFunctions::byFieldsMatchInArray)
@Test
@DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison")
fun byFieldsNoMatchInArray() =
PgDB().use(JsonFunctions::byFieldsNoMatchInArray)
@Test
@DisplayName("byContains retrieves matching documents")
fun byContainsMatch() =
PgDB().use(JsonFunctions::byContainsMatch)
@Test
@DisplayName("byContains retrieves ordered matching documents")
fun byContainsMatchOrdered() =
PgDB().use(JsonFunctions::byContainsMatchOrdered)
@Test
@DisplayName("byContains succeeds when no documents match")
fun byContainsNoMatch() =
PgDB().use(JsonFunctions::byContainsNoMatch)
@Test
@DisplayName("byJsonPath retrieves matching documents")
fun byJsonPathMatch() =
PgDB().use(JsonFunctions::byJsonPathMatch)
@Test
@DisplayName("byJsonPath retrieves ordered matching documents")
fun byJsonPathMatchOrdered() =
PgDB().use(JsonFunctions::byJsonPathMatchOrdered)
@Test
@DisplayName("byJsonPath succeeds when no documents match")
fun byJsonPathNoMatch() =
PgDB().use(JsonFunctions::byJsonPathNoMatch)
@Test
@DisplayName("firstByFields retrieves a matching document")
fun firstByFieldsMatchOne() =
PgDB().use(JsonFunctions::firstByFieldsMatchOne)
@Test
@DisplayName("firstByFields retrieves a matching document among many")
fun firstByFieldsMatchMany() =
PgDB().use(JsonFunctions::firstByFieldsMatchMany)
@Test
@DisplayName("firstByFields retrieves a matching document among many (ordered)")
fun firstByFieldsMatchOrdered() =
PgDB().use(JsonFunctions::firstByFieldsMatchOrdered)
@Test
@DisplayName("firstByFields returns null when no document matches")
fun firstByFieldsNoMatch() =
PgDB().use(JsonFunctions::firstByFieldsNoMatch)
@Test
@DisplayName("firstByContains retrieves a matching document")
fun firstByContainsMatchOne() =
PgDB().use(JsonFunctions::firstByContainsMatchOne)
@Test
@DisplayName("firstByContains retrieves a matching document among many")
fun firstByContainsMatchMany() =
PgDB().use(JsonFunctions::firstByContainsMatchMany)
@Test
@DisplayName("firstByContains retrieves a matching document among many (ordered)")
fun firstByContainsMatchOrdered() =
PgDB().use(JsonFunctions::firstByContainsMatchOrdered)
@Test
@DisplayName("firstByContains returns null when no document matches")
fun firstByContainsNoMatch() =
PgDB().use(JsonFunctions::firstByContainsNoMatch)
@Test
@DisplayName("firstByJsonPath retrieves a matching document")
fun firstByJsonPathMatchOne() =
PgDB().use(JsonFunctions::firstByJsonPathMatchOne)
@Test
@DisplayName("firstByJsonPath retrieves a matching document among many")
fun firstByJsonPathMatchMany() =
PgDB().use(JsonFunctions::firstByJsonPathMatchMany)
@Test
@DisplayName("firstByJsonPath retrieves a matching document among many (ordered)")
fun firstByJsonPathMatchOrdered() =
PgDB().use(JsonFunctions::firstByJsonPathMatchOrdered)
@Test
@DisplayName("firstByJsonPath returns null when no document matches")
fun firstByJsonPathNoMatch() =
PgDB().use(JsonFunctions::firstByJsonPathNoMatch)
@Test
@DisplayName("writeAll retrieves all documents")
fun writeAllDefault() =
PgDB().use(JsonFunctions::writeAllDefault)
@Test
@DisplayName("writeAll succeeds with an empty table")
fun writeAllEmpty() =
PgDB().use(JsonFunctions::writeAllEmpty)
@Test
@DisplayName("writeById retrieves a document via a string ID")
fun writeByIdString() =
PgDB().use(JsonFunctions::writeByIdString)
@Test
@DisplayName("writeById retrieves a document via a numeric ID")
fun writeByIdNumber() =
PgDB().use(JsonFunctions::writeByIdNumber)
@Test
@DisplayName("writeById returns null when a matching ID is not found")
fun writeByIdNotFound() =
PgDB().use(JsonFunctions::writeByIdNotFound)
@Test
@DisplayName("writeByFields retrieves matching documents")
fun writeByFieldsMatch() =
PgDB().use(JsonFunctions::writeByFieldsMatch)
@Test
@DisplayName("writeByFields retrieves ordered matching documents")
fun writeByFieldsMatchOrdered() =
PgDB().use(JsonFunctions::writeByFieldsMatchOrdered)
@Test
@DisplayName("writeByFields retrieves matching documents with a numeric IN clause")
fun writeByFieldsMatchNumIn() =
PgDB().use(JsonFunctions::writeByFieldsMatchNumIn)
@Test
@DisplayName("writeByFields succeeds when no documents match")
fun writeByFieldsNoMatch() =
PgDB().use(JsonFunctions::writeByFieldsNoMatch)
@Test
@DisplayName("writeByFields retrieves matching documents with an IN_ARRAY comparison")
fun writeByFieldsMatchInArray() =
PgDB().use(JsonFunctions::writeByFieldsMatchInArray)
@Test
@DisplayName("writeByFields succeeds when no documents match an IN_ARRAY comparison")
fun writeByFieldsNoMatchInArray() =
PgDB().use(JsonFunctions::writeByFieldsNoMatchInArray)
@Test
@DisplayName("writeByContains retrieves matching documents")
fun writeByContainsMatch() =
PgDB().use(JsonFunctions::writeByContainsMatch)
@Test
@DisplayName("writeByContains retrieves ordered matching documents")
fun writeByContainsMatchOrdered() =
PgDB().use(JsonFunctions::writeByContainsMatchOrdered)
@Test
@DisplayName("writeByContains succeeds when no documents match")
fun writeByContainsNoMatch() =
PgDB().use(JsonFunctions::writeByContainsNoMatch)
@Test
@DisplayName("writeByJsonPath retrieves matching documents")
fun writeByJsonPathMatch() =
PgDB().use(JsonFunctions::writeByJsonPathMatch)
@Test
@DisplayName("writeByJsonPath retrieves ordered matching documents")
fun writeByJsonPathMatchOrdered() =
PgDB().use(JsonFunctions::writeByJsonPathMatchOrdered)
@Test
@DisplayName("writeByJsonPath succeeds when no documents match")
fun writeByJsonPathNoMatch() =
PgDB().use(JsonFunctions::writeByJsonPathNoMatch)
@Test
@DisplayName("writeFirstByFields retrieves a matching document")
fun writeFirstByFieldsMatchOne() =
PgDB().use(JsonFunctions::writeFirstByFieldsMatchOne)
@Test
@DisplayName("writeFirstByFields retrieves a matching document among many")
fun writeFirstByFieldsMatchMany() =
PgDB().use(JsonFunctions::writeFirstByFieldsMatchMany)
@Test
@DisplayName("writeFirstByFields retrieves a matching document among many (ordered)")
fun writeFirstByFieldsMatchOrdered() =
PgDB().use(JsonFunctions::writeFirstByFieldsMatchOrdered)
@Test
@DisplayName("writeFirstByFields returns null when no document matches")
fun writeFirstByFieldsNoMatch() =
PgDB().use(JsonFunctions::writeFirstByFieldsNoMatch)
@Test
@DisplayName("writeFirstByContains retrieves a matching document")
fun writeFirstByContainsMatchOne() =
PgDB().use(JsonFunctions::writeFirstByContainsMatchOne)
@Test
@DisplayName("writeFirstByContains retrieves a matching document among many")
fun writeFirstByContainsMatchMany() =
PgDB().use(JsonFunctions::writeFirstByContainsMatchMany)
@Test
@DisplayName("writeFirstByContains retrieves a matching document among many (ordered)")
fun writeFirstByContainsMatchOrdered() =
PgDB().use(JsonFunctions::writeFirstByContainsMatchOrdered)
@Test
@DisplayName("writeFirstByContains returns null when no document matches")
fun writeFirstByContainsNoMatch() =
PgDB().use(JsonFunctions::writeFirstByContainsNoMatch)
@Test
@DisplayName("writeFirstByJsonPath retrieves a matching document")
fun writeFirstByJsonPathMatchOne() =
PgDB().use(JsonFunctions::writeFirstByJsonPathMatchOne)
@Test
@DisplayName("writeFirstByJsonPath retrieves a matching document among many")
fun writeFirstByJsonPathMatchMany() =
PgDB().use(JsonFunctions::writeFirstByJsonPathMatchMany)
@Test
@DisplayName("writeFirstByJsonPath retrieves a matching document among many (ordered)")
fun writeFirstByJsonPathMatchOrdered() =
PgDB().use(JsonFunctions::writeFirstByJsonPathMatchOrdered)
@Test
@DisplayName("writeFirstByJsonPath returns null when no document matches")
fun writeFirstByJsonPathNoMatch() =
PgDB().use(JsonFunctions::writeFirstByJsonPathNoMatch)
}

View File

@@ -0,0 +1,51 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions
*/
@DisplayName("KotlinX | PostgreSQL: Patch")
class PostgreSQLPatchIT {
@Test
@DisplayName("byId patches an existing document")
fun byIdMatch() =
PgDB().use(PatchFunctions::byIdMatch)
@Test
@DisplayName("byId succeeds for a non-existent document")
fun byIdNoMatch() =
PgDB().use(PatchFunctions::byIdNoMatch)
@Test
@DisplayName("byFields patches matching document")
fun byFieldsMatch() =
PgDB().use(PatchFunctions::byFieldsMatch)
@Test
@DisplayName("byFields succeeds when no documents match")
fun byFieldsNoMatch() =
PgDB().use(PatchFunctions::byFieldsNoMatch)
@Test
@DisplayName("byContains patches matching document")
fun byContainsMatch() =
PgDB().use(PatchFunctions::byContainsMatch)
@Test
@DisplayName("byContains succeeds when no documents match")
fun byContainsNoMatch() =
PgDB().use(PatchFunctions::byContainsNoMatch)
@Test
@DisplayName("byJsonPath patches matching document")
fun byJsonPathMatch() =
PgDB().use(PatchFunctions::byJsonPathMatch)
@Test
@DisplayName("byJsonPath succeeds when no documents match")
fun byJsonPathNoMatch() =
PgDB().use(PatchFunctions::byJsonPathNoMatch)
}

View File

@@ -0,0 +1,71 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
*/
@DisplayName("KotlinX | PostgreSQL: RemoveFields")
class PostgreSQLRemoveFieldsIT {
@Test
@DisplayName("byId removes fields from an existing document")
fun byIdMatchFields() =
PgDB().use(RemoveFieldsFunctions::byIdMatchFields)
@Test
@DisplayName("byId succeeds when fields do not exist on an existing document")
fun byIdMatchNoFields() =
PgDB().use(RemoveFieldsFunctions::byIdMatchNoFields)
@Test
@DisplayName("byId succeeds when no document exists")
fun byIdNoMatch() =
PgDB().use(RemoveFieldsFunctions::byIdNoMatch)
@Test
@DisplayName("byFields removes fields from matching documents")
fun byFieldsMatchFields() =
PgDB().use(RemoveFieldsFunctions::byFieldsMatchFields)
@Test
@DisplayName("byFields succeeds when fields do not exist on matching documents")
fun byFieldsMatchNoFields() =
PgDB().use(RemoveFieldsFunctions::byFieldsMatchNoFields)
@Test
@DisplayName("byFields succeeds when no matching documents exist")
fun byFieldsNoMatch() =
PgDB().use(RemoveFieldsFunctions::byFieldsNoMatch)
@Test
@DisplayName("byContains removes fields from matching documents")
fun byContainsMatchFields() =
PgDB().use(RemoveFieldsFunctions::byContainsMatchFields)
@Test
@DisplayName("byContains succeeds when fields do not exist on matching documents")
fun byContainsMatchNoFields() =
PgDB().use(RemoveFieldsFunctions::byContainsMatchNoFields)
@Test
@DisplayName("byContains succeeds when no matching documents exist")
fun byContainsNoMatch() =
PgDB().use(RemoveFieldsFunctions::byContainsNoMatch)
@Test
@DisplayName("byJsonPath removes fields from matching documents")
fun byJsonPathMatchFields() =
PgDB().use(RemoveFieldsFunctions::byJsonPathMatchFields)
@Test
@DisplayName("byJsonPath succeeds when fields do not exist on matching documents")
fun byJsonPathMatchNoFields() =
PgDB().use(RemoveFieldsFunctions::byJsonPathMatchNoFields)
@Test
@DisplayName("byJsonPath succeeds when no matching documents exist")
fun byJsonPathNoMatch() =
PgDB().use(RemoveFieldsFunctions::byJsonPathNoMatch)
}

View File

@@ -0,0 +1,108 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.JsonDocument
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
import kotlin.test.*
/**
* Integration tests for the `RemoveFields` object
*/
object RemoveFieldsFunctions {
fun byIdMatchFields(db: ThrowawayDatabase) {
JsonDocument.load(db)
db.conn.removeFieldsById(TEST_TABLE, "two", listOf("sub", "value"))
val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "two")
assertNotNull(doc, "There should have been a document returned")
assertEquals("", doc.value, "The value should have been empty")
assertNull(doc.sub, "The sub-document should have been removed")
}
fun byIdMatchNoFields(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse { db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("a_field_that_does_not_exist"))) }
db.conn.removeFieldsById(TEST_TABLE, "one", listOf("a_field_that_does_not_exist")) // no exception = pass
}
fun byIdNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse { db.conn.existsById(TEST_TABLE, "fifty") }
db.conn.removeFieldsById(TEST_TABLE, "fifty", listOf("sub")) // no exception = pass
}
fun byFieldsMatchFields(db: ThrowawayDatabase) {
JsonDocument.load(db)
val fields = listOf(Field.equal("numValue", 17))
db.conn.removeFieldsByFields(TEST_TABLE, fields, listOf("sub"))
val doc = db.conn.findFirstByFields<JsonDocument>(TEST_TABLE, fields)
assertNotNull(doc, "The document should have been returned")
assertEquals("four", doc.id, "An incorrect document was returned")
assertNull(doc.sub, "The sub-document should have been removed")
}
fun byFieldsMatchNoFields(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse { db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("nada"))) }
db.conn.removeFieldsByFields(TEST_TABLE, listOf(Field.equal("numValue", 17)), listOf("nada")) // no exn = pass
}
fun byFieldsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val fields = listOf(Field.notEqual("missing", "nope"))
assertFalse { db.conn.existsByFields(TEST_TABLE, fields) }
db.conn.removeFieldsByFields(TEST_TABLE, fields, listOf("value")) // no exception = pass
}
fun byContainsMatchFields(db: ThrowawayDatabase) {
JsonDocument.load(db)
val criteria = mapOf("sub" to mapOf("foo" to "green"))
db.conn.removeFieldsByContains(TEST_TABLE, criteria, listOf("value"))
val docs = db.conn.findByContains<JsonDocument, Map<String, Map<String, String>>>(TEST_TABLE, criteria)
assertEquals(2, docs.size, "There should have been 2 documents returned")
docs.forEach {
assertTrue(listOf("two", "four").contains(it.id), "An incorrect document was returned (${it.id})")
assertEquals("", it.value, "The value should have been empty")
}
}
fun byContainsMatchNoFields(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse { db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("invalid_field"))) }
db.conn.removeFieldsByContains(TEST_TABLE, mapOf("sub" to mapOf("foo" to "green")), listOf("invalid_field"))
// no exception = pass
}
fun byContainsNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val contains = mapOf("value" to "substantial")
assertFalse { db.conn.existsByContains(TEST_TABLE, contains) }
db.conn.removeFieldsByContains(TEST_TABLE, contains, listOf("numValue"))
}
fun byJsonPathMatchFields(db: ThrowawayDatabase) {
JsonDocument.load(db)
val path = "$.value ? (@ == \"purple\")"
db.conn.removeFieldsByJsonPath(TEST_TABLE, path, listOf("sub"))
val docs = db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, path)
assertEquals(2, docs.size, "There should have been 2 documents returned")
docs.forEach {
assertTrue(listOf("four", "five").contains(it.id), "An incorrect document was returned (${it.id})")
assertNull(it.sub, "The sub-document should have been removed")
}
}
fun byJsonPathMatchNoFields(db: ThrowawayDatabase) {
JsonDocument.load(db)
assertFalse { db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("submarine"))) }
db.conn.removeFieldsByJsonPath(TEST_TABLE, "$.value ? (@ == \"purple\")", listOf("submarine")) // no exn = pass
}
fun byJsonPathNoMatch(db: ThrowawayDatabase) {
JsonDocument.load(db)
val path = "$.value ? (@ == \"mauve\")"
assertFalse { db.conn.existsByJsonPath(TEST_TABLE, path) }
db.conn.removeFieldsByJsonPath(TEST_TABLE, path, listOf("value")) // no exception = pass
}
}

View File

@@ -0,0 +1,40 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.DocumentException
import kotlin.test.Test
/**
* SQLite integration tests for the `Count` object / `count*` connection extension functions
*/
@DisplayName("KotlinX | SQLite: Count")
class SQLiteCountIT {
@Test
@DisplayName("all counts all documents")
fun all() =
SQLiteDB().use(CountFunctions::all)
@Test
@DisplayName("byFields counts documents by a numeric value")
fun byFieldsNumeric() =
SQLiteDB().use(CountFunctions::byFieldsNumeric)
@Test
@DisplayName("byFields counts documents by a alphanumeric value")
fun byFieldsAlpha() =
SQLiteDB().use(CountFunctions::byFieldsAlpha)
@Test
@DisplayName("byContains fails")
fun byContainsMatch() {
assertThrows<DocumentException> { SQLiteDB().use(CountFunctions::byContainsMatch) }
}
@Test
@DisplayName("byJsonPath fails")
fun byJsonPathMatch() {
assertThrows<DocumentException> { SQLiteDB().use(CountFunctions::byJsonPathMatch) }
}
}

View File

@@ -0,0 +1,86 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* SQLite integration tests for the `Custom` object / `custom*` connection extension functions
*/
@DisplayName("KotlinX | SQLite: Custom")
class SQLiteCustomIT {
@Test
@DisplayName("list succeeds with empty list")
fun listEmpty() =
SQLiteDB().use(CustomFunctions::listEmpty)
@Test
@DisplayName("list succeeds with a non-empty list")
fun listAll() =
SQLiteDB().use(CustomFunctions::listAll)
@Test
@DisplayName("jsonArray succeeds with empty array")
fun jsonArrayEmpty() =
SQLiteDB().use(CustomFunctions::jsonArrayEmpty)
@Test
@DisplayName("jsonArray succeeds with a single-item array")
fun jsonArraySingle() =
SQLiteDB().use(CustomFunctions::jsonArraySingle)
@Test
@DisplayName("jsonArray succeeds with a multi-item array")
fun jsonArrayMany() =
SQLiteDB().use(CustomFunctions::jsonArrayMany)
@Test
@DisplayName("writeJsonArray succeeds with empty array")
fun writeJsonArrayEmpty() =
SQLiteDB().use(CustomFunctions::writeJsonArrayEmpty)
@Test
@DisplayName("writeJsonArray succeeds with a single-item array")
fun writeJsonArraySingle() =
SQLiteDB().use(CustomFunctions::writeJsonArraySingle)
@Test
@DisplayName("writeJsonArray succeeds with a multi-item array")
fun writeJsonArrayMany() =
SQLiteDB().use(CustomFunctions::writeJsonArrayMany)
@Test
@DisplayName("single succeeds when document not found")
fun singleNone() =
SQLiteDB().use(CustomFunctions::singleNone)
@Test
@DisplayName("single succeeds when a document is found")
fun singleOne() =
SQLiteDB().use(CustomFunctions::singleOne)
@Test
@DisplayName("jsonSingle succeeds when document not found")
fun jsonSingleNone() =
SQLiteDB().use(CustomFunctions::jsonSingleNone)
@Test
@DisplayName("jsonSingle succeeds when a document is found")
fun jsonSingleOne() =
SQLiteDB().use(CustomFunctions::jsonSingleOne)
@Test
@DisplayName("nonQuery makes changes")
fun nonQueryChanges() =
SQLiteDB().use(CustomFunctions::nonQueryChanges)
@Test
@DisplayName("nonQuery makes no changes when where clause matches nothing")
fun nonQueryNoChanges() =
SQLiteDB().use(CustomFunctions::nonQueryNoChanges)
@Test
@DisplayName("scalar succeeds")
fun scalar() =
SQLiteDB().use(CustomFunctions::scalar)
}

View File

@@ -0,0 +1,32 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlinx.extensions.*
import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE
import solutions.bitbadger.documents.kotlinx.Results
import java.io.File
/**
* A wrapper for a throwaway SQLite database
*/
class SQLiteDB : ThrowawayDatabase() {
init {
Configuration.connectionString = "jdbc:sqlite:$dbName"
}
override val conn = Configuration.dbConn()
init {
conn.ensureTable(TEST_TABLE)
}
override fun close() {
conn.close()
File(dbName).delete()
}
override fun dbObjectExists(name: String) =
conn.customScalar("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE name = :name) AS it",
listOf(Parameter(":name", ParameterType.STRING, name)), Results::toExists)
}

View File

@@ -0,0 +1,35 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.DocumentException
import kotlin.test.Test
/**
* SQLite integration tests for the `Definition` object / `ensure*` connection extension functions
*/
@DisplayName("KotlinX | SQLite: Definition")
class SQLiteDefinitionIT {
@Test
@DisplayName("ensureTable creates table and index")
fun ensureTable() =
SQLiteDB().use(DefinitionFunctions::ensureTable)
@Test
@DisplayName("ensureFieldIndex creates an index")
fun ensureFieldIndex() =
SQLiteDB().use(DefinitionFunctions::ensureFieldIndex)
@Test
@DisplayName("ensureDocumentIndex fails for full index")
fun ensureDocumentIndexFull() {
assertThrows<DocumentException> { SQLiteDB().use(DefinitionFunctions::ensureDocumentIndexFull) }
}
@Test
@DisplayName("ensureDocumentIndex fails for optimized index")
fun ensureDocumentIndexOptimized() {
assertThrows<DocumentException> { SQLiteDB().use(DefinitionFunctions::ensureDocumentIndexOptimized) }
}
}

View File

@@ -0,0 +1,45 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.DocumentException
import kotlin.test.Test
/**
* SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions
*/
@DisplayName("KotlinX | SQLite: Delete")
class SQLiteDeleteIT {
@Test
@DisplayName("byId deletes a matching ID")
fun byIdMatch() =
SQLiteDB().use(DeleteFunctions::byIdMatch)
@Test
@DisplayName("byId succeeds when no ID matches")
fun byIdNoMatch() =
SQLiteDB().use(DeleteFunctions::byIdNoMatch)
@Test
@DisplayName("byFields deletes matching documents")
fun byFieldsMatch() =
SQLiteDB().use(DeleteFunctions::byFieldsMatch)
@Test
@DisplayName("byFields succeeds when no documents match")
fun byFieldsNoMatch() =
SQLiteDB().use(DeleteFunctions::byFieldsNoMatch)
@Test
@DisplayName("byContains fails")
fun byContainsFails() {
assertThrows<DocumentException> { SQLiteDB().use(DeleteFunctions::byContainsMatch) }
}
@Test
@DisplayName("byJsonPath fails")
fun byJsonPathFails() {
assertThrows<DocumentException> { SQLiteDB().use(DeleteFunctions::byJsonPathMatch) }
}
}

View File

@@ -0,0 +1,56 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import kotlin.test.Test
/**
* SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
*/
@DisplayName("KotlinX | SQLite: Document")
class SQLiteDocumentIT {
@Test
@DisplayName("insert works with default values")
fun insertDefault() =
SQLiteDB().use(DocumentFunctions::insertDefault)
@Test
@DisplayName("insert fails with duplicate key")
fun insertDupe() =
SQLiteDB().use(DocumentFunctions::insertDupe)
@Test
@DisplayName("insert succeeds with numeric auto IDs")
fun insertNumAutoId() =
SQLiteDB().use(DocumentFunctions::insertNumAutoId)
@Test
@DisplayName("insert succeeds with UUID auto ID")
fun insertUUIDAutoId() =
SQLiteDB().use(DocumentFunctions::insertUUIDAutoId)
@Test
@DisplayName("insert succeeds with random string auto ID")
fun insertStringAutoId() =
SQLiteDB().use(DocumentFunctions::insertStringAutoId)
@Test
@DisplayName("save updates an existing document")
fun saveMatch() =
SQLiteDB().use(DocumentFunctions::saveMatch)
@Test
@DisplayName("save inserts a new document")
fun saveNoMatch() =
SQLiteDB().use(DocumentFunctions::saveNoMatch)
@Test
@DisplayName("update replaces an existing document")
fun updateMatch() =
SQLiteDB().use(DocumentFunctions::updateMatch)
@Test
@DisplayName("update succeeds when no document exists")
fun updateNoMatch() =
SQLiteDB().use(DocumentFunctions::updateNoMatch)
}

View File

@@ -0,0 +1,45 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.DocumentException
import kotlin.test.Test
/**
* SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions
*/
@DisplayName("KotlinX | SQLite: Exists")
class SQLiteExistsIT {
@Test
@DisplayName("byId returns true when a document matches the ID")
fun byIdMatch() =
SQLiteDB().use(ExistsFunctions::byIdMatch)
@Test
@DisplayName("byId returns false when no document matches the ID")
fun byIdNoMatch() =
SQLiteDB().use(ExistsFunctions::byIdNoMatch)
@Test
@DisplayName("byFields returns true when documents match")
fun byFieldsMatch() =
SQLiteDB().use(ExistsFunctions::byFieldsMatch)
@Test
@DisplayName("byFields returns false when no documents match")
fun byFieldsNoMatch() =
SQLiteDB().use(ExistsFunctions::byFieldsNoMatch)
@Test
@DisplayName("byContains fails")
fun byContainsFails() {
assertThrows<DocumentException> { SQLiteDB().use(ExistsFunctions::byContainsMatch) }
}
@Test
@DisplayName("byJsonPath fails")
fun byJsonPathFails() {
assertThrows<DocumentException> { SQLiteDB().use(ExistsFunctions::byJsonPathMatch) }
}
}

View File

@@ -0,0 +1,127 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.DocumentException
import kotlin.test.Test
/**
* SQLite integration tests for the `Find` object / `find*` connection extension functions
*/
@DisplayName("KotlinX | SQLite: Find")
class SQLiteFindIT {
@Test
@DisplayName("all retrieves all documents")
fun allDefault() =
SQLiteDB().use(FindFunctions::allDefault)
@Test
@DisplayName("all sorts data ascending")
fun allAscending() =
SQLiteDB().use(FindFunctions::allAscending)
@Test
@DisplayName("all sorts data descending")
fun allDescending() =
SQLiteDB().use(FindFunctions::allDescending)
@Test
@DisplayName("all sorts data numerically")
fun allNumOrder() =
SQLiteDB().use(FindFunctions::allNumOrder)
@Test
@DisplayName("all succeeds with an empty table")
fun allEmpty() =
SQLiteDB().use(FindFunctions::allEmpty)
@Test
@DisplayName("byId retrieves a document via a string ID")
fun byIdString() =
SQLiteDB().use(FindFunctions::byIdString)
@Test
@DisplayName("byId retrieves a document via a numeric ID")
fun byIdNumber() =
SQLiteDB().use(FindFunctions::byIdNumber)
@Test
@DisplayName("byId returns null when a matching ID is not found")
fun byIdNotFound() =
SQLiteDB().use(FindFunctions::byIdNotFound)
@Test
@DisplayName("byFields retrieves matching documents")
fun byFieldsMatch() =
SQLiteDB().use(FindFunctions::byFieldsMatch)
@Test
@DisplayName("byFields retrieves ordered matching documents")
fun byFieldsMatchOrdered() =
SQLiteDB().use(FindFunctions::byFieldsMatchOrdered)
@Test
@DisplayName("byFields retrieves matching documents with a numeric IN clause")
fun byFieldsMatchNumIn() =
SQLiteDB().use(FindFunctions::byFieldsMatchNumIn)
@Test
@DisplayName("byFields succeeds when no documents match")
fun byFieldsNoMatch() =
SQLiteDB().use(FindFunctions::byFieldsNoMatch)
@Test
@DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison")
fun byFieldsMatchInArray() =
SQLiteDB().use(FindFunctions::byFieldsMatchInArray)
@Test
@DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison")
fun byFieldsNoMatchInArray() =
SQLiteDB().use(FindFunctions::byFieldsNoMatchInArray)
@Test
@DisplayName("byContains fails")
fun byContainsFails() {
assertThrows<DocumentException> { SQLiteDB().use(FindFunctions::byContainsMatch) }
}
@Test
@DisplayName("byJsonPath fails")
fun byJsonPathFails() {
assertThrows<DocumentException> { SQLiteDB().use(FindFunctions::byJsonPathMatch) }
}
@Test
@DisplayName("firstByFields retrieves a matching document")
fun firstByFieldsMatchOne() =
SQLiteDB().use(FindFunctions::firstByFieldsMatchOne)
@Test
@DisplayName("firstByFields retrieves a matching document among many")
fun firstByFieldsMatchMany() =
SQLiteDB().use(FindFunctions::firstByFieldsMatchMany)
@Test
@DisplayName("firstByFields retrieves a matching document among many (ordered)")
fun firstByFieldsMatchOrdered() =
SQLiteDB().use(FindFunctions::firstByFieldsMatchOrdered)
@Test
@DisplayName("firstByFields returns null when no document matches")
fun firstByFieldsNoMatch() =
SQLiteDB().use(FindFunctions::firstByFieldsNoMatch)
@Test
@DisplayName("firstByContains fails")
fun firstByContainsFails() {
assertThrows<DocumentException> { SQLiteDB().use(FindFunctions::firstByContainsMatchOne) }
}
@Test
@DisplayName("firstByJsonPath fails")
fun firstByJsonPathFails() {
assertThrows<DocumentException> { SQLiteDB().use(FindFunctions::firstByJsonPathMatchOne) }
}
}

View File

@@ -0,0 +1,211 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.DocumentException
import kotlin.test.Test
/**
* SQLite integration tests for the `Json` object / `json*` connection extension functions
*/
@DisplayName("KotlinX | SQLite: Json")
class SQLiteJsonIT {
@Test
@DisplayName("all retrieves all documents")
fun allDefault() =
SQLiteDB().use(JsonFunctions::allDefault)
@Test
@DisplayName("all succeeds with an empty table")
fun allEmpty() =
SQLiteDB().use(JsonFunctions::allEmpty)
@Test
@DisplayName("byId retrieves a document via a string ID")
fun byIdString() =
SQLiteDB().use(JsonFunctions::byIdString)
@Test
@DisplayName("byId retrieves a document via a numeric ID")
fun byIdNumber() =
SQLiteDB().use(JsonFunctions::byIdNumber)
@Test
@DisplayName("byId returns null when a matching ID is not found")
fun byIdNotFound() =
SQLiteDB().use(JsonFunctions::byIdNotFound)
@Test
@DisplayName("byFields retrieves matching documents")
fun byFieldsMatch() =
SQLiteDB().use(JsonFunctions::byFieldsMatch)
@Test
@DisplayName("byFields retrieves ordered matching documents")
fun byFieldsMatchOrdered() =
SQLiteDB().use(JsonFunctions::byFieldsMatchOrdered)
@Test
@DisplayName("byFields retrieves matching documents with a numeric IN clause")
fun byFieldsMatchNumIn() =
SQLiteDB().use(JsonFunctions::byFieldsMatchNumIn)
@Test
@DisplayName("byFields succeeds when no documents match")
fun byFieldsNoMatch() =
SQLiteDB().use(JsonFunctions::byFieldsNoMatch)
@Test
@DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison")
fun byFieldsMatchInArray() =
SQLiteDB().use(JsonFunctions::byFieldsMatchInArray)
@Test
@DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison")
fun byFieldsNoMatchInArray() =
SQLiteDB().use(JsonFunctions::byFieldsNoMatchInArray)
@Test
@DisplayName("byContains fails")
fun byContainsFails() {
assertThrows<DocumentException> { SQLiteDB().use(JsonFunctions::byContainsMatch) }
}
@Test
@DisplayName("byJsonPath fails")
fun byJsonPathFails() {
assertThrows<DocumentException> { SQLiteDB().use(JsonFunctions::byJsonPathMatch) }
}
@Test
@DisplayName("firstByFields retrieves a matching document")
fun firstByFieldsMatchOne() =
SQLiteDB().use(JsonFunctions::firstByFieldsMatchOne)
@Test
@DisplayName("firstByFields retrieves a matching document among many")
fun firstByFieldsMatchMany() =
SQLiteDB().use(JsonFunctions::firstByFieldsMatchMany)
@Test
@DisplayName("firstByFields retrieves a matching document among many (ordered)")
fun firstByFieldsMatchOrdered() =
SQLiteDB().use(JsonFunctions::firstByFieldsMatchOrdered)
@Test
@DisplayName("firstByFields returns null when no document matches")
fun firstByFieldsNoMatch() =
SQLiteDB().use(JsonFunctions::firstByFieldsNoMatch)
@Test
@DisplayName("firstByContains fails")
fun firstByContainsFails() {
assertThrows<DocumentException> { SQLiteDB().use(JsonFunctions::firstByContainsMatchOne) }
}
@Test
@DisplayName("firstByJsonPath fails")
fun firstByJsonPathFails() {
assertThrows<DocumentException> { SQLiteDB().use(JsonFunctions::firstByJsonPathMatchOne) }
}
@Test
@DisplayName("writeAll retrieves all documents")
fun writeAllDefault() =
SQLiteDB().use(JsonFunctions::writeAllDefault)
@Test
@DisplayName("writeAll succeeds with an empty table")
fun writeAllEmpty() =
SQLiteDB().use(JsonFunctions::writeAllEmpty)
@Test
@DisplayName("writeById retrieves a document via a string ID")
fun writeByIdString() =
SQLiteDB().use(JsonFunctions::writeByIdString)
@Test
@DisplayName("writeById retrieves a document via a numeric ID")
fun writeByIdNumber() =
SQLiteDB().use(JsonFunctions::writeByIdNumber)
@Test
@DisplayName("writeById returns null when a matching ID is not found")
fun writeByIdNotFound() =
SQLiteDB().use(JsonFunctions::writeByIdNotFound)
@Test
@DisplayName("writeByFields retrieves matching documents")
fun writeByFieldsMatch() =
SQLiteDB().use(JsonFunctions::writeByFieldsMatch)
@Test
@DisplayName("writeByFields retrieves ordered matching documents")
fun writeByFieldsMatchOrdered() =
SQLiteDB().use(JsonFunctions::writeByFieldsMatchOrdered)
@Test
@DisplayName("writeByFields retrieves matching documents with a numeric IN clause")
fun writeByFieldsMatchNumIn() =
SQLiteDB().use(JsonFunctions::writeByFieldsMatchNumIn)
@Test
@DisplayName("writeByFields succeeds when no documents match")
fun writeByFieldsNoMatch() =
SQLiteDB().use(JsonFunctions::writeByFieldsNoMatch)
@Test
@DisplayName("writeByFields retrieves matching documents with an IN_ARRAY comparison")
fun writeByFieldsMatchInArray() =
SQLiteDB().use(JsonFunctions::writeByFieldsMatchInArray)
@Test
@DisplayName("writeByFields succeeds when no documents match an IN_ARRAY comparison")
fun writeByFieldsNoMatchInArray() =
SQLiteDB().use(JsonFunctions::writeByFieldsNoMatchInArray)
@Test
@DisplayName("writeByContains fails")
fun writeByContainsFails() {
assertThrows<DocumentException> { SQLiteDB().use(JsonFunctions::writeByContainsMatch) }
}
@Test
@DisplayName("writeByJsonPath fails")
fun writeByJsonPathFails() {
assertThrows<DocumentException> { SQLiteDB().use(JsonFunctions::writeByJsonPathMatch) }
}
@Test
@DisplayName("writeFirstByFields retrieves a matching document")
fun writeFirstByFieldsMatchOne() =
SQLiteDB().use(JsonFunctions::writeFirstByFieldsMatchOne)
@Test
@DisplayName("writeFirstByFields retrieves a matching document among many")
fun writeFirstByFieldsMatchMany() =
SQLiteDB().use(JsonFunctions::writeFirstByFieldsMatchMany)
@Test
@DisplayName("writeFirstByFields retrieves a matching document among many (ordered)")
fun writeFirstByFieldsMatchOrdered() =
SQLiteDB().use(JsonFunctions::writeFirstByFieldsMatchOrdered)
@Test
@DisplayName("writeFirstByFields returns null when no document matches")
fun writeFirstByFieldsNoMatch() =
SQLiteDB().use(JsonFunctions::writeFirstByFieldsNoMatch)
@Test
@DisplayName("writeFirstByContains fails")
fun writeFirstByContainsFails() {
assertThrows<DocumentException> { SQLiteDB().use(JsonFunctions::writeFirstByContainsMatchOne) }
}
@Test
@DisplayName("writeFirstByJsonPath fails")
fun writeFirstByJsonPathFails() {
assertThrows<DocumentException> { SQLiteDB().use(JsonFunctions::writeFirstByJsonPathMatchOne) }
}
}

View File

@@ -0,0 +1,45 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.DocumentException
import kotlin.test.Test
/**
* SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions
*/
@DisplayName("KotlinX | SQLite: Patch")
class SQLitePatchIT {
@Test
@DisplayName("byId patches an existing document")
fun byIdMatch() =
SQLiteDB().use(PatchFunctions::byIdMatch)
@Test
@DisplayName("byId succeeds for a non-existent document")
fun byIdNoMatch() =
SQLiteDB().use(PatchFunctions::byIdNoMatch)
@Test
@DisplayName("byFields patches matching document")
fun byFieldsMatch() =
SQLiteDB().use(PatchFunctions::byFieldsMatch)
@Test
@DisplayName("byFields succeeds when no documents match")
fun byFieldsNoMatch() =
SQLiteDB().use(PatchFunctions::byFieldsNoMatch)
@Test
@DisplayName("byContains fails")
fun byContainsFails() {
assertThrows<DocumentException> { SQLiteDB().use(PatchFunctions::byContainsMatch) }
}
@Test
@DisplayName("byJsonPath fails")
fun byJsonPathFails() {
assertThrows<DocumentException> { SQLiteDB().use(PatchFunctions::byJsonPathMatch) }
}
}

View File

@@ -0,0 +1,55 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.DocumentException
import kotlin.test.Test
/**
* SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
*/
@DisplayName("KotlinX | SQLite: RemoveFields")
class SQLiteRemoveFieldsIT {
@Test
@DisplayName("byId removes fields from an existing document")
fun byIdMatchFields() =
SQLiteDB().use(RemoveFieldsFunctions::byIdMatchFields)
@Test
@DisplayName("byId succeeds when fields do not exist on an existing document")
fun byIdMatchNoFields() =
SQLiteDB().use(RemoveFieldsFunctions::byIdMatchNoFields)
@Test
@DisplayName("byId succeeds when no document exists")
fun byIdNoMatch() =
SQLiteDB().use(RemoveFieldsFunctions::byIdNoMatch)
@Test
@DisplayName("byFields removes fields from matching documents")
fun byFieldsMatchFields() =
SQLiteDB().use(RemoveFieldsFunctions::byFieldsMatchFields)
@Test
@DisplayName("byFields succeeds when fields do not exist on matching documents")
fun byFieldsMatchNoFields() =
SQLiteDB().use(RemoveFieldsFunctions::byFieldsMatchNoFields)
@Test
@DisplayName("byFields succeeds when no matching documents exist")
fun byFieldsNoMatch() =
SQLiteDB().use(RemoveFieldsFunctions::byFieldsNoMatch)
@Test
@DisplayName("byContains fails")
fun byContainsFails() {
assertThrows<DocumentException> { SQLiteDB().use(RemoveFieldsFunctions::byContainsMatchFields) }
}
@Test
@DisplayName("byJsonPath fails")
fun byJsonPathFails() {
assertThrows<DocumentException> { SQLiteDB().use(RemoveFieldsFunctions::byJsonPathMatchFields) }
}
}

View File

@@ -0,0 +1,24 @@
package solutions.bitbadger.documents.kotlinx.tests.integration
import solutions.bitbadger.documents.AutoId
import java.sql.Connection
/**
* Common interface for PostgreSQL and SQLite throwaway databases
*/
abstract class ThrowawayDatabase : AutoCloseable {
/** The name of the throwaway database */
protected val dbName = "throwaway_${AutoId.generateRandomString(8)}"
/** The database connection for the throwaway database */
abstract val conn: Connection
/**
* Determine if a database object exists
*
* @param name The name of the object whose existence should be checked
* @return True if the object exists, false if not
*/
abstract fun dbObjectExists(name: String): Boolean
}