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" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="documents" />
<module name="jvm" />
<module name="kotlin" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel>
<module name="common" target="1.8" />
<module name="documents" target="9" />
<module name="sqlite" target="1.8" />
</bytecodeTargetLevel>
</component>

6
.idea/encodings.xml generated
View File

@ -1,9 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<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/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/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>
</project>

2
.idea/kotlinc.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.1.0" />
<option name="version" value="2.1.10" />
</component>
</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>
</properties>
<!-- <repositories>
<repository>
<id>mavenCentral</id>
<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>
<modules>
<module>src</module>
</modules>
</project>

View File

@ -24,7 +24,7 @@ fun <TDoc> Connection.customList(
clazz: Class<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
@ -41,7 +41,7 @@ fun <TDoc> Connection.customSingle(
clazz: Class<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
@ -50,7 +50,7 @@ fun <TDoc> Connection.customSingle(
* @param parameters Parameters to use for the query
*/
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
@ -67,7 +67,7 @@ fun <T : Any> Connection.customScalar(
clazz: Class<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 ~~~
@ -109,7 +109,7 @@ fun Connection.ensureDocumentIndex(tableName: String, indexType: DocumentIndex)
* @param document The document to be inserted
*/
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")
@ -149,6 +149,7 @@ fun Connection.countAll(tableName: String) =
* @param howMatched How the fields should be matched
* @return A count of the matching documents in the table
*/
@JvmOverloads
fun Connection.countByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
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
* @return True if any matching documents exist, false if not
*/
@JvmOverloads
fun Connection.existsByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
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 howMatched How the fields should be matched
*/
@JvmOverloads
fun Connection.deleteByFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
Delete.byFields(tableName, fields, howMatched, this)

View File

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

View File

@ -4,7 +4,7 @@ import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.DocumentIndex
import solutions.bitbadger.documents.extensions.customNonQuery
import solutions.bitbadger.documents.query.Definition
import solutions.bitbadger.documents.query.DefinitionQuery
import java.sql.Connection
/**
@ -21,8 +21,8 @@ object Definition {
@JvmStatic
fun ensureTable(tableName: String, conn: Connection) =
Configuration.dialect("ensure $tableName exists").let {
conn.customNonQuery(Definition.ensureTable(tableName, it))
conn.customNonQuery(Definition.ensureKey(tableName, it))
conn.customNonQuery(DefinitionQuery.ensureTable(tableName, it))
conn.customNonQuery(DefinitionQuery.ensureKey(tableName, it))
}
/**
@ -44,7 +44,7 @@ object Definition {
*/
@JvmStatic
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
@ -68,7 +68,7 @@ object Definition {
@Throws(DocumentException::class)
@JvmStatic
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)

View File

@ -2,7 +2,7 @@ package solutions.bitbadger.documents.jvm
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.extensions.customNonQuery
import solutions.bitbadger.documents.query.Delete
import solutions.bitbadger.documents.query.DeleteQuery
import java.sql.Connection
/**
@ -20,7 +20,7 @@ object Delete {
@JvmStatic
fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) =
conn.customNonQuery(
Delete.byId(tableName, docId),
DeleteQuery.byId(tableName, docId),
Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id")))
)
@ -46,7 +46,7 @@ object Delete {
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null, conn: Connection) {
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)
@JvmStatic
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)
@ -97,7 +97,7 @@ object Delete {
@Throws(DocumentException::class)
@JvmStatic
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)

View File

@ -2,10 +2,9 @@ package solutions.bitbadger.documents.jvm
import solutions.bitbadger.documents.AutoId
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.extensions.customNonQuery
import solutions.bitbadger.documents.query.Document
import solutions.bitbadger.documents.query.DocumentQuery
import solutions.bitbadger.documents.query.Where
import solutions.bitbadger.documents.query.statementWhere
import java.sql.Connection
@ -25,35 +24,10 @@ object Document {
@JvmStatic
fun <TDoc> insert(tableName: String, document: TDoc, conn: Connection) {
val strategy = Configuration.autoIdStrategy
val query = if (strategy == AutoId.DISABLED) {
Document.insert(tableName)
val query = if (strategy == AutoId.DISABLED || !AutoId.needsAutoId(strategy, document, Configuration.idField)) {
DocumentQuery.insert(tableName)
} else {
val idField = Configuration.idField
val dialect = Configuration.dialect("Create auto-ID insert query")
val dataParam = if (AutoId.needsAutoId(strategy, document, idField)) {
when (dialect) {
Dialect.POSTGRESQL ->
when (strategy) {
AutoId.NUMBER -> "' || (SELECT coalesce(max(data->>'$idField')::numeric, 0) + 1 " +
"FROM $tableName) || '"
AutoId.UUID -> "\"${AutoId.generateUUID()}\""
AutoId.RANDOM_STRING -> "\"${AutoId.generateRandomString()}\""
else -> "\"' || (:data)->>'$idField' || '\""
}.let { ":data::jsonb || ('{\"$idField\":$it}')::jsonb" }
Dialect.SQLITE ->
when (strategy) {
AutoId.NUMBER -> "(SELECT coalesce(max(data->>'$idField'), 0) + 1 FROM $tableName)"
AutoId.UUID -> "'${AutoId.generateUUID()}'"
AutoId.RANDOM_STRING -> "'${AutoId.generateRandomString()}'"
else -> "(:data)->>'$idField'"
}.let { "json_set(:data, '$.$idField', $it)" }
}
} else {
":data"
}
Document.insert(tableName).replace(":data", dataParam)
DocumentQuery.insert(tableName, strategy)
}
conn.customNonQuery(query, listOf(Parameters.json(":data", document)))
}
@ -77,7 +51,7 @@ object Document {
*/
@JvmStatic
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")
@ -100,7 +74,7 @@ object Document {
@JvmStatic
fun <TKey, TDoc> update(tableName: String, docId: TKey, document: TDoc, conn: Connection) =
conn.customNonQuery(
statementWhere(Document.update(tableName), Where.byId(":id", docId)),
statementWhere(DocumentQuery.update(tableName), Where.byId(":id", docId)),
Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")),
mutableListOf(Parameters.json(":data", document))

View File

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

View File

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

View File

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

View File

@ -1,13 +1,15 @@
package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field
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
*/
object Count {
object CountQuery {
/**
* 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)
* @return A query to count documents
*/
@JvmStatic
fun all(tableName: String) =
"SELECT COUNT(*) AS it FROM $tableName"
@ -26,6 +29,8 @@ object Count {
* @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to count documents matching the given fields
*/
@JvmStatic
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
byFieldsBase(all(tableName), fields, howMatched)
@ -34,7 +39,10 @@ object Count {
*
* @param tableName The table in which to count documents (may include schema)
* @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) =
statementWhere(all(tableName), Where.jsonContains())
@ -43,7 +51,10 @@ object Count {
*
* @param tableName The table in which to count documents (may include schema)
* @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) =
statementWhere(all(tableName), Where.jsonPathMatches())
}

View File

@ -1,11 +1,12 @@
package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.*
import kotlin.jvm.Throws
/**
* Functions to create queries to define tables and indexes
*/
object Definition {
object DefinitionQuery {
/**
* 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.)
* @return A query to create a document table
*/
@JvmStatic
fun ensureTableFor(tableName: String, dataType: String) =
"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 dialect The dialect to generate (optional, used in place of current)
* @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) =
when (dialect ?: Configuration.dialect("create table creation query")) {
Dialect.POSTGRESQL -> ensureTableFor(tableName, "JSONB")
@ -47,7 +53,11 @@ object Definition {
* @param fields One or more fields to include in the index
* @param dialect The SQL dialect to use when creating this index (optional, used in place of current)
* @return A query to create the field index
* @throws DocumentException If the dialect is neither provided nor configured
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun ensureIndexOn(
tableName: 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 dialect The SQL dialect to use when creating this index (optional, used in place of current)
* @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) =
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
* @throws DocumentException If the database mode is not PostgreSQL
*/
@Throws(DocumentException::class)
@JvmStatic
fun ensureDocumentIndexOn(tableName: String, indexType: DocumentIndex): String {
if (Configuration.dialect("create document index query") != Dialect.POSTGRESQL) {
throw DocumentException("'Document indexes are only supported on PostgreSQL")

View File

@ -1,14 +1,16 @@
package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch
import kotlin.jvm.Throws
import solutions.bitbadger.documents.query.byFields as byFieldsBase
import solutions.bitbadger.documents.query.byId as byIdBase
/**
* Functions to delete documents
*/
object Delete {
object DeleteQuery {
/**
* 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)
* @return A query to delete a document by its ID
*/
@JvmStatic
@JvmOverloads
fun <TKey> byId(tableName: String, docId: TKey? = null) =
byIdBase(delete(tableName), docId)
@ -37,6 +41,8 @@ object Delete {
* @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to delete documents matching for the given fields
*/
@JvmStatic
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
byFieldsBase(delete(tableName), fields, howMatched)
@ -45,7 +51,10 @@ object Delete {
*
* @param tableName The table from which documents should be deleted (may include schema)
* @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) =
statementWhere(delete(tableName), Where.jsonContains())
@ -54,7 +63,10 @@ object Delete {
*
* @param tableName The table from which documents should be deleted (may include schema)
* @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) =
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.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import kotlin.jvm.Throws
/**
* Functions for document-level operations
*/
object Document {
object DocumentQuery {
/**
* Query to insert a document
*
* @param tableName The table into which to insert (may include schema)
* @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 {
val id = Configuration.idField
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)
* @return A query to save a document
* @throws DocumentException If the dialect is not configured
*/
@Throws(DocumentException::class)
@JvmStatic
fun save(tableName: String) =
insert(tableName, AutoId.DISABLED) +
" 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)
* @return A query to update documents
*/
@JvmStatic
fun update(tableName: String) =
"UPDATE $tableName SET data = :data"
}

View File

@ -1,12 +1,14 @@
package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch
import kotlin.jvm.Throws
/**
* Functions to check for document existence
*/
object Exists {
object ExistsQuery {
/**
* 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)
* @return A query to determine document existence by ID
*/
@JvmStatic
@JvmOverloads
fun <TKey> byId(tableName: String, docId: TKey? = null) =
exists(tableName, Where.byId(docId = docId))
@ -36,6 +40,8 @@ object Exists {
* @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to determine document existence for the given fields
*/
@JvmStatic
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
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)
* @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) =
exists(tableName, Where.jsonContains())
@ -53,7 +62,10 @@ object Exists {
*
* @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
* @throws DocumentException If the database dialect is not PostgreSQL
*/
@Throws(DocumentException::class)
@JvmStatic
fun byJsonPath(tableName: String) =
exists(tableName, Where.jsonPathMatches())
}

View File

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

View File

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

View File

@ -1,3 +1,4 @@
@file:JvmName("QueryUtils")
package solutions.bitbadger.documents.query
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)
* @return A query addressing documents by field matching conditions
*/
@JvmOverloads
fun byFields(statement: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
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
* @return An `ORDER BY` clause for the given fields
*/
@JvmOverloads
fun orderBy(fields: Collection<Field<*>>, dialect: Dialect? = null): String {
val mode = dialect ?: Configuration.dialect("generate ORDER BY clause")
if (fields.isEmpty()) return ""

View File

@ -1,13 +1,14 @@
package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.*
import kotlin.jvm.Throws
import solutions.bitbadger.documents.query.byFields as byFieldsBase
import solutions.bitbadger.documents.query.byId as byIdBase
/**
* Functions to create queries to remove fields from documents
*/
object RemoveFields {
object RemoveFieldsQuery {
/**
* 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 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
* @throws DocumentException If the dialect is not configured
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun <TKey> byId(tableName: String, toRemove: Collection<Parameter<*>>, docId: TKey? = null) =
byIdBase(removeFields(tableName, toRemove), docId)
@ -43,7 +48,11 @@ object RemoveFields {
* @param fields The field criteria
* @param howMatched How the fields should be matched (optional, defaults to `ALL`)
* @return A query to patch JSON documents by field match criteria
* @throws DocumentException If the dialect is not configured
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun byFields(
tableName: String,
toRemove: Collection<Parameter<*>>,
@ -58,7 +67,10 @@ object RemoveFields {
* @param tableName The name of the table where the document is stored
* @param toRemove The parameters for the fields to be removed
* @return A query to patch JSON documents by JSON containment
* @throws DocumentException If the database dialect is not PostgreSQL
*/
@Throws(DocumentException::class)
@JvmStatic
fun byContains(tableName: String, toRemove: Collection<Parameter<*>>) =
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 toRemove The parameters for the fields to be removed
* @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<*>>) =
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.Field
import solutions.bitbadger.documents.FieldMatch
import kotlin.jvm.Throws
/**
* Functions to create `WHERE` clause fragments
@ -18,6 +19,8 @@ object Where {
* @param howMatched How the fields should be matched (optional, defaults to `ALL`)
* @return A `WHERE` clause fragment to match the given fields
*/
@JvmStatic
@JvmOverloads
fun byFields(fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
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 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) =
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
* @throws DocumentException If called against a SQLite database
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun jsonContains(parameterName: String = ":criteria") =
when (Configuration.dialect("create containment WHERE clause")) {
Dialect.POSTGRESQL -> "data @> $parameterName"
@ -50,6 +58,9 @@ object Where {
* @return A `WHERE` clause fragment to implement a JSON path match criterion
* @throws DocumentException If called against a SQLite database
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun jsonPathMatches(parameterName: String = ":path") =
when (Configuration.dialect("create JSON path match WHERE clause")) {
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
*/
@DisplayName("Java | Common | AutoId")
@DisplayName("JVM | Java | AutoId")
final public class AutoIdTest {
@Test

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,15 @@
package solutions.bitbadger.documents.java.jvm.integration.common;
import solutions.bitbadger.documents.DocumentException;
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.support.ThrowawayDatabase;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static solutions.bitbadger.documents.extensions.ConnExt.*;
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
/**
@ -18,42 +19,42 @@ final public class CountFunctions {
public static void all(ThrowawayDatabase 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) {
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");
}
public static void byFieldsAlpha(ThrowawayDatabase 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");
}
public static void byContainsMatch(ThrowawayDatabase db) {
public static void byContainsMatch(ThrowawayDatabase db) throws DocumentException {
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");
}
public static void byContainsNoMatch(ThrowawayDatabase db) {
public static void byContainsNoMatch(ThrowawayDatabase db) throws DocumentException {
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");
}
public static void byJsonPathMatch(ThrowawayDatabase db) {
public static void byJsonPathMatch(ThrowawayDatabase db) throws DocumentException {
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");
}
public static void byJsonPathNoMatch(ThrowawayDatabase db) {
public static void byJsonPathNoMatch(ThrowawayDatabase db) throws DocumentException {
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");
}

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

View File

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

View File

@ -3,6 +3,7 @@ package solutions.bitbadger.documents
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.support.*
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
@ -11,7 +12,7 @@ import kotlin.test.assertTrue
/**
* Unit tests for the `AutoId` enum
*/
@DisplayName("Kotlin | Common | AutoId")
@DisplayName("JVM | Kotlin | AutoId")
class AutoIdTest {
@Test
@ -157,9 +158,3 @@ class AutoIdTest {
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
*/
@DisplayName("ComparisonBetween")
@DisplayName("JVM | Kotlin | ComparisonBetween")
class ComparisonBetweenTest {
@Test
@ -50,7 +50,7 @@ class ComparisonBetweenTest {
/**
* Unit tests for the `ComparisonIn` class
*/
@DisplayName("ComparisonIn")
@DisplayName("JVM | Kotlin | ComparisonIn")
class ComparisonInTest {
@Test
@ -92,7 +92,7 @@ class ComparisonInTest {
/**
* Unit tests for the `ComparisonInArray` class
*/
@DisplayName("ComparisonInArray")
@DisplayName("JVM | Kotlin | ComparisonInArray")
class ComparisonInArrayTest {
@Test
@ -138,7 +138,7 @@ class ComparisonInArrayTest {
/**
* Unit tests for the `ComparisonSingle` class
*/
@DisplayName("ComparisonSingle")
@DisplayName("JVM | Kotlin | ComparisonSingle")
class ComparisonSingleTest {
@Test

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,9 +3,9 @@ package solutions.bitbadger.documents.jvm.integration.common
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.extensions.*
import solutions.bitbadger.documents.jvm.Results
import solutions.bitbadger.documents.query.Count
import solutions.bitbadger.documents.query.Delete
import solutions.bitbadger.documents.query.Find
import solutions.bitbadger.documents.query.CountQuery
import solutions.bitbadger.documents.query.DeleteQuery
import solutions.bitbadger.documents.query.FindQuery
import solutions.bitbadger.documents.support.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
@ -19,26 +19,26 @@ object Custom {
fun listEmpty(db: ThrowawayDatabase) {
JsonDocument.load(db)
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")
}
fun listAll(db: ThrowawayDatabase) {
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")
}
fun singleNone(db: ThrowawayDatabase) =
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"
)
fun singleOne(db: ThrowawayDatabase) {
JsonDocument.load(db)
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"
)
}
@ -46,12 +46,12 @@ object Custom {
fun nonQueryChanges(db: ThrowawayDatabase) {
JsonDocument.load(db)
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"
)
db.conn.customNonQuery("DELETE FROM $TEST_TABLE")
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"
)
}
@ -59,15 +59,15 @@ object Custom {
fun nonQueryNoChanges(db: ThrowawayDatabase) {
JsonDocument.load(db)
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"
)
db.conn.customNonQuery(
Delete.byId(TEST_TABLE, "eighty-two"),
DeleteQuery.byId(TEST_TABLE, "eighty-two"),
listOf(Parameter(":id", ParameterType.STRING, "eighty-two"))
)
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"
)
}

View File

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

View File

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

View File

@ -7,7 +7,7 @@ import kotlin.test.Test
/**
* 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 {
@Test

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ import kotlin.test.Test
/**
* 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 {
@Test

View File

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

View File

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

View File

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

View File

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

View File

@ -13,8 +13,8 @@ import kotlin.test.assertEquals
/**
* Unit tests for the `Count` object
*/
@DisplayName("Kotlin | Common | Query: Count")
class CountTest {
@DisplayName("JVM | Kotlin | Query | CountQuery")
class CountQueryTest {
/** Test table name */
private val tbl = "test_table"
@ -30,7 +30,7 @@ class CountTest {
@Test
@DisplayName("all generates correctly")
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
@DisplayName("byFields generates correctly (PostgreSQL)")
@ -38,7 +38,7 @@ class CountTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"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"
)
}
@ -49,7 +49,7 @@ class CountTest {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"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"
)
}
@ -59,7 +59,7 @@ class CountTest {
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
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"
)
}
@ -68,7 +68,7 @@ class CountTest {
@DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Count.byContains(tbl) }
assertThrows<DocumentException> { CountQuery.byContains(tbl) }
}
@Test
@ -77,7 +77,7 @@ class CountTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"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)")
fun byJsonPathSQLite() {
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
*/
@DisplayName("Kotlin | Common | Query: Definition")
class DefinitionTest {
@DisplayName("JVM | Kotlin | Query | DefinitionQuery")
class DefinitionQueryTest {
/** Test table name */
private val tbl = "test_table"
@ -32,27 +32,27 @@ class DefinitionTest {
fun ensureTableFor() =
assertEquals(
"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
@DisplayName("ensureTable generates correctly (PostgreSQL)")
fun ensureTablePostgres() {
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
@DisplayName("ensureTable generates correctly (SQLite)")
fun ensureTableSQLite() {
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
@DisplayName("ensureTable fails when no dialect is set")
fun ensureTableFailsUnknown() {
assertThrows<DocumentException> { Definition.ensureTable(tbl) }
assertThrows<DocumentException> { DefinitionQuery.ensureTable(tbl) }
}
@Test
@ -60,7 +60,7 @@ class DefinitionTest {
fun ensureKeyWithSchema() =
assertEquals(
"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"
)
@ -69,7 +69,7 @@ class DefinitionTest {
fun ensureKeyWithoutSchema() =
assertEquals(
"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"
)
@ -78,7 +78,7 @@ class DefinitionTest {
fun ensureIndexOnMultipleFields() =
assertEquals(
"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"),
Dialect.POSTGRESQL
),
@ -90,7 +90,7 @@ class DefinitionTest {
fun ensureIndexOnNestedPostgres() =
assertEquals(
"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"
)
@ -99,7 +99,7 @@ class DefinitionTest {
fun ensureIndexOnNestedSQLite() =
assertEquals(
"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"
)
@ -109,7 +109,7 @@ class DefinitionTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"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"
)
}
@ -120,7 +120,7 @@ class DefinitionTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"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"
)
}
@ -129,6 +129,6 @@ class DefinitionTest {
@DisplayName("ensureDocumentIndexOn fails for SQLite")
fun ensureDocumentIndexOnFailsSQLite() {
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
*/
@DisplayName("Kotlin | Common | Query: Delete")
class DeleteTest {
@DisplayName("JVM | Kotlin | Query | DeleteQuery")
class DeleteQueryTest {
/** Test table name */
private val tbl = "test_table"
@ -33,7 +33,7 @@ class DeleteTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"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
assertEquals(
"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() {
Configuration.dialectValue = Dialect.POSTGRESQL
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"
)
}
@ -62,7 +62,7 @@ class DeleteTest {
fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
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"
)
}
@ -72,7 +72,7 @@ class DeleteTest {
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
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)")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Delete.byContains(tbl) }
assertThrows<DocumentException> { DeleteQuery.byContains(tbl) }
}
@Test
@ -88,7 +88,7 @@ class DeleteTest {
fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
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"
)
}
@ -97,6 +97,6 @@ class DeleteTest {
@DisplayName("byJsonPath fails (SQLite)")
fun byJsonPathSQLite() {
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
*/
@DisplayName("Kotlin | Common | Query: Document")
class DocumentTest {
@DisplayName("JVM | Kotlin | Query | DocumentQuery")
class DocumentQueryTest {
/** Test table name */
private val tbl = "test_table"
@ -32,14 +32,14 @@ class DocumentTest {
@DisplayName("insert generates no auto ID (PostgreSQL)")
fun insertNoAutoPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals("INSERT INTO $tbl VALUES (:data)", Document.insert(tbl))
assertEquals("INSERT INTO $tbl VALUES (:data)", DocumentQuery.insert(tbl))
}
@Test
@DisplayName("insert generates no auto ID (SQLite)")
fun insertNoAutoSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertEquals("INSERT INTO $tbl VALUES (:data)", Document.insert(tbl))
assertEquals("INSERT INTO $tbl VALUES (:data)", DocumentQuery.insert(tbl))
}
@Test
@ -49,7 +49,7 @@ class DocumentTest {
assertEquals(
"INSERT INTO $tbl VALUES (:data::jsonb || ('{\"id\":' " +
"|| (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(
"INSERT INTO $tbl VALUES (json_set(:data, '$.id', " +
"(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)")
fun insertAutoUUIDPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
val query = Document.insert(tbl, AutoId.UUID)
val query = DocumentQuery.insert(tbl, AutoId.UUID)
assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""),
"Query start not correct (actual: $query)"
@ -80,7 +80,7 @@ class DocumentTest {
@DisplayName("insert generates auto UUID (SQLite)")
fun insertAutoUUIDSQLite() {
Configuration.dialectValue = Dialect.SQLITE
val query = Document.insert(tbl, AutoId.UUID)
val query = DocumentQuery.insert(tbl, AutoId.UUID)
assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"),
"Query start not correct (actual: $query)"
@ -94,7 +94,7 @@ class DocumentTest {
try {
Configuration.dialectValue = Dialect.POSTGRESQL
Configuration.idStringLength = 8
val query = Document.insert(tbl, AutoId.RANDOM_STRING)
val query = DocumentQuery.insert(tbl, AutoId.RANDOM_STRING)
assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""),
"Query start not correct (actual: $query)"
@ -114,7 +114,7 @@ class DocumentTest {
@DisplayName("insert generates auto random string (SQLite)")
fun insertAutoRandomSQLite() {
Configuration.dialectValue = Dialect.SQLITE
val query = Document.insert(tbl, AutoId.RANDOM_STRING)
val query = DocumentQuery.insert(tbl, AutoId.RANDOM_STRING)
assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"),
"Query start not correct (actual: $query)"
@ -130,7 +130,7 @@ class DocumentTest {
@Test
@DisplayName("insert fails when no dialect is set")
fun insertFailsUnknown() {
assertThrows<DocumentException> { Document.insert(tbl) }
assertThrows<DocumentException> { DocumentQuery.insert(tbl) }
}
@Test
@ -139,12 +139,12 @@ class DocumentTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"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
@DisplayName("update generates successfully")
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
*/
@DisplayName("Kotlin | Common | Query: Exists")
class ExistsTest {
class ExistsQueryTest {
/** Test table name */
private val tbl = "test_table"
@ -33,7 +33,7 @@ class ExistsTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"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
assertEquals(
"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
assertEquals(
"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"
)
}
@ -64,7 +64,7 @@ class ExistsTest {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"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"
)
}
@ -74,7 +74,7 @@ class ExistsTest {
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
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"
)
}
@ -83,7 +83,7 @@ class ExistsTest {
@DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Exists.byContains(tbl) }
assertThrows<DocumentException> { ExistsQuery.byContains(tbl) }
}
@Test
@ -92,7 +92,7 @@ class ExistsTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"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)")
fun byJsonPathSQLite() {
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.DocumentException
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals
/**
* Unit tests for the `Find` object
*/
@DisplayName("Kotlin | Common | Query: Find")
class FindTest {
/** Test table name */
private val tbl = "test_table"
@DisplayName("JVM | Kotlin | Query | FindQuery")
class FindQueryTest {
/**
* Clear the connection string (resets Dialect)
@ -30,15 +28,15 @@ class FindTest {
@Test
@DisplayName("all generates correctly")
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
@DisplayName("byId generates correctly (PostgreSQL)")
fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"SELECT data FROM $tbl WHERE data->>'id' = :id",
Find.byId<String>(tbl), "Find query not constructed correctly"
"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id",
FindQuery.byId<String>(TEST_TABLE), "Find query not constructed correctly"
)
}
@ -47,8 +45,8 @@ class FindTest {
fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"SELECT data FROM $tbl WHERE data->>'id' = :id",
Find.byId<String>(tbl), "Find query not constructed correctly"
"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id",
FindQuery.byId<String>(TEST_TABLE), "Find query not constructed correctly"
)
}
@ -57,8 +55,8 @@ class FindTest {
fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"SELECT data FROM $tbl WHERE data->>'a' = :b AND (data->>'c')::numeric < :d",
Find.byFields(tbl, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))),
"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND (data->>'c')::numeric < :d",
FindQuery.byFields(TEST_TABLE, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))),
"Find query not constructed correctly"
)
}
@ -68,8 +66,8 @@ class FindTest {
fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"SELECT data FROM $tbl WHERE data->>'a' = :b AND data->>'c' < :d",
Find.byFields(tbl, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))),
"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND data->>'c' < :d",
FindQuery.byFields(TEST_TABLE, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))),
"Find query not constructed correctly"
)
}
@ -79,7 +77,7 @@ class FindTest {
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
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"
)
}
@ -88,7 +86,7 @@ class FindTest {
@DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Find.byContains(tbl) }
assertThrows<DocumentException> { FindQuery.byContains(TEST_TABLE) }
}
@Test
@ -96,7 +94,8 @@ class FindTest {
fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
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"
)
}
@ -105,6 +104,6 @@ class FindTest {
@DisplayName("byJsonPath fails (SQLite)")
fun byJsonPathSQLite() {
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
*/
@DisplayName("Kotlin | Common | Query: Patch")
class PatchTest {
@DisplayName("JVM | Kotlin | Query | PatchQuery")
class PatchQueryTest {
/** Test table name */
private val tbl = "test_table"
@ -33,7 +33,7 @@ class PatchTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"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
assertEquals(
"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
assertEquals(
"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"
)
}
@ -64,7 +64,7 @@ class PatchTest {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"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"
)
}
@ -74,7 +74,7 @@ class PatchTest {
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
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"
)
}
@ -83,7 +83,7 @@ class PatchTest {
@DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Patch.byContains(tbl) }
assertThrows<DocumentException> { PatchQuery.byContains(tbl) }
}
@Test
@ -92,7 +92,7 @@ class PatchTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"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)")
fun byJsonPathSQLite() {
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
/**
* 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 {
/**

View File

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

View File

@ -1,22 +1,18 @@
package solutions.bitbadger.documents.support
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())
@ -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) {
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) }
}
}
// 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>
<artifactId>jvm</artifactId>
<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>
</dependency>
</dependencies>

View File

@ -2,7 +2,7 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Count
import solutions.bitbadger.documents.query.CountQuery
import java.sql.Connection
@ -19,7 +19,7 @@ object Count {
* @return A count of the documents in the table
*/
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
@ -47,7 +47,7 @@ object Count {
): Long {
val named = Parameters.nameFields(fields)
return conn.customScalar(
Count.byFields(tableName, named, howMatched),
CountQuery.byFields(tableName, named, howMatched),
Parameters.addFields(named),
Results::toCount
)
@ -74,7 +74,7 @@ object Count {
* @throws DocumentException If called on a SQLite 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)
@ -98,7 +98,7 @@ object Count {
*/
fun byJsonPath(tableName: String, path: String, conn: Connection) =
conn.customScalar(
Count.byJsonPath(tableName),
CountQuery.byJsonPath(tableName),
listOf(Parameter(":path", ParameterType.STRING, path)),
Results::toCount
)

View File

@ -3,7 +3,7 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.jvm.Delete as JvmDelete
import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Delete
import solutions.bitbadger.documents.query.DeleteQuery
import java.sql.Connection
/**
@ -60,7 +60,7 @@ object Delete {
* @throws DocumentException If called on a SQLite 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)

View File

@ -5,7 +5,7 @@ import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.extensions.customNonQuery
import solutions.bitbadger.documents.query.Document
import solutions.bitbadger.documents.query.DocumentQuery
import solutions.bitbadger.documents.query.Where
import solutions.bitbadger.documents.query.statementWhere
import java.sql.Connection
@ -25,7 +25,7 @@ object Document {
inline fun <reified TDoc> insert(tableName: String, document: TDoc, conn: Connection) {
val strategy = Configuration.autoIdStrategy
val query = if (strategy == AutoId.DISABLED) {
Document.insert(tableName)
DocumentQuery.insert(tableName)
} else {
val idField = Configuration.idField
val dialect = Configuration.dialect("Create auto-ID insert query")
@ -52,7 +52,7 @@ object Document {
":data"
}
Document.insert(tableName).replace(":data", dataParam)
DocumentQuery.insert(tableName).replace(":data", dataParam)
}
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
*/
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")
@ -95,7 +95,7 @@ object Document {
*/
inline fun <TKey, reified TDoc> update(tableName: String, docId: TKey, document: TDoc, conn: Connection) =
conn.customNonQuery(
statementWhere(Document.update(tableName), Where.byId(":id", docId)),
statementWhere(DocumentQuery.update(tableName), Where.byId(":id", docId)),
Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")),
mutableListOf(Parameters.json(":data", document))

View File

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

View File

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

View File

@ -2,7 +2,7 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Patch
import solutions.bitbadger.documents.query.PatchQuery
import java.sql.Connection
/**
@ -20,7 +20,7 @@ object Patch {
*/
inline fun <TKey, reified TPatch> byId(tableName: String, docId: TKey, patch: TPatch, conn: Connection) =
conn.customNonQuery(
Patch.byId(tableName, docId),
PatchQuery.byId(tableName, docId),
Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")),
mutableListOf(Parameters.json(":data", patch))
@ -55,7 +55,7 @@ object Patch {
) {
val named = Parameters.nameFields(fields)
conn.customNonQuery(
Patch.byFields(tableName, named, howMatched), Parameters.addFields(
PatchQuery.byFields(tableName, named, howMatched), Parameters.addFields(
named,
mutableListOf(Parameters.json(":data", patch))
)
@ -94,7 +94,7 @@ object Patch {
conn: Connection
) =
conn.customNonQuery(
Patch.byContains(tableName),
PatchQuery.byContains(tableName),
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) =
conn.customNonQuery(
Patch.byJsonPath(tableName),
PatchQuery.byJsonPath(tableName),
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.jvm.RemoveFields as JvmRemoveFields
import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.RemoveFields
import solutions.bitbadger.documents.query.RemoveFieldsQuery
import java.sql.Connection
/**
@ -83,7 +83,7 @@ object RemoveFields {
) {
val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery(
RemoveFields.byContains(tableName, nameParams),
RemoveFieldsQuery.byContains(tableName, nameParams),
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) }
}
}