From 14f0178b639bc96af975fb59b02a7309a6d5ad66 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Wed, 12 Mar 2025 16:28:12 -0400 Subject: [PATCH] WIP on reorg --- documents.iml | 12 +- .../src/main/kotlin/DocumentSerializer.kt | 24 + .../documents/common/java/AutoIdTest.java | 2 +- src/common/src/test/kotlin/ComparisonTest.kt | 3 +- .../src/test/kotlin/query/RemoveFieldsTest.kt | 32 +- src/java/pom.xml | 113 +++++ .../src/main/kotlin/ConnectionExtensions.kt | 477 ++++++++++++++++++ src/java/src/main/kotlin/Count.kt | 127 +++++ .../java => java/src/main/kotlin}/Custom.kt | 1 - src/{ => java/src}/main/kotlin/Definition.kt | 3 +- src/java/src/main/kotlin/Delete.kt | 100 ++++ src/{ => java/src}/main/kotlin/Document.kt | 3 +- src/java/src/main/kotlin/DocumentConfig.kt | 15 + src/java/src/main/kotlin/Exists.kt | 127 +++++ src/java/src/main/kotlin/Find.kt | 403 +++++++++++++++ .../src/main/kotlin/NullDocumentSerializer.kt | 21 + src/java/src/main/kotlin/Parameters.kt | 130 +++++ src/java/src/main/kotlin/Patch.kt | 138 +++++ src/java/src/main/kotlin/RemoveFields.kt | 154 ++++++ .../java => java/src/main/kotlin}/Results.kt | 4 +- .../documents/java}/java/ParametersTest.java | 11 +- .../java/java/integration/common/Count.java | 20 + .../java/integration/postgresql/CountIT.java | 6 +- .../java}/java/testDocs/ByteIdClass.java | 2 +- .../java}/java/testDocs/IntIdClass.java | 2 +- .../java}/java/testDocs/JsonDocument.java | 8 +- .../java}/java/testDocs/LongIdClass.java | 2 +- .../java}/java/testDocs/ShortIdClass.java | 2 +- .../java}/java/testDocs/StringIdClass.java | 2 +- .../java}/java/testDocs/SubDocument.java | 2 +- .../src}/test/kotlin/ParametersTest.kt | 4 +- .../kotlin/integration/ThrowawayDatabase.kt | 2 +- .../src}/test/kotlin/integration/Types.kt | 4 +- .../test/kotlin/integration/common/Count.kt | 13 +- .../test/kotlin/integration/common/Custom.kt | 17 +- .../kotlin/integration/common/Definition.kt | 10 +- .../test/kotlin/integration/common/Delete.kt | 10 +- .../kotlin/integration/common/Document.kt | 4 +- .../test/kotlin/integration/common/Exists.kt | 13 +- .../test/kotlin/integration/common/Find.kt | 7 +- .../test/kotlin/integration/common/Patch.kt | 9 +- .../kotlin/integration/common/RemoveFields.kt | 9 +- .../kotlin/integration/postgresql/CountIT.kt | 4 +- .../kotlin/integration/postgresql/CustomIT.kt | 4 +- .../integration/postgresql/DefinitionIT.kt | 4 +- .../kotlin/integration/postgresql/DeleteIT.kt | 4 +- .../integration/postgresql/DocumentIT.kt | 4 +- .../kotlin/integration/postgresql/ExistsIT.kt | 4 +- .../kotlin/integration/postgresql/FindIT.kt | 4 +- .../kotlin/integration/postgresql/PatchIT.kt | 4 +- .../kotlin/integration/postgresql/PgDB.kt | 6 +- .../integration/postgresql/RemoveFieldsIT.kt | 4 +- .../test/kotlin/integration/sqlite/CountIT.kt | 4 +- .../kotlin/integration/sqlite/CustomIT.kt | 4 +- .../kotlin/integration/sqlite/DefinitionIT.kt | 4 +- .../kotlin/integration/sqlite/DeleteIT.kt | 4 +- .../kotlin/integration/sqlite/DocumentIT.kt | 4 +- .../kotlin/integration/sqlite/ExistsIT.kt | 4 +- .../test/kotlin/integration/sqlite/FindIT.kt | 4 +- .../test/kotlin/integration/sqlite/PatchIT.kt | 4 +- .../integration/sqlite/RemoveFieldsIT.kt | 4 +- .../kotlin/integration/sqlite/SQLiteDB.kt | 6 +- src/kotlin/pom.xml | 119 +++++ .../src}/main/kotlin/Configuration.kt | 8 +- .../src}/main/kotlin/ConnectionExtensions.kt | 3 +- src/{ => kotlin/src}/main/kotlin/Count.kt | 4 +- src/kotlin/src/main/kotlin/Custom.kt | 121 +++++ src/kotlin/src/main/kotlin/Definition.kt | 70 +++ src/{ => kotlin/src}/main/kotlin/Delete.kt | 3 - src/{ => kotlin/src}/main/kotlin/Exists.kt | 3 - src/{ => kotlin/src}/main/kotlin/Find.kt | 3 - .../src}/main/kotlin/Parameters.kt | 3 - src/{ => kotlin/src}/main/kotlin/Patch.kt | 3 - .../src}/main/kotlin/RemoveFields.kt | 3 - src/kotlin/src/main/kotlin/Results.kt | 80 +++ src/pom.xml | 1 + .../documents/java/ConfigurationTest.java | 1 - .../java/integration/common/Count.java | 20 - 78 files changed, 2397 insertions(+), 180 deletions(-) create mode 100644 src/common/src/main/kotlin/DocumentSerializer.kt create mode 100644 src/java/pom.xml create mode 100644 src/java/src/main/kotlin/ConnectionExtensions.kt create mode 100644 src/java/src/main/kotlin/Count.kt rename src/{main/kotlin/java => java/src/main/kotlin}/Custom.kt (99%) rename src/{ => java/src}/main/kotlin/Definition.kt (96%) create mode 100644 src/java/src/main/kotlin/Delete.kt rename src/{ => java/src}/main/kotlin/Document.kt (98%) create mode 100644 src/java/src/main/kotlin/DocumentConfig.kt create mode 100644 src/java/src/main/kotlin/Exists.kt create mode 100644 src/java/src/main/kotlin/Find.kt create mode 100644 src/java/src/main/kotlin/NullDocumentSerializer.kt create mode 100644 src/java/src/main/kotlin/Parameters.kt create mode 100644 src/java/src/main/kotlin/Patch.kt create mode 100644 src/java/src/main/kotlin/RemoveFields.kt rename src/{main/kotlin/java => java/src/main/kotlin}/Results.kt (96%) rename src/{test/java/solutions/bitbadger/documents => java/src/test/java/solutions/bitbadger/documents/java}/java/ParametersTest.java (94%) create mode 100644 src/java/src/test/java/solutions/bitbadger/documents/java/java/integration/common/Count.java rename src/{test/java/solutions/bitbadger/documents => java/src/test/java/solutions/bitbadger/documents/java}/java/integration/postgresql/CountIT.java (65%) rename src/{test/java/solutions/bitbadger/documents => java/src/test/java/solutions/bitbadger/documents/java}/java/testDocs/ByteIdClass.java (79%) rename src/{test/java/solutions/bitbadger/documents => java/src/test/java/solutions/bitbadger/documents/java}/java/testDocs/IntIdClass.java (79%) rename src/{test/java/solutions/bitbadger/documents => java/src/test/java/solutions/bitbadger/documents/java}/java/testDocs/JsonDocument.java (87%) rename src/{test/java/solutions/bitbadger/documents => java/src/test/java/solutions/bitbadger/documents/java}/java/testDocs/LongIdClass.java (79%) rename src/{test/java/solutions/bitbadger/documents => java/src/test/java/solutions/bitbadger/documents/java}/java/testDocs/ShortIdClass.java (80%) rename src/{test/java/solutions/bitbadger/documents => java/src/test/java/solutions/bitbadger/documents/java}/java/testDocs/StringIdClass.java (80%) rename src/{test/java/solutions/bitbadger/documents => java/src/test/java/solutions/bitbadger/documents/java}/java/testDocs/SubDocument.java (89%) rename src/{ => java/src}/test/kotlin/ParametersTest.kt (98%) rename src/{ => java/src}/test/kotlin/integration/ThrowawayDatabase.kt (89%) rename src/{ => java/src}/test/kotlin/integration/Types.kt (93%) rename src/{ => java/src}/test/kotlin/integration/common/Count.kt (80%) rename src/{ => java/src}/test/kotlin/integration/common/Custom.kt (81%) rename src/{ => java/src}/test/kotlin/integration/common/Definition.kt (85%) rename src/{ => java/src}/test/kotlin/integration/common/Delete.kt (90%) rename src/{ => java/src}/test/kotlin/integration/common/Document.kt (97%) rename src/{ => java/src}/test/kotlin/integration/common/Exists.kt (80%) rename src/{ => java/src}/test/kotlin/integration/common/Find.kt (97%) rename src/{ => java/src}/test/kotlin/integration/common/Patch.kt (92%) rename src/{ => java/src}/test/kotlin/integration/common/RemoveFields.kt (93%) rename src/{ => java/src}/test/kotlin/integration/postgresql/CountIT.kt (90%) rename src/{ => java/src}/test/kotlin/integration/postgresql/CustomIT.kt (89%) rename src/{ => java/src}/test/kotlin/integration/postgresql/DefinitionIT.kt (86%) rename src/{ => java/src}/test/kotlin/integration/postgresql/DeleteIT.kt (90%) rename src/{ => java/src}/test/kotlin/integration/postgresql/DocumentIT.kt (91%) rename src/{ => java/src}/test/kotlin/integration/postgresql/ExistsIT.kt (91%) rename src/{ => java/src}/test/kotlin/integration/postgresql/FindIT.kt (97%) rename src/{ => java/src}/test/kotlin/integration/postgresql/PatchIT.kt (90%) rename src/{ => java/src}/test/kotlin/integration/postgresql/PgDB.kt (88%) rename src/{ => java/src}/test/kotlin/integration/postgresql/RemoveFieldsIT.kt (94%) rename src/{ => java/src}/test/kotlin/integration/sqlite/CountIT.kt (89%) rename src/{ => java/src}/test/kotlin/integration/sqlite/CustomIT.kt (89%) rename src/{ => java/src}/test/kotlin/integration/sqlite/DefinitionIT.kt (88%) rename src/{ => java/src}/test/kotlin/integration/sqlite/DeleteIT.kt (90%) rename src/{ => java/src}/test/kotlin/integration/sqlite/DocumentIT.kt (91%) rename src/{ => java/src}/test/kotlin/integration/sqlite/ExistsIT.kt (90%) rename src/{ => java/src}/test/kotlin/integration/sqlite/FindIT.kt (96%) rename src/{ => java/src}/test/kotlin/integration/sqlite/PatchIT.kt (90%) rename src/{ => java/src}/test/kotlin/integration/sqlite/RemoveFieldsIT.kt (92%) rename src/{ => java/src}/test/kotlin/integration/sqlite/SQLiteDB.kt (81%) create mode 100644 src/kotlin/pom.xml rename src/{ => kotlin/src}/main/kotlin/Configuration.kt (68%) rename src/{ => kotlin/src}/main/kotlin/ConnectionExtensions.kt (99%) rename src/{ => kotlin/src}/main/kotlin/Count.kt (97%) create mode 100644 src/kotlin/src/main/kotlin/Custom.kt create mode 100644 src/kotlin/src/main/kotlin/Definition.kt rename src/{ => kotlin/src}/main/kotlin/Delete.kt (97%) rename src/{ => kotlin/src}/main/kotlin/Exists.kt (98%) rename src/{ => kotlin/src}/main/kotlin/Find.kt (99%) rename src/{ => kotlin/src}/main/kotlin/Parameters.kt (98%) rename src/{ => kotlin/src}/main/kotlin/Patch.kt (98%) rename src/{ => kotlin/src}/main/kotlin/RemoveFields.kt (98%) create mode 100644 src/kotlin/src/main/kotlin/Results.kt delete mode 100644 src/test/java/solutions/bitbadger/documents/java/integration/common/Count.java diff --git a/documents.iml b/documents.iml index ab059ae..f0b3e9b 100644 --- a/documents.iml +++ b/documents.iml @@ -2,10 +2,14 @@ - - - - + + + + + + + + \ No newline at end of file diff --git a/src/common/src/main/kotlin/DocumentSerializer.kt b/src/common/src/main/kotlin/DocumentSerializer.kt new file mode 100644 index 0000000..09cb1c9 --- /dev/null +++ b/src/common/src/main/kotlin/DocumentSerializer.kt @@ -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 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 deserialize(json: String, clazz: Class): TDoc +} diff --git a/src/common/src/test/java/solutions/bitbadger/documents/common/java/AutoIdTest.java b/src/common/src/test/java/solutions/bitbadger/documents/common/java/AutoIdTest.java index 1708332..9321974 100644 --- a/src/common/src/test/java/solutions/bitbadger/documents/common/java/AutoIdTest.java +++ b/src/common/src/test/java/solutions/bitbadger/documents/common/java/AutoIdTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import solutions.bitbadger.documents.common.AutoId; import solutions.bitbadger.documents.common.DocumentException; -import solutions.bitbadger.documents.java.testDocs.*; +import solutions.bitbadger.documents.java.java.testDocs.*; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/common/src/test/kotlin/ComparisonTest.kt b/src/common/src/test/kotlin/ComparisonTest.kt index 3cd54ad..b40a89e 100644 --- a/src/common/src/test/kotlin/ComparisonTest.kt +++ b/src/common/src/test/kotlin/ComparisonTest.kt @@ -2,7 +2,6 @@ package solutions.bitbadger.documents.common import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import solutions.bitbadger.documents.integration.TEST_TABLE import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -101,7 +100,7 @@ class ComparisonInArrayTest { fun op() = assertEquals( Op.IN_ARRAY, - ComparisonInArray(Pair(TEST_TABLE, listOf())).op, + ComparisonInArray(Pair("tbl", listOf())).op, "InArray comparison should have IN_ARRAY op" ) diff --git a/src/common/src/test/kotlin/query/RemoveFieldsTest.kt b/src/common/src/test/kotlin/query/RemoveFieldsTest.kt index 498a435..1710e0b 100644 --- a/src/common/src/test/kotlin/query/RemoveFieldsTest.kt +++ b/src/common/src/test/kotlin/query/RemoveFieldsTest.kt @@ -4,11 +4,7 @@ import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.common.Configuration -import solutions.bitbadger.documents.common.Dialect -import solutions.bitbadger.documents.common.DocumentException -import solutions.bitbadger.documents.common.Field +import solutions.bitbadger.documents.common.* import kotlin.test.assertEquals /** @@ -34,7 +30,7 @@ class RemoveFieldsTest { Configuration.dialectValue = Dialect.POSTGRESQL assertEquals( "UPDATE $tbl SET data = data - :name::text[] WHERE data->>'id' = :id", - RemoveFields.byId(tbl, Parameters.fieldNames(listOf("a", "z"))), + RemoveFields.byId(tbl, listOf(Parameter(":name", ParameterType.STRING, "{a,z}"))), "Remove Fields query not constructed correctly" ) } @@ -45,7 +41,13 @@ class RemoveFieldsTest { Configuration.dialectValue = Dialect.SQLITE assertEquals( "UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id", - RemoveFields.byId(tbl, Parameters.fieldNames(listOf("a", "z"))), + RemoveFields.byId( + tbl, + listOf( + Parameter(":name0", ParameterType.STRING, "a"), + Parameter(":name1", ParameterType.STRING, "z") + ) + ), "Remove Field query not constructed correctly" ) } @@ -56,7 +58,11 @@ class RemoveFieldsTest { Configuration.dialectValue = Dialect.POSTGRESQL assertEquals( "UPDATE $tbl SET data = data - :name::text[] WHERE data->>'f' > :g", - RemoveFields.byFields(tbl, Parameters.fieldNames(listOf("b", "c")), listOf(Field.greater("f", "", ":g"))), + RemoveFields.byFields( + tbl, + listOf(Parameter(":name", ParameterType.STRING, "{b,c}")), + listOf(Field.greater("f", "", ":g")) + ), "Remove Field query not constructed correctly" ) } @@ -67,7 +73,11 @@ class RemoveFieldsTest { Configuration.dialectValue = Dialect.SQLITE assertEquals( "UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g", - RemoveFields.byFields(tbl, Parameters.fieldNames(listOf("b", "c")), listOf(Field.greater("f", "", ":g"))), + RemoveFields.byFields( + tbl, + listOf(Parameter(":name0", ParameterType.STRING, "b"), Parameter(":name1", ParameterType.STRING, "c")), + listOf(Field.greater("f", "", ":g")) + ), "Remove Field query not constructed correctly" ) } @@ -78,7 +88,7 @@ class RemoveFieldsTest { Configuration.dialectValue = Dialect.POSTGRESQL assertEquals( "UPDATE $tbl SET data = data - :name::text[] WHERE data @> :criteria", - RemoveFields.byContains(tbl, Parameters.fieldNames(listOf("m", "n"))), + RemoveFields.byContains(tbl, listOf(Parameter(":name", ParameterType.STRING, "{m,n}"))), "Remove Field query not constructed correctly" ) } @@ -96,7 +106,7 @@ class RemoveFieldsTest { Configuration.dialectValue = Dialect.POSTGRESQL assertEquals( "UPDATE $tbl SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)", - RemoveFields.byJsonPath(tbl, Parameters.fieldNames(listOf("o", "p"))), + RemoveFields.byJsonPath(tbl, listOf(Parameter(":name", ParameterType.STRING, "{o,p}"))), "Remove Field query not constructed correctly" ) } diff --git a/src/java/pom.xml b/src/java/pom.xml new file mode 100644 index 0000000..0b272c0 --- /dev/null +++ b/src/java/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + solutions.bitbadger.documents + java + 4.0.0-alpha1-SNAPSHOT + jar + + + solutions.bitbadger + documents + 4.0.0-alpha1-SNAPSHOT + + + ${project.groupId}:${project.artifactId} + Expose a document store interface for PostgreSQL and SQLite (Java Library) + https://bitbadger.solutions/open-source/relational-documents/jvm/ + + + scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git + scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git + https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents + + + + + solutions.bitbadger.documents + common + 4.0.0-alpha1-SNAPSHOT + system + ${project.basedir}/../common/target/common-4.0.0-alpha1-SNAPSHOT.jar + jar + + + + + src/main/kotlin + src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + process-sources + + compile + + + + ${project.basedir}/src/main/kotlin + + + + + test-compile + test-compile + + test-compile + + + + ${project.basedir}/src/test/java + ${project.basedir}/src/test/kotlin + + + + + + + kotlinx-serialization + + + + + org.jetbrains.kotlin + kotlin-maven-serialization + ${kotlin.version} + + + + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 2.22.2 + + + + integration-test + verify + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + diff --git a/src/java/src/main/kotlin/ConnectionExtensions.kt b/src/java/src/main/kotlin/ConnectionExtensions.kt new file mode 100644 index 0000000..a5230c2 --- /dev/null +++ b/src/java/src/main/kotlin/ConnectionExtensions.kt @@ -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 Connection.customList( + query: String, parameters: Collection> = listOf(), noinline mapFunc: (ResultSet, Class) -> 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 Connection.customSingle( + query: String, parameters: Collection> = listOf(), noinline mapFunc: (ResultSet, Class) -> 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> = 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 Connection.customScalar( + query: String, + parameters: Collection> = listOf(), + noinline mapFunc: (ResultSet, Class) -> 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) = + 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 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 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 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>, 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 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 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>, 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 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 Connection.findAll(tableName: String, orderBy: Collection>? = null) = + solutions.bitbadger.documents.java.Find.all(tableName, orderBy, this) + +/** + * Retrieve a document by its ID + * + * @param tableName The table from which the document should be retrieved + * @param docId The ID of the document to retrieve + * @return The document if it is found, `null` otherwise + */ +inline fun Connection.findById(tableName: String, docId: TKey) = + solutions.bitbadger.documents.java.Find.byId(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 Connection.findByFields( + tableName: String, + fields: Collection>, + howMatched: FieldMatch? = null, + orderBy: Collection>? = null +) = + solutions.bitbadger.documents.java.Find.byFields(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 Connection.findByContains( + tableName: String, + criteria: TContains, + orderBy: Collection>? = null +) = + solutions.bitbadger.documents.java.Find.byContains(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 Connection.findByJsonPath( + tableName: String, + path: String, + orderBy: Collection>? = null +) = + solutions.bitbadger.documents.java.Find.byJsonPath(tableName, path, orderBy, this) + +/** + * Retrieve the first document using a field comparison and optional ordering fields + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`) + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return The first document matching the field comparison, or `null` if no matches are found + */ +inline fun Connection.findFirstByFields( + tableName: String, + fields: Collection>, + howMatched: FieldMatch? = null, + orderBy: Collection>? = null +) = + solutions.bitbadger.documents.java.Find.firstByFields(tableName, fields, howMatched, orderBy, this) + +/** + * Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param criteria The object for which JSON containment should be checked + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return The first document matching the JSON containment query, or `null` if no matches are found + * @throws DocumentException If called on a SQLite connection + */ +inline fun Connection.findFirstByContains( + tableName: String, + criteria: TContains, + orderBy: Collection>? = null +) = + solutions.bitbadger.documents.java.Find.firstByContains(tableName, criteria, orderBy, this) + +/** + * Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param path The JSON path comparison to match + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return The first document matching the JSON Path match query, or `null` if no matches are found + * @throws DocumentException If called on a SQLite connection + */ +inline fun Connection.findFirstByJsonPath( + tableName: String, + path: String, + orderBy: Collection>? = null +) = + solutions.bitbadger.documents.java.Find.firstByJsonPath(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 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 Connection.patchByFields( + tableName: String, + fields: Collection>, + 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 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 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 Connection.removeFieldsById(tableName: String, docId: TKey, toRemove: Collection) = + 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>, + toRemove: Collection, + 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 Connection.removeFieldsByContains( + tableName: String, + criteria: TContains, + toRemove: Collection +) = + 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) = + 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 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>, 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 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) diff --git a/src/java/src/main/kotlin/Count.kt b/src/java/src/main/kotlin/Count.kt new file mode 100644 index 0000000..5d42b38 --- /dev/null +++ b/src/java/src/main/kotlin/Count.kt @@ -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>, + 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>, 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 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 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) } +} diff --git a/src/main/kotlin/java/Custom.kt b/src/java/src/main/kotlin/Custom.kt similarity index 99% rename from src/main/kotlin/java/Custom.kt rename to src/java/src/main/kotlin/Custom.kt index 7d5ee67..5503022 100644 --- a/src/main/kotlin/java/Custom.kt +++ b/src/java/src/main/kotlin/Custom.kt @@ -2,7 +2,6 @@ package solutions.bitbadger.documents.java import solutions.bitbadger.documents.common.Configuration import solutions.bitbadger.documents.common.Parameter -import solutions.bitbadger.documents.Parameters import java.sql.Connection import java.sql.ResultSet diff --git a/src/main/kotlin/Definition.kt b/src/java/src/main/kotlin/Definition.kt similarity index 96% rename from src/main/kotlin/Definition.kt rename to src/java/src/main/kotlin/Definition.kt index 424d742..7cd749b 100644 --- a/src/main/kotlin/Definition.kt +++ b/src/java/src/main/kotlin/Definition.kt @@ -1,8 +1,7 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.common.DocumentIndex import java.sql.Connection -import solutions.bitbadger.documents.common.query.Definition /** * Functions to define tables and indexes diff --git a/src/java/src/main/kotlin/Delete.kt b/src/java/src/main/kotlin/Delete.kt new file mode 100644 index 0000000..6f9bd52 --- /dev/null +++ b/src/java/src/main/kotlin/Delete.kt @@ -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 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 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>, 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>, 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 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 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) } +} diff --git a/src/main/kotlin/Document.kt b/src/java/src/main/kotlin/Document.kt similarity index 98% rename from src/main/kotlin/Document.kt rename to src/java/src/main/kotlin/Document.kt index 6984e5d..6a29a7c 100644 --- a/src/main/kotlin/Document.kt +++ b/src/java/src/main/kotlin/Document.kt @@ -1,6 +1,7 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.common.AutoId +import solutions.bitbadger.documents.common.Configuration import solutions.bitbadger.documents.common.Dialect import solutions.bitbadger.documents.common.Field import java.sql.Connection diff --git a/src/java/src/main/kotlin/DocumentConfig.kt b/src/java/src/main/kotlin/DocumentConfig.kt new file mode 100644 index 0000000..81431d7 --- /dev/null +++ b/src/java/src/main/kotlin/DocumentConfig.kt @@ -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() +} diff --git a/src/java/src/main/kotlin/Exists.kt b/src/java/src/main/kotlin/Exists.kt new file mode 100644 index 0000000..5f9deda --- /dev/null +++ b/src/java/src/main/kotlin/Exists.kt @@ -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 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 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>, + 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>, 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 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 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) } +} diff --git a/src/java/src/main/kotlin/Find.kt b/src/java/src/main/kotlin/Find.kt new file mode 100644 index 0000000..87f74a5 --- /dev/null +++ b/src/java/src/main/kotlin/Find.kt @@ -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 all(tableName: String, orderBy: Collection>? = null, conn: Connection) = + conn.customList(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 all(tableName: String, orderBy: Collection>? = null) = + Configuration.dbConn().use { all(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 all(tableName: String, conn: Connection) = + all(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 byId(tableName: String, docId: TKey, conn: Connection) = + conn.customSingle( + 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 byId(tableName: String, docId: TKey) = + Configuration.dbConn().use { byId(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 byFields( + tableName: String, + fields: Collection>, + howMatched: FieldMatch? = null, + orderBy: Collection>? = null, + conn: Connection + ): List { + val named = Parameters.nameFields(fields) + return conn.customList( + 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 byFields( + tableName: String, + fields: Collection>, + howMatched: FieldMatch? = null, + orderBy: Collection>? = null + ) = + Configuration.dbConn().use { byFields(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 byFields( + tableName: String, + fields: Collection>, + howMatched: FieldMatch? = null, + conn: Connection + ) = + byFields(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 byFields( + tableName: String, + fields: Collection>, + howMatched: FieldMatch? = null + ) = + Configuration.dbConn().use { byFields(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 byContains( + tableName: String, + criteria: TContains, + orderBy: Collection>? = null, + conn: Connection + ) = + conn.customList( + 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 byContains( + tableName: String, + criteria: TContains, + orderBy: Collection>? = null + ) = + Configuration.dbConn().use { byContains(tableName, criteria, orderBy, it) } + + /** + * Retrieve documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param criteria The object for which JSON containment should be checked + * @param conn The connection over which documents should be retrieved + * @return A list of documents matching the JSON containment query + * @throws DocumentException If called on a SQLite connection + */ + inline fun byContains(tableName: String, criteria: TContains, conn: Connection) = + byContains(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 byContains(tableName: String, criteria: TContains) = + Configuration.dbConn().use { byContains(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 byJsonPath( + tableName: String, + path: String, + orderBy: Collection>? = null, + conn: Connection + ) = + conn.customList( + 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 byJsonPath(tableName: String, path: String, orderBy: Collection>? = null) = + Configuration.dbConn().use { byJsonPath(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 byJsonPath(tableName: String, path: String, conn: Connection) = + byJsonPath(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 firstByFields( + tableName: String, + fields: Collection>, + howMatched: FieldMatch? = null, + orderBy: Collection>? = null, + conn: Connection + ): TDoc? { + val named = Parameters.nameFields(fields) + return conn.customSingle( + 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 firstByFields( + tableName: String, + fields: Collection>, + howMatched: FieldMatch? = null, + orderBy: Collection>? = null + ) = + Configuration.dbConn().use { firstByFields(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 firstByFields( + tableName: String, + fields: Collection>, + howMatched: FieldMatch? = null, + conn: Connection + ) = + firstByFields(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 firstByContains( + tableName: String, + criteria: TContains, + orderBy: Collection>? = null, + conn: Connection + ) = + conn.customSingle( + 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 firstByContains(tableName: String, criteria: TContains, conn: Connection) = + firstByContains(tableName, criteria, null, conn) + + /** + * Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only) + * + * @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 firstByContains(tableName: String, criteria: TContains, orderBy: Collection>? = null) = + Configuration.dbConn().use { firstByContains(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 firstByJsonPath( + tableName: String, + path: String, + orderBy: Collection>? = null, + conn: Connection + ) = + conn.customSingle( + 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 firstByJsonPath(tableName: String, path: String, conn: Connection) = + firstByJsonPath(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 firstByJsonPath(tableName: String, path: String, orderBy: Collection>? = null) = + Configuration.dbConn().use { firstByJsonPath(tableName, path, orderBy, it) } +} diff --git a/src/java/src/main/kotlin/NullDocumentSerializer.kt b/src/java/src/main/kotlin/NullDocumentSerializer.kt new file mode 100644 index 0000000..0fd5ea4 --- /dev/null +++ b/src/java/src/main/kotlin/NullDocumentSerializer.kt @@ -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 serialize(document: TDoc): String { + TODO("Replace this serializer in DocumentConfig.serializer") + } + + override fun deserialize(json: String, clazz: Class): TDoc { + TODO("Replace this serializer in DocumentConfig.serializer") + } + +} diff --git a/src/java/src/main/kotlin/Parameters.kt b/src/java/src/main/kotlin/Parameters.kt new file mode 100644 index 0000000..e8b94b6 --- /dev/null +++ b/src/java/src/main/kotlin/Parameters.kt @@ -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 + */ +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>): Collection> { + 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 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>, existing: MutableCollection> = 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>) = + 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>): 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>>() + 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, parameterName: String = ":name"): MutableCollection> = + 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() + } +} diff --git a/src/java/src/main/kotlin/Patch.kt b/src/java/src/main/kotlin/Patch.kt new file mode 100644 index 0000000..7b48698 --- /dev/null +++ b/src/java/src/main/kotlin/Patch.kt @@ -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 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 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 byFields( + tableName: String, + fields: Collection>, + 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 byFields( + tableName: String, + fields: Collection>, + 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 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 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 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 byJsonPath(tableName: String, path: String, patch: TPatch) = + Configuration.dbConn().use { byJsonPath(tableName, path, patch, it) } +} diff --git a/src/java/src/main/kotlin/RemoveFields.kt b/src/java/src/main/kotlin/RemoveFields.kt new file mode 100644 index 0000000..0bb10f0 --- /dev/null +++ b/src/java/src/main/kotlin/RemoveFields.kt @@ -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>): MutableCollection> { + 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 byId(tableName: String, docId: TKey, toRemove: Collection, 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 byId(tableName: String, docId: TKey, toRemove: Collection) = + 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>, + toRemove: Collection, + 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>, + toRemove: Collection, + 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 byContains( + tableName: String, + criteria: TContains, + toRemove: Collection, + 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 byContains(tableName: String, criteria: TContains, toRemove: Collection) = + 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, 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) = + Configuration.dbConn().use { byJsonPath(tableName, path, toRemove, it) } +} diff --git a/src/main/kotlin/java/Results.kt b/src/java/src/main/kotlin/Results.kt similarity index 96% rename from src/main/kotlin/java/Results.kt rename to src/java/src/main/kotlin/Results.kt index 9b6f9eb..6438eaa 100644 --- a/src/main/kotlin/java/Results.kt +++ b/src/java/src/main/kotlin/Results.kt @@ -1,6 +1,6 @@ package solutions.bitbadger.documents.java -import solutions.bitbadger.documents.Configuration +import solutions.bitbadger.documents.common.Configuration import solutions.bitbadger.documents.common.Dialect import solutions.bitbadger.documents.common.DocumentException import java.sql.PreparedStatement @@ -20,7 +20,7 @@ object Results { */ @JvmStatic fun fromDocument(field: String, rs: ResultSet, clazz: Class) = - Configuration.serializer.deserialize(rs.getString(field), clazz) + DocumentConfig.serializer.deserialize(rs.getString(field), clazz) /** * Create a domain item from a document diff --git a/src/test/java/solutions/bitbadger/documents/java/ParametersTest.java b/src/java/src/test/java/solutions/bitbadger/documents/java/java/ParametersTest.java similarity index 94% rename from src/test/java/solutions/bitbadger/documents/java/ParametersTest.java rename to src/java/src/test/java/solutions/bitbadger/documents/java/java/ParametersTest.java index c3b98c8..d2f2ee4 100644 --- a/src/test/java/solutions/bitbadger/documents/java/ParametersTest.java +++ b/src/java/src/test/java/solutions/bitbadger/documents/java/java/ParametersTest.java @@ -1,13 +1,10 @@ -package solutions.bitbadger.documents.java; +package solutions.bitbadger.documents.java.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import solutions.bitbadger.documents.*; -import solutions.bitbadger.documents.common.DocumentException; -import solutions.bitbadger.documents.common.Field; -import solutions.bitbadger.documents.common.Parameter; -import solutions.bitbadger.documents.common.ParameterType; +import solutions.bitbadger.documents.common.*; +import solutions.bitbadger.documents.java.Parameters; import java.util.List; @@ -16,7 +13,7 @@ import static org.junit.jupiter.api.Assertions.*; /** * Unit tests for the `Parameters` object */ -@DisplayName("Java | Parameters") +@DisplayName("Java | Java | Parameters") final public class ParametersTest { /** diff --git a/src/java/src/test/java/solutions/bitbadger/documents/java/java/integration/common/Count.java b/src/java/src/test/java/solutions/bitbadger/documents/java/java/integration/common/Count.java new file mode 100644 index 0000000..4ffc4ce --- /dev/null +++ b/src/java/src/test/java/solutions/bitbadger/documents/java/java/integration/common/Count.java @@ -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() { + } +} diff --git a/src/test/java/solutions/bitbadger/documents/java/integration/postgresql/CountIT.java b/src/java/src/test/java/solutions/bitbadger/documents/java/java/integration/postgresql/CountIT.java similarity index 65% rename from src/test/java/solutions/bitbadger/documents/java/integration/postgresql/CountIT.java rename to src/java/src/test/java/solutions/bitbadger/documents/java/java/integration/postgresql/CountIT.java index 32fb88d..525aee5 100644 --- a/src/test/java/solutions/bitbadger/documents/java/integration/postgresql/CountIT.java +++ b/src/java/src/test/java/solutions/bitbadger/documents/java/java/integration/postgresql/CountIT.java @@ -1,9 +1,9 @@ -package solutions.bitbadger.documents.java.integration.postgresql; +package solutions.bitbadger.documents.java.java.integration.postgresql; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import solutions.bitbadger.documents.integration.postgresql.PgDB; -import solutions.bitbadger.documents.java.integration.common.Count; +import solutions.bitbadger.documents.java.integration.postgresql.PgDB; +import solutions.bitbadger.documents.java.java.integration.common.Count; /** * PostgreSQL integration tests for the `Count` object / `count*` connection extension functions diff --git a/src/test/java/solutions/bitbadger/documents/java/testDocs/ByteIdClass.java b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/ByteIdClass.java similarity index 79% rename from src/test/java/solutions/bitbadger/documents/java/testDocs/ByteIdClass.java rename to src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/ByteIdClass.java index c0d41e7..4ffdf15 100644 --- a/src/test/java/solutions/bitbadger/documents/java/testDocs/ByteIdClass.java +++ b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/ByteIdClass.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.testDocs; +package solutions.bitbadger.documents.java.java.testDocs; public class ByteIdClass { diff --git a/src/test/java/solutions/bitbadger/documents/java/testDocs/IntIdClass.java b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/IntIdClass.java similarity index 79% rename from src/test/java/solutions/bitbadger/documents/java/testDocs/IntIdClass.java rename to src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/IntIdClass.java index 1d7a0cc..25a5613 100644 --- a/src/test/java/solutions/bitbadger/documents/java/testDocs/IntIdClass.java +++ b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/IntIdClass.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.testDocs; +package solutions.bitbadger.documents.java.java.testDocs; public class IntIdClass { diff --git a/src/test/java/solutions/bitbadger/documents/java/testDocs/JsonDocument.java b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/JsonDocument.java similarity index 87% rename from src/test/java/solutions/bitbadger/documents/java/testDocs/JsonDocument.java rename to src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/JsonDocument.java index 7bd9906..d1d02c6 100644 --- a/src/test/java/solutions/bitbadger/documents/java/testDocs/JsonDocument.java +++ b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/JsonDocument.java @@ -1,12 +1,12 @@ -package solutions.bitbadger.documents.java.testDocs; +package solutions.bitbadger.documents.java.java.testDocs; import kotlinx.serialization.Serializable; -import solutions.bitbadger.documents.Document; -import solutions.bitbadger.documents.integration.ThrowawayDatabase; +import solutions.bitbadger.documents.java.Document; +import solutions.bitbadger.documents.java.integration.ThrowawayDatabase; import java.util.List; -import static solutions.bitbadger.documents.integration.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.java.integration.TypesKt.TEST_TABLE; @Serializable public class JsonDocument { diff --git a/src/test/java/solutions/bitbadger/documents/java/testDocs/LongIdClass.java b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/LongIdClass.java similarity index 79% rename from src/test/java/solutions/bitbadger/documents/java/testDocs/LongIdClass.java rename to src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/LongIdClass.java index 1ee8bc0..9a56846 100644 --- a/src/test/java/solutions/bitbadger/documents/java/testDocs/LongIdClass.java +++ b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/LongIdClass.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.testDocs; +package solutions.bitbadger.documents.java.java.testDocs; public class LongIdClass { diff --git a/src/test/java/solutions/bitbadger/documents/java/testDocs/ShortIdClass.java b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/ShortIdClass.java similarity index 80% rename from src/test/java/solutions/bitbadger/documents/java/testDocs/ShortIdClass.java rename to src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/ShortIdClass.java index 83a2f3c..c5913cc 100644 --- a/src/test/java/solutions/bitbadger/documents/java/testDocs/ShortIdClass.java +++ b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/ShortIdClass.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.testDocs; +package solutions.bitbadger.documents.java.java.testDocs; public class ShortIdClass { diff --git a/src/test/java/solutions/bitbadger/documents/java/testDocs/StringIdClass.java b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/StringIdClass.java similarity index 80% rename from src/test/java/solutions/bitbadger/documents/java/testDocs/StringIdClass.java rename to src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/StringIdClass.java index 3013885..35e4945 100644 --- a/src/test/java/solutions/bitbadger/documents/java/testDocs/StringIdClass.java +++ b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/StringIdClass.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.testDocs; +package solutions.bitbadger.documents.java.java.testDocs; public class StringIdClass { diff --git a/src/test/java/solutions/bitbadger/documents/java/testDocs/SubDocument.java b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/SubDocument.java similarity index 89% rename from src/test/java/solutions/bitbadger/documents/java/testDocs/SubDocument.java rename to src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/SubDocument.java index f0b46ec..fa4c730 100644 --- a/src/test/java/solutions/bitbadger/documents/java/testDocs/SubDocument.java +++ b/src/java/src/test/java/solutions/bitbadger/documents/java/java/testDocs/SubDocument.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.testDocs; +package solutions.bitbadger.documents.java.java.testDocs; public class SubDocument { diff --git a/src/test/kotlin/ParametersTest.kt b/src/java/src/test/kotlin/ParametersTest.kt similarity index 98% rename from src/test/kotlin/ParametersTest.kt rename to src/java/src/test/kotlin/ParametersTest.kt index 566c267..ac91104 100644 --- a/src/test/kotlin/ParametersTest.kt +++ b/src/java/src/test/kotlin/ParametersTest.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.java import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -12,7 +12,7 @@ import kotlin.test.assertSame /** * Unit tests for the `Parameters` object */ -@DisplayName("Kotlin | Parameters") +@DisplayName("Kotlin | Java | Parameters") class ParametersTest { /** diff --git a/src/test/kotlin/integration/ThrowawayDatabase.kt b/src/java/src/test/kotlin/integration/ThrowawayDatabase.kt similarity index 89% rename from src/test/kotlin/integration/ThrowawayDatabase.kt rename to src/java/src/test/kotlin/integration/ThrowawayDatabase.kt index 5138ba4..1e46251 100644 --- a/src/test/kotlin/integration/ThrowawayDatabase.kt +++ b/src/java/src/test/kotlin/integration/ThrowawayDatabase.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.integration +package solutions.bitbadger.documents.java.integration import java.sql.Connection diff --git a/src/test/kotlin/integration/Types.kt b/src/java/src/test/kotlin/integration/Types.kt similarity index 93% rename from src/test/kotlin/integration/Types.kt rename to src/java/src/test/kotlin/integration/Types.kt index 54adfb4..730caa0 100644 --- a/src/test/kotlin/integration/Types.kt +++ b/src/java/src/test/kotlin/integration/Types.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration +package solutions.bitbadger.documents.java.integration import kotlinx.serialization.Serializable -import solutions.bitbadger.documents.insert +import solutions.bitbadger.documents.java.insert /** The test table name to use for integration tests */ const val TEST_TABLE = "test_table" diff --git a/src/test/kotlin/integration/common/Count.kt b/src/java/src/test/kotlin/integration/common/Count.kt similarity index 80% rename from src/test/kotlin/integration/common/Count.kt rename to src/java/src/test/kotlin/integration/common/Count.kt index e360977..e8b3258 100644 --- a/src/test/kotlin/integration/common/Count.kt +++ b/src/java/src/test/kotlin/integration/common/Count.kt @@ -1,10 +1,13 @@ -package solutions.bitbadger.documents.integration.common +package solutions.bitbadger.documents.java.integration.common -import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.Field -import solutions.bitbadger.documents.integration.JsonDocument -import solutions.bitbadger.documents.integration.TEST_TABLE -import solutions.bitbadger.documents.integration.ThrowawayDatabase +import solutions.bitbadger.documents.java.countAll +import solutions.bitbadger.documents.java.countByContains +import solutions.bitbadger.documents.java.countByFields +import solutions.bitbadger.documents.java.countByJsonPath +import solutions.bitbadger.documents.java.integration.JsonDocument +import solutions.bitbadger.documents.java.integration.TEST_TABLE +import solutions.bitbadger.documents.java.integration.ThrowawayDatabase import kotlin.test.assertEquals /** diff --git a/src/test/kotlin/integration/common/Custom.kt b/src/java/src/test/kotlin/integration/common/Custom.kt similarity index 81% rename from src/test/kotlin/integration/common/Custom.kt rename to src/java/src/test/kotlin/integration/common/Custom.kt index f8361c6..05073d3 100644 --- a/src/test/kotlin/integration/common/Custom.kt +++ b/src/java/src/test/kotlin/integration/common/Custom.kt @@ -1,15 +1,10 @@ -package solutions.bitbadger.documents.integration.common +package solutions.bitbadger.documents.java.integration.common -import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.common.Field -import solutions.bitbadger.documents.common.Parameter -import solutions.bitbadger.documents.common.ParameterType -import solutions.bitbadger.documents.integration.JsonDocument -import solutions.bitbadger.documents.integration.TEST_TABLE -import solutions.bitbadger.documents.integration.ThrowawayDatabase -import solutions.bitbadger.documents.common.query.Count -import solutions.bitbadger.documents.common.query.Delete -import solutions.bitbadger.documents.common.query.Find +import solutions.bitbadger.documents.common.* +import solutions.bitbadger.documents.java.integration.JsonDocument +import solutions.bitbadger.documents.java.integration.TEST_TABLE +import solutions.bitbadger.documents.java.Results +import solutions.bitbadger.documents.java.integration.ThrowawayDatabase import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull diff --git a/src/test/kotlin/integration/common/Definition.kt b/src/java/src/test/kotlin/integration/common/Definition.kt similarity index 85% rename from src/test/kotlin/integration/common/Definition.kt rename to src/java/src/test/kotlin/integration/common/Definition.kt index e667ce8..6f36226 100644 --- a/src/test/kotlin/integration/common/Definition.kt +++ b/src/java/src/test/kotlin/integration/common/Definition.kt @@ -1,9 +1,11 @@ -package solutions.bitbadger.documents.integration.common +package solutions.bitbadger.documents.java.integration.common -import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.DocumentIndex -import solutions.bitbadger.documents.integration.TEST_TABLE -import solutions.bitbadger.documents.integration.ThrowawayDatabase +import solutions.bitbadger.documents.java.ensureDocumentIndex +import solutions.bitbadger.documents.java.ensureFieldIndex +import solutions.bitbadger.documents.java.ensureTable +import solutions.bitbadger.documents.java.integration.TEST_TABLE +import solutions.bitbadger.documents.java.integration.ThrowawayDatabase import kotlin.test.assertFalse import kotlin.test.assertTrue diff --git a/src/test/kotlin/integration/common/Delete.kt b/src/java/src/test/kotlin/integration/common/Delete.kt similarity index 90% rename from src/test/kotlin/integration/common/Delete.kt rename to src/java/src/test/kotlin/integration/common/Delete.kt index 1e69426..2ea4338 100644 --- a/src/test/kotlin/integration/common/Delete.kt +++ b/src/java/src/test/kotlin/integration/common/Delete.kt @@ -1,10 +1,10 @@ -package solutions.bitbadger.documents.integration.common +package solutions.bitbadger.documents.java.integration.common -import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.Field -import solutions.bitbadger.documents.integration.JsonDocument -import solutions.bitbadger.documents.integration.TEST_TABLE -import solutions.bitbadger.documents.integration.ThrowawayDatabase +import solutions.bitbadger.documents.java.* +import solutions.bitbadger.documents.java.integration.JsonDocument +import solutions.bitbadger.documents.java.integration.TEST_TABLE +import solutions.bitbadger.documents.java.integration.ThrowawayDatabase import kotlin.test.assertEquals /** diff --git a/src/test/kotlin/integration/common/Document.kt b/src/java/src/test/kotlin/integration/common/Document.kt similarity index 97% rename from src/test/kotlin/integration/common/Document.kt rename to src/java/src/test/kotlin/integration/common/Document.kt index bb3c41b..e698525 100644 --- a/src/test/kotlin/integration/common/Document.kt +++ b/src/java/src/test/kotlin/integration/common/Document.kt @@ -1,8 +1,10 @@ -package solutions.bitbadger.documents.integration.common +package solutions.bitbadger.documents.java.integration.common import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.integration.* +import solutions.bitbadger.documents.java.* +import solutions.bitbadger.documents.java.integration.* import kotlin.test.* /** diff --git a/src/test/kotlin/integration/common/Exists.kt b/src/java/src/test/kotlin/integration/common/Exists.kt similarity index 80% rename from src/test/kotlin/integration/common/Exists.kt rename to src/java/src/test/kotlin/integration/common/Exists.kt index 2dc51b8..3d5513e 100644 --- a/src/test/kotlin/integration/common/Exists.kt +++ b/src/java/src/test/kotlin/integration/common/Exists.kt @@ -1,10 +1,13 @@ -package solutions.bitbadger.documents.integration.common +package solutions.bitbadger.documents.java.integration.common -import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.Field -import solutions.bitbadger.documents.integration.JsonDocument -import solutions.bitbadger.documents.integration.TEST_TABLE -import solutions.bitbadger.documents.integration.ThrowawayDatabase +import solutions.bitbadger.documents.java.existsByContains +import solutions.bitbadger.documents.java.existsByFields +import solutions.bitbadger.documents.java.existsById +import solutions.bitbadger.documents.java.existsByJsonPath +import solutions.bitbadger.documents.java.integration.JsonDocument +import solutions.bitbadger.documents.java.integration.TEST_TABLE +import solutions.bitbadger.documents.java.integration.ThrowawayDatabase import kotlin.test.assertFalse import kotlin.test.assertTrue diff --git a/src/test/kotlin/integration/common/Find.kt b/src/java/src/test/kotlin/integration/common/Find.kt similarity index 97% rename from src/test/kotlin/integration/common/Find.kt rename to src/java/src/test/kotlin/integration/common/Find.kt index 839991d..d7f124e 100644 --- a/src/test/kotlin/integration/common/Find.kt +++ b/src/java/src/test/kotlin/integration/common/Find.kt @@ -1,9 +1,11 @@ -package solutions.bitbadger.documents.integration.common +package solutions.bitbadger.documents.java.integration.common import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.integration.* +import solutions.bitbadger.documents.java.* +import solutions.bitbadger.documents.java.integration.* import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull @@ -211,7 +213,8 @@ object Find { fun firstByFieldsMatchOrdered(db: ThrowawayDatabase) { JsonDocument.load(db) - val doc = db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("sub.foo", "green")), orderBy = listOf( + val doc = db.conn.findFirstByFields( + TEST_TABLE, listOf(Field.equal("sub.foo", "green")), orderBy = listOf( Field.named("n:numValue DESC"))) assertNotNull(doc, "There should have been a document returned") assertEquals("four", doc.id, "An incorrect document was returned") diff --git a/src/test/kotlin/integration/common/Patch.kt b/src/java/src/test/kotlin/integration/common/Patch.kt similarity index 92% rename from src/test/kotlin/integration/common/Patch.kt rename to src/java/src/test/kotlin/integration/common/Patch.kt index 37d7315..9a4144c 100644 --- a/src/test/kotlin/integration/common/Patch.kt +++ b/src/java/src/test/kotlin/integration/common/Patch.kt @@ -1,10 +1,11 @@ -package solutions.bitbadger.documents.integration.common +package solutions.bitbadger.documents.java.integration.common import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.Field -import solutions.bitbadger.documents.integration.JsonDocument -import solutions.bitbadger.documents.integration.TEST_TABLE -import solutions.bitbadger.documents.integration.ThrowawayDatabase +import solutions.bitbadger.documents.java.* +import solutions.bitbadger.documents.java.integration.JsonDocument +import solutions.bitbadger.documents.java.integration.TEST_TABLE +import solutions.bitbadger.documents.java.integration.ThrowawayDatabase import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotNull diff --git a/src/test/kotlin/integration/common/RemoveFields.kt b/src/java/src/test/kotlin/integration/common/RemoveFields.kt similarity index 93% rename from src/test/kotlin/integration/common/RemoveFields.kt rename to src/java/src/test/kotlin/integration/common/RemoveFields.kt index e8381a1..022c484 100644 --- a/src/test/kotlin/integration/common/RemoveFields.kt +++ b/src/java/src/test/kotlin/integration/common/RemoveFields.kt @@ -1,10 +1,11 @@ -package solutions.bitbadger.documents.integration.common +package solutions.bitbadger.documents.java.integration.common import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.Field -import solutions.bitbadger.documents.integration.JsonDocument -import solutions.bitbadger.documents.integration.TEST_TABLE -import solutions.bitbadger.documents.integration.ThrowawayDatabase +import solutions.bitbadger.documents.java.* +import solutions.bitbadger.documents.java.integration.JsonDocument +import solutions.bitbadger.documents.java.integration.TEST_TABLE +import solutions.bitbadger.documents.java.integration.ThrowawayDatabase import kotlin.test.* diff --git a/src/test/kotlin/integration/postgresql/CountIT.kt b/src/java/src/test/kotlin/integration/postgresql/CountIT.kt similarity index 90% rename from src/test/kotlin/integration/postgresql/CountIT.kt rename to src/java/src/test/kotlin/integration/postgresql/CountIT.kt index 9b4dc33..bb6e416 100644 --- a/src/test/kotlin/integration/postgresql/CountIT.kt +++ b/src/java/src/test/kotlin/integration/postgresql/CountIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.postgresql +package solutions.bitbadger.documents.java.integration.postgresql import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.Count +import solutions.bitbadger.documents.java.integration.common.Count import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/postgresql/CustomIT.kt b/src/java/src/test/kotlin/integration/postgresql/CustomIT.kt similarity index 89% rename from src/test/kotlin/integration/postgresql/CustomIT.kt rename to src/java/src/test/kotlin/integration/postgresql/CustomIT.kt index 5729df9..803bfb1 100644 --- a/src/test/kotlin/integration/postgresql/CustomIT.kt +++ b/src/java/src/test/kotlin/integration/postgresql/CustomIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.postgresql +package solutions.bitbadger.documents.java.integration.postgresql import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.Custom +import solutions.bitbadger.documents.java.integration.common.Custom import kotlin.test.Test diff --git a/src/test/kotlin/integration/postgresql/DefinitionIT.kt b/src/java/src/test/kotlin/integration/postgresql/DefinitionIT.kt similarity index 86% rename from src/test/kotlin/integration/postgresql/DefinitionIT.kt rename to src/java/src/test/kotlin/integration/postgresql/DefinitionIT.kt index 363c92d..9c94abc 100644 --- a/src/test/kotlin/integration/postgresql/DefinitionIT.kt +++ b/src/java/src/test/kotlin/integration/postgresql/DefinitionIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.postgresql +package solutions.bitbadger.documents.java.integration.postgresql import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.Definition +import solutions.bitbadger.documents.java.integration.common.Definition import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/postgresql/DeleteIT.kt b/src/java/src/test/kotlin/integration/postgresql/DeleteIT.kt similarity index 90% rename from src/test/kotlin/integration/postgresql/DeleteIT.kt rename to src/java/src/test/kotlin/integration/postgresql/DeleteIT.kt index 707be57..803400c 100644 --- a/src/test/kotlin/integration/postgresql/DeleteIT.kt +++ b/src/java/src/test/kotlin/integration/postgresql/DeleteIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.postgresql +package solutions.bitbadger.documents.java.integration.postgresql import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.Delete +import solutions.bitbadger.documents.java.integration.common.Delete import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/postgresql/DocumentIT.kt b/src/java/src/test/kotlin/integration/postgresql/DocumentIT.kt similarity index 91% rename from src/test/kotlin/integration/postgresql/DocumentIT.kt rename to src/java/src/test/kotlin/integration/postgresql/DocumentIT.kt index 36d15af..382d64b 100644 --- a/src/test/kotlin/integration/postgresql/DocumentIT.kt +++ b/src/java/src/test/kotlin/integration/postgresql/DocumentIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.postgresql +package solutions.bitbadger.documents.java.integration.postgresql import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.Document +import solutions.bitbadger.documents.java.integration.common.Document import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/postgresql/ExistsIT.kt b/src/java/src/test/kotlin/integration/postgresql/ExistsIT.kt similarity index 91% rename from src/test/kotlin/integration/postgresql/ExistsIT.kt rename to src/java/src/test/kotlin/integration/postgresql/ExistsIT.kt index be02740..757ec75 100644 --- a/src/test/kotlin/integration/postgresql/ExistsIT.kt +++ b/src/java/src/test/kotlin/integration/postgresql/ExistsIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.postgresql +package solutions.bitbadger.documents.java.integration.postgresql import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.Exists +import solutions.bitbadger.documents.java.integration.common.Exists import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/postgresql/FindIT.kt b/src/java/src/test/kotlin/integration/postgresql/FindIT.kt similarity index 97% rename from src/test/kotlin/integration/postgresql/FindIT.kt rename to src/java/src/test/kotlin/integration/postgresql/FindIT.kt index 6b00cc3..c6a95fe 100644 --- a/src/test/kotlin/integration/postgresql/FindIT.kt +++ b/src/java/src/test/kotlin/integration/postgresql/FindIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.postgresql +package solutions.bitbadger.documents.java.integration.postgresql import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.Find +import solutions.bitbadger.documents.java.integration.common.Find import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/postgresql/PatchIT.kt b/src/java/src/test/kotlin/integration/postgresql/PatchIT.kt similarity index 90% rename from src/test/kotlin/integration/postgresql/PatchIT.kt rename to src/java/src/test/kotlin/integration/postgresql/PatchIT.kt index df1c79d..d650355 100644 --- a/src/test/kotlin/integration/postgresql/PatchIT.kt +++ b/src/java/src/test/kotlin/integration/postgresql/PatchIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.postgresql +package solutions.bitbadger.documents.java.integration.postgresql import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.Patch +import solutions.bitbadger.documents.java.integration.common.Patch import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/postgresql/PgDB.kt b/src/java/src/test/kotlin/integration/postgresql/PgDB.kt similarity index 88% rename from src/test/kotlin/integration/postgresql/PgDB.kt rename to src/java/src/test/kotlin/integration/postgresql/PgDB.kt index c72af32..7fecb54 100644 --- a/src/test/kotlin/integration/postgresql/PgDB.kt +++ b/src/java/src/test/kotlin/integration/postgresql/PgDB.kt @@ -1,10 +1,10 @@ -package solutions.bitbadger.documents.integration.postgresql +package solutions.bitbadger.documents.java.integration.postgresql import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.ParameterType -import solutions.bitbadger.documents.integration.TEST_TABLE -import solutions.bitbadger.documents.integration.ThrowawayDatabase +import solutions.bitbadger.documents.java.integration.TEST_TABLE +import solutions.bitbadger.documents.java.integration.ThrowawayDatabase /** * A wrapper for a throwaway PostgreSQL database diff --git a/src/test/kotlin/integration/postgresql/RemoveFieldsIT.kt b/src/java/src/test/kotlin/integration/postgresql/RemoveFieldsIT.kt similarity index 94% rename from src/test/kotlin/integration/postgresql/RemoveFieldsIT.kt rename to src/java/src/test/kotlin/integration/postgresql/RemoveFieldsIT.kt index 9719447..3a10709 100644 --- a/src/test/kotlin/integration/postgresql/RemoveFieldsIT.kt +++ b/src/java/src/test/kotlin/integration/postgresql/RemoveFieldsIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.postgresql +package solutions.bitbadger.documents.java.integration.postgresql import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.RemoveFields +import solutions.bitbadger.documents.java.integration.common.RemoveFields import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/sqlite/CountIT.kt b/src/java/src/test/kotlin/integration/sqlite/CountIT.kt similarity index 89% rename from src/test/kotlin/integration/sqlite/CountIT.kt rename to src/java/src/test/kotlin/integration/sqlite/CountIT.kt index 7b08ddf..62fdb63 100644 --- a/src/test/kotlin/integration/sqlite/CountIT.kt +++ b/src/java/src/test/kotlin/integration/sqlite/CountIT.kt @@ -1,9 +1,9 @@ -package solutions.bitbadger.documents.integration.sqlite +package solutions.bitbadger.documents.java.integration.sqlite import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.common.DocumentException -import solutions.bitbadger.documents.integration.common.Count +import solutions.bitbadger.documents.java.integration.common.Count import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/sqlite/CustomIT.kt b/src/java/src/test/kotlin/integration/sqlite/CustomIT.kt similarity index 89% rename from src/test/kotlin/integration/sqlite/CustomIT.kt rename to src/java/src/test/kotlin/integration/sqlite/CustomIT.kt index ab37b5f..21adab3 100644 --- a/src/test/kotlin/integration/sqlite/CustomIT.kt +++ b/src/java/src/test/kotlin/integration/sqlite/CustomIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.sqlite +package solutions.bitbadger.documents.java.integration.sqlite import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.Custom +import solutions.bitbadger.documents.java.integration.common.Custom import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/sqlite/DefinitionIT.kt b/src/java/src/test/kotlin/integration/sqlite/DefinitionIT.kt similarity index 88% rename from src/test/kotlin/integration/sqlite/DefinitionIT.kt rename to src/java/src/test/kotlin/integration/sqlite/DefinitionIT.kt index 0cd15fa..3e553ed 100644 --- a/src/test/kotlin/integration/sqlite/DefinitionIT.kt +++ b/src/java/src/test/kotlin/integration/sqlite/DefinitionIT.kt @@ -1,9 +1,9 @@ -package solutions.bitbadger.documents.integration.sqlite +package solutions.bitbadger.documents.java.integration.sqlite import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.common.DocumentException -import solutions.bitbadger.documents.integration.common.Definition +import solutions.bitbadger.documents.java.integration.common.Definition import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/sqlite/DeleteIT.kt b/src/java/src/test/kotlin/integration/sqlite/DeleteIT.kt similarity index 90% rename from src/test/kotlin/integration/sqlite/DeleteIT.kt rename to src/java/src/test/kotlin/integration/sqlite/DeleteIT.kt index 40c100e..b3d056a 100644 --- a/src/test/kotlin/integration/sqlite/DeleteIT.kt +++ b/src/java/src/test/kotlin/integration/sqlite/DeleteIT.kt @@ -1,9 +1,9 @@ -package solutions.bitbadger.documents.integration.sqlite +package solutions.bitbadger.documents.java.integration.sqlite import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.common.DocumentException -import solutions.bitbadger.documents.integration.common.Delete +import solutions.bitbadger.documents.java.integration.common.Delete import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/sqlite/DocumentIT.kt b/src/java/src/test/kotlin/integration/sqlite/DocumentIT.kt similarity index 91% rename from src/test/kotlin/integration/sqlite/DocumentIT.kt rename to src/java/src/test/kotlin/integration/sqlite/DocumentIT.kt index 402cd6e..1745237 100644 --- a/src/test/kotlin/integration/sqlite/DocumentIT.kt +++ b/src/java/src/test/kotlin/integration/sqlite/DocumentIT.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.integration.sqlite +package solutions.bitbadger.documents.java.integration.sqlite import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.integration.common.Document +import solutions.bitbadger.documents.java.integration.common.Document import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/sqlite/ExistsIT.kt b/src/java/src/test/kotlin/integration/sqlite/ExistsIT.kt similarity index 90% rename from src/test/kotlin/integration/sqlite/ExistsIT.kt rename to src/java/src/test/kotlin/integration/sqlite/ExistsIT.kt index 42bf633..55933ac 100644 --- a/src/test/kotlin/integration/sqlite/ExistsIT.kt +++ b/src/java/src/test/kotlin/integration/sqlite/ExistsIT.kt @@ -1,9 +1,9 @@ -package solutions.bitbadger.documents.integration.sqlite +package solutions.bitbadger.documents.java.integration.sqlite import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.common.DocumentException -import solutions.bitbadger.documents.integration.common.Exists +import solutions.bitbadger.documents.java.integration.common.Exists import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/sqlite/FindIT.kt b/src/java/src/test/kotlin/integration/sqlite/FindIT.kt similarity index 96% rename from src/test/kotlin/integration/sqlite/FindIT.kt rename to src/java/src/test/kotlin/integration/sqlite/FindIT.kt index 6750c03..aef0003 100644 --- a/src/test/kotlin/integration/sqlite/FindIT.kt +++ b/src/java/src/test/kotlin/integration/sqlite/FindIT.kt @@ -1,9 +1,9 @@ -package solutions.bitbadger.documents.integration.sqlite +package solutions.bitbadger.documents.java.integration.sqlite import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.common.DocumentException -import solutions.bitbadger.documents.integration.common.Find +import solutions.bitbadger.documents.java.integration.common.Find import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/sqlite/PatchIT.kt b/src/java/src/test/kotlin/integration/sqlite/PatchIT.kt similarity index 90% rename from src/test/kotlin/integration/sqlite/PatchIT.kt rename to src/java/src/test/kotlin/integration/sqlite/PatchIT.kt index 76e9c27..ea1b147 100644 --- a/src/test/kotlin/integration/sqlite/PatchIT.kt +++ b/src/java/src/test/kotlin/integration/sqlite/PatchIT.kt @@ -1,9 +1,9 @@ -package solutions.bitbadger.documents.integration.sqlite +package solutions.bitbadger.documents.java.integration.sqlite import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.common.DocumentException -import solutions.bitbadger.documents.integration.common.Patch +import solutions.bitbadger.documents.java.integration.common.Patch import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/sqlite/RemoveFieldsIT.kt b/src/java/src/test/kotlin/integration/sqlite/RemoveFieldsIT.kt similarity index 92% rename from src/test/kotlin/integration/sqlite/RemoveFieldsIT.kt rename to src/java/src/test/kotlin/integration/sqlite/RemoveFieldsIT.kt index 096d1fb..3fbad3c 100644 --- a/src/test/kotlin/integration/sqlite/RemoveFieldsIT.kt +++ b/src/java/src/test/kotlin/integration/sqlite/RemoveFieldsIT.kt @@ -1,9 +1,9 @@ -package solutions.bitbadger.documents.integration.sqlite +package solutions.bitbadger.documents.java.integration.sqlite import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.common.DocumentException -import solutions.bitbadger.documents.integration.common.RemoveFields +import solutions.bitbadger.documents.java.integration.common.RemoveFields import kotlin.test.Test /** diff --git a/src/test/kotlin/integration/sqlite/SQLiteDB.kt b/src/java/src/test/kotlin/integration/sqlite/SQLiteDB.kt similarity index 81% rename from src/test/kotlin/integration/sqlite/SQLiteDB.kt rename to src/java/src/test/kotlin/integration/sqlite/SQLiteDB.kt index e6be940..34d977b 100644 --- a/src/test/kotlin/integration/sqlite/SQLiteDB.kt +++ b/src/java/src/test/kotlin/integration/sqlite/SQLiteDB.kt @@ -1,10 +1,10 @@ -package solutions.bitbadger.documents.integration.sqlite +package solutions.bitbadger.documents.java.integration.sqlite import solutions.bitbadger.documents.* import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.ParameterType -import solutions.bitbadger.documents.integration.TEST_TABLE -import solutions.bitbadger.documents.integration.ThrowawayDatabase +import solutions.bitbadger.documents.java.integration.TEST_TABLE +import solutions.bitbadger.documents.java.integration.ThrowawayDatabase import java.io.File /** diff --git a/src/kotlin/pom.xml b/src/kotlin/pom.xml new file mode 100644 index 0000000..88bafc0 --- /dev/null +++ b/src/kotlin/pom.xml @@ -0,0 +1,119 @@ + + + 4.0.0 + + solutions.bitbadger.documents + kotlin + 4.0.0-alpha1-SNAPSHOT + jar + + + solutions.bitbadger + documents + 4.0.0-alpha1-SNAPSHOT + + + ${project.groupId}:${project.artifactId} + Expose a document store interface for PostgreSQL and SQLite (Kotlin Library) + https://bitbadger.solutions/open-source/relational-documents/jvm/ + + + scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git + scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git + https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents + + + + + solutions.bitbadger.documents + common + system + ${project.basedir}/../common/target/common-4.0.0-alpha1-SNAPSHOT.jar + jar + + + solutions.bitbadger.documents + java + system + ${project.basedir}/../java/target/java-4.0.0-alpha1-SNAPSHOT.jar + jar + + + + + src/main/kotlin + src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + process-sources + + compile + + + + ${project.basedir}/src/main/kotlin + + + + + test-compile + test-compile + + test-compile + + + + ${project.basedir}/src/test/java + ${project.basedir}/src/test/kotlin + + + + + + + kotlinx-serialization + + + + + org.jetbrains.kotlin + kotlin-maven-serialization + ${kotlin.version} + + + + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 2.22.2 + + + + integration-test + verify + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + diff --git a/src/main/kotlin/Configuration.kt b/src/kotlin/src/main/kotlin/Configuration.kt similarity index 68% rename from src/main/kotlin/Configuration.kt rename to src/kotlin/src/main/kotlin/Configuration.kt index 705a1d9..f8becb9 100644 --- a/src/main/kotlin/Configuration.kt +++ b/src/kotlin/src/main/kotlin/Configuration.kt @@ -1,12 +1,6 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.kotlin import kotlinx.serialization.json.Json -import solutions.bitbadger.documents.common.AutoId -import solutions.bitbadger.documents.common.Dialect -import solutions.bitbadger.documents.common.DocumentException -import java.sql.Connection -import java.sql.DriverManager -import kotlin.jvm.Throws object Configuration { diff --git a/src/main/kotlin/ConnectionExtensions.kt b/src/kotlin/src/main/kotlin/ConnectionExtensions.kt similarity index 99% rename from src/main/kotlin/ConnectionExtensions.kt rename to src/kotlin/src/main/kotlin/ConnectionExtensions.kt index c63406b..28e951b 100644 --- a/src/main/kotlin/ConnectionExtensions.kt +++ b/src/kotlin/src/main/kotlin/ConnectionExtensions.kt @@ -1,9 +1,10 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.kotlin import solutions.bitbadger.documents.common.DocumentIndex import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.Parameter +import solutions.bitbadger.documents.java.Document import java.sql.Connection import java.sql.ResultSet diff --git a/src/main/kotlin/Count.kt b/src/kotlin/src/main/kotlin/Count.kt similarity index 97% rename from src/main/kotlin/Count.kt rename to src/kotlin/src/main/kotlin/Count.kt index b865af0..e2ac2d5 100644 --- a/src/main/kotlin/Count.kt +++ b/src/kotlin/src/main/kotlin/Count.kt @@ -1,10 +1,12 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.kotlin +import solutions.bitbadger.documents.common.Configuration import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.ParameterType import solutions.bitbadger.documents.common.query.Count + import java.sql.Connection /** diff --git a/src/kotlin/src/main/kotlin/Custom.kt b/src/kotlin/src/main/kotlin/Custom.kt new file mode 100644 index 0000000..b3f75fd --- /dev/null +++ b/src/kotlin/src/main/kotlin/Custom.kt @@ -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 list( + query: String, + parameters: Collection> = listOf(), + conn: Connection, + noinline mapFunc: (ResultSet, Class) -> 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 list( + query: String, + parameters: Collection> = listOf(), + noinline mapFunc: (ResultSet, Class) -> 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 single( + query: String, + parameters: Collection> = listOf(), + conn: Connection, + noinline mapFunc: (ResultSet, Class) -> 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 single( + query: String, + parameters: Collection> = listOf(), + noinline mapFunc: (ResultSet, Class) -> 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> = 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> = 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 scalar( + query: String, + parameters: Collection> = listOf(), + conn: Connection, + noinline mapFunc: (ResultSet, Class) -> 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 scalar( + query: String, parameters: Collection> = listOf(), noinline mapFunc: (ResultSet, Class) -> T + ) = Configuration.dbConn().use { scalar(query, parameters, it, mapFunc) } +} diff --git a/src/kotlin/src/main/kotlin/Definition.kt b/src/kotlin/src/main/kotlin/Definition.kt new file mode 100644 index 0000000..07af62a --- /dev/null +++ b/src/kotlin/src/main/kotlin/Definition.kt @@ -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, 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) = + 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) } +} diff --git a/src/main/kotlin/Delete.kt b/src/kotlin/src/main/kotlin/Delete.kt similarity index 97% rename from src/main/kotlin/Delete.kt rename to src/kotlin/src/main/kotlin/Delete.kt index eca0352..07288a2 100644 --- a/src/main/kotlin/Delete.kt +++ b/src/kotlin/src/main/kotlin/Delete.kt @@ -1,10 +1,7 @@ -package solutions.bitbadger.documents - import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.ParameterType -import solutions.bitbadger.documents.common.query.Delete import java.sql.Connection /** diff --git a/src/main/kotlin/Exists.kt b/src/kotlin/src/main/kotlin/Exists.kt similarity index 98% rename from src/main/kotlin/Exists.kt rename to src/kotlin/src/main/kotlin/Exists.kt index 4b0072a..dfb1eb2 100644 --- a/src/main/kotlin/Exists.kt +++ b/src/kotlin/src/main/kotlin/Exists.kt @@ -1,10 +1,7 @@ -package solutions.bitbadger.documents - import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.ParameterType -import solutions.bitbadger.documents.common.query.Exists import java.sql.Connection /** diff --git a/src/main/kotlin/Find.kt b/src/kotlin/src/main/kotlin/Find.kt similarity index 99% rename from src/main/kotlin/Find.kt rename to src/kotlin/src/main/kotlin/Find.kt index 6135145..3e58e71 100644 --- a/src/main/kotlin/Find.kt +++ b/src/kotlin/src/main/kotlin/Find.kt @@ -1,11 +1,8 @@ -package solutions.bitbadger.documents - import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.ParameterType import java.sql.Connection -import solutions.bitbadger.documents.common.query.Find import solutions.bitbadger.documents.common.query.orderBy /** diff --git a/src/main/kotlin/Parameters.kt b/src/kotlin/src/main/kotlin/Parameters.kt similarity index 98% rename from src/main/kotlin/Parameters.kt rename to src/kotlin/src/main/kotlin/Parameters.kt index f31a5db..46cd843 100644 --- a/src/main/kotlin/Parameters.kt +++ b/src/kotlin/src/main/kotlin/Parameters.kt @@ -1,7 +1,4 @@ -package solutions.bitbadger.documents - import solutions.bitbadger.documents.common.* -import solutions.bitbadger.documents.common.ParameterName import java.sql.Connection import java.sql.PreparedStatement import java.sql.SQLException diff --git a/src/main/kotlin/Patch.kt b/src/kotlin/src/main/kotlin/Patch.kt similarity index 98% rename from src/main/kotlin/Patch.kt rename to src/kotlin/src/main/kotlin/Patch.kt index 4b23b78..5217b4a 100644 --- a/src/main/kotlin/Patch.kt +++ b/src/kotlin/src/main/kotlin/Patch.kt @@ -1,10 +1,7 @@ -package solutions.bitbadger.documents - import solutions.bitbadger.documents.common.Field import solutions.bitbadger.documents.common.FieldMatch import solutions.bitbadger.documents.common.Parameter import solutions.bitbadger.documents.common.ParameterType -import solutions.bitbadger.documents.common.query.Patch import java.sql.Connection /** diff --git a/src/main/kotlin/RemoveFields.kt b/src/kotlin/src/main/kotlin/RemoveFields.kt similarity index 98% rename from src/main/kotlin/RemoveFields.kt rename to src/kotlin/src/main/kotlin/RemoveFields.kt index 486351a..d75c63b 100644 --- a/src/main/kotlin/RemoveFields.kt +++ b/src/kotlin/src/main/kotlin/RemoveFields.kt @@ -1,7 +1,4 @@ -package solutions.bitbadger.documents - import solutions.bitbadger.documents.common.* -import solutions.bitbadger.documents.common.query.RemoveFields import java.sql.Connection /** diff --git a/src/kotlin/src/main/kotlin/Results.kt b/src/kotlin/src/main/kotlin/Results.kt new file mode 100644 index 0000000..f961b00 --- /dev/null +++ b/src/kotlin/src/main/kotlin/Results.kt @@ -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 fromDocument(field: String): (ResultSet, Class) -> 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 fromData(rs: ResultSet, clazz: Class = 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 toCustomList(stmt: PreparedStatement, mapFunc: (ResultSet) -> TDoc) = + try { + stmt.executeQuery().use { + val results = mutableListOf() + 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::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::class.java) = + when (Configuration.dialect()) { + Dialect.POSTGRESQL -> rs.getBoolean("it") + Dialect.SQLITE -> toCount(rs) > 0L + } +} diff --git a/src/pom.xml b/src/pom.xml index 60ce46a..544ca00 100644 --- a/src/pom.xml +++ b/src/pom.xml @@ -46,6 +46,7 @@ common + java diff --git a/src/test/java/solutions/bitbadger/documents/java/ConfigurationTest.java b/src/test/java/solutions/bitbadger/documents/java/ConfigurationTest.java index c24b49c..fe67b44 100644 --- a/src/test/java/solutions/bitbadger/documents/java/ConfigurationTest.java +++ b/src/test/java/solutions/bitbadger/documents/java/ConfigurationTest.java @@ -2,7 +2,6 @@ package solutions.bitbadger.documents.java; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import solutions.bitbadger.documents.*; import solutions.bitbadger.documents.common.Dialect; import solutions.bitbadger.documents.common.DocumentException; diff --git a/src/test/java/solutions/bitbadger/documents/java/integration/common/Count.java b/src/test/java/solutions/bitbadger/documents/java/integration/common/Count.java deleted file mode 100644 index 8258386..0000000 --- a/src/test/java/solutions/bitbadger/documents/java/integration/common/Count.java +++ /dev/null @@ -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() { - } -}