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"> <module version="4">
<component name="AdditionalModuleElements"> <component name="AdditionalModuleElements">
<content url="file://$MODULE_DIR$" dumb="true"> <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/src/main/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/common/test/java" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/common/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/common/test/kotlin" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/common/src/test/kotlin" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" 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> </content>
</component> </component>
</module> </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 org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.common.AutoId; import solutions.bitbadger.documents.common.AutoId;
import solutions.bitbadger.documents.common.DocumentException; 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.*; 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.DisplayName
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.integration.TEST_TABLE
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -101,7 +100,7 @@ class ComparisonInArrayTest {
fun op() = fun op() =
assertEquals( assertEquals(
Op.IN_ARRAY, Op.IN_ARRAY,
ComparisonInArray(Pair(TEST_TABLE, listOf<String>())).op, ComparisonInArray(Pair("tbl", listOf<String>())).op,
"InArray comparison should have IN_ARRAY 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.DisplayName
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.*
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 kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
@ -34,7 +30,7 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data->>'id' = :id", "UPDATE $tbl SET data = data - :name::text[] WHERE data->>'id' = :id",
RemoveFields.byId<String>(tbl, Parameters.fieldNames(listOf("a", "z"))), RemoveFields.byId<String>(tbl, listOf(Parameter(":name", ParameterType.STRING, "{a,z}"))),
"Remove Fields query not constructed correctly" "Remove Fields query not constructed correctly"
) )
} }
@ -45,7 +41,13 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id", "UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id",
RemoveFields.byId<String>(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" "Remove Field query not constructed correctly"
) )
} }
@ -56,7 +58,11 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data->>'f' > :g", "UPDATE $tbl SET data = data - :name::text[] WHERE data->>'f' > :g",
RemoveFields.byFields(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" "Remove Field query not constructed correctly"
) )
} }
@ -67,7 +73,11 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.SQLITE Configuration.dialectValue = Dialect.SQLITE
assertEquals( assertEquals(
"UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g", "UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g",
RemoveFields.byFields(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" "Remove Field query not constructed correctly"
) )
} }
@ -78,7 +88,7 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data @> :criteria", "UPDATE $tbl SET data = data - :name::text[] WHERE data @> :criteria",
RemoveFields.byContains(tbl, Parameters.fieldNames(listOf("m", "n"))), RemoveFields.byContains(tbl, listOf(Parameter(":name", ParameterType.STRING, "{m,n}"))),
"Remove Field query not constructed correctly" "Remove Field query not constructed correctly"
) )
} }
@ -96,7 +106,7 @@ class RemoveFieldsTest {
Configuration.dialectValue = Dialect.POSTGRESQL Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)", "UPDATE $tbl SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)",
RemoveFields.byJsonPath(tbl, Parameters.fieldNames(listOf("o", "p"))), RemoveFields.byJsonPath(tbl, listOf(Parameter(":name", ParameterType.STRING, "{o,p}"))),
"Remove Field query not constructed correctly" "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.Configuration
import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.Parameters
import java.sql.Connection import java.sql.Connection
import java.sql.ResultSet 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 solutions.bitbadger.documents.common.DocumentIndex
import java.sql.Connection import java.sql.Connection
import solutions.bitbadger.documents.common.query.Definition
/** /**
* Functions to define tables and indexes * 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.AutoId
import solutions.bitbadger.documents.common.Configuration
import solutions.bitbadger.documents.common.Dialect import solutions.bitbadger.documents.common.Dialect
import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.Field
import java.sql.Connection 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 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.Dialect
import solutions.bitbadger.documents.common.DocumentException import solutions.bitbadger.documents.common.DocumentException
import java.sql.PreparedStatement import java.sql.PreparedStatement
@ -20,7 +20,7 @@ object Results {
*/ */
@JvmStatic @JvmStatic
fun <TDoc> fromDocument(field: String, rs: ResultSet, clazz: Class<TDoc>) = 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 * 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.AfterEach;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.*; import solutions.bitbadger.documents.common.*;
import solutions.bitbadger.documents.common.DocumentException; import solutions.bitbadger.documents.java.Parameters;
import solutions.bitbadger.documents.common.Field;
import solutions.bitbadger.documents.common.Parameter;
import solutions.bitbadger.documents.common.ParameterType;
import java.util.List; import java.util.List;
@ -16,7 +13,7 @@ import static org.junit.jupiter.api.Assertions.*;
/** /**
* Unit tests for the `Parameters` object * Unit tests for the `Parameters` object
*/ */
@DisplayName("Java | Parameters") @DisplayName("Java | Java | Parameters")
final public class ParametersTest { 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.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.integration.postgresql.PgDB; import solutions.bitbadger.documents.java.integration.postgresql.PgDB;
import solutions.bitbadger.documents.java.integration.common.Count; import solutions.bitbadger.documents.java.java.integration.common.Count;
/** /**
* PostgreSQL integration tests for the `Count` object / `count*` connection extension functions * 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 { 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 { 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 kotlinx.serialization.Serializable;
import solutions.bitbadger.documents.Document; import solutions.bitbadger.documents.java.Document;
import solutions.bitbadger.documents.integration.ThrowawayDatabase; import solutions.bitbadger.documents.java.integration.ThrowawayDatabase;
import java.util.List; import java.util.List;
import static solutions.bitbadger.documents.integration.TypesKt.TEST_TABLE; import static solutions.bitbadger.documents.java.integration.TypesKt.TEST_TABLE;
@Serializable @Serializable
public class JsonDocument { 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 { 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 { 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 { 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 { 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.AfterEach
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
@ -12,7 +12,7 @@ import kotlin.test.assertSame
/** /**
* Unit tests for the `Parameters` object * Unit tests for the `Parameters` object
*/ */
@DisplayName("Kotlin | Parameters") @DisplayName("Kotlin | Java | Parameters")
class ParametersTest { class ParametersTest {
/** /**

View File

@ -1,4 +1,4 @@
package solutions.bitbadger.documents.integration package solutions.bitbadger.documents.java.integration
import java.sql.Connection 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 kotlinx.serialization.Serializable
import solutions.bitbadger.documents.insert import solutions.bitbadger.documents.java.insert
/** The test table name to use for integration tests */ /** The test table name to use for integration tests */
const val TEST_TABLE = "test_table" const val TEST_TABLE = "test_table"

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.common.Field
import solutions.bitbadger.documents.integration.JsonDocument import solutions.bitbadger.documents.java.countAll
import solutions.bitbadger.documents.integration.TEST_TABLE import solutions.bitbadger.documents.java.countByContains
import solutions.bitbadger.documents.integration.ThrowawayDatabase 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 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.*
import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.java.integration.JsonDocument
import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.common.ParameterType import solutions.bitbadger.documents.java.Results
import solutions.bitbadger.documents.integration.JsonDocument import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
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 kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertNull 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.common.DocumentIndex
import solutions.bitbadger.documents.integration.TEST_TABLE import solutions.bitbadger.documents.java.ensureDocumentIndex
import solutions.bitbadger.documents.integration.ThrowawayDatabase 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.assertFalse
import kotlin.test.assertTrue 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.common.Field
import solutions.bitbadger.documents.integration.JsonDocument import solutions.bitbadger.documents.java.*
import solutions.bitbadger.documents.integration.TEST_TABLE import solutions.bitbadger.documents.java.integration.JsonDocument
import solutions.bitbadger.documents.integration.ThrowawayDatabase import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import kotlin.test.assertEquals 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.*
import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.integration.* import solutions.bitbadger.documents.integration.*
import solutions.bitbadger.documents.java.*
import solutions.bitbadger.documents.java.integration.*
import kotlin.test.* 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.common.Field
import solutions.bitbadger.documents.integration.JsonDocument import solutions.bitbadger.documents.java.existsByContains
import solutions.bitbadger.documents.integration.TEST_TABLE import solutions.bitbadger.documents.java.existsByFields
import solutions.bitbadger.documents.integration.ThrowawayDatabase 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.assertFalse
import kotlin.test.assertTrue 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.*
import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.integration.* import solutions.bitbadger.documents.integration.*
import solutions.bitbadger.documents.java.*
import solutions.bitbadger.documents.java.integration.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertNull import kotlin.test.assertNull
@ -211,7 +213,8 @@ object Find {
fun firstByFieldsMatchOrdered(db: ThrowawayDatabase) { fun firstByFieldsMatchOrdered(db: ThrowawayDatabase) {
JsonDocument.load(db) 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"))) Field.named("n:numValue DESC")))
assertNotNull(doc, "There should have been a document returned") assertNotNull(doc, "There should have been a document returned")
assertEquals("four", doc.id, "An incorrect document was 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.*
import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.integration.JsonDocument import solutions.bitbadger.documents.java.*
import solutions.bitbadger.documents.integration.TEST_TABLE import solutions.bitbadger.documents.java.integration.JsonDocument
import solutions.bitbadger.documents.integration.ThrowawayDatabase import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertNotNull 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.*
import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.integration.JsonDocument import solutions.bitbadger.documents.java.*
import solutions.bitbadger.documents.integration.TEST_TABLE import solutions.bitbadger.documents.java.integration.JsonDocument
import solutions.bitbadger.documents.integration.ThrowawayDatabase import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import kotlin.test.* 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Count import solutions.bitbadger.documents.java.integration.common.Count
import kotlin.test.Test 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Custom import solutions.bitbadger.documents.java.integration.common.Custom
import kotlin.test.Test 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Definition import solutions.bitbadger.documents.java.integration.common.Definition
import kotlin.test.Test 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Delete import solutions.bitbadger.documents.java.integration.common.Delete
import kotlin.test.Test 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Document import solutions.bitbadger.documents.java.integration.common.Document
import kotlin.test.Test 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Exists import solutions.bitbadger.documents.java.integration.common.Exists
import kotlin.test.Test 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Find import solutions.bitbadger.documents.java.integration.common.Find
import kotlin.test.Test 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Patch import solutions.bitbadger.documents.java.integration.common.Patch
import kotlin.test.Test 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.*
import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.integration.TEST_TABLE import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
/** /**
* A wrapper for a throwaway PostgreSQL database * 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.RemoveFields import solutions.bitbadger.documents.java.integration.common.RemoveFields
import kotlin.test.Test 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.DisplayName
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException 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 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Custom import solutions.bitbadger.documents.java.integration.common.Custom
import kotlin.test.Test 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.DisplayName
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException 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 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.DisplayName
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException 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 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 org.junit.jupiter.api.DisplayName
import solutions.bitbadger.documents.integration.common.Document import solutions.bitbadger.documents.java.integration.common.Document
import kotlin.test.Test 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.DisplayName
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException 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 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.DisplayName
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException 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 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.DisplayName
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException 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 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.DisplayName
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.common.DocumentException 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 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.*
import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.integration.TEST_TABLE import solutions.bitbadger.documents.java.integration.TEST_TABLE
import solutions.bitbadger.documents.integration.ThrowawayDatabase import solutions.bitbadger.documents.java.integration.ThrowawayDatabase
import java.io.File 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 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 { 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.DocumentIndex
import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.Field
import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.java.Document
import java.sql.Connection import java.sql.Connection
import java.sql.ResultSet 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.Field
import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.common.query.Count import solutions.bitbadger.documents.common.query.Count
import java.sql.Connection 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.Field
import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.common.query.Delete
import java.sql.Connection 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.Field
import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.common.query.Exists
import java.sql.Connection 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.Field
import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType import solutions.bitbadger.documents.common.ParameterType
import java.sql.Connection import java.sql.Connection
import solutions.bitbadger.documents.common.query.Find
import solutions.bitbadger.documents.common.query.orderBy 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.*
import solutions.bitbadger.documents.common.ParameterName
import java.sql.Connection import java.sql.Connection
import java.sql.PreparedStatement import java.sql.PreparedStatement
import java.sql.SQLException 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.Field
import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.FieldMatch
import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.Parameter
import solutions.bitbadger.documents.common.ParameterType import solutions.bitbadger.documents.common.ParameterType
import solutions.bitbadger.documents.common.query.Patch
import java.sql.Connection import java.sql.Connection
/** /**

View File

@ -1,7 +1,4 @@
package solutions.bitbadger.documents
import solutions.bitbadger.documents.common.* import solutions.bitbadger.documents.common.*
import solutions.bitbadger.documents.common.query.RemoveFields
import java.sql.Connection 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> <modules>
<module>common</module> <module>common</module>
<module>java</module>
</modules> </modules>
<dependencies> <dependencies>

View File

@ -2,7 +2,6 @@ package solutions.bitbadger.documents.java;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.*;
import solutions.bitbadger.documents.common.Dialect; import solutions.bitbadger.documents.common.Dialect;
import solutions.bitbadger.documents.common.DocumentException; 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() {
}
}