WIP on reorg

This commit is contained in:
Daniel J. Summers 2025-03-12 16:28:12 -04:00
parent 573d96c6aa
commit 14f0178b63
78 changed files with 2397 additions and 180 deletions

View File

@ -2,10 +2,14 @@
<module version="4">
<component name="AdditionalModuleElements">
<content url="file://$MODULE_DIR$" dumb="true">
<sourceFolder url="file://$MODULE_DIR$/src/common/src/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/common/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/common/test/kotlin" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/common/src/main/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/common/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/common/src/test/kotlin" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/java/src/main/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/java/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/java/src/test/kotlin" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/kotlin/src/main/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/kotlin/src/test/kotlin" isTestSource="true" />
</content>
</component>
</module>

View File

@ -0,0 +1,24 @@
package solutions.bitbadger.documents.common
/**
* The interface for a document serializer/deserializer
*/
interface DocumentSerializer {
/**
* Serialize a document to its JSON representation
*
* @param document The document to be serialized
* @return The JSON representation of the document
*/
fun <TDoc> serialize(document: TDoc): String
/**
* Deserialize a document from its JSON representation
*
* @param json The JSON representation of the document
* @param clazz The class of the document to be deserialized
* @return The document instance represented by the given JSON string
*/
fun <TDoc> deserialize(json: String, clazz: Class<TDoc>): TDoc
}

View File

@ -4,7 +4,7 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.common.AutoId;
import solutions.bitbadger.documents.common.DocumentException;
import solutions.bitbadger.documents.java.testDocs.*;
import solutions.bitbadger.documents.java.java.testDocs.*;
import static org.junit.jupiter.api.Assertions.*;

View File

@ -2,7 +2,6 @@ package solutions.bitbadger.documents.common
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.integration.TEST_TABLE
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@ -101,7 +100,7 @@ class ComparisonInArrayTest {
fun op() =
assertEquals(
Op.IN_ARRAY,
ComparisonInArray(Pair(TEST_TABLE, listOf<String>())).op,
ComparisonInArray(Pair("tbl", listOf<String>())).op,
"InArray comparison should have IN_ARRAY op"
)

View File

@ -4,11 +4,7 @@ import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Configuration
import solutions.bitbadger.documents.common.Dialect
import solutions.bitbadger.documents.common.DocumentException
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.*
import kotlin.test.assertEquals
/**
@ -34,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, Parameters.fieldNames(listOf("a", "z"))),
RemoveFields.byId<String>(tbl, listOf(Parameter(":name", ParameterType.STRING, "{a,z}"))),
"Remove Fields query not constructed correctly"
)
}
@ -45,7 +41,13 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id",
RemoveFields.byId<String>(tbl, Parameters.fieldNames(listOf("a", "z"))),
RemoveFields.byId<String>(
tbl,
listOf(
Parameter(":name0", ParameterType.STRING, "a"),
Parameter(":name1", ParameterType.STRING, "z")
)
),
"Remove Field query not constructed correctly"
)
}
@ -56,7 +58,11 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data->>'f' > :g",
RemoveFields.byFields(tbl, Parameters.fieldNames(listOf("b", "c")), listOf(Field.greater("f", "", ":g"))),
RemoveFields.byFields(
tbl,
listOf(Parameter(":name", ParameterType.STRING, "{b,c}")),
listOf(Field.greater("f", "", ":g"))
),
"Remove Field query not constructed correctly"
)
}
@ -67,7 +73,11 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g",
RemoveFields.byFields(tbl, Parameters.fieldNames(listOf("b", "c")), listOf(Field.greater("f", "", ":g"))),
RemoveFields.byFields(
tbl,
listOf(Parameter(":name0", ParameterType.STRING, "b"), Parameter(":name1", ParameterType.STRING, "c")),
listOf(Field.greater("f", "", ":g"))
),
"Remove Field query not constructed correctly"
)
}
@ -78,7 +88,7 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data @> :criteria",
RemoveFields.byContains(tbl, Parameters.fieldNames(listOf("m", "n"))),
RemoveFields.byContains(tbl, listOf(Parameter(":name", ParameterType.STRING, "{m,n}"))),
"Remove Field query not constructed correctly"
)
}
@ -96,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, Parameters.fieldNames(listOf("o", "p"))),
RemoveFields.byJsonPath(tbl, listOf(Parameter(":name", ParameterType.STRING, "{o,p}"))),
"Remove Field query not constructed correctly"
)
}

113
src/java/pom.xml Normal file
View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>solutions.bitbadger.documents</groupId>
<artifactId>java</artifactId>
<version>4.0.0-alpha1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>solutions.bitbadger</groupId>
<artifactId>documents</artifactId>
<version>4.0.0-alpha1-SNAPSHOT</version>
</parent>
<name>${project.groupId}:${project.artifactId}</name>
<description>Expose a document store interface for PostgreSQL and SQLite (Java Library)</description>
<url>https://bitbadger.solutions/open-source/relational-documents/jvm/</url>
<scm>
<connection>scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git</connection>
<developerConnection>scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git</developerConnection>
<url>https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents</url>
</scm>
<dependencies>
<dependency>
<groupId>solutions.bitbadger.documents</groupId>
<artifactId>common</artifactId>
<version>4.0.0-alpha1-SNAPSHOT</version>
<scope>system</scope>
<systemPath>${project.basedir}/../common/target/common-4.0.0-alpha1-SNAPSHOT.jar</systemPath>
<type>jar</type>
</dependency>
</dependencies>
<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>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/java</sourceDir>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
<configuration>
<compilerPlugins>
<plugin>kotlinx-serialization</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-serialization</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>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.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

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

View File

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

View File

@ -2,7 +2,6 @@ package solutions.bitbadger.documents.java
import solutions.bitbadger.documents.common.Configuration
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.Parameters
import java.sql.Connection
import java.sql.ResultSet

View File

@ -1,8 +1,7 @@
package solutions.bitbadger.documents
package solutions.bitbadger.documents.java
import solutions.bitbadger.documents.common.DocumentIndex
import java.sql.Connection
import solutions.bitbadger.documents.common.query.Definition
/**
* Functions to define tables and indexes

View File

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

View File

@ -1,6 +1,7 @@
package solutions.bitbadger.documents
package solutions.bitbadger.documents.java
import solutions.bitbadger.documents.common.AutoId
import solutions.bitbadger.documents.common.Configuration
import solutions.bitbadger.documents.common.Dialect
import solutions.bitbadger.documents.common.Field
import java.sql.Connection

View File

@ -0,0 +1,15 @@
package solutions.bitbadger.documents.java
import solutions.bitbadger.documents.common.DocumentSerializer
/**
* Configuration for document serialization
*/
object DocumentConfig {
/**
* The serializer to use for documents
*/
@JvmStatic
var serializer: DocumentSerializer = NullDocumentSerializer()
}

View File

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

View File

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

View File

@ -0,0 +1,21 @@
package solutions.bitbadger.documents.java
import solutions.bitbadger.documents.common.DocumentSerializer
/**
* A serializer that tells the user to implement another one
*
* This is the default serializer, so the library itself does not have any firm dependency on any JSON serialization
* library. The tests for this library (will) have an example Jackson-based serializer.
*/
class NullDocumentSerializer : DocumentSerializer {
override fun <TDoc> serialize(document: TDoc): String {
TODO("Replace this serializer in DocumentConfig.serializer")
}
override fun <TDoc> deserialize(json: String, clazz: Class<TDoc>): TDoc {
TODO("Replace this serializer in DocumentConfig.serializer")
}
}

View File

@ -0,0 +1,130 @@
package solutions.bitbadger.documents.java
import solutions.bitbadger.documents.common.*
import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.SQLException
import kotlin.jvm.Throws
/**
* Functions to assist with the creation and implementation of parameters for SQL queries
*
* @author Daniel J. Summers <daniel@bitbadger.solutions>
*/
object Parameters {
/**
* Assign parameter names to any fields that do not have them assigned
*
* @param fields The collection of fields to be named
* @return The collection of fields with parameter names assigned
*/
@JvmStatic
fun nameFields(fields: Collection<Field<*>>): Collection<Field<*>> {
val name = ParameterName()
return fields.map {
if (it.parameterName.isNullOrEmpty() && !listOf(Op.EXISTS, Op.NOT_EXISTS).contains(it.comparison.op)) {
it.withParameterName(name.derive(null))
} else {
it
}
}
}
/**
* Create a parameter by encoding a JSON object
*
* @param name The parameter name
* @param value The object to be encoded as JSON
* @return A parameter with the value encoded
*/
@JvmStatic
fun <T> json(name: String, value: T) =
Parameter(name, ParameterType.JSON, DocumentConfig.serializer.serialize(value))
/**
* Add field parameters to the given set of parameters
*
* @param fields The fields being compared in the query
* @param existing Any existing parameters for the query (optional, defaults to empty collection)
* @return A collection of parameters for the query
*/
@JvmStatic
fun addFields(fields: Collection<Field<*>>, existing: MutableCollection<Parameter<*>> = mutableListOf()) =
fields.fold(existing) { acc, field -> field.appendParameter(acc) }
/**
* Replace the parameter names in the query with question marks
*
* @param query The query with named placeholders
* @param parameters The parameters for the query
* @return The query, with name parameters changed to `?`s
*/
@JvmStatic
fun replaceNamesInQuery(query: String, parameters: Collection<Parameter<*>>) =
parameters.sortedByDescending { it.name.length }.fold(query) { acc, param -> acc.replace(param.name, "?") }
/**
* Apply the given parameters to the given query, returning a prepared statement
*
* @param conn The active JDBC connection
* @param query The query
* @param parameters The parameters for the query
* @return A `PreparedStatement` with the parameter names replaced with `?` and parameter values bound
* @throws DocumentException If parameter names are invalid or number value types are invalid
*/
@Throws(DocumentException::class)
@JvmStatic
fun apply(conn: Connection, query: String, parameters: Collection<Parameter<*>>): PreparedStatement {
if (parameters.isEmpty()) return try {
conn.prepareStatement(query)
} catch (ex: SQLException) {
throw DocumentException("Error preparing no-parameter query: ${ex.message}", ex)
}
val replacements = mutableListOf<Pair<Int, Parameter<*>>>()
parameters.sortedByDescending { it.name.length }.forEach {
var startPos = query.indexOf(it.name)
while (startPos > -1) {
replacements.add(Pair(startPos, it))
startPos = query.indexOf(it.name, startPos + it.name.length + 1)
}
}
return try {
replaceNamesInQuery(query, parameters)
//.also(::println)
.let { conn.prepareStatement(it) }
.also { stmt ->
replacements.sortedBy { it.first }
.map { it.second }
.forEachIndexed { index, param -> param.bind(stmt, index + 1) }
}
} catch (ex: SQLException) {
throw DocumentException("Error creating query / binding parameters: ${ex.message}", ex)
}
}
/**
* Create parameters for field names to be removed from a document
*
* @param names The names of the fields to be removed
* @param parameterName The parameter name to use for the query
* @return A list of parameters to use for building the query
* @throws DocumentException If the dialect has not been set
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun fieldNames(names: Collection<String>, parameterName: String = ":name"): MutableCollection<Parameter<*>> =
when (Configuration.dialect("generate field name parameters")) {
Dialect.POSTGRESQL -> mutableListOf(
Parameter(parameterName, ParameterType.STRING, names.joinToString(",").let { "{$it}" })
)
Dialect.SQLITE -> names.mapIndexed { index, name ->
Parameter("$parameterName$index", ParameterType.STRING, name)
}.toMutableList()
}
}

View File

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

View File

@ -0,0 +1,154 @@
package solutions.bitbadger.documents.java
import solutions.bitbadger.documents.common.*
import java.sql.Connection
/**
* Functions to remove fields from documents
*/
object RemoveFields {
/**
* Translate field paths to JSON paths for SQLite queries
*
* @param parameters The parameters for the specified fields
* @return The parameters for the specified fields, translated if used for SQLite
*/
private fun translatePath(parameters: MutableCollection<Parameter<*>>): MutableCollection<Parameter<*>> {
val dialect = Configuration.dialect("remove fields")
return when (dialect) {
Dialect.POSTGRESQL -> parameters
Dialect.SQLITE -> parameters.map { Parameter(it.name, it.type, "$.${it.value}") }.toMutableList()
}
}
/**
* Remove fields from a document by its ID
*
* @param tableName The name of the table in which the document's fields should be removed
* @param docId The ID of the document to have fields removed
* @param toRemove The names of the fields to be removed
* @param conn The connection on which the update should be executed
*/
fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>, conn: Connection) {
val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery(
byId(tableName, nameParams, docId),
Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")),
translatePath(nameParams)
)
)
}
/**
* Remove fields from a document by its ID
*
* @param tableName The name of the table in which the document's fields should be removed
* @param docId The ID of the document to have fields removed
* @param toRemove The names of the fields to be removed
*/
fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>) =
Configuration.dbConn().use { byId(tableName, docId, toRemove, it) }
/**
* Remove fields from documents using a field comparison
*
* @param tableName The name of the table in which document fields should be removed
* @param fields The fields which should be compared
* @param toRemove The names of the fields to be removed
* @param howMatched How the fields should be matched
* @param conn The connection on which the update should be executed
*/
fun byFields(
tableName: String,
fields: Collection<Field<*>>,
toRemove: Collection<String>,
howMatched: FieldMatch? = null,
conn: Connection
) {
val named = Parameters.nameFields(fields)
val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery(
byFields(tableName, nameParams, named, howMatched),
Parameters.addFields(named, translatePath(nameParams))
)
}
/**
* Remove fields from documents using a field comparison
*
* @param tableName The name of the table in which document fields should be removed
* @param fields The fields which should be compared
* @param toRemove The names of the fields to be removed
* @param howMatched How the fields should be matched
*/
fun byFields(
tableName: String,
fields: Collection<Field<*>>,
toRemove: Collection<String>,
howMatched: FieldMatch? = null
) =
Configuration.dbConn().use { byFields(tableName, fields, toRemove, howMatched, it) }
/**
* Remove fields from documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which document fields should be removed
* @param criteria The object against which JSON containment should be checked
* @param toRemove The names of the fields to be removed
* @param conn The connection on which the update should be executed
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(
tableName: String,
criteria: TContains,
toRemove: Collection<String>,
conn: Connection
) {
val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery(
RemoveFields.byContains(tableName, nameParams),
listOf(Parameters.json(":criteria", criteria), *nameParams.toTypedArray())
)
}
/**
* Remove fields from documents using a JSON containment query (PostgreSQL only)
*
* @param tableName The name of the table in which document fields should be removed
* @param criteria The object against which JSON containment should be checked
* @param toRemove The names of the fields to be removed
* @throws DocumentException If called on a SQLite connection
*/
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, toRemove: Collection<String>) =
Configuration.dbConn().use { byContains(tableName, criteria, toRemove, it) }
/**
* Remove fields from documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which document fields should be removed
* @param path The JSON path comparison to match
* @param toRemove The names of the fields to be removed
* @param conn The connection on which the update should be executed
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>, conn: Connection) {
val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery(
RemoveFields.byJsonPath(tableName, nameParams),
listOf(Parameter(":path", ParameterType.STRING, path), *nameParams.toTypedArray())
)
}
/**
* Remove fields from documents using a JSON Path match query (PostgreSQL only)
*
* @param tableName The name of the table in which document fields should be removed
* @param path The JSON path comparison to match
* @param toRemove The names of the fields to be removed
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>) =
Configuration.dbConn().use { byJsonPath(tableName, path, toRemove, it) }
}

View File

@ -1,6 +1,6 @@
package solutions.bitbadger.documents.java
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.common.Configuration
import solutions.bitbadger.documents.common.Dialect
import solutions.bitbadger.documents.common.DocumentException
import java.sql.PreparedStatement
@ -20,7 +20,7 @@ object Results {
*/
@JvmStatic
fun <TDoc> fromDocument(field: String, rs: ResultSet, clazz: Class<TDoc>) =
Configuration.serializer.deserialize(rs.getString(field), clazz)
DocumentConfig.serializer.deserialize(rs.getString(field), clazz)
/**
* Create a domain item from a document

View File

@ -1,13 +1,10 @@
package solutions.bitbadger.documents.java;
package solutions.bitbadger.documents.java.java;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.*;
import solutions.bitbadger.documents.common.DocumentException;
import solutions.bitbadger.documents.common.Field;
import solutions.bitbadger.documents.common.Parameter;
import solutions.bitbadger.documents.common.ParameterType;
import solutions.bitbadger.documents.common.*;
import solutions.bitbadger.documents.java.Parameters;
import java.util.List;
@ -16,7 +13,7 @@ import static org.junit.jupiter.api.Assertions.*;
/**
* Unit tests for the `Parameters` object
*/
@DisplayName("Java | Parameters")
@DisplayName("Java | Java | Parameters")
final public class ParametersTest {
/**

View File

@ -0,0 +1,20 @@
package solutions.bitbadger.documents.java.java.integration.common;
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase;
import solutions.bitbadger.documents.java.java.testDocs.JsonDocument;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static solutions.bitbadger.documents.java.integration.TypesKt.TEST_TABLE;
final public class Count {
public static void all(ThrowawayDatabase db) {
JsonDocument.load(db);
assertEquals(5L, solutions.bitbadger.documents.java.Count.all(TEST_TABLE, db.getConn()),
"There should have been 5 documents in the table");
}
private Count() {
}
}

View File

@ -1,9 +1,9 @@
package solutions.bitbadger.documents.java.integration.postgresql;
package solutions.bitbadger.documents.java.java.integration.postgresql;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.integration.postgresql.PgDB;
import solutions.bitbadger.documents.java.integration.common.Count;
import solutions.bitbadger.documents.java.integration.postgresql.PgDB;
import solutions.bitbadger.documents.java.java.integration.common.Count;
/**
* PostgreSQL integration tests for the `Count` object / `count*` connection extension functions

View File

@ -1,4 +1,4 @@
package solutions.bitbadger.documents.java.testDocs;
package solutions.bitbadger.documents.java.java.testDocs;
public class ByteIdClass {

View File

@ -1,4 +1,4 @@
package solutions.bitbadger.documents.java.testDocs;
package solutions.bitbadger.documents.java.java.testDocs;
public class IntIdClass {

View File

@ -1,12 +1,12 @@
package solutions.bitbadger.documents.java.testDocs;
package solutions.bitbadger.documents.java.java.testDocs;
import kotlinx.serialization.Serializable;
import solutions.bitbadger.documents.Document;
import solutions.bitbadger.documents.integration.ThrowawayDatabase;
import solutions.bitbadger.documents.java.Document;
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase;
import java.util.List;
import static solutions.bitbadger.documents.integration.TypesKt.TEST_TABLE;
import static solutions.bitbadger.documents.java.integration.TypesKt.TEST_TABLE;
@Serializable
public class JsonDocument {

View File

@ -1,4 +1,4 @@
package solutions.bitbadger.documents.java.testDocs;
package solutions.bitbadger.documents.java.java.testDocs;
public class LongIdClass {

View File

@ -1,4 +1,4 @@
package solutions.bitbadger.documents.java.testDocs;
package solutions.bitbadger.documents.java.java.testDocs;
public class ShortIdClass {

View File

@ -1,4 +1,4 @@
package solutions.bitbadger.documents.java.testDocs;
package solutions.bitbadger.documents.java.java.testDocs;
public class StringIdClass {

View File

@ -1,4 +1,4 @@
package solutions.bitbadger.documents.java.testDocs;
package solutions.bitbadger.documents.java.java.testDocs;
public class SubDocument {

View File

@ -1,4 +1,4 @@
package solutions.bitbadger.documents
package solutions.bitbadger.documents.java
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName
@ -12,7 +12,7 @@ import kotlin.test.assertSame
/**
* Unit tests for the `Parameters` object
*/
@DisplayName("Kotlin | Parameters")
@DisplayName("Kotlin | Java | Parameters")
class ParametersTest {
/**

View File

@ -1,4 +1,4 @@
package solutions.bitbadger.documents.integration
package solutions.bitbadger.documents.java.integration
import java.sql.Connection

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration
package solutions.bitbadger.documents.java.integration
import kotlinx.serialization.Serializable
import solutions.bitbadger.documents.insert
import solutions.bitbadger.documents.java.insert
/** The test table name to use for integration tests */
const val TEST_TABLE = "test_table"

View File

@ -1,10 +1,13 @@
package solutions.bitbadger.documents.integration.common
package solutions.bitbadger.documents.java.integration.common
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.integration.JsonDocument
import solutions.bitbadger.documents.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase
import solutions.bitbadger.documents.java.countAll
import solutions.bitbadger.documents.java.countByContains
import solutions.bitbadger.documents.java.countByFields
import solutions.bitbadger.documents.java.countByJsonPath
import solutions.bitbadger.documents.java.integration.JsonDocument
import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import kotlin.test.assertEquals
/**

View File

@ -1,15 +1,10 @@
package solutions.bitbadger.documents.integration.common
package solutions.bitbadger.documents.java.integration.common
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.integration.JsonDocument
import solutions.bitbadger.documents.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase
import solutions.bitbadger.documents.common.query.Count
import solutions.bitbadger.documents.common.query.Delete
import solutions.bitbadger.documents.common.query.Find
import solutions.bitbadger.documents.common.*
import solutions.bitbadger.documents.java.integration.JsonDocument
import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.Results
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull

View File

@ -1,9 +1,11 @@
package solutions.bitbadger.documents.integration.common
package solutions.bitbadger.documents.java.integration.common
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.DocumentIndex
import solutions.bitbadger.documents.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase
import solutions.bitbadger.documents.java.ensureDocumentIndex
import solutions.bitbadger.documents.java.ensureFieldIndex
import solutions.bitbadger.documents.java.ensureTable
import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import kotlin.test.assertFalse
import kotlin.test.assertTrue

View File

@ -1,10 +1,10 @@
package solutions.bitbadger.documents.integration.common
package solutions.bitbadger.documents.java.integration.common
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.integration.JsonDocument
import solutions.bitbadger.documents.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase
import solutions.bitbadger.documents.java.*
import solutions.bitbadger.documents.java.integration.JsonDocument
import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import kotlin.test.assertEquals
/**

View File

@ -1,8 +1,10 @@
package solutions.bitbadger.documents.integration.common
package solutions.bitbadger.documents.java.integration.common
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.integration.*
import solutions.bitbadger.documents.java.*
import solutions.bitbadger.documents.java.integration.*
import kotlin.test.*
/**

View File

@ -1,10 +1,13 @@
package solutions.bitbadger.documents.integration.common
package solutions.bitbadger.documents.java.integration.common
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.integration.JsonDocument
import solutions.bitbadger.documents.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase
import solutions.bitbadger.documents.java.existsByContains
import solutions.bitbadger.documents.java.existsByFields
import solutions.bitbadger.documents.java.existsById
import solutions.bitbadger.documents.java.existsByJsonPath
import solutions.bitbadger.documents.java.integration.JsonDocument
import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import kotlin.test.assertFalse
import kotlin.test.assertTrue

View File

@ -1,9 +1,11 @@
package solutions.bitbadger.documents.integration.common
package solutions.bitbadger.documents.java.integration.common
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.integration.*
import solutions.bitbadger.documents.java.*
import solutions.bitbadger.documents.java.integration.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
@ -211,7 +213,8 @@ object Find {
fun firstByFieldsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db)
val doc = db.conn.findFirstByFields<JsonDocument>(TEST_TABLE, listOf(Field.equal("sub.foo", "green")), orderBy = listOf(
val doc = db.conn.findFirstByFields<JsonDocument>(
TEST_TABLE, listOf(Field.equal("sub.foo", "green")), orderBy = listOf(
Field.named("n:numValue DESC")))
assertNotNull(doc, "There should have been a document returned")
assertEquals("four", doc.id, "An incorrect document was returned")

View File

@ -1,10 +1,11 @@
package solutions.bitbadger.documents.integration.common
package solutions.bitbadger.documents.java.integration.common
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.integration.JsonDocument
import solutions.bitbadger.documents.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase
import solutions.bitbadger.documents.java.*
import solutions.bitbadger.documents.java.integration.JsonDocument
import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull

View File

@ -1,10 +1,11 @@
package solutions.bitbadger.documents.integration.common
package solutions.bitbadger.documents.java.integration.common
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.integration.JsonDocument
import solutions.bitbadger.documents.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase
import solutions.bitbadger.documents.java.*
import solutions.bitbadger.documents.java.integration.JsonDocument
import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import kotlin.test.*

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.postgresql
package solutions.bitbadger.documents.java.integration.postgresql
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Count
import solutions.bitbadger.documents.java.integration.common.Count
import kotlin.test.Test
/**

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.postgresql
package solutions.bitbadger.documents.java.integration.postgresql
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Custom
import solutions.bitbadger.documents.java.integration.common.Custom
import kotlin.test.Test

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.postgresql
package solutions.bitbadger.documents.java.integration.postgresql
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Definition
import solutions.bitbadger.documents.java.integration.common.Definition
import kotlin.test.Test
/**

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.postgresql
package solutions.bitbadger.documents.java.integration.postgresql
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Delete
import solutions.bitbadger.documents.java.integration.common.Delete
import kotlin.test.Test
/**

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.postgresql
package solutions.bitbadger.documents.java.integration.postgresql
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Document
import solutions.bitbadger.documents.java.integration.common.Document
import kotlin.test.Test
/**

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.postgresql
package solutions.bitbadger.documents.java.integration.postgresql
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Exists
import solutions.bitbadger.documents.java.integration.common.Exists
import kotlin.test.Test
/**

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.postgresql
package solutions.bitbadger.documents.java.integration.postgresql
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Find
import solutions.bitbadger.documents.java.integration.common.Find
import kotlin.test.Test
/**

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.postgresql
package solutions.bitbadger.documents.java.integration.postgresql
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Patch
import solutions.bitbadger.documents.java.integration.common.Patch
import kotlin.test.Test
/**

View File

@ -1,10 +1,10 @@
package solutions.bitbadger.documents.integration.postgresql
package solutions.bitbadger.documents.java.integration.postgresql
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase
import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
/**
* A wrapper for a throwaway PostgreSQL database

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.postgresql
package solutions.bitbadger.documents.java.integration.postgresql
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.RemoveFields
import solutions.bitbadger.documents.java.integration.common.RemoveFields
import kotlin.test.Test
/**

View File

@ -1,9 +1,9 @@
package solutions.bitbadger.documents.integration.sqlite
package solutions.bitbadger.documents.java.integration.sqlite
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException
import solutions.bitbadger.documents.integration.common.Count
import solutions.bitbadger.documents.java.integration.common.Count
import kotlin.test.Test
/**

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.sqlite
package solutions.bitbadger.documents.java.integration.sqlite
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Custom
import solutions.bitbadger.documents.java.integration.common.Custom
import kotlin.test.Test
/**

View File

@ -1,9 +1,9 @@
package solutions.bitbadger.documents.integration.sqlite
package solutions.bitbadger.documents.java.integration.sqlite
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException
import solutions.bitbadger.documents.integration.common.Definition
import solutions.bitbadger.documents.java.integration.common.Definition
import kotlin.test.Test
/**

View File

@ -1,9 +1,9 @@
package solutions.bitbadger.documents.integration.sqlite
package solutions.bitbadger.documents.java.integration.sqlite
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException
import solutions.bitbadger.documents.integration.common.Delete
import solutions.bitbadger.documents.java.integration.common.Delete
import kotlin.test.Test
/**

View File

@ -1,7 +1,7 @@
package solutions.bitbadger.documents.integration.sqlite
package solutions.bitbadger.documents.java.integration.sqlite
import org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Document
import solutions.bitbadger.documents.java.integration.common.Document
import kotlin.test.Test
/**

View File

@ -1,9 +1,9 @@
package solutions.bitbadger.documents.integration.sqlite
package solutions.bitbadger.documents.java.integration.sqlite
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException
import solutions.bitbadger.documents.integration.common.Exists
import solutions.bitbadger.documents.java.integration.common.Exists
import kotlin.test.Test
/**

View File

@ -1,9 +1,9 @@
package solutions.bitbadger.documents.integration.sqlite
package solutions.bitbadger.documents.java.integration.sqlite
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException
import solutions.bitbadger.documents.integration.common.Find
import solutions.bitbadger.documents.java.integration.common.Find
import kotlin.test.Test
/**

View File

@ -1,9 +1,9 @@
package solutions.bitbadger.documents.integration.sqlite
package solutions.bitbadger.documents.java.integration.sqlite
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException
import solutions.bitbadger.documents.integration.common.Patch
import solutions.bitbadger.documents.java.integration.common.Patch
import kotlin.test.Test
/**

View File

@ -1,9 +1,9 @@
package solutions.bitbadger.documents.integration.sqlite
package solutions.bitbadger.documents.java.integration.sqlite
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException
import solutions.bitbadger.documents.integration.common.RemoveFields
import solutions.bitbadger.documents.java.integration.common.RemoveFields
import kotlin.test.Test
/**

View File

@ -1,10 +1,10 @@
package solutions.bitbadger.documents.integration.sqlite
package solutions.bitbadger.documents.java.integration.sqlite
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase
import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import java.io.File
/**

119
src/kotlin/pom.xml Normal file
View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>solutions.bitbadger.documents</groupId>
<artifactId>kotlin</artifactId>
<version>4.0.0-alpha1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>solutions.bitbadger</groupId>
<artifactId>documents</artifactId>
<version>4.0.0-alpha1-SNAPSHOT</version>
</parent>
<name>${project.groupId}:${project.artifactId}</name>
<description>Expose a document store interface for PostgreSQL and SQLite (Kotlin Library)</description>
<url>https://bitbadger.solutions/open-source/relational-documents/jvm/</url>
<scm>
<connection>scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git</connection>
<developerConnection>scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git</developerConnection>
<url>https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents</url>
</scm>
<dependencies>
<dependency>
<groupId>solutions.bitbadger.documents</groupId>
<artifactId>common</artifactId>
<scope>system</scope>
<systemPath>${project.basedir}/../common/target/common-4.0.0-alpha1-SNAPSHOT.jar</systemPath>
<type>jar</type>
</dependency>
<dependency>
<groupId>solutions.bitbadger.documents</groupId>
<artifactId>java</artifactId>
<scope>system</scope>
<systemPath>${project.basedir}/../java/target/java-4.0.0-alpha1-SNAPSHOT.jar</systemPath>
<type>jar</type>
</dependency>
</dependencies>
<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>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/java</sourceDir>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
<configuration>
<compilerPlugins>
<plugin>kotlinx-serialization</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-serialization</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>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.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,12 +1,6 @@
package solutions.bitbadger.documents
package solutions.bitbadger.documents.kotlin
import kotlinx.serialization.json.Json
import solutions.bitbadger.documents.common.AutoId
import solutions.bitbadger.documents.common.Dialect
import solutions.bitbadger.documents.common.DocumentException
import java.sql.Connection
import java.sql.DriverManager
import kotlin.jvm.Throws
object Configuration {

View File

@ -1,9 +1,10 @@
package solutions.bitbadger.documents
package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.common.DocumentIndex
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.java.Document
import java.sql.Connection
import java.sql.ResultSet

View File

@ -1,10 +1,12 @@
package solutions.bitbadger.documents
package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.common.Configuration
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.common.query.Count
import java.sql.Connection
/**

View File

@ -0,0 +1,121 @@
package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.common.Configuration
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.java.Custom
import java.sql.Connection
import java.sql.ResultSet
/**
* Custom query execution functions
*/
object Custom {
/**
* Execute a query that returns a list of results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param clazz The class of the document to be returned
* @param conn The connection over which the query should be executed
* @param mapFunc The mapping function between the document and the domain item
* @return A list of results for the given query
*/
inline fun <reified TDoc> list(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
conn: Connection,
noinline mapFunc: (ResultSet, Class<TDoc>) -> TDoc
) = Custom.list(query, parameters, TDoc::class.java, conn, mapFunc)
/**
* Execute a query that returns a list of results (creates connection)
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return A list of results for the given query
*/
inline fun <reified TDoc> list(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
noinline mapFunc: (ResultSet, Class<TDoc>) -> TDoc
) = Configuration.dbConn().use { list(query, parameters, it, mapFunc) }
/**
* Execute a query that returns one or no results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param conn The connection over which the query should be executed
* @param mapFunc The mapping function between the document and the domain item
* @return The document if one matches the query, `null` otherwise
*/
inline fun <reified TDoc> single(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
conn: Connection,
noinline mapFunc: (ResultSet, Class<TDoc>) -> TDoc
) = Custom.single(query, parameters, TDoc::class.java, conn, mapFunc)
/**
* Execute a query that returns one or no results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return The document if one matches the query, `null` otherwise
*/
inline fun <reified TDoc> single(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
noinline mapFunc: (ResultSet, Class<TDoc>) -> TDoc
) = Configuration.dbConn().use { single(query, parameters, it, mapFunc) }
/**
* Execute a query that returns no results
*
* @param query The query to retrieve the results
* @param conn The connection over which the query should be executed
* @param parameters Parameters to use for the query
*/
fun nonQuery(query: String, parameters: Collection<Parameter<*>> = listOf(), conn: Connection) =
Custom.nonQuery(query, parameters, conn)
/**
* Execute a query that returns no results
*
* @param query The query to retrieve the results
* @param parameters Parameters to use for the query
*/
fun nonQuery(query: String, parameters: Collection<Parameter<*>> = listOf()) =
Configuration.dbConn().use { nonQuery(query, parameters, it) }
/**
* Execute a query that returns a scalar result
*
* @param query The query to retrieve the result
* @param parameters Parameters to use for the query
* @param conn The connection over which the query should be executed
* @param mapFunc The mapping function between the document and the domain item
* @return The scalar value from the query
*/
inline fun <reified T : Any> scalar(
query: String,
parameters: Collection<Parameter<*>> = listOf(),
conn: Connection,
noinline mapFunc: (ResultSet, Class<T>) -> T
) = Custom.scalar(query, parameters, T::class.java, conn, mapFunc)
/**
* Execute a query that returns a scalar result
*
* @param query The query to retrieve the result
* @param parameters Parameters to use for the query
* @param mapFunc The mapping function between the document and the domain item
* @return The scalar value from the query
*/
inline fun <reified T : Any> scalar(
query: String, parameters: Collection<Parameter<*>> = listOf(), noinline mapFunc: (ResultSet, Class<T>) -> T
) = Configuration.dbConn().use { scalar(query, parameters, it, mapFunc) }
}

View File

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

View File

@ -1,10 +1,7 @@
package solutions.bitbadger.documents
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.common.query.Delete
import java.sql.Connection
/**

View File

@ -1,10 +1,7 @@
package solutions.bitbadger.documents
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.common.query.Exists
import java.sql.Connection
/**

View File

@ -1,11 +1,8 @@
package solutions.bitbadger.documents
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType
import java.sql.Connection
import solutions.bitbadger.documents.common.query.Find
import solutions.bitbadger.documents.common.query.orderBy
/**

View File

@ -1,7 +1,4 @@
package solutions.bitbadger.documents
import solutions.bitbadger.documents.common.*
import solutions.bitbadger.documents.common.ParameterName
import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.SQLException

View File

@ -1,10 +1,7 @@
package solutions.bitbadger.documents
import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.common.query.Patch
import java.sql.Connection
/**

View File

@ -1,7 +1,4 @@
package solutions.bitbadger.documents
import solutions.bitbadger.documents.common.*
import solutions.bitbadger.documents.common.query.RemoveFields
import java.sql.Connection
/**

View File

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

View File

@ -46,6 +46,7 @@
<modules>
<module>common</module>
<module>java</module>
</modules>
<dependencies>

View File

@ -2,7 +2,6 @@ package solutions.bitbadger.documents.java;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.*;
import solutions.bitbadger.documents.common.Dialect;
import solutions.bitbadger.documents.common.DocumentException;

View File

@ -1,20 +0,0 @@
package solutions.bitbadger.documents.java.integration.common;
import solutions.bitbadger.documents.integration.ThrowawayDatabase;
import solutions.bitbadger.documents.java.testDocs.JsonDocument;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static solutions.bitbadger.documents.integration.TypesKt.TEST_TABLE;
final public class Count {
public static void all(ThrowawayDatabase db) {
JsonDocument.load(db);
assertEquals(5L, solutions.bitbadger.documents.Count.all(TEST_TABLE, db.getConn()),
"There should have been 5 documents in the table");
}
private Count() {
}
}