Initial Development #1

Merged
danieljsummers merged 88 commits from v1-rc into main 2025-04-16 01:29:20 +00:00
82 changed files with 450 additions and 428 deletions
Showing only changes of commit c4ef2b1d9a - Show all commits

4
.idea/compiler.xml generated
View File

@ -6,11 +6,13 @@
<sourceOutputDir name="target/generated-sources/annotations" /> <sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" /> <outputRelativeToContentRoot value="true" />
<module name="documents" /> <module name="jvm" />
<module name="kotlin" />
</profile> </profile>
</annotationProcessing> </annotationProcessing>
<bytecodeTargetLevel> <bytecodeTargetLevel>
<module name="common" target="1.8" /> <module name="common" target="1.8" />
<module name="documents" target="9" />
<module name="sqlite" target="1.8" /> <module name="sqlite" target="1.8" />
</bytecodeTargetLevel> </bytecodeTargetLevel>
</component> </component>

6
.idea/encodings.xml generated
View File

@ -1,9 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Encoding"> <component name="Encoding">
<file url="file://$PROJECT_DIR$/src/jvm/src/main/kotlin" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/jvm/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/kotlin/src/main/kotlin" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/kotlin/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/kotlin" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/src/main/kotlin" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/sqlite/src/main/kotlin" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/src/sqlite/src/main/kotlin" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/sqlite/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/src/sqlite/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/src/main/resources" charset="UTF-8" />
</component> </component>
</project> </project>

2
.idea/kotlinc.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="2.1.0" /> <option name="version" value="2.1.10" />
</component> </component>
</project> </project>

8
.idea/modules.xml generated
View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/documents.iml" filepath="$PROJECT_DIR$/documents.iml" />
</modules>
</component>
</project>

123
pom.xml
View File

@ -43,125 +43,8 @@
<serialization.version>1.8.0</serialization.version> <serialization.version>1.8.0</serialization.version>
</properties> </properties>
<!-- <repositories> <modules>
<repository> <module>src</module>
<id>mavenCentral</id> </modules>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories> -->
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<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>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</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>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>MainKt</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-json</artifactId>
<version>${serialization.version}</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.46.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.5</version>
<scope>test</scope>
</dependency>
</dependencies>
</project> </project>

View File

@ -24,7 +24,7 @@ fun <TDoc> Connection.customList(
clazz: Class<TDoc>, clazz: Class<TDoc>,
mapFunc: (ResultSet, Class<TDoc>) -> TDoc mapFunc: (ResultSet, Class<TDoc>) -> TDoc
) = ) =
solutions.bitbadger.documents.jvm.Custom.list(query, parameters, clazz, this, mapFunc) Custom.list(query, parameters, clazz, this, mapFunc)
/** /**
* Execute a query that returns one or no results * Execute a query that returns one or no results
@ -41,7 +41,7 @@ fun <TDoc> Connection.customSingle(
clazz: Class<TDoc>, clazz: Class<TDoc>,
mapFunc: (ResultSet, Class<TDoc>) -> TDoc mapFunc: (ResultSet, Class<TDoc>) -> TDoc
) = ) =
solutions.bitbadger.documents.jvm.Custom.single(query, parameters, clazz, this, mapFunc) Custom.single(query, parameters, clazz, this, mapFunc)
/** /**
* Execute a query that returns no results * Execute a query that returns no results
@ -50,7 +50,7 @@ fun <TDoc> Connection.customSingle(
* @param parameters Parameters to use for the query * @param parameters Parameters to use for the query
*/ */
fun Connection.customNonQuery(query: String, parameters: Collection<Parameter<*>> = listOf()) = fun Connection.customNonQuery(query: String, parameters: Collection<Parameter<*>> = listOf()) =
solutions.bitbadger.documents.jvm.Custom.nonQuery(query, parameters, this) Custom.nonQuery(query, parameters, this)
/** /**
* Execute a query that returns a scalar result * Execute a query that returns a scalar result
@ -67,7 +67,7 @@ fun <T : Any> Connection.customScalar(
clazz: Class<T>, clazz: Class<T>,
mapFunc: (ResultSet, Class<T>) -> T mapFunc: (ResultSet, Class<T>) -> T
) = ) =
solutions.bitbadger.documents.jvm.Custom.scalar(query, parameters, clazz, this, mapFunc) Custom.scalar(query, parameters, clazz, this, mapFunc)
// ~~~ DEFINITION QUERIES ~~~ // ~~~ DEFINITION QUERIES ~~~
@ -109,7 +109,7 @@ fun Connection.ensureDocumentIndex(tableName: String, indexType: DocumentIndex)
* @param document The document to be inserted * @param document The document to be inserted
*/ */
fun <TDoc> Connection.insert(tableName: String, document: TDoc) = fun <TDoc> Connection.insert(tableName: String, document: TDoc) =
Document.insert(tableName, document, this) Document.insert<TDoc>(tableName, document, this)
/** /**
* Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert") * Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
@ -149,6 +149,7 @@ fun Connection.countAll(tableName: String) =
* @param howMatched How the fields should be matched * @param howMatched How the fields should be matched
* @return A count of the matching documents in the table * @return A count of the matching documents in the table
*/ */
@JvmOverloads
fun Connection.countByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun Connection.countByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
Count.byFields(tableName, fields, howMatched, this) Count.byFields(tableName, fields, howMatched, this)
@ -196,6 +197,7 @@ fun <TKey> Connection.existsById(tableName: String, docId: TKey) =
* @param howMatched How the fields should be matched * @param howMatched How the fields should be matched
* @return True if any matching documents exist, false if not * @return True if any matching documents exist, false if not
*/ */
@JvmOverloads
fun Connection.existsByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun Connection.existsByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
Exists.byFields(tableName, fields, howMatched, this) Exists.byFields(tableName, fields, howMatched, this)
@ -499,6 +501,7 @@ fun <TKey> Connection.deleteById(tableName: String, docId: TKey) =
* @param fields The fields which should be compared * @param fields The fields which should be compared
* @param howMatched How the fields should be matched * @param howMatched How the fields should be matched
*/ */
@JvmOverloads
fun Connection.deleteByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun Connection.deleteByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
Delete.byFields(tableName, fields, howMatched, this) Delete.byFields(tableName, fields, howMatched, this)

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.jvm package solutions.bitbadger.documents.jvm
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.query.Count import solutions.bitbadger.documents.query.CountQuery
import solutions.bitbadger.documents.extensions.customScalar import solutions.bitbadger.documents.extensions.customScalar
import java.sql.Connection import java.sql.Connection
@ -19,7 +19,7 @@ object Count {
*/ */
@JvmStatic @JvmStatic
fun all(tableName: String, conn: Connection) = fun all(tableName: String, conn: Connection) =
conn.customScalar(Count.all(tableName), listOf(), Long::class.java, Results::toCount) conn.customScalar(CountQuery.all(tableName), listOf(), Long::class.java, Results::toCount)
/** /**
* Count all documents in the table * Count all documents in the table
@ -50,7 +50,7 @@ object Count {
): Long { ): Long {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
return conn.customScalar( return conn.customScalar(
Count.byFields(tableName, named, howMatched), CountQuery.byFields(tableName, named, howMatched),
Parameters.addFields(named), Parameters.addFields(named),
Long::class.java, Long::class.java,
Results::toCount Results::toCount
@ -82,7 +82,7 @@ object Count {
@JvmStatic @JvmStatic
fun <TContains> byContains(tableName: String, criteria: TContains, conn: Connection) = fun <TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
conn.customScalar( conn.customScalar(
Count.byContains(tableName), CountQuery.byContains(tableName),
listOf(Parameters.json(":criteria", criteria)), listOf(Parameters.json(":criteria", criteria)),
Long::class.java, Long::class.java,
Results::toCount Results::toCount
@ -112,7 +112,7 @@ object Count {
@JvmStatic @JvmStatic
fun byJsonPath(tableName: String, path: String, conn: Connection) = fun byJsonPath(tableName: String, path: String, conn: Connection) =
conn.customScalar( conn.customScalar(
Count.byJsonPath(tableName), CountQuery.byJsonPath(tableName),
listOf(Parameter(":path", ParameterType.STRING, path)), listOf(Parameter(":path", ParameterType.STRING, path)),
Long::class.java, Long::class.java,
Results::toCount Results::toCount

View File

@ -4,7 +4,7 @@ import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.DocumentIndex import solutions.bitbadger.documents.DocumentIndex
import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.extensions.customNonQuery
import solutions.bitbadger.documents.query.Definition import solutions.bitbadger.documents.query.DefinitionQuery
import java.sql.Connection import java.sql.Connection
/** /**
@ -21,8 +21,8 @@ object Definition {
@JvmStatic @JvmStatic
fun ensureTable(tableName: String, conn: Connection) = fun ensureTable(tableName: String, conn: Connection) =
Configuration.dialect("ensure $tableName exists").let { Configuration.dialect("ensure $tableName exists").let {
conn.customNonQuery(Definition.ensureTable(tableName, it)) conn.customNonQuery(DefinitionQuery.ensureTable(tableName, it))
conn.customNonQuery(Definition.ensureKey(tableName, it)) conn.customNonQuery(DefinitionQuery.ensureKey(tableName, it))
} }
/** /**
@ -44,7 +44,7 @@ object Definition {
*/ */
@JvmStatic @JvmStatic
fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>, conn: Connection) = fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>, conn: Connection) =
conn.customNonQuery(Definition.ensureIndexOn(tableName, indexName, fields)) conn.customNonQuery(DefinitionQuery.ensureIndexOn(tableName, indexName, fields))
/** /**
* Create an index on field(s) within documents in the specified table if necessary * Create an index on field(s) within documents in the specified table if necessary
@ -68,7 +68,7 @@ object Definition {
@Throws(DocumentException::class) @Throws(DocumentException::class)
@JvmStatic @JvmStatic
fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex, conn: Connection) = fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex, conn: Connection) =
conn.customNonQuery(Definition.ensureDocumentIndexOn(tableName, indexType)) conn.customNonQuery(DefinitionQuery.ensureDocumentIndexOn(tableName, indexType))
/** /**
* Create a document index on a table (PostgreSQL only) * Create a document index on a table (PostgreSQL only)

View File

@ -2,7 +2,7 @@ package solutions.bitbadger.documents.jvm
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.extensions.customNonQuery
import solutions.bitbadger.documents.query.Delete import solutions.bitbadger.documents.query.DeleteQuery
import java.sql.Connection import java.sql.Connection
/** /**
@ -20,7 +20,7 @@ object Delete {
@JvmStatic @JvmStatic
fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) = fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) =
conn.customNonQuery( conn.customNonQuery(
Delete.byId(tableName, docId), DeleteQuery.byId(tableName, docId),
Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))) Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id")))
) )
@ -46,7 +46,7 @@ object Delete {
@JvmOverloads @JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null, conn: Connection) { fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null, conn: Connection) {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
conn.customNonQuery(Delete.byFields(tableName, named, howMatched), Parameters.addFields(named)) conn.customNonQuery(DeleteQuery.byFields(tableName, named, howMatched), Parameters.addFields(named))
} }
/** /**
@ -72,7 +72,7 @@ object Delete {
@Throws(DocumentException::class) @Throws(DocumentException::class)
@JvmStatic @JvmStatic
fun <TContains> byContains(tableName: String, criteria: TContains, conn: Connection) = fun <TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
conn.customNonQuery(Delete.byContains(tableName), listOf(Parameters.json(":criteria", criteria))) conn.customNonQuery(DeleteQuery.byContains(tableName), listOf(Parameters.json(":criteria", criteria)))
/** /**
* Delete documents using a JSON containment query (PostgreSQL only) * Delete documents using a JSON containment query (PostgreSQL only)
@ -97,7 +97,7 @@ object Delete {
@Throws(DocumentException::class) @Throws(DocumentException::class)
@JvmStatic @JvmStatic
fun byJsonPath(tableName: String, path: String, conn: Connection) = fun byJsonPath(tableName: String, path: String, conn: Connection) =
conn.customNonQuery(Delete.byJsonPath(tableName), listOf(Parameter(":path", ParameterType.STRING, path))) conn.customNonQuery(DeleteQuery.byJsonPath(tableName), listOf(Parameter(":path", ParameterType.STRING, path)))
/** /**
* Delete documents using a JSON Path match query (PostgreSQL only) * Delete documents using a JSON Path match query (PostgreSQL only)

View File

@ -2,10 +2,9 @@ package solutions.bitbadger.documents.jvm
import solutions.bitbadger.documents.AutoId import solutions.bitbadger.documents.AutoId
import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.extensions.customNonQuery
import solutions.bitbadger.documents.query.Document import solutions.bitbadger.documents.query.DocumentQuery
import solutions.bitbadger.documents.query.Where import solutions.bitbadger.documents.query.Where
import solutions.bitbadger.documents.query.statementWhere import solutions.bitbadger.documents.query.statementWhere
import java.sql.Connection import java.sql.Connection
@ -25,35 +24,10 @@ object Document {
@JvmStatic @JvmStatic
fun <TDoc> insert(tableName: String, document: TDoc, conn: Connection) { fun <TDoc> insert(tableName: String, document: TDoc, conn: Connection) {
val strategy = Configuration.autoIdStrategy val strategy = Configuration.autoIdStrategy
val query = if (strategy == AutoId.DISABLED) { val query = if (strategy == AutoId.DISABLED || !AutoId.needsAutoId(strategy, document, Configuration.idField)) {
Document.insert(tableName) DocumentQuery.insert(tableName)
} else { } else {
val idField = Configuration.idField DocumentQuery.insert(tableName, strategy)
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"
}
Document.insert(tableName).replace(":data", dataParam)
} }
conn.customNonQuery(query, listOf(Parameters.json(":data", document))) conn.customNonQuery(query, listOf(Parameters.json(":data", document)))
} }
@ -77,7 +51,7 @@ object Document {
*/ */
@JvmStatic @JvmStatic
fun <TDoc> save(tableName: String, document: TDoc, conn: Connection) = fun <TDoc> save(tableName: String, document: TDoc, conn: Connection) =
conn.customNonQuery(Document.save(tableName), listOf(Parameters.json(":data", document))) 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") * Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
@ -100,7 +74,7 @@ object Document {
@JvmStatic @JvmStatic
fun <TKey, TDoc> update(tableName: String, docId: TKey, document: TDoc, conn: Connection) = fun <TKey, TDoc> update(tableName: String, docId: TKey, document: TDoc, conn: Connection) =
conn.customNonQuery( conn.customNonQuery(
statementWhere(Document.update(tableName), Where.byId(":id", docId)), statementWhere(DocumentQuery.update(tableName), Where.byId(":id", docId)),
Parameters.addFields( Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")), listOf(Field.equal(Configuration.idField, docId, ":id")),
mutableListOf(Parameters.json(":data", document)) mutableListOf(Parameters.json(":data", document))

View File

@ -2,7 +2,7 @@ package solutions.bitbadger.documents.jvm
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.extensions.customScalar import solutions.bitbadger.documents.extensions.customScalar
import solutions.bitbadger.documents.query.Exists import solutions.bitbadger.documents.query.ExistsQuery
import java.sql.Connection import java.sql.Connection
/** /**
@ -21,7 +21,7 @@ object Exists {
@JvmStatic @JvmStatic
fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) = fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) =
conn.customScalar( conn.customScalar(
Exists.byId(tableName, docId), ExistsQuery.byId(tableName, docId),
Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))), Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))),
Boolean::class.java, Boolean::class.java,
Results::toExists Results::toExists
@ -57,7 +57,7 @@ object Exists {
): Boolean { ): Boolean {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
return conn.customScalar( return conn.customScalar(
Exists.byFields(tableName, named, howMatched), ExistsQuery.byFields(tableName, named, howMatched),
Parameters.addFields(named), Parameters.addFields(named),
Boolean::class.java, Boolean::class.java,
Results::toExists Results::toExists
@ -90,7 +90,7 @@ object Exists {
@JvmStatic @JvmStatic
fun <TContains> byContains(tableName: String, criteria: TContains, conn: Connection) = fun <TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
conn.customScalar( conn.customScalar(
Exists.byContains(tableName), ExistsQuery.byContains(tableName),
listOf(Parameters.json(":criteria", criteria)), listOf(Parameters.json(":criteria", criteria)),
Boolean::class.java, Boolean::class.java,
Results::toExists Results::toExists
@ -122,7 +122,7 @@ object Exists {
@JvmStatic @JvmStatic
fun byJsonPath(tableName: String, path: String, conn: Connection) = fun byJsonPath(tableName: String, path: String, conn: Connection) =
conn.customScalar( conn.customScalar(
Exists.byJsonPath(tableName), ExistsQuery.byJsonPath(tableName),
listOf(Parameter(":path", ParameterType.STRING, path)), listOf(Parameter(":path", ParameterType.STRING, path)),
Boolean::class.java, Boolean::class.java,
Results::toExists Results::toExists

View File

@ -3,7 +3,7 @@ package solutions.bitbadger.documents.jvm
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.extensions.customList import solutions.bitbadger.documents.extensions.customList
import solutions.bitbadger.documents.extensions.customSingle import solutions.bitbadger.documents.extensions.customSingle
import solutions.bitbadger.documents.query.Find import solutions.bitbadger.documents.query.FindQuery
import solutions.bitbadger.documents.query.orderBy import solutions.bitbadger.documents.query.orderBy
import java.sql.Connection import java.sql.Connection
@ -23,7 +23,7 @@ object Find {
*/ */
@JvmStatic @JvmStatic
fun <TDoc> all(tableName: String, clazz: Class<TDoc>, orderBy: Collection<Field<*>>? = null, conn: Connection) = fun <TDoc> all(tableName: String, clazz: Class<TDoc>, orderBy: Collection<Field<*>>? = null, conn: Connection) =
conn.customList(Find.all(tableName) + (orderBy?.let(::orderBy) ?: ""), listOf(), clazz, Results::fromData) conn.customList(FindQuery.all(tableName) + (orderBy?.let(::orderBy) ?: ""), listOf(), clazz, Results::fromData)
/** /**
* Retrieve all documents in the given table * Retrieve all documents in the given table
@ -62,7 +62,7 @@ object Find {
@JvmStatic @JvmStatic
fun <TKey, TDoc> byId(tableName: String, docId: TKey, clazz: Class<TDoc>, conn: Connection) = fun <TKey, TDoc> byId(tableName: String, docId: TKey, clazz: Class<TDoc>, conn: Connection) =
conn.customSingle( conn.customSingle(
Find.byId(tableName, docId), FindQuery.byId(tableName, docId),
Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))), Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))),
clazz, clazz,
Results::fromData Results::fromData
@ -102,7 +102,7 @@ object Find {
): List<TDoc> { ): List<TDoc> {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
return conn.customList( return conn.customList(
Find.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""),
Parameters.addFields(named), Parameters.addFields(named),
clazz, clazz,
Results::fromData Results::fromData
@ -171,7 +171,7 @@ object Find {
conn: Connection conn: Connection
) = ) =
conn.customList( conn.customList(
Find.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameters.json(":criteria", criteria)), listOf(Parameters.json(":criteria", criteria)),
clazz, clazz,
Results::fromData Results::fromData
@ -234,7 +234,7 @@ object Find {
conn: Connection conn: Connection
) = ) =
conn.customList( conn.customList(
Find.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameter(":path", ParameterType.STRING, path)), listOf(Parameter(":path", ParameterType.STRING, path)),
clazz, clazz,
Results::fromData Results::fromData
@ -293,7 +293,7 @@ object Find {
): TDoc? { ): TDoc? {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
return conn.customSingle( return conn.customSingle(
Find.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""),
Parameters.addFields(named), Parameters.addFields(named),
clazz, clazz,
Results::fromData Results::fromData
@ -361,7 +361,7 @@ object Find {
conn: Connection conn: Connection
) = ) =
conn.customSingle( conn.customSingle(
Find.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameters.json(":criteria", criteria)), listOf(Parameters.json(":criteria", criteria)),
clazz, clazz,
Results::fromData Results::fromData
@ -429,7 +429,7 @@ object Find {
conn: Connection conn: Connection
) = ) =
conn.customSingle( conn.customSingle(
Find.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameter(":path", ParameterType.STRING, path)), listOf(Parameter(":path", ParameterType.STRING, path)),
clazz, clazz,
Results::fromData Results::fromData

View File

@ -2,7 +2,7 @@ package solutions.bitbadger.documents.jvm
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.extensions.customNonQuery
import solutions.bitbadger.documents.query.Patch import solutions.bitbadger.documents.query.PatchQuery
import java.sql.Connection import java.sql.Connection
/** /**
@ -21,7 +21,7 @@ object Patch {
@JvmStatic @JvmStatic
fun <TKey, TPatch> byId(tableName: String, docId: TKey, patch: TPatch, conn: Connection) = fun <TKey, TPatch> byId(tableName: String, docId: TKey, patch: TPatch, conn: Connection) =
conn.customNonQuery( conn.customNonQuery(
Patch.byId(tableName, docId), PatchQuery.byId(tableName, docId),
Parameters.addFields( Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")), listOf(Field.equal(Configuration.idField, docId, ":id")),
mutableListOf(Parameters.json(":data", patch)) mutableListOf(Parameters.json(":data", patch))
@ -58,7 +58,7 @@ object Patch {
) { ) {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
conn.customNonQuery( conn.customNonQuery(
Patch.byFields(tableName, named, howMatched), Parameters.addFields( PatchQuery.byFields(tableName, named, howMatched), Parameters.addFields(
named, named,
mutableListOf(Parameters.json(":data", patch)) mutableListOf(Parameters.json(":data", patch))
) )
@ -96,7 +96,7 @@ object Patch {
@JvmStatic @JvmStatic
fun <TContains, TPatch> byContains(tableName: String, criteria: TContains, patch: TPatch, conn: Connection) = fun <TContains, TPatch> byContains(tableName: String, criteria: TContains, patch: TPatch, conn: Connection) =
conn.customNonQuery( conn.customNonQuery(
Patch.byContains(tableName), PatchQuery.byContains(tableName),
listOf(Parameters.json(":criteria", criteria), Parameters.json(":data", patch)) listOf(Parameters.json(":criteria", criteria), Parameters.json(":data", patch))
) )
@ -126,7 +126,7 @@ object Patch {
@JvmStatic @JvmStatic
fun <TPatch> byJsonPath(tableName: String, path: String, patch: TPatch, conn: Connection) = fun <TPatch> byJsonPath(tableName: String, path: String, patch: TPatch, conn: Connection) =
conn.customNonQuery( conn.customNonQuery(
Patch.byJsonPath(tableName), PatchQuery.byJsonPath(tableName),
listOf(Parameter(":path", ParameterType.STRING, path), Parameters.json(":data", patch)) listOf(Parameter(":path", ParameterType.STRING, path), Parameters.json(":data", patch))
) )

View File

@ -2,7 +2,7 @@ package solutions.bitbadger.documents.jvm
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.extensions.customNonQuery
import solutions.bitbadger.documents.query.RemoveFields import solutions.bitbadger.documents.query.RemoveFieldsQuery
import java.sql.Connection import java.sql.Connection
/** /**
@ -36,7 +36,7 @@ object RemoveFields {
fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>, conn: Connection) { fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>, conn: Connection) {
val nameParams = Parameters.fieldNames(toRemove) val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery( conn.customNonQuery(
RemoveFields.byId(tableName, nameParams, docId), RemoveFieldsQuery.byId(tableName, nameParams, docId),
Parameters.addFields( Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")), listOf(Field.equal(Configuration.idField, docId, ":id")),
translatePath(nameParams) translatePath(nameParams)
@ -75,7 +75,7 @@ object RemoveFields {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
val nameParams = Parameters.fieldNames(toRemove) val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery( conn.customNonQuery(
RemoveFields.byFields(tableName, nameParams, named, howMatched), RemoveFieldsQuery.byFields(tableName, nameParams, named, howMatched),
Parameters.addFields(named, translatePath(nameParams)) Parameters.addFields(named, translatePath(nameParams))
) )
} }
@ -117,7 +117,7 @@ object RemoveFields {
) { ) {
val nameParams = Parameters.fieldNames(toRemove) val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery( conn.customNonQuery(
RemoveFields.byContains(tableName, nameParams), RemoveFieldsQuery.byContains(tableName, nameParams),
listOf(Parameters.json(":criteria", criteria), *nameParams.toTypedArray()) listOf(Parameters.json(":criteria", criteria), *nameParams.toTypedArray())
) )
} }
@ -149,7 +149,7 @@ object RemoveFields {
fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>, conn: Connection) { fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>, conn: Connection) {
val nameParams = Parameters.fieldNames(toRemove) val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery( conn.customNonQuery(
RemoveFields.byJsonPath(tableName, nameParams), RemoveFieldsQuery.byJsonPath(tableName, nameParams),
listOf(Parameter(":path", ParameterType.STRING, path), *nameParams.toTypedArray()) listOf(Parameter(":path", ParameterType.STRING, path), *nameParams.toTypedArray())
) )
} }

View File

@ -1,13 +1,15 @@
package solutions.bitbadger.documents.query package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.query.byFields as byFieldsBase; import kotlin.jvm.Throws
import solutions.bitbadger.documents.query.byFields as byFieldsBase
/** /**
* Functions to count documents * Functions to count documents
*/ */
object Count { object CountQuery {
/** /**
* Query to count all documents in a table * Query to count all documents in a table
@ -15,6 +17,7 @@ object Count {
* @param tableName The table in which to count documents (may include schema) * @param tableName The table in which to count documents (may include schema)
* @return A query to count documents * @return A query to count documents
*/ */
@JvmStatic
fun all(tableName: String) = fun all(tableName: String) =
"SELECT COUNT(*) AS it FROM $tableName" "SELECT COUNT(*) AS it FROM $tableName"
@ -26,6 +29,8 @@ object Count {
* @param howMatched How fields should be compared (optional, defaults to ALL) * @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to count documents matching the given fields * @return A query to count documents matching the given fields
*/ */
@JvmStatic
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
byFieldsBase(all(tableName), fields, howMatched) byFieldsBase(all(tableName), fields, howMatched)
@ -34,7 +39,10 @@ object Count {
* *
* @param tableName The table in which to count documents (may include schema) * @param tableName The table in which to count documents (may include schema)
* @return A query to count documents via JSON containment * @return A query to count documents via JSON containment
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byContains(tableName: String) = fun byContains(tableName: String) =
statementWhere(all(tableName), Where.jsonContains()) statementWhere(all(tableName), Where.jsonContains())
@ -43,7 +51,10 @@ object Count {
* *
* @param tableName The table in which to count documents (may include schema) * @param tableName The table in which to count documents (may include schema)
* @return A query to count documents via a JSON path match * @return A query to count documents via a JSON path match
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byJsonPath(tableName: String) = fun byJsonPath(tableName: String) =
statementWhere(all(tableName), Where.jsonPathMatches()) statementWhere(all(tableName), Where.jsonPathMatches())
} }

View File

@ -1,11 +1,12 @@
package solutions.bitbadger.documents.query package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import kotlin.jvm.Throws
/** /**
* Functions to create queries to define tables and indexes * Functions to create queries to define tables and indexes
*/ */
object Definition { object DefinitionQuery {
/** /**
* SQL statement to create a document table * SQL statement to create a document table
@ -14,6 +15,7 @@ object Definition {
* @param dataType The type of data for the column (`JSON`, `JSONB`, etc.) * @param dataType The type of data for the column (`JSON`, `JSONB`, etc.)
* @return A query to create a document table * @return A query to create a document table
*/ */
@JvmStatic
fun ensureTableFor(tableName: String, dataType: String) = fun ensureTableFor(tableName: String, dataType: String) =
"CREATE TABLE IF NOT EXISTS $tableName (data $dataType NOT NULL)" "CREATE TABLE IF NOT EXISTS $tableName (data $dataType NOT NULL)"
@ -23,7 +25,11 @@ object Definition {
* @param tableName The name of the table to create (may include schema) * @param tableName The name of the table to create (may include schema)
* @param dialect The dialect to generate (optional, used in place of current) * @param dialect The dialect to generate (optional, used in place of current)
* @return A query to create a document table * @return A query to create a document table
* @throws DocumentException If the dialect is neither provided nor configured
*/ */
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun ensureTable(tableName: String, dialect: Dialect? = null) = fun ensureTable(tableName: String, dialect: Dialect? = null) =
when (dialect ?: Configuration.dialect("create table creation query")) { when (dialect ?: Configuration.dialect("create table creation query")) {
Dialect.POSTGRESQL -> ensureTableFor(tableName, "JSONB") Dialect.POSTGRESQL -> ensureTableFor(tableName, "JSONB")
@ -47,7 +53,11 @@ object Definition {
* @param fields One or more fields to include in the index * @param fields One or more fields to include in the index
* @param dialect The SQL dialect to use when creating this index (optional, used in place of current) * @param dialect The SQL dialect to use when creating this index (optional, used in place of current)
* @return A query to create the field index * @return A query to create the field index
* @throws DocumentException If the dialect is neither provided nor configured
*/ */
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun ensureIndexOn( fun ensureIndexOn(
tableName: String, tableName: String,
indexName: String, indexName: String,
@ -70,7 +80,11 @@ object Definition {
* @param tableName The table on which a key index should be created (may include schema) * @param tableName The table on which a key index should be created (may include schema)
* @param dialect The SQL dialect to use when creating this index (optional, used in place of current) * @param dialect The SQL dialect to use when creating this index (optional, used in place of current)
* @return A query to create the key index * @return A query to create the key index
* @throws DocumentException If the dialect is neither provided nor configured
*/ */
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun ensureKey(tableName: String, dialect: Dialect? = null) = fun ensureKey(tableName: String, dialect: Dialect? = null) =
ensureIndexOn(tableName, "key", listOf(Configuration.idField), dialect).replace("INDEX", "UNIQUE INDEX") ensureIndexOn(tableName, "key", listOf(Configuration.idField), dialect).replace("INDEX", "UNIQUE INDEX")
@ -82,6 +96,8 @@ object Definition {
* @return The SQL statement to create an index on JSON documents in the specified table * @return The SQL statement to create an index on JSON documents in the specified table
* @throws DocumentException If the database mode is not PostgreSQL * @throws DocumentException If the database mode is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun ensureDocumentIndexOn(tableName: String, indexType: DocumentIndex): String { fun ensureDocumentIndexOn(tableName: String, indexType: DocumentIndex): String {
if (Configuration.dialect("create document index query") != Dialect.POSTGRESQL) { if (Configuration.dialect("create document index query") != Dialect.POSTGRESQL) {
throw DocumentException("'Document indexes are only supported on PostgreSQL") throw DocumentException("'Document indexes are only supported on PostgreSQL")

View File

@ -1,14 +1,16 @@
package solutions.bitbadger.documents.query package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch import solutions.bitbadger.documents.FieldMatch
import kotlin.jvm.Throws
import solutions.bitbadger.documents.query.byFields as byFieldsBase import solutions.bitbadger.documents.query.byFields as byFieldsBase
import solutions.bitbadger.documents.query.byId as byIdBase import solutions.bitbadger.documents.query.byId as byIdBase
/** /**
* Functions to delete documents * Functions to delete documents
*/ */
object Delete { object DeleteQuery {
/** /**
* Query to delete documents from a table * Query to delete documents from a table
@ -26,6 +28,8 @@ object Delete {
* @param docId The ID of the document (optional, used for type checking) * @param docId The ID of the document (optional, used for type checking)
* @return A query to delete a document by its ID * @return A query to delete a document by its ID
*/ */
@JvmStatic
@JvmOverloads
fun <TKey> byId(tableName: String, docId: TKey? = null) = fun <TKey> byId(tableName: String, docId: TKey? = null) =
byIdBase(delete(tableName), docId) byIdBase(delete(tableName), docId)
@ -37,6 +41,8 @@ object Delete {
* @param howMatched How fields should be compared (optional, defaults to ALL) * @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to delete documents matching for the given fields * @return A query to delete documents matching for the given fields
*/ */
@JvmStatic
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
byFieldsBase(delete(tableName), fields, howMatched) byFieldsBase(delete(tableName), fields, howMatched)
@ -45,7 +51,10 @@ object Delete {
* *
* @param tableName The table from which documents should be deleted (may include schema) * @param tableName The table from which documents should be deleted (may include schema)
* @return A query to delete documents via JSON containment * @return A query to delete documents via JSON containment
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byContains(tableName: String) = fun byContains(tableName: String) =
statementWhere(delete(tableName), Where.jsonContains()) statementWhere(delete(tableName), Where.jsonContains())
@ -54,7 +63,10 @@ object Delete {
* *
* @param tableName The table from which documents should be deleted (may include schema) * @param tableName The table from which documents should be deleted (may include schema)
* @return A query to delete documents via a JSON path match * @return A query to delete documents via a JSON path match
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byJsonPath(tableName: String) = fun byJsonPath(tableName: String) =
statementWhere(delete(tableName), Where.jsonPathMatches()) statementWhere(delete(tableName), Where.jsonPathMatches())
} }

View File

@ -3,18 +3,24 @@ package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.AutoId import solutions.bitbadger.documents.AutoId
import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import kotlin.jvm.Throws
/** /**
* Functions for document-level operations * Functions for document-level operations
*/ */
object Document { object DocumentQuery {
/** /**
* Query to insert a document * Query to insert a document
* *
* @param tableName The table into which to insert (may include schema) * @param tableName The table into which to insert (may include schema)
* @return A query to insert a document * @return A query to insert a document
* @throws DocumentException If the dialect is not configured
*/ */
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun insert(tableName: String, autoId: AutoId? = null): String { fun insert(tableName: String, autoId: AutoId? = null): String {
val id = Configuration.idField val id = Configuration.idField
val values = when (Configuration.dialect("create INSERT statement")) { val values = when (Configuration.dialect("create INSERT statement")) {
@ -42,7 +48,10 @@ object Document {
* *
* @param tableName The table into which to save (may include schema) * @param tableName The table into which to save (may include schema)
* @return A query to save a document * @return A query to save a document
* @throws DocumentException If the dialect is not configured
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun save(tableName: String) = fun save(tableName: String) =
insert(tableName, AutoId.DISABLED) + insert(tableName, AutoId.DISABLED) +
" ON CONFLICT ((data->>'${Configuration.idField}')) DO UPDATE SET data = EXCLUDED.data" " ON CONFLICT ((data->>'${Configuration.idField}')) DO UPDATE SET data = EXCLUDED.data"
@ -53,6 +62,7 @@ object Document {
* @param tableName The table in which documents should be replaced (may include schema) * @param tableName The table in which documents should be replaced (may include schema)
* @return A query to update documents * @return A query to update documents
*/ */
@JvmStatic
fun update(tableName: String) = fun update(tableName: String) =
"UPDATE $tableName SET data = :data" "UPDATE $tableName SET data = :data"
} }

View File

@ -1,12 +1,14 @@
package solutions.bitbadger.documents.query package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch import solutions.bitbadger.documents.FieldMatch
import kotlin.jvm.Throws
/** /**
* Functions to check for document existence * Functions to check for document existence
*/ */
object Exists { object ExistsQuery {
/** /**
* Query to check for document existence in a table * Query to check for document existence in a table
@ -25,6 +27,8 @@ object Exists {
* @param docId The ID of the document (optional, used for type checking) * @param docId The ID of the document (optional, used for type checking)
* @return A query to determine document existence by ID * @return A query to determine document existence by ID
*/ */
@JvmStatic
@JvmOverloads
fun <TKey> byId(tableName: String, docId: TKey? = null) = fun <TKey> byId(tableName: String, docId: TKey? = null) =
exists(tableName, Where.byId(docId = docId)) exists(tableName, Where.byId(docId = docId))
@ -36,6 +40,8 @@ object Exists {
* @param howMatched How fields should be compared (optional, defaults to ALL) * @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to determine document existence for the given fields * @return A query to determine document existence for the given fields
*/ */
@JvmStatic
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
exists(tableName, Where.byFields(fields, howMatched)) exists(tableName, Where.byFields(fields, howMatched))
@ -44,7 +50,10 @@ object Exists {
* *
* @param tableName The table in which existence should be checked (may include schema) * @param tableName The table in which existence should be checked (may include schema)
* @return A query to determine document existence via JSON containment * @return A query to determine document existence via JSON containment
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byContains(tableName: String) = fun byContains(tableName: String) =
exists(tableName, Where.jsonContains()) exists(tableName, Where.jsonContains())
@ -53,7 +62,10 @@ object Exists {
* *
* @param tableName The table in which existence should be checked (may include schema) * @param tableName The table in which existence should be checked (may include schema)
* @return A query to determine document existence via a JSON path match * @return A query to determine document existence via a JSON path match
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byJsonPath(tableName: String) = fun byJsonPath(tableName: String) =
exists(tableName, Where.jsonPathMatches()) exists(tableName, Where.jsonPathMatches())
} }

View File

@ -1,14 +1,16 @@
package solutions.bitbadger.documents.query package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch import solutions.bitbadger.documents.FieldMatch
import kotlin.jvm.Throws
import solutions.bitbadger.documents.query.byId as byIdBase import solutions.bitbadger.documents.query.byId as byIdBase
import solutions.bitbadger.documents.query.byFields as byFieldsBase import solutions.bitbadger.documents.query.byFields as byFieldsBase
/** /**
* Functions to retrieve documents * Functions to retrieve documents
*/ */
object Find { object FindQuery {
/** /**
* Query to retrieve all documents from a table * Query to retrieve all documents from a table
@ -16,6 +18,7 @@ object Find {
* @param tableName The table from which documents should be retrieved (may include schema) * @param tableName The table from which documents should be retrieved (may include schema)
* @return A query to retrieve documents * @return A query to retrieve documents
*/ */
@JvmStatic
fun all(tableName: String) = fun all(tableName: String) =
"SELECT data FROM $tableName" "SELECT data FROM $tableName"
@ -26,6 +29,8 @@ object Find {
* @param docId The ID of the document (optional, used for type checking) * @param docId The ID of the document (optional, used for type checking)
* @return A query to retrieve a document by its ID * @return A query to retrieve a document by its ID
*/ */
@JvmStatic
@JvmOverloads
fun <TKey> byId(tableName: String, docId: TKey? = null) = fun <TKey> byId(tableName: String, docId: TKey? = null) =
byIdBase(all(tableName), docId) byIdBase(all(tableName), docId)
@ -37,6 +42,8 @@ object Find {
* @param howMatched How fields should be compared (optional, defaults to ALL) * @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to retrieve documents matching the given fields * @return A query to retrieve documents matching the given fields
*/ */
@JvmStatic
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
byFieldsBase(all(tableName), fields, howMatched) byFieldsBase(all(tableName), fields, howMatched)
@ -45,7 +52,10 @@ object Find {
* *
* @param tableName The table from which documents should be retrieved (may include schema) * @param tableName The table from which documents should be retrieved (may include schema)
* @return A query to retrieve documents via JSON containment * @return A query to retrieve documents via JSON containment
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byContains(tableName: String) = fun byContains(tableName: String) =
statementWhere(all(tableName), Where.jsonContains()) statementWhere(all(tableName), Where.jsonContains())
@ -54,7 +64,10 @@ object Find {
* *
* @param tableName The table from which documents should be retrieved (may include schema) * @param tableName The table from which documents should be retrieved (may include schema)
* @return A query to retrieve documents via a JSON path match * @return A query to retrieve documents via a JSON path match
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byJsonPath(tableName: String) = fun byJsonPath(tableName: String) =
statementWhere(all(tableName), Where.jsonPathMatches()) statementWhere(all(tableName), Where.jsonPathMatches())
} }

View File

@ -1,16 +1,14 @@
package solutions.bitbadger.documents.query package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.Dialect import kotlin.jvm.Throws
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.query.byFields as byFieldsBase import solutions.bitbadger.documents.query.byFields as byFieldsBase
import solutions.bitbadger.documents.query.byId as byIdBase import solutions.bitbadger.documents.query.byId as byIdBase
/** /**
* Functions to create queries to patch (partially update) JSON documents * Functions to create queries to patch (partially update) JSON documents
*/ */
object Patch { object PatchQuery {
/** /**
* Create an `UPDATE` statement to patch documents * Create an `UPDATE` statement to patch documents
@ -30,7 +28,11 @@ object Patch {
* @param tableName The name of the table where the document is stored * @param tableName The name of the table where the document is stored
* @param docId The ID of the document to be updated (optional, used for type checking) * @param docId The ID of the document to be updated (optional, used for type checking)
* @return A query to patch a JSON document by its ID * @return A query to patch a JSON document by its ID
* @throws DocumentException If the dialect is not configured
*/ */
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun <TKey> byId(tableName: String, docId: TKey? = null) = fun <TKey> byId(tableName: String, docId: TKey? = null) =
byIdBase(patch(tableName), docId) byIdBase(patch(tableName), docId)
@ -41,7 +43,11 @@ object Patch {
* @param fields The field criteria * @param fields The field criteria
* @param howMatched How the fields should be matched (optional, defaults to `ALL`) * @param howMatched How the fields should be matched (optional, defaults to `ALL`)
* @return A query to patch JSON documents by field match criteria * @return A query to patch JSON documents by field match criteria
* @throws DocumentException If the dialect is not configured
*/ */
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
byFieldsBase(patch(tableName), fields, howMatched) byFieldsBase(patch(tableName), fields, howMatched)
@ -50,7 +56,10 @@ object Patch {
* *
* @param tableName The name of the table where the document is stored * @param tableName The name of the table where the document is stored
* @return A query to patch JSON documents by JSON containment * @return A query to patch JSON documents by JSON containment
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byContains(tableName: String) = fun byContains(tableName: String) =
statementWhere(patch(tableName), Where.jsonContains()) statementWhere(patch(tableName), Where.jsonContains())
@ -59,7 +68,10 @@ object Patch {
* *
* @param tableName The name of the table where the document is stored * @param tableName The name of the table where the document is stored
* @return A query to patch JSON documents by JSON path match * @return A query to patch JSON documents by JSON path match
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byJsonPath(tableName: String) = fun byJsonPath(tableName: String) =
statementWhere(patch(tableName), Where.jsonPathMatches()) statementWhere(patch(tableName), Where.jsonPathMatches())
} }

View File

@ -1,3 +1,4 @@
@file:JvmName("QueryUtils")
package solutions.bitbadger.documents.query package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.Configuration
@ -35,6 +36,7 @@ fun <TKey> byId(statement: String, docId: TKey) =
* @param howMatched Whether to match any or all of the field conditions (optional; default ALL) * @param howMatched Whether to match any or all of the field conditions (optional; default ALL)
* @return A query addressing documents by field matching conditions * @return A query addressing documents by field matching conditions
*/ */
@JvmOverloads
fun byFields(statement: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(statement: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
statementWhere(statement, Where.byFields(fields, howMatched)) statementWhere(statement, Where.byFields(fields, howMatched))
@ -45,6 +47,7 @@ fun byFields(statement: String, fields: Collection<Field<*>>, howMatched: FieldM
* @param dialect The SQL dialect for the generated clause * @param dialect The SQL dialect for the generated clause
* @return An `ORDER BY` clause for the given fields * @return An `ORDER BY` clause for the given fields
*/ */
@JvmOverloads
fun orderBy(fields: Collection<Field<*>>, dialect: Dialect? = null): String { fun orderBy(fields: Collection<Field<*>>, dialect: Dialect? = null): String {
val mode = dialect ?: Configuration.dialect("generate ORDER BY clause") val mode = dialect ?: Configuration.dialect("generate ORDER BY clause")
if (fields.isEmpty()) return "" if (fields.isEmpty()) return ""

View File

@ -1,13 +1,14 @@
package solutions.bitbadger.documents.query package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import kotlin.jvm.Throws
import solutions.bitbadger.documents.query.byFields as byFieldsBase import solutions.bitbadger.documents.query.byFields as byFieldsBase
import solutions.bitbadger.documents.query.byId as byIdBase import solutions.bitbadger.documents.query.byId as byIdBase
/** /**
* Functions to create queries to remove fields from documents * Functions to create queries to remove fields from documents
*/ */
object RemoveFields { object RemoveFieldsQuery {
/** /**
* Create a query to remove fields based on the given parameters * Create a query to remove fields based on the given parameters
@ -31,7 +32,11 @@ object RemoveFields {
* @param toRemove The parameters for the fields to be removed * @param toRemove The parameters for the fields to be removed
* @param docId The ID of the document to be updated (optional, used for type checking) * @param docId The ID of the document to be updated (optional, used for type checking)
* @return A query to patch a JSON document by its ID * @return A query to patch a JSON document by its ID
* @throws DocumentException If the dialect is not configured
*/ */
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun <TKey> byId(tableName: String, toRemove: Collection<Parameter<*>>, docId: TKey? = null) = fun <TKey> byId(tableName: String, toRemove: Collection<Parameter<*>>, docId: TKey? = null) =
byIdBase(removeFields(tableName, toRemove), docId) byIdBase(removeFields(tableName, toRemove), docId)
@ -43,7 +48,11 @@ object RemoveFields {
* @param fields The field criteria * @param fields The field criteria
* @param howMatched How the fields should be matched (optional, defaults to `ALL`) * @param howMatched How the fields should be matched (optional, defaults to `ALL`)
* @return A query to patch JSON documents by field match criteria * @return A query to patch JSON documents by field match criteria
* @throws DocumentException If the dialect is not configured
*/ */
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun byFields( fun byFields(
tableName: String, tableName: String,
toRemove: Collection<Parameter<*>>, toRemove: Collection<Parameter<*>>,
@ -58,7 +67,10 @@ object RemoveFields {
* @param tableName The name of the table where the document is stored * @param tableName The name of the table where the document is stored
* @param toRemove The parameters for the fields to be removed * @param toRemove The parameters for the fields to be removed
* @return A query to patch JSON documents by JSON containment * @return A query to patch JSON documents by JSON containment
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byContains(tableName: String, toRemove: Collection<Parameter<*>>) = fun byContains(tableName: String, toRemove: Collection<Parameter<*>>) =
statementWhere(removeFields(tableName, toRemove), Where.jsonContains()) statementWhere(removeFields(tableName, toRemove), Where.jsonContains())
@ -68,7 +80,10 @@ object RemoveFields {
* @param tableName The name of the table where the document is stored * @param tableName The name of the table where the document is stored
* @param toRemove The parameters for the fields to be removed * @param toRemove The parameters for the fields to be removed
* @return A query to patch JSON documents by JSON path match * @return A query to patch JSON documents by JSON path match
* @throws DocumentException If the database dialect is not PostgreSQL
*/ */
@Throws(DocumentException::class)
@JvmStatic
fun byJsonPath(tableName: String, toRemove: Collection<Parameter<*>>) = fun byJsonPath(tableName: String, toRemove: Collection<Parameter<*>>) =
statementWhere(removeFields(tableName, toRemove), Where.jsonPathMatches()) statementWhere(removeFields(tableName, toRemove), Where.jsonPathMatches())
} }

View File

@ -5,6 +5,7 @@ import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch import solutions.bitbadger.documents.FieldMatch
import kotlin.jvm.Throws
/** /**
* Functions to create `WHERE` clause fragments * Functions to create `WHERE` clause fragments
@ -18,6 +19,8 @@ object Where {
* @param howMatched How the fields should be matched (optional, defaults to `ALL`) * @param howMatched How the fields should be matched (optional, defaults to `ALL`)
* @return A `WHERE` clause fragment to match the given fields * @return A `WHERE` clause fragment to match the given fields
*/ */
@JvmStatic
@JvmOverloads
fun byFields(fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
fields.joinToString(" ${(howMatched ?: FieldMatch.ALL).sql} ") { it.toWhere() } fields.joinToString(" ${(howMatched ?: FieldMatch.ALL).sql} ") { it.toWhere() }
@ -27,6 +30,8 @@ object Where {
* @param parameterName The parameter name to use for the ID placeholder (optional, defaults to ":id") * @param parameterName The parameter name to use for the ID placeholder (optional, defaults to ":id")
* @param docId The ID value (optional; used for type determinations, string assumed if not provided) * @param docId The ID value (optional; used for type determinations, string assumed if not provided)
*/ */
@JvmStatic
@JvmOverloads
fun <TKey> byId(parameterName: String = ":id", docId: TKey? = null) = fun <TKey> byId(parameterName: String = ":id", docId: TKey? = null) =
byFields(listOf(Field.equal(Configuration.idField, docId ?: "", parameterName))) byFields(listOf(Field.equal(Configuration.idField, docId ?: "", parameterName)))
@ -37,6 +42,9 @@ object Where {
* @return A `WHERE` clause fragment to implement a JSON containment criterion * @return A `WHERE` clause fragment to implement a JSON containment criterion
* @throws DocumentException If called against a SQLite database * @throws DocumentException If called against a SQLite database
*/ */
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun jsonContains(parameterName: String = ":criteria") = fun jsonContains(parameterName: String = ":criteria") =
when (Configuration.dialect("create containment WHERE clause")) { when (Configuration.dialect("create containment WHERE clause")) {
Dialect.POSTGRESQL -> "data @> $parameterName" Dialect.POSTGRESQL -> "data @> $parameterName"
@ -50,6 +58,9 @@ object Where {
* @return A `WHERE` clause fragment to implement a JSON path match criterion * @return A `WHERE` clause fragment to implement a JSON path match criterion
* @throws DocumentException If called against a SQLite database * @throws DocumentException If called against a SQLite database
*/ */
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun jsonPathMatches(parameterName: String = ":path") = fun jsonPathMatches(parameterName: String = ":path") =
when (Configuration.dialect("create JSON path match WHERE clause")) { when (Configuration.dialect("create JSON path match WHERE clause")) {
Dialect.POSTGRESQL -> "jsonb_path_exists(data, $parameterName::jsonpath)" Dialect.POSTGRESQL -> "jsonb_path_exists(data, $parameterName::jsonpath)"

View File

@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.*;
/** /**
* Unit tests for the `AutoId` enum * Unit tests for the `AutoId` enum
*/ */
@DisplayName("Java | Common | AutoId") @DisplayName("JVM | Java | AutoId")
final public class AutoIdTest { final public class AutoIdTest {
@Test @Test

View File

@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
/** /**
* Unit tests for the `DocumentIndex` enum * Unit tests for the `DocumentIndex` enum
*/ */
@DisplayName("Java | Common | DocumentIndex") @DisplayName("JVM | Java | DocumentIndex")
final public class DocumentIndexTest { final public class DocumentIndexTest {
@Test @Test

View File

@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
/** /**
* Unit tests for the `FieldMatch` enum * Unit tests for the `FieldMatch` enum
*/ */
@DisplayName("Java | Common | FieldMatch") @DisplayName("JVM | Java | FieldMatch")
final public class FieldMatchTest { final public class FieldMatchTest {
@Test @Test

View File

@ -14,7 +14,7 @@ import static org.junit.jupiter.api.Assertions.*;
/** /**
* Unit tests for the `Field` class * Unit tests for the `Field` class
*/ */
@DisplayName("Java | Common | Field") @DisplayName("JVM | Java | Field")
final public class FieldTest { final public class FieldTest {
/** /**

View File

@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
/** /**
* Unit tests for the `Op` enum * Unit tests for the `Op` enum
*/ */
@DisplayName("Java | Common | Op") @DisplayName("JVM | Java | Op")
final public class OpTest { final public class OpTest {
@Test @Test

View File

@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
/** /**
* Unit tests for the `ParameterName` class * Unit tests for the `ParameterName` class
*/ */
@DisplayName("Java | Common | ParameterName") @DisplayName("JVM | Java | ParameterName")
final public class ParameterNameTest { final public class ParameterNameTest {
@Test @Test

View File

@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.*;
/** /**
* Unit tests for the `Parameter` class * Unit tests for the `Parameter` class
*/ */
@DisplayName("Java | Common | Parameter") @DisplayName("JVM | Java | Parameter")
final public class ParameterTest { final public class ParameterTest {
@Test @Test

View File

@ -13,7 +13,7 @@ import static org.junit.jupiter.api.Assertions.*;
/** /**
* Unit tests for the `Parameters` object * Unit tests for the `Parameters` object
*/ */
@DisplayName("Java | Java | Parameters") @DisplayName("JVM | Java | Parameters")
final public class ParametersTest { final public class ParametersTest {
/** /**

View File

@ -1,14 +1,15 @@
package solutions.bitbadger.documents.java.jvm.integration.common; package solutions.bitbadger.documents.java.jvm.integration.common;
import solutions.bitbadger.documents.DocumentException;
import solutions.bitbadger.documents.Field; import solutions.bitbadger.documents.Field;
import solutions.bitbadger.documents.jvm.Count;
import solutions.bitbadger.documents.support.ThrowawayDatabase;
import solutions.bitbadger.documents.java.support.JsonDocument; import solutions.bitbadger.documents.java.support.JsonDocument;
import solutions.bitbadger.documents.support.ThrowawayDatabase;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static solutions.bitbadger.documents.extensions.ConnExt.*;
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE; import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
/** /**
@ -18,42 +19,42 @@ final public class CountFunctions {
public static void all(ThrowawayDatabase db) { public static void all(ThrowawayDatabase db) {
JsonDocument.load(db); JsonDocument.load(db);
assertEquals(5L, Count.all(TEST_TABLE, db.getConn()), "There should have been 5 documents in the table"); assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should have been 5 documents in the table");
} }
public static void byFieldsNumeric(ThrowawayDatabase db) { public static void byFieldsNumeric(ThrowawayDatabase db) {
JsonDocument.load(db); JsonDocument.load(db);
assertEquals(3L, Count.byFields(TEST_TABLE, List.of(Field.between("numValue", 10, 20)), db.getConn()), assertEquals(3L, countByFields(db.getConn(), TEST_TABLE, List.of(Field.between("numValue", 10, 20))),
"There should have been 3 matching documents"); "There should have been 3 matching documents");
} }
public static void byFieldsAlpha(ThrowawayDatabase db) { public static void byFieldsAlpha(ThrowawayDatabase db) {
JsonDocument.load(db); JsonDocument.load(db);
assertEquals(1L, Count.byFields(TEST_TABLE, List.of(Field.between("value", "aardvark", "apple")), db.getConn()), assertEquals(1L, countByFields(db.getConn(), TEST_TABLE, List.of(Field.between("value", "aardvark", "apple"))),
"There should have been 1 matching document"); "There should have been 1 matching document");
} }
public static void byContainsMatch(ThrowawayDatabase db) { public static void byContainsMatch(ThrowawayDatabase db) throws DocumentException {
JsonDocument.load(db); JsonDocument.load(db);
assertEquals(2L, Count.byContains(TEST_TABLE, Map.of("value", "purple"), db.getConn()), assertEquals(2L, countByContains(db.getConn(), TEST_TABLE, Map.of("value", "purple")),
"There should have been 2 matching documents"); "There should have been 2 matching documents");
} }
public static void byContainsNoMatch(ThrowawayDatabase db) { public static void byContainsNoMatch(ThrowawayDatabase db) throws DocumentException {
JsonDocument.load(db); JsonDocument.load(db);
assertEquals(0L, Count.byContains(TEST_TABLE, Map.of("value", "magenta"), db.getConn()), assertEquals(0L, countByContains(db.getConn(), TEST_TABLE, Map.of("value", "magenta")),
"There should have been no matching documents"); "There should have been no matching documents");
} }
public static void byJsonPathMatch(ThrowawayDatabase db) { public static void byJsonPathMatch(ThrowawayDatabase db) throws DocumentException {
JsonDocument.load(db); JsonDocument.load(db);
assertEquals(2L, Count.byJsonPath(TEST_TABLE, "$.numValue ? (@ < 5)", db.getConn()), assertEquals(2L, countByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ < 5)"),
"There should have been 2 matching documents"); "There should have been 2 matching documents");
} }
public static void byJsonPathNoMatch(ThrowawayDatabase db) { public static void byJsonPathNoMatch(ThrowawayDatabase db) throws DocumentException {
JsonDocument.load(db); JsonDocument.load(db);
assertEquals(0L, Count.byJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)", db.getConn()), assertEquals(0L, countByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ > 100)"),
"There should have been no matching documents"); "There should have been no matching documents");
} }

View File

@ -2,13 +2,14 @@ package solutions.bitbadger.documents.java.jvm.integration.postgresql;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.DocumentException;
import solutions.bitbadger.documents.java.jvm.integration.common.CountFunctions; import solutions.bitbadger.documents.java.jvm.integration.common.CountFunctions;
import solutions.bitbadger.documents.jvm.integration.postgresql.PgDB; import solutions.bitbadger.documents.jvm.integration.postgresql.PgDB;
/** /**
* PostgreSQL integration tests for the `Count` object / `count*` connection extension functions * PostgreSQL integration tests for the `Count` object / `count*` connection extension functions
*/ */
@DisplayName("Java | Java | PostgreSQL: Count") @DisplayName("JVM | Java | PostgreSQL: Count")
public class CountIT { public class CountIT {
@Test @Test
@ -37,7 +38,7 @@ public class CountIT {
@Test @Test
@DisplayName("byContains counts documents when matches are found") @DisplayName("byContains counts documents when matches are found")
public void byContainsMatch() { public void byContainsMatch() throws DocumentException {
try (PgDB db = new PgDB()) { try (PgDB db = new PgDB()) {
CountFunctions.byContainsMatch(db); CountFunctions.byContainsMatch(db);
} }
@ -45,7 +46,7 @@ public class CountIT {
@Test @Test
@DisplayName("byContains counts documents when no matches are found") @DisplayName("byContains counts documents when no matches are found")
public void byContainsNoMatch() { public void byContainsNoMatch() throws DocumentException {
try (PgDB db = new PgDB()) { try (PgDB db = new PgDB()) {
CountFunctions.byContainsNoMatch(db); CountFunctions.byContainsNoMatch(db);
} }
@ -53,7 +54,7 @@ public class CountIT {
@Test @Test
@DisplayName("byJsonPath counts documents when matches are found") @DisplayName("byJsonPath counts documents when matches are found")
public void byJsonPathMatch() { public void byJsonPathMatch() throws DocumentException {
try (PgDB db = new PgDB()) { try (PgDB db = new PgDB()) {
CountFunctions.byJsonPathMatch(db); CountFunctions.byJsonPathMatch(db);
} }
@ -61,7 +62,7 @@ public class CountIT {
@Test @Test
@DisplayName("byJsonPath counts documents when no matches are found") @DisplayName("byJsonPath counts documents when no matches are found")
public void byJsonPathNoMatch() { public void byJsonPathNoMatch() throws DocumentException {
try (PgDB db = new PgDB()) { try (PgDB db = new PgDB()) {
CountFunctions.byJsonPathNoMatch(db); CountFunctions.byJsonPathNoMatch(db);
} }

View File

@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
/** /**
* SQLite integration tests for the `Count` object / `count*` connection extension functions * SQLite integration tests for the `Count` object / `count*` connection extension functions
*/ */
@DisplayName("Java | Java | SQLite: Count") @DisplayName("JVM | Java | SQLite: Count")
public class CountIT { public class CountIT {
@Test @Test

View File

@ -1,6 +1,5 @@
package solutions.bitbadger.documents.java.support; package solutions.bitbadger.documents.java.support;
import kotlinx.serialization.Serializable;
import solutions.bitbadger.documents.jvm.Document; import solutions.bitbadger.documents.jvm.Document;
import solutions.bitbadger.documents.support.ThrowawayDatabase; import solutions.bitbadger.documents.support.ThrowawayDatabase;
@ -8,7 +7,6 @@ import java.util.List;
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE; import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
@Serializable
public class JsonDocument { public class JsonDocument {
private String id; private String id;

View File

@ -3,6 +3,7 @@ package solutions.bitbadger.documents
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.support.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
@ -11,7 +12,7 @@ import kotlin.test.assertTrue
/** /**
* Unit tests for the `AutoId` enum * Unit tests for the `AutoId` enum
*/ */
@DisplayName("Kotlin | Common | AutoId") @DisplayName("JVM | Kotlin | AutoId")
class AutoIdTest { class AutoIdTest {
@Test @Test
@ -157,9 +158,3 @@ class AutoIdTest {
assertThrows<DocumentException> { AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id") } assertThrows<DocumentException> { AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id") }
} }
} }
data class ByteIdClass(val id: Byte)
data class ShortIdClass(val id: Short)
data class IntIdClass(val id: Int)
data class LongIdClass(val id: Long)
data class StringIdClass(val id: String)

View File

@ -9,7 +9,7 @@ import kotlin.test.assertTrue
/** /**
* Unit tests for the `ComparisonBetween` class * Unit tests for the `ComparisonBetween` class
*/ */
@DisplayName("ComparisonBetween") @DisplayName("JVM | Kotlin | ComparisonBetween")
class ComparisonBetweenTest { class ComparisonBetweenTest {
@Test @Test
@ -50,7 +50,7 @@ class ComparisonBetweenTest {
/** /**
* Unit tests for the `ComparisonIn` class * Unit tests for the `ComparisonIn` class
*/ */
@DisplayName("ComparisonIn") @DisplayName("JVM | Kotlin | ComparisonIn")
class ComparisonInTest { class ComparisonInTest {
@Test @Test
@ -92,7 +92,7 @@ class ComparisonInTest {
/** /**
* Unit tests for the `ComparisonInArray` class * Unit tests for the `ComparisonInArray` class
*/ */
@DisplayName("ComparisonInArray") @DisplayName("JVM | Kotlin | ComparisonInArray")
class ComparisonInArrayTest { class ComparisonInArrayTest {
@Test @Test
@ -138,7 +138,7 @@ class ComparisonInArrayTest {
/** /**
* Unit tests for the `ComparisonSingle` class * Unit tests for the `ComparisonSingle` class
*/ */
@DisplayName("ComparisonSingle") @DisplayName("JVM | Kotlin | ComparisonSingle")
class ComparisonSingleTest { class ComparisonSingleTest {
@Test @Test

View File

@ -8,7 +8,7 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `Configuration` object * Unit tests for the `Configuration` object
*/ */
@DisplayName("Kotlin | Common | Configuration") @DisplayName("JVM | Kotlin | Configuration")
class ConfigurationTest { class ConfigurationTest {
@Test @Test

View File

@ -9,7 +9,7 @@ import kotlin.test.assertTrue
/** /**
* Unit tests for the `Dialect` enum * Unit tests for the `Dialect` enum
*/ */
@DisplayName("Kotlin | Common | Dialect") @DisplayName("JVM | Kotlin | Dialect")
class DialectTest { class DialectTest {
@Test @Test

View File

@ -7,7 +7,7 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `DocumentIndex` enum * Unit tests for the `DocumentIndex` enum
*/ */
@DisplayName("Kotlin | Common | DocumentIndex") @DisplayName("JVM | Kotlin | DocumentIndex")
class DocumentIndexTest { class DocumentIndexTest {
@Test @Test

View File

@ -7,7 +7,7 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `FieldMatch` enum * Unit tests for the `FieldMatch` enum
*/ */
@DisplayName("Kotlin | Common | FieldMatch") @DisplayName("JVM | Kotlin | FieldMatch")
class FieldMatchTest { class FieldMatchTest {
@Test @Test

View File

@ -11,7 +11,7 @@ import kotlin.test.assertNull
/** /**
* Unit tests for the `Field` class * Unit tests for the `Field` class
*/ */
@DisplayName("Kotlin | Common | Field") @DisplayName("JVM | Kotlin | Field")
class FieldTest { class FieldTest {
/** /**

View File

@ -7,7 +7,7 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `Op` enum * Unit tests for the `Op` enum
*/ */
@DisplayName("Kotlin | Common | Op") @DisplayName("JVM | Kotlin | Op")
class OpTest { class OpTest {
@Test @Test

View File

@ -7,7 +7,7 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `ParameterName` class * Unit tests for the `ParameterName` class
*/ */
@DisplayName("Kotlin | Common | ParameterName") @DisplayName("JVM | Kotlin | ParameterName")
class ParameterNameTest { class ParameterNameTest {
@Test @Test

View File

@ -9,7 +9,7 @@ import kotlin.test.assertNull
/** /**
* Unit tests for the `Parameter` class * Unit tests for the `Parameter` class
*/ */
@DisplayName("Kotlin | Common | Parameter") @DisplayName("JVM | Kotlin | Parameter")
class ParameterTest { class ParameterTest {
@Test @Test

View File

@ -12,7 +12,7 @@ import kotlin.test.assertSame
/** /**
* Unit tests for the `Parameters` object * Unit tests for the `Parameters` object
*/ */
@DisplayName("Kotlin | Java | Parameters") @DisplayName("JVM | Kotlin | Parameters")
class ParametersTest { class ParametersTest {
/** /**

View File

@ -3,9 +3,9 @@ package solutions.bitbadger.documents.jvm.integration.common
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.extensions.* import solutions.bitbadger.documents.extensions.*
import solutions.bitbadger.documents.jvm.Results import solutions.bitbadger.documents.jvm.Results
import solutions.bitbadger.documents.query.Count import solutions.bitbadger.documents.query.CountQuery
import solutions.bitbadger.documents.query.Delete import solutions.bitbadger.documents.query.DeleteQuery
import solutions.bitbadger.documents.query.Find import solutions.bitbadger.documents.query.FindQuery
import solutions.bitbadger.documents.support.* import solutions.bitbadger.documents.support.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
@ -19,26 +19,26 @@ object Custom {
fun listEmpty(db: ThrowawayDatabase) { fun listEmpty(db: ThrowawayDatabase) {
JsonDocument.load(db) JsonDocument.load(db)
db.conn.deleteByFields(TEST_TABLE, listOf(Field.exists(Configuration.idField))) db.conn.deleteByFields(TEST_TABLE, listOf(Field.exists(Configuration.idField)))
val result = db.conn.customList(Find.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData) val result = db.conn.customList(FindQuery.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData)
assertEquals(0, result.size, "There should have been no results") assertEquals(0, result.size, "There should have been no results")
} }
fun listAll(db: ThrowawayDatabase) { fun listAll(db: ThrowawayDatabase) {
JsonDocument.load(db) JsonDocument.load(db)
val result = db.conn.customList(Find.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData) val result = db.conn.customList(FindQuery.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData)
assertEquals(5, result.size, "There should have been 5 results") assertEquals(5, result.size, "There should have been 5 results")
} }
fun singleNone(db: ThrowawayDatabase) = fun singleNone(db: ThrowawayDatabase) =
assertNull( assertNull(
db.conn.customSingle(Find.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData), db.conn.customSingle(FindQuery.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData),
"There should not have been a document returned" "There should not have been a document returned"
) )
fun singleOne(db: ThrowawayDatabase) { fun singleOne(db: ThrowawayDatabase) {
JsonDocument.load(db) JsonDocument.load(db)
assertNotNull( assertNotNull(
db.conn.customSingle(Find.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData), db.conn.customSingle(FindQuery.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData),
"There should not have been a document returned" "There should not have been a document returned"
) )
} }
@ -46,12 +46,12 @@ object Custom {
fun nonQueryChanges(db: ThrowawayDatabase) { fun nonQueryChanges(db: ThrowawayDatabase) {
JsonDocument.load(db) JsonDocument.load(db)
assertEquals( assertEquals(
5L, db.conn.customScalar(Count.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount), 5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount),
"There should have been 5 documents in the table" "There should have been 5 documents in the table"
) )
db.conn.customNonQuery("DELETE FROM $TEST_TABLE") db.conn.customNonQuery("DELETE FROM $TEST_TABLE")
assertEquals( assertEquals(
0L, db.conn.customScalar(Count.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount), 0L, db.conn.customScalar(CountQuery.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount),
"There should have been no documents in the table" "There should have been no documents in the table"
) )
} }
@ -59,15 +59,15 @@ object Custom {
fun nonQueryNoChanges(db: ThrowawayDatabase) { fun nonQueryNoChanges(db: ThrowawayDatabase) {
JsonDocument.load(db) JsonDocument.load(db)
assertEquals( assertEquals(
5L, db.conn.customScalar(Count.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount), 5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount),
"There should have been 5 documents in the table" "There should have been 5 documents in the table"
) )
db.conn.customNonQuery( db.conn.customNonQuery(
Delete.byId(TEST_TABLE, "eighty-two"), DeleteQuery.byId(TEST_TABLE, "eighty-two"),
listOf(Parameter(":id", ParameterType.STRING, "eighty-two")) listOf(Parameter(":id", ParameterType.STRING, "eighty-two"))
) )
assertEquals( assertEquals(
5L, db.conn.customScalar(Count.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount), 5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), listOf(), Long::class.java, Results::toCount),
"There should still have been 5 documents in the table" "There should still have been 5 documents in the table"
) )
} }

View File

@ -7,7 +7,7 @@ import kotlin.test.Test
/** /**
* PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions * PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | PostgreSQL: Definition") @DisplayName("JVM | Kotlin | PostgreSQL: Definition")
class DefinitionIT { class DefinitionIT {
@Test @Test

View File

@ -7,7 +7,7 @@ import kotlin.test.Test
/** /**
* PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions * PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | PostgreSQL: Delete") @DisplayName("JVM | Kotlin | PostgreSQL: Delete")
class DeleteIT { class DeleteIT {
@Test @Test

View File

@ -7,7 +7,7 @@ import kotlin.test.Test
/** /**
* PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions * PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
*/ */
@DisplayName("Java | Kotlin | PostgreSQL: Document") @DisplayName("JVM | Kotlin | PostgreSQL: Document")
class DocumentIT { class DocumentIT {
@Test @Test

View File

@ -7,7 +7,7 @@ import kotlin.test.Test
/** /**
* PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions * PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | PostgreSQL: Exists") @DisplayName("JVM | Kotlin | PostgreSQL: Exists")
class ExistsIT { class ExistsIT {
@Test @Test

View File

@ -7,7 +7,7 @@ import kotlin.test.Test
/** /**
* PostgreSQL integration tests for the `Find` object / `find*` connection extension functions * PostgreSQL integration tests for the `Find` object / `find*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | PostgreSQL: Find") @DisplayName("JVM | Kotlin | PostgreSQL: Find")
class FindIT { class FindIT {
@Test @Test

View File

@ -7,7 +7,7 @@ import kotlin.test.Test
/** /**
* PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions * PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | PostgreSQL: Patch") @DisplayName("JVM | Kotlin | PostgreSQL: Patch")
class PatchIT { class PatchIT {
@Test @Test

View File

@ -7,7 +7,7 @@ import kotlin.test.Test
/** /**
* PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions * PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | PostgreSQL: RemoveFields") @DisplayName("JVM | Kotlin | PostgreSQL: RemoveFields")
class RemoveFieldsIT { class RemoveFieldsIT {
@Test @Test

View File

@ -9,7 +9,7 @@ import kotlin.test.Test
/** /**
* SQLite integration tests for the `Definition` object / `ensure*` connection extension functions * SQLite integration tests for the `Definition` object / `ensure*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | SQLite: Definition") @DisplayName("JVM | Kotlin | SQLite: Definition")
class DefinitionIT { class DefinitionIT {
@Test @Test

View File

@ -9,7 +9,7 @@ import kotlin.test.Test
/** /**
* SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions * SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | SQLite: Delete") @DisplayName("JVM | Kotlin | SQLite: Delete")
class DeleteIT { class DeleteIT {
@Test @Test

View File

@ -7,7 +7,7 @@ import kotlin.test.Test
/** /**
* SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions * SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
*/ */
@DisplayName("Java | Kotlin | SQLite: Document") @DisplayName("JVM | Kotlin | SQLite: Document")
class DocumentIT { class DocumentIT {
@Test @Test

View File

@ -9,7 +9,7 @@ import kotlin.test.Test
/** /**
* SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions * SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | SQLite: Exists") @DisplayName("JVM | Kotlin | SQLite: Exists")
class ExistsIT { class ExistsIT {
@Test @Test

View File

@ -9,7 +9,7 @@ import kotlin.test.Test
/** /**
* SQLite integration tests for the `Find` object / `find*` connection extension functions * SQLite integration tests for the `Find` object / `find*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | SQLite: Find") @DisplayName("JVM | Kotlin | SQLite: Find")
class FindIT { class FindIT {
@Test @Test

View File

@ -9,7 +9,7 @@ import kotlin.test.Test
/** /**
* SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions * SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | SQLite: Patch") @DisplayName("JVM | Kotlin | SQLite: Patch")
class PatchIT { class PatchIT {
@Test @Test

View File

@ -9,7 +9,7 @@ import kotlin.test.Test
/** /**
* SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions * SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
*/ */
@DisplayName("Java | Kotlin | SQLite: RemoveFields") @DisplayName("JVM | Kotlin | SQLite: RemoveFields")
class RemoveFieldsIT { class RemoveFieldsIT {
@Test @Test

View File

@ -13,8 +13,8 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `Count` object * Unit tests for the `Count` object
*/ */
@DisplayName("Kotlin | Common | Query: Count") @DisplayName("JVM | Kotlin | Query | CountQuery")
class CountTest { class CountQueryTest {
/** Test table name */ /** Test table name */
private val tbl = "test_table" private val tbl = "test_table"
@ -30,7 +30,7 @@ class CountTest {
@Test @Test
@DisplayName("all generates correctly") @DisplayName("all generates correctly")
fun all() = fun all() =
assertEquals("SELECT COUNT(*) AS it FROM $tbl", Count.all(tbl), "Count query not constructed correctly") assertEquals("SELECT COUNT(*) AS it FROM $tbl", CountQuery.all(tbl), "Count query not constructed correctly")
@Test @Test
@DisplayName("byFields generates correctly (PostgreSQL)") @DisplayName("byFields generates correctly (PostgreSQL)")
@ -38,7 +38,7 @@ class CountTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0", "SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0",
Count.byFields(tbl, listOf(Field.equal("test", "", ":field0"))), CountQuery.byFields(tbl, listOf(Field.equal("test", "", ":field0"))),
"Count query not constructed correctly" "Count query not constructed correctly"
) )
} }
@ -49,7 +49,7 @@ class CountTest {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0", "SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0",
Count.byFields(tbl, listOf(Field.equal("test", "", ":field0"))), CountQuery.byFields(tbl, listOf(Field.equal("test", "", ":field0"))),
"Count query not constructed correctly" "Count query not constructed correctly"
) )
} }
@ -59,7 +59,7 @@ class CountTest {
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT COUNT(*) AS it FROM $tbl WHERE data @> :criteria", Count.byContains(tbl), "SELECT COUNT(*) AS it FROM $tbl WHERE data @> :criteria", CountQuery.byContains(tbl),
"Count query not constructed correctly" "Count query not constructed correctly"
) )
} }
@ -68,7 +68,7 @@ class CountTest {
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Count.byContains(tbl) } assertThrows<DocumentException> { CountQuery.byContains(tbl) }
} }
@Test @Test
@ -77,7 +77,7 @@ class CountTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT COUNT(*) AS it FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)", "SELECT COUNT(*) AS it FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)",
Count.byJsonPath(tbl), "Count query not constructed correctly" CountQuery.byJsonPath(tbl), "Count query not constructed correctly"
) )
} }
@ -85,6 +85,6 @@ class CountTest {
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails (SQLite)")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Count.byJsonPath(tbl) } assertThrows<DocumentException> { CountQuery.byJsonPath(tbl) }
} }
} }

View File

@ -13,8 +13,8 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `Definition` object * Unit tests for the `Definition` object
*/ */
@DisplayName("Kotlin | Common | Query: Definition") @DisplayName("JVM | Kotlin | Query | DefinitionQuery")
class DefinitionTest { class DefinitionQueryTest {
/** Test table name */ /** Test table name */
private val tbl = "test_table" private val tbl = "test_table"
@ -32,27 +32,27 @@ class DefinitionTest {
fun ensureTableFor() = fun ensureTableFor() =
assertEquals( assertEquals(
"CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)", "CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)",
Definition.ensureTableFor("my.table", "JSONB"), "CREATE TABLE statement not constructed correctly" DefinitionQuery.ensureTableFor("my.table", "JSONB"), "CREATE TABLE statement not constructed correctly"
) )
@Test @Test
@DisplayName("ensureTable generates correctly (PostgreSQL)") @DisplayName("ensureTable generates correctly (PostgreSQL)")
fun ensureTablePostgres() { fun ensureTablePostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals("CREATE TABLE IF NOT EXISTS $tbl (data JSONB NOT NULL)", Definition.ensureTable(tbl)) assertEquals("CREATE TABLE IF NOT EXISTS $tbl (data JSONB NOT NULL)", DefinitionQuery.ensureTable(tbl))
} }
@Test @Test
@DisplayName("ensureTable generates correctly (SQLite)") @DisplayName("ensureTable generates correctly (SQLite)")
fun ensureTableSQLite() { fun ensureTableSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals("CREATE TABLE IF NOT EXISTS $tbl (data TEXT NOT NULL)", Definition.ensureTable(tbl)) assertEquals("CREATE TABLE IF NOT EXISTS $tbl (data TEXT NOT NULL)", DefinitionQuery.ensureTable(tbl))
} }
@Test @Test
@DisplayName("ensureTable fails when no dialect is set") @DisplayName("ensureTable fails when no dialect is set")
fun ensureTableFailsUnknown() { fun ensureTableFailsUnknown() {
assertThrows<DocumentException> { Definition.ensureTable(tbl) } assertThrows<DocumentException> { DefinitionQuery.ensureTable(tbl) }
} }
@Test @Test
@ -60,7 +60,7 @@ class DefinitionTest {
fun ensureKeyWithSchema() = fun ensureKeyWithSchema() =
assertEquals( assertEquals(
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'id'))", "CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'id'))",
Definition.ensureKey("test.table", Dialect.POSTGRESQL), DefinitionQuery.ensureKey("test.table", Dialect.POSTGRESQL),
"CREATE INDEX for key statement with schema not constructed correctly" "CREATE INDEX for key statement with schema not constructed correctly"
) )
@ -69,7 +69,7 @@ class DefinitionTest {
fun ensureKeyWithoutSchema() = fun ensureKeyWithoutSchema() =
assertEquals( assertEquals(
"CREATE UNIQUE INDEX IF NOT EXISTS idx_${tbl}_key ON $tbl ((data->>'id'))", "CREATE UNIQUE INDEX IF NOT EXISTS idx_${tbl}_key ON $tbl ((data->>'id'))",
Definition.ensureKey(tbl, Dialect.SQLITE), DefinitionQuery.ensureKey(tbl, Dialect.SQLITE),
"CREATE INDEX for key statement without schema not constructed correctly" "CREATE INDEX for key statement without schema not constructed correctly"
) )
@ -78,7 +78,7 @@ class DefinitionTest {
fun ensureIndexOnMultipleFields() = fun ensureIndexOnMultipleFields() =
assertEquals( assertEquals(
"CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table ((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)", "CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table ((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)",
Definition.ensureIndexOn( DefinitionQuery.ensureIndexOn(
"test.table", "gibberish", listOf("taco", "guac DESC", "salsa ASC"), "test.table", "gibberish", listOf("taco", "guac DESC", "salsa ASC"),
Dialect.POSTGRESQL Dialect.POSTGRESQL
), ),
@ -90,7 +90,7 @@ class DefinitionTest {
fun ensureIndexOnNestedPostgres() = fun ensureIndexOnNestedPostgres() =
assertEquals( assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data#>>'{a,b,c}'))", "CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data#>>'{a,b,c}'))",
Definition.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.POSTGRESQL), DefinitionQuery.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.POSTGRESQL),
"CREATE INDEX for nested PostgreSQL field incorrect" "CREATE INDEX for nested PostgreSQL field incorrect"
) )
@ -99,7 +99,7 @@ class DefinitionTest {
fun ensureIndexOnNestedSQLite() = fun ensureIndexOnNestedSQLite() =
assertEquals( assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data->'a'->'b'->>'c'))", "CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data->'a'->'b'->>'c'))",
Definition.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.SQLITE), DefinitionQuery.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.SQLITE),
"CREATE INDEX for nested SQLite field incorrect" "CREATE INDEX for nested SQLite field incorrect"
) )
@ -109,7 +109,7 @@ class DefinitionTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${tbl}_document ON $tbl USING GIN (data)", "CREATE INDEX IF NOT EXISTS idx_${tbl}_document ON $tbl USING GIN (data)",
Definition.ensureDocumentIndexOn(tbl, DocumentIndex.FULL), DefinitionQuery.ensureDocumentIndexOn(tbl, DocumentIndex.FULL),
"CREATE INDEX for full document index incorrect" "CREATE INDEX for full document index incorrect"
) )
} }
@ -120,7 +120,7 @@ class DefinitionTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${tbl}_document ON $tbl USING GIN (data jsonb_path_ops)", "CREATE INDEX IF NOT EXISTS idx_${tbl}_document ON $tbl USING GIN (data jsonb_path_ops)",
Definition.ensureDocumentIndexOn(tbl, DocumentIndex.OPTIMIZED), DefinitionQuery.ensureDocumentIndexOn(tbl, DocumentIndex.OPTIMIZED),
"CREATE INDEX for optimized document index incorrect" "CREATE INDEX for optimized document index incorrect"
) )
} }
@ -129,6 +129,6 @@ class DefinitionTest {
@DisplayName("ensureDocumentIndexOn fails for SQLite") @DisplayName("ensureDocumentIndexOn fails for SQLite")
fun ensureDocumentIndexOnFailsSQLite() { fun ensureDocumentIndexOnFailsSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Definition.ensureDocumentIndexOn(tbl, DocumentIndex.FULL) } assertThrows<DocumentException> { DefinitionQuery.ensureDocumentIndexOn(tbl, DocumentIndex.FULL) }
} }
} }

View File

@ -13,8 +13,8 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `Delete` object * Unit tests for the `Delete` object
*/ */
@DisplayName("Kotlin | Common | Query: Delete") @DisplayName("JVM | Kotlin | Query | DeleteQuery")
class DeleteTest { class DeleteQueryTest {
/** Test table name */ /** Test table name */
private val tbl = "test_table" private val tbl = "test_table"
@ -33,7 +33,7 @@ class DeleteTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE data->>'id' = :id", "DELETE FROM $tbl WHERE data->>'id' = :id",
Delete.byId<String>(tbl), "Delete query not constructed correctly" DeleteQuery.byId<String>(tbl), "Delete query not constructed correctly"
) )
} }
@ -43,7 +43,7 @@ class DeleteTest {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE data->>'id' = :id", "DELETE FROM $tbl WHERE data->>'id' = :id",
Delete.byId<String>(tbl), "Delete query not constructed correctly" DeleteQuery.byId<String>(tbl), "Delete query not constructed correctly"
) )
} }
@ -52,7 +52,7 @@ class DeleteTest {
fun byFieldsPostgres() { fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE data->>'a' = :b", Delete.byFields(tbl, listOf(Field.equal("a", "", ":b"))), "DELETE FROM $tbl WHERE data->>'a' = :b", DeleteQuery.byFields(tbl, listOf(Field.equal("a", "", ":b"))),
"Delete query not constructed correctly" "Delete query not constructed correctly"
) )
} }
@ -62,7 +62,7 @@ class DeleteTest {
fun byFieldsSQLite() { fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE data->>'a' = :b", Delete.byFields(tbl, listOf(Field.equal("a", "", ":b"))), "DELETE FROM $tbl WHERE data->>'a' = :b", DeleteQuery.byFields(tbl, listOf(Field.equal("a", "", ":b"))),
"Delete query not constructed correctly" "Delete query not constructed correctly"
) )
} }
@ -72,7 +72,7 @@ class DeleteTest {
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE data @> :criteria", Delete.byContains(tbl), "Delete query not constructed correctly" "DELETE FROM $tbl WHERE data @> :criteria", DeleteQuery.byContains(tbl), "Delete query not constructed correctly"
) )
} }
@ -80,7 +80,7 @@ class DeleteTest {
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Delete.byContains(tbl) } assertThrows<DocumentException> { DeleteQuery.byContains(tbl) }
} }
@Test @Test
@ -88,7 +88,7 @@ class DeleteTest {
fun byJsonPathPostgres() { fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)", Delete.byJsonPath(tbl), "DELETE FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)", DeleteQuery.byJsonPath(tbl),
"Delete query not constructed correctly" "Delete query not constructed correctly"
) )
} }
@ -97,6 +97,6 @@ class DeleteTest {
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails (SQLite)")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Delete.byJsonPath(tbl) } assertThrows<DocumentException> { DeleteQuery.byJsonPath(tbl) }
} }
} }

View File

@ -14,8 +14,8 @@ import kotlin.test.assertTrue
/** /**
* Unit tests for the `Document` object * Unit tests for the `Document` object
*/ */
@DisplayName("Kotlin | Common | Query: Document") @DisplayName("JVM | Kotlin | Query | DocumentQuery")
class DocumentTest { class DocumentQueryTest {
/** Test table name */ /** Test table name */
private val tbl = "test_table" private val tbl = "test_table"
@ -32,14 +32,14 @@ class DocumentTest {
@DisplayName("insert generates no auto ID (PostgreSQL)") @DisplayName("insert generates no auto ID (PostgreSQL)")
fun insertNoAutoPostgres() { fun insertNoAutoPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals("INSERT INTO $tbl VALUES (:data)", Document.insert(tbl)) assertEquals("INSERT INTO $tbl VALUES (:data)", DocumentQuery.insert(tbl))
} }
@Test @Test
@DisplayName("insert generates no auto ID (SQLite)") @DisplayName("insert generates no auto ID (SQLite)")
fun insertNoAutoSQLite() { fun insertNoAutoSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals("INSERT INTO $tbl VALUES (:data)", Document.insert(tbl)) assertEquals("INSERT INTO $tbl VALUES (:data)", DocumentQuery.insert(tbl))
} }
@Test @Test
@ -49,7 +49,7 @@ class DocumentTest {
assertEquals( assertEquals(
"INSERT INTO $tbl VALUES (:data::jsonb || ('{\"id\":' " + "INSERT INTO $tbl VALUES (:data::jsonb || ('{\"id\":' " +
"|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $tbl) || '}')::jsonb)", "|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $tbl) || '}')::jsonb)",
Document.insert(tbl, AutoId.NUMBER) DocumentQuery.insert(tbl, AutoId.NUMBER)
) )
} }
@ -60,7 +60,7 @@ class DocumentTest {
assertEquals( assertEquals(
"INSERT INTO $tbl VALUES (json_set(:data, '$.id', " + "INSERT INTO $tbl VALUES (json_set(:data, '$.id', " +
"(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $tbl)))", "(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $tbl)))",
Document.insert(tbl, AutoId.NUMBER) DocumentQuery.insert(tbl, AutoId.NUMBER)
) )
} }
@ -68,7 +68,7 @@ class DocumentTest {
@DisplayName("insert generates auto UUID (PostgreSQL)") @DisplayName("insert generates auto UUID (PostgreSQL)")
fun insertAutoUUIDPostgres() { fun insertAutoUUIDPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
val query = Document.insert(tbl, AutoId.UUID) val query = DocumentQuery.insert(tbl, AutoId.UUID)
assertTrue( assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""), query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""),
"Query start not correct (actual: $query)" "Query start not correct (actual: $query)"
@ -80,7 +80,7 @@ class DocumentTest {
@DisplayName("insert generates auto UUID (SQLite)") @DisplayName("insert generates auto UUID (SQLite)")
fun insertAutoUUIDSQLite() { fun insertAutoUUIDSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
val query = Document.insert(tbl, AutoId.UUID) val query = DocumentQuery.insert(tbl, AutoId.UUID)
assertTrue( assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"), query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"),
"Query start not correct (actual: $query)" "Query start not correct (actual: $query)"
@ -94,7 +94,7 @@ class DocumentTest {
try { try {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
Configuration.idStringLength = 8 Configuration.idStringLength = 8
val query = Document.insert(tbl, AutoId.RANDOM_STRING) val query = DocumentQuery.insert(tbl, AutoId.RANDOM_STRING)
assertTrue( assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""), query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""),
"Query start not correct (actual: $query)" "Query start not correct (actual: $query)"
@ -114,7 +114,7 @@ class DocumentTest {
@DisplayName("insert generates auto random string (SQLite)") @DisplayName("insert generates auto random string (SQLite)")
fun insertAutoRandomSQLite() { fun insertAutoRandomSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
val query = Document.insert(tbl, AutoId.RANDOM_STRING) val query = DocumentQuery.insert(tbl, AutoId.RANDOM_STRING)
assertTrue( assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"), query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"),
"Query start not correct (actual: $query)" "Query start not correct (actual: $query)"
@ -130,7 +130,7 @@ class DocumentTest {
@Test @Test
@DisplayName("insert fails when no dialect is set") @DisplayName("insert fails when no dialect is set")
fun insertFailsUnknown() { fun insertFailsUnknown() {
assertThrows<DocumentException> { Document.insert(tbl) } assertThrows<DocumentException> { DocumentQuery.insert(tbl) }
} }
@Test @Test
@ -139,12 +139,12 @@ class DocumentTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"INSERT INTO $tbl VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data", "INSERT INTO $tbl VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data",
Document.save(tbl), "INSERT ON CONFLICT UPDATE statement not constructed correctly" DocumentQuery.save(tbl), "INSERT ON CONFLICT UPDATE statement not constructed correctly"
) )
} }
@Test @Test
@DisplayName("update generates successfully") @DisplayName("update generates successfully")
fun update() = fun update() =
assertEquals("UPDATE $tbl SET data = :data", Document.update(tbl), "Update query not constructed correctly") assertEquals("UPDATE $tbl SET data = :data", DocumentQuery.update(tbl), "Update query not constructed correctly")
} }

View File

@ -14,7 +14,7 @@ import kotlin.test.assertEquals
* Unit tests for the `Exists` object * Unit tests for the `Exists` object
*/ */
@DisplayName("Kotlin | Common | Query: Exists") @DisplayName("Kotlin | Common | Query: Exists")
class ExistsTest { class ExistsQueryTest {
/** Test table name */ /** Test table name */
private val tbl = "test_table" private val tbl = "test_table"
@ -33,7 +33,7 @@ class ExistsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'id' = :id) AS it", "SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'id' = :id) AS it",
Exists.byId<String>(tbl), "Exists query not constructed correctly" ExistsQuery.byId<String>(tbl), "Exists query not constructed correctly"
) )
} }
@ -43,7 +43,7 @@ class ExistsTest {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'id' = :id) AS it", "SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'id' = :id) AS it",
Exists.byId<String>(tbl), "Exists query not constructed correctly" ExistsQuery.byId<String>(tbl), "Exists query not constructed correctly"
) )
} }
@ -53,7 +53,7 @@ class ExistsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE (data->>'it')::numeric = :test) AS it", "SELECT EXISTS (SELECT 1 FROM $tbl WHERE (data->>'it')::numeric = :test) AS it",
Exists.byFields(tbl, listOf(Field.equal("it", 7, ":test"))), ExistsQuery.byFields(tbl, listOf(Field.equal("it", 7, ":test"))),
"Exists query not constructed correctly" "Exists query not constructed correctly"
) )
} }
@ -64,7 +64,7 @@ class ExistsTest {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'it' = :test) AS it", "SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'it' = :test) AS it",
Exists.byFields(tbl, listOf(Field.equal("it", 7, ":test"))), ExistsQuery.byFields(tbl, listOf(Field.equal("it", 7, ":test"))),
"Exists query not constructed correctly" "Exists query not constructed correctly"
) )
} }
@ -74,7 +74,7 @@ class ExistsTest {
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data @> :criteria) AS it", Exists.byContains(tbl), "SELECT EXISTS (SELECT 1 FROM $tbl WHERE data @> :criteria) AS it", ExistsQuery.byContains(tbl),
"Exists query not constructed correctly" "Exists query not constructed correctly"
) )
} }
@ -83,7 +83,7 @@ class ExistsTest {
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Exists.byContains(tbl) } assertThrows<DocumentException> { ExistsQuery.byContains(tbl) }
} }
@Test @Test
@ -92,7 +92,7 @@ class ExistsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)) AS it", "SELECT EXISTS (SELECT 1 FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)) AS it",
Exists.byJsonPath(tbl), "Exists query not constructed correctly" ExistsQuery.byJsonPath(tbl), "Exists query not constructed correctly"
) )
} }
@ -100,6 +100,6 @@ class ExistsTest {
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails (SQLite)")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Exists.byJsonPath(tbl) } assertThrows<DocumentException> { ExistsQuery.byJsonPath(tbl) }
} }
} }

View File

@ -8,16 +8,14 @@ import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
* Unit tests for the `Find` object * Unit tests for the `Find` object
*/ */
@DisplayName("Kotlin | Common | Query: Find") @DisplayName("JVM | Kotlin | Query | FindQuery")
class FindTest { class FindQueryTest {
/** Test table name */
private val tbl = "test_table"
/** /**
* Clear the connection string (resets Dialect) * Clear the connection string (resets Dialect)
@ -30,15 +28,15 @@ class FindTest {
@Test @Test
@DisplayName("all generates correctly") @DisplayName("all generates correctly")
fun all() = fun all() =
assertEquals("SELECT data FROM $tbl", Find.all(tbl), "Find query not constructed correctly") assertEquals("SELECT data FROM $TEST_TABLE", FindQuery.all(TEST_TABLE), "Find query not constructed correctly")
@Test @Test
@DisplayName("byId generates correctly (PostgreSQL)") @DisplayName("byId generates correctly (PostgreSQL)")
fun byIdPostgres() { fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT data FROM $tbl WHERE data->>'id' = :id", "SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id",
Find.byId<String>(tbl), "Find query not constructed correctly" FindQuery.byId<String>(TEST_TABLE), "Find query not constructed correctly"
) )
} }
@ -47,8 +45,8 @@ class FindTest {
fun byIdSQLite() { fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"SELECT data FROM $tbl WHERE data->>'id' = :id", "SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id",
Find.byId<String>(tbl), "Find query not constructed correctly" FindQuery.byId<String>(TEST_TABLE), "Find query not constructed correctly"
) )
} }
@ -57,8 +55,8 @@ class FindTest {
fun byFieldsPostgres() { fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT data FROM $tbl WHERE data->>'a' = :b AND (data->>'c')::numeric < :d", "SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND (data->>'c')::numeric < :d",
Find.byFields(tbl, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))), FindQuery.byFields(TEST_TABLE, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))),
"Find query not constructed correctly" "Find query not constructed correctly"
) )
} }
@ -68,8 +66,8 @@ class FindTest {
fun byFieldsSQLite() { fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"SELECT data FROM $tbl WHERE data->>'a' = :b AND data->>'c' < :d", "SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND data->>'c' < :d",
Find.byFields(tbl, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))), FindQuery.byFields(TEST_TABLE, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))),
"Find query not constructed correctly" "Find query not constructed correctly"
) )
} }
@ -79,7 +77,7 @@ class FindTest {
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT data FROM $tbl WHERE data @> :criteria", Find.byContains(tbl), "SELECT data FROM $TEST_TABLE WHERE data @> :criteria", FindQuery.byContains(TEST_TABLE),
"Find query not constructed correctly" "Find query not constructed correctly"
) )
} }
@ -88,7 +86,7 @@ class FindTest {
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Find.byContains(tbl) } assertThrows<DocumentException> { FindQuery.byContains(TEST_TABLE) }
} }
@Test @Test
@ -96,7 +94,8 @@ class FindTest {
fun byJsonPathPostgres() { fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"SELECT data FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)", Find.byJsonPath(tbl), "SELECT data FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
FindQuery.byJsonPath(TEST_TABLE),
"Find query not constructed correctly" "Find query not constructed correctly"
) )
} }
@ -105,6 +104,6 @@ class FindTest {
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails (SQLite)")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Find.byJsonPath(tbl) } assertThrows<DocumentException> { FindQuery.byJsonPath(TEST_TABLE) }
} }
} }

View File

@ -13,8 +13,8 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `Patch` object * Unit tests for the `Patch` object
*/ */
@DisplayName("Kotlin | Common | Query: Patch") @DisplayName("JVM | Kotlin | Query | PatchQuery")
class PatchTest { class PatchQueryTest {
/** Test table name */ /** Test table name */
private val tbl = "test_table" private val tbl = "test_table"
@ -33,7 +33,7 @@ class PatchTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data->>'id' = :id", "UPDATE $tbl SET data = data || :data WHERE data->>'id' = :id",
Patch.byId<String>(tbl), "Patch query not constructed correctly" PatchQuery.byId<String>(tbl), "Patch query not constructed correctly"
) )
} }
@ -43,7 +43,7 @@ class PatchTest {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id", "UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id",
Patch.byId<String>(tbl), "Patch query not constructed correctly" PatchQuery.byId<String>(tbl), "Patch query not constructed correctly"
) )
} }
@ -53,7 +53,7 @@ class PatchTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data->>'z' = :y", "UPDATE $tbl SET data = data || :data WHERE data->>'z' = :y",
Patch.byFields(tbl, listOf(Field.equal("z", "", ":y"))), PatchQuery.byFields(tbl, listOf(Field.equal("z", "", ":y"))),
"Patch query not constructed correctly" "Patch query not constructed correctly"
) )
} }
@ -64,7 +64,7 @@ class PatchTest {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y", "UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y",
Patch.byFields(tbl, listOf(Field.equal("z", "", ":y"))), PatchQuery.byFields(tbl, listOf(Field.equal("z", "", ":y"))),
"Patch query not constructed correctly" "Patch query not constructed correctly"
) )
} }
@ -74,7 +74,7 @@ class PatchTest {
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data @> :criteria", Patch.byContains(tbl), "UPDATE $tbl SET data = data || :data WHERE data @> :criteria", PatchQuery.byContains(tbl),
"Patch query not constructed correctly" "Patch query not constructed correctly"
) )
} }
@ -83,7 +83,7 @@ class PatchTest {
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Patch.byContains(tbl) } assertThrows<DocumentException> { PatchQuery.byContains(tbl) }
} }
@Test @Test
@ -92,7 +92,7 @@ class PatchTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)", "UPDATE $tbl SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)",
Patch.byJsonPath(tbl), "Patch query not constructed correctly" PatchQuery.byJsonPath(tbl), "Patch query not constructed correctly"
) )
} }
@ -100,6 +100,6 @@ class PatchTest {
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails (SQLite)")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Patch.byJsonPath(tbl) } assertThrows<DocumentException> { PatchQuery.byJsonPath(tbl) }
} }
} }

View File

@ -10,9 +10,9 @@ import solutions.bitbadger.documents.FieldMatch
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
* Unit tests for the top-level query functions * Unit tests for the package-level query functions
*/ */
@DisplayName("Kotlin | Common | Query") @DisplayName("JVM | Kotlin | Query | Package Functions")
class QueryTest { class QueryTest {
/** /**

View File

@ -10,8 +10,8 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `RemoveFields` object * Unit tests for the `RemoveFields` object
*/ */
@DisplayName("Kotlin | Common | Query: RemoveFields") @DisplayName("JVM | Kotlin | Query | RemoveFieldsQuery")
class RemoveFieldsTest { class RemoveFieldsQueryTest {
/** Test table name */ /** Test table name */
private val tbl = "test_table" private val tbl = "test_table"
@ -30,7 +30,7 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data->>'id' = :id", "UPDATE $tbl SET data = data - :name::text[] WHERE data->>'id' = :id",
RemoveFields.byId<String>(tbl, listOf(Parameter(":name", ParameterType.STRING, "{a,z}"))), RemoveFieldsQuery.byId<String>(tbl, listOf(Parameter(":name", ParameterType.STRING, "{a,z}"))),
"Remove Fields query not constructed correctly" "Remove Fields query not constructed correctly"
) )
} }
@ -41,7 +41,7 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id", "UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id",
RemoveFields.byId<String>( RemoveFieldsQuery.byId<String>(
tbl, tbl,
listOf( listOf(
Parameter(":name0", ParameterType.STRING, "a"), Parameter(":name0", ParameterType.STRING, "a"),
@ -58,7 +58,7 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data->>'f' > :g", "UPDATE $tbl SET data = data - :name::text[] WHERE data->>'f' > :g",
RemoveFields.byFields( RemoveFieldsQuery.byFields(
tbl, tbl,
listOf(Parameter(":name", ParameterType.STRING, "{b,c}")), listOf(Parameter(":name", ParameterType.STRING, "{b,c}")),
listOf(Field.greater("f", "", ":g")) listOf(Field.greater("f", "", ":g"))
@ -73,7 +73,7 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g", "UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g",
RemoveFields.byFields( RemoveFieldsQuery.byFields(
tbl, tbl,
listOf(Parameter(":name0", ParameterType.STRING, "b"), Parameter(":name1", ParameterType.STRING, "c")), listOf(Parameter(":name0", ParameterType.STRING, "b"), Parameter(":name1", ParameterType.STRING, "c")),
listOf(Field.greater("f", "", ":g")) listOf(Field.greater("f", "", ":g"))
@ -88,7 +88,7 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data @> :criteria", "UPDATE $tbl SET data = data - :name::text[] WHERE data @> :criteria",
RemoveFields.byContains(tbl, listOf(Parameter(":name", ParameterType.STRING, "{m,n}"))), RemoveFieldsQuery.byContains(tbl, listOf(Parameter(":name", ParameterType.STRING, "{m,n}"))),
"Remove Field query not constructed correctly" "Remove Field query not constructed correctly"
) )
} }
@ -97,7 +97,7 @@ class RemoveFieldsTest {
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { RemoveFields.byContains(tbl, listOf()) } assertThrows<DocumentException> { RemoveFieldsQuery.byContains(tbl, listOf()) }
} }
@Test @Test
@ -106,7 +106,7 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)", "UPDATE $tbl SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)",
RemoveFields.byJsonPath(tbl, listOf(Parameter(":name", ParameterType.STRING, "{o,p}"))), RemoveFieldsQuery.byJsonPath(tbl, listOf(Parameter(":name", ParameterType.STRING, "{o,p}"))),
"Remove Field query not constructed correctly" "Remove Field query not constructed correctly"
) )
} }
@ -115,6 +115,6 @@ class RemoveFieldsTest {
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails (SQLite)")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { RemoveFields.byJsonPath(tbl, listOf()) } assertThrows<DocumentException> { RemoveFieldsQuery.byJsonPath(tbl, listOf()) }
} }
} }

View File

@ -10,7 +10,7 @@ import kotlin.test.assertEquals
/** /**
* Unit tests for the `Where` object * Unit tests for the `Where` object
*/ */
@DisplayName("Kotlin | Common | Query: Where") @DisplayName("JVM | Kotlin | Query | Where")
class WhereTest { class WhereTest {
/** /**

View File

@ -1,22 +1,18 @@
package solutions.bitbadger.documents.support package solutions.bitbadger.documents.support
import kotlinx.serialization.Serializable
import solutions.bitbadger.documents.extensions.insert import solutions.bitbadger.documents.extensions.insert
/** The test table name to use for integration tests */ /** The test table name to use for integration tests */
const val TEST_TABLE = "test_table" const val TEST_TABLE = "test_table"
@Serializable
data class NumIdDocument(val key: Int, val text: String) { data class NumIdDocument(val key: Int, val text: String) {
constructor() : this(0, "") constructor() : this(0, "")
} }
@Serializable
data class SubDocument(val foo: String, val bar: String) { data class SubDocument(val foo: String, val bar: String) {
constructor() : this("", "") constructor() : this("", "")
} }
@Serializable
data class ArrayDocument(val id: String, val values: List<String>) { data class ArrayDocument(val id: String, val values: List<String>) {
constructor() : this("", listOf()) constructor() : this("", listOf())
@ -31,7 +27,6 @@ data class ArrayDocument(val id: String, val values: List<String>) {
} }
} }
@Serializable
data class JsonDocument(val id: String, val value: String = "", val numValue: Int = 0, val sub: SubDocument? = null) { data class JsonDocument(val id: String, val value: String = "", val numValue: Int = 0, val sub: SubDocument? = null) {
constructor() : this("") constructor() : this("")
@ -50,3 +45,11 @@ data class JsonDocument(val id: String, val value: String = "", val numValue: In
testDocuments.forEach { db.conn.insert(tableName, it) } testDocuments.forEach { db.conn.insert(tableName, it) }
} }
} }
// Test classes for AutoId generation
data class ByteIdClass(val id: Byte)
data class ShortIdClass(val id: Short)
data class IntIdClass(val id: Int)
data class LongIdClass(val id: Long)
data class StringIdClass(val id: String)

View File

@ -30,8 +30,6 @@
<groupId>solutions.bitbadger.documents</groupId> <groupId>solutions.bitbadger.documents</groupId>
<artifactId>jvm</artifactId> <artifactId>jvm</artifactId>
<version>4.0.0-alpha1-SNAPSHOT</version> <version>4.0.0-alpha1-SNAPSHOT</version>
<scope>system</scope>
<systemPath>${project.basedir}/../jvm/target/jvm-4.0.0-alpha1-SNAPSHOT.jar</systemPath>
<type>jar</type> <type>jar</type>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -2,7 +2,7 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlin.extensions.* import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Count import solutions.bitbadger.documents.query.CountQuery
import java.sql.Connection import java.sql.Connection
@ -19,7 +19,7 @@ object Count {
* @return A count of the documents in the table * @return A count of the documents in the table
*/ */
fun all(tableName: String, conn: Connection) = fun all(tableName: String, conn: Connection) =
conn.customScalar(Count.all(tableName), mapFunc = Results::toCount) conn.customScalar(CountQuery.all(tableName), mapFunc = Results::toCount)
/** /**
* Count all documents in the table * Count all documents in the table
@ -47,7 +47,7 @@ object Count {
): Long { ): Long {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
return conn.customScalar( return conn.customScalar(
Count.byFields(tableName, named, howMatched), CountQuery.byFields(tableName, named, howMatched),
Parameters.addFields(named), Parameters.addFields(named),
Results::toCount Results::toCount
) )
@ -74,7 +74,7 @@ object Count {
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) = inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
conn.customScalar(Count.byContains(tableName), listOf(Parameters.json(":criteria", criteria)), Results::toCount) conn.customScalar(CountQuery.byContains(tableName), listOf(Parameters.json(":criteria", criteria)), Results::toCount)
/** /**
* Count documents using a JSON containment query (PostgreSQL only) * Count documents using a JSON containment query (PostgreSQL only)
@ -98,7 +98,7 @@ object Count {
*/ */
fun byJsonPath(tableName: String, path: String, conn: Connection) = fun byJsonPath(tableName: String, path: String, conn: Connection) =
conn.customScalar( conn.customScalar(
Count.byJsonPath(tableName), CountQuery.byJsonPath(tableName),
listOf(Parameter(":path", ParameterType.STRING, path)), listOf(Parameter(":path", ParameterType.STRING, path)),
Results::toCount Results::toCount
) )

View File

@ -3,7 +3,7 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.jvm.Delete as JvmDelete import solutions.bitbadger.documents.jvm.Delete as JvmDelete
import solutions.bitbadger.documents.kotlin.extensions.* import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Delete import solutions.bitbadger.documents.query.DeleteQuery
import java.sql.Connection import java.sql.Connection
/** /**
@ -60,7 +60,7 @@ object Delete {
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) = inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
conn.customNonQuery(Delete.byContains(tableName), listOf(Parameters.json(":criteria", criteria))) conn.customNonQuery(DeleteQuery.byContains(tableName), listOf(Parameters.json(":criteria", criteria)))
/** /**
* Delete documents using a JSON containment query (PostgreSQL only) * Delete documents using a JSON containment query (PostgreSQL only)

View File

@ -5,7 +5,7 @@ import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.extensions.customNonQuery
import solutions.bitbadger.documents.query.Document import solutions.bitbadger.documents.query.DocumentQuery
import solutions.bitbadger.documents.query.Where import solutions.bitbadger.documents.query.Where
import solutions.bitbadger.documents.query.statementWhere import solutions.bitbadger.documents.query.statementWhere
import java.sql.Connection import java.sql.Connection
@ -25,7 +25,7 @@ object Document {
inline fun <reified TDoc> insert(tableName: String, document: TDoc, conn: Connection) { inline fun <reified TDoc> insert(tableName: String, document: TDoc, conn: Connection) {
val strategy = Configuration.autoIdStrategy val strategy = Configuration.autoIdStrategy
val query = if (strategy == AutoId.DISABLED) { val query = if (strategy == AutoId.DISABLED) {
Document.insert(tableName) DocumentQuery.insert(tableName)
} else { } else {
val idField = Configuration.idField val idField = Configuration.idField
val dialect = Configuration.dialect("Create auto-ID insert query") val dialect = Configuration.dialect("Create auto-ID insert query")
@ -52,7 +52,7 @@ object Document {
":data" ":data"
} }
Document.insert(tableName).replace(":data", dataParam) DocumentQuery.insert(tableName).replace(":data", dataParam)
} }
conn.customNonQuery(query, listOf(Parameters.json(":data", document))) conn.customNonQuery(query, listOf(Parameters.json(":data", document)))
} }
@ -74,7 +74,7 @@ object Document {
* @param conn The connection on which the query should be executed * @param conn The connection on which the query should be executed
*/ */
inline fun <reified TDoc> save(tableName: String, document: TDoc, conn: Connection) = inline fun <reified TDoc> save(tableName: String, document: TDoc, conn: Connection) =
conn.customNonQuery(Document.save(tableName), listOf(Parameters.json(":data", document))) 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") * Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
@ -95,7 +95,7 @@ object Document {
*/ */
inline fun <TKey, reified TDoc> update(tableName: String, docId: TKey, document: TDoc, conn: Connection) = inline fun <TKey, reified TDoc> update(tableName: String, docId: TKey, document: TDoc, conn: Connection) =
conn.customNonQuery( conn.customNonQuery(
statementWhere(Document.update(tableName), Where.byId(":id", docId)), statementWhere(DocumentQuery.update(tableName), Where.byId(":id", docId)),
Parameters.addFields( Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")), listOf(Field.equal(Configuration.idField, docId, ":id")),
mutableListOf(Parameters.json(":data", document)) mutableListOf(Parameters.json(":data", document))

View File

@ -3,7 +3,7 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.jvm.Exists as JvmExists import solutions.bitbadger.documents.jvm.Exists as JvmExists
import solutions.bitbadger.documents.kotlin.extensions.* import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Exists import solutions.bitbadger.documents.query.ExistsQuery
import java.sql.Connection import java.sql.Connection
/** /**
@ -66,7 +66,7 @@ object Exists {
*/ */
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) = inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
conn.customScalar( conn.customScalar(
Exists.byContains(tableName), ExistsQuery.byContains(tableName),
listOf(Parameters.json(":criteria", criteria)), listOf(Parameters.json(":criteria", criteria)),
Results::toExists Results::toExists
) )

View File

@ -2,7 +2,7 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlin.extensions.* import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Find import solutions.bitbadger.documents.query.FindQuery
import solutions.bitbadger.documents.query.orderBy import solutions.bitbadger.documents.query.orderBy
import java.sql.Connection import java.sql.Connection
@ -20,7 +20,7 @@ object Find {
* @return A list of documents from the given table * @return A list of documents from the given table
*/ */
inline fun <reified TDoc : Any> all(tableName: String, orderBy: Collection<Field<*>>? = null, conn: Connection) = inline fun <reified TDoc : Any> all(tableName: String, orderBy: Collection<Field<*>>? = null, conn: Connection) =
conn.customList<TDoc>(Find.all(tableName) + (orderBy?.let(::orderBy) ?: ""), mapFunc = Results::fromData) conn.customList<TDoc>(FindQuery.all(tableName) + (orderBy?.let(::orderBy) ?: ""), mapFunc = Results::fromData)
/** /**
* Retrieve all documents in the given table * Retrieve all documents in the given table
@ -52,7 +52,7 @@ object Find {
*/ */
inline fun <TKey, reified TDoc : Any> byId(tableName: String, docId: TKey, conn: Connection) = inline fun <TKey, reified TDoc : Any> byId(tableName: String, docId: TKey, conn: Connection) =
conn.customSingle<TDoc>( conn.customSingle<TDoc>(
Find.byId(tableName, docId), FindQuery.byId(tableName, docId),
Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))), Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))),
Results::fromData Results::fromData
) )
@ -86,7 +86,7 @@ object Find {
): List<TDoc> { ): List<TDoc> {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
return conn.customList<TDoc>( return conn.customList<TDoc>(
Find.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""),
Parameters.addFields(named), Parameters.addFields(named),
Results::fromData Results::fromData
) )
@ -158,7 +158,7 @@ object Find {
conn: Connection conn: Connection
) = ) =
conn.customList<TDoc>( conn.customList<TDoc>(
Find.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameters.json(":criteria", criteria)), listOf(Parameters.json(":criteria", criteria)),
Results::fromData Results::fromData
) )
@ -223,7 +223,7 @@ object Find {
conn: Connection conn: Connection
) = ) =
conn.customList<TDoc>( conn.customList<TDoc>(
Find.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameter(":path", ParameterType.STRING, path)), listOf(Parameter(":path", ParameterType.STRING, path)),
Results::fromData Results::fromData
) )
@ -271,7 +271,7 @@ object Find {
): TDoc? { ): TDoc? {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
return conn.customSingle<TDoc>( return conn.customSingle<TDoc>(
Find.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""),
Parameters.addFields(named), Parameters.addFields(named),
Results::fromData Results::fromData
) )
@ -328,7 +328,7 @@ object Find {
conn: Connection conn: Connection
) = ) =
conn.customSingle<TDoc>( conn.customSingle<TDoc>(
Find.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameters.json(":criteria", criteria)), listOf(Parameters.json(":criteria", criteria)),
Results::fromData Results::fromData
) )
@ -382,7 +382,7 @@ object Find {
conn: Connection conn: Connection
) = ) =
conn.customSingle<TDoc>( conn.customSingle<TDoc>(
Find.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""), FindQuery.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""),
listOf(Parameter(":path", ParameterType.STRING, path)), listOf(Parameter(":path", ParameterType.STRING, path)),
Results::fromData Results::fromData
) )

View File

@ -2,7 +2,7 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlin.extensions.* import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Patch import solutions.bitbadger.documents.query.PatchQuery
import java.sql.Connection import java.sql.Connection
/** /**
@ -20,7 +20,7 @@ object Patch {
*/ */
inline fun <TKey, reified TPatch> byId(tableName: String, docId: TKey, patch: TPatch, conn: Connection) = inline fun <TKey, reified TPatch> byId(tableName: String, docId: TKey, patch: TPatch, conn: Connection) =
conn.customNonQuery( conn.customNonQuery(
Patch.byId(tableName, docId), PatchQuery.byId(tableName, docId),
Parameters.addFields( Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")), listOf(Field.equal(Configuration.idField, docId, ":id")),
mutableListOf(Parameters.json(":data", patch)) mutableListOf(Parameters.json(":data", patch))
@ -55,7 +55,7 @@ object Patch {
) { ) {
val named = Parameters.nameFields(fields) val named = Parameters.nameFields(fields)
conn.customNonQuery( conn.customNonQuery(
Patch.byFields(tableName, named, howMatched), Parameters.addFields( PatchQuery.byFields(tableName, named, howMatched), Parameters.addFields(
named, named,
mutableListOf(Parameters.json(":data", patch)) mutableListOf(Parameters.json(":data", patch))
) )
@ -94,7 +94,7 @@ object Patch {
conn: Connection conn: Connection
) = ) =
conn.customNonQuery( conn.customNonQuery(
Patch.byContains(tableName), PatchQuery.byContains(tableName),
listOf(Parameters.json(":criteria", criteria), Parameters.json(":data", patch)) listOf(Parameters.json(":criteria", criteria), Parameters.json(":data", patch))
) )
@ -120,7 +120,7 @@ object Patch {
*/ */
inline fun <reified TPatch> byJsonPath(tableName: String, path: String, patch: TPatch, conn: Connection) = inline fun <reified TPatch> byJsonPath(tableName: String, path: String, patch: TPatch, conn: Connection) =
conn.customNonQuery( conn.customNonQuery(
Patch.byJsonPath(tableName), PatchQuery.byJsonPath(tableName),
listOf(Parameter(":path", ParameterType.STRING, path), Parameters.json(":data", patch)) listOf(Parameter(":path", ParameterType.STRING, path), Parameters.json(":data", patch))
) )

View File

@ -3,7 +3,7 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.jvm.RemoveFields as JvmRemoveFields import solutions.bitbadger.documents.jvm.RemoveFields as JvmRemoveFields
import solutions.bitbadger.documents.kotlin.extensions.* import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.RemoveFields import solutions.bitbadger.documents.query.RemoveFieldsQuery
import java.sql.Connection import java.sql.Connection
/** /**
@ -83,7 +83,7 @@ object RemoveFields {
) { ) {
val nameParams = Parameters.fieldNames(toRemove) val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery( conn.customNonQuery(
RemoveFields.byContains(tableName, nameParams), RemoveFieldsQuery.byContains(tableName, nameParams),
listOf(Parameters.json(":criteria", criteria), *nameParams.toTypedArray()) listOf(Parameters.json(":criteria", criteria), *nameParams.toTypedArray())
) )
} }

View File

@ -0,0 +1,52 @@
package solutions.bitbadger.documents.kotlin
import kotlinx.serialization.Serializable
import solutions.bitbadger.documents.extensions.insert
/** 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) }
}
}