diff --git a/src/jvm/pom.xml b/src/jvm/pom.xml
index 2da78a7..33c4d65 100644
--- a/src/jvm/pom.xml
+++ b/src/jvm/pom.xml
@@ -5,7 +5,7 @@
4.0.0
solutions.bitbadger.documents
- java
+ jvm
4.0.0-alpha1-SNAPSHOT
jar
@@ -16,7 +16,7 @@
${project.groupId}:${project.artifactId}
- Expose a document store interface for PostgreSQL and SQLite (Java Library)
+ Expose a document store interface for PostgreSQL and SQLite (Standard JVM Library)
https://bitbadger.solutions/open-source/relational-documents/jvm/
diff --git a/src/kotlin/pom.xml b/src/kotlin/pom.xml
index 88bafc0..9889629 100644
--- a/src/kotlin/pom.xml
+++ b/src/kotlin/pom.xml
@@ -28,16 +28,10 @@
solutions.bitbadger.documents
- common
+ jvm
+ 4.0.0-alpha1-SNAPSHOT
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
+ ${project.basedir}/../jvm/target/jvm-4.0.0-alpha1-SNAPSHOT.jar
jar
@@ -57,11 +51,6 @@
compile
-
-
- ${project.basedir}/src/main/kotlin
-
-
test-compile
@@ -69,12 +58,6 @@
test-compile
-
-
- ${project.basedir}/src/test/java
- ${project.basedir}/src/test/kotlin
-
-
diff --git a/src/kotlin/src/main/kotlin/Count.kt b/src/kotlin/src/main/kotlin/Count.kt
index 4b2f3f5..d8fbf7b 100644
--- a/src/kotlin/src/main/kotlin/Count.kt
+++ b/src/kotlin/src/main/kotlin/Count.kt
@@ -1,10 +1,7 @@
package solutions.bitbadger.documents.kotlin
-import solutions.bitbadger.documents.Configuration
-import solutions.bitbadger.documents.Field
-import solutions.bitbadger.documents.FieldMatch
-import solutions.bitbadger.documents.Parameter
-import solutions.bitbadger.documents.ParameterType
+import solutions.bitbadger.documents.*
+import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Count
import java.sql.Connection
@@ -21,7 +18,6 @@ object Count {
* @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(Count.all(tableName), mapFunc = Results::toCount)
@@ -31,7 +27,6 @@ object Count {
* @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) }
@@ -44,8 +39,6 @@ object Count {
* @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>,
@@ -68,8 +61,6 @@ object Count {
* @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) }
@@ -82,7 +73,6 @@ object Count {
* @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)
@@ -94,7 +84,6 @@ object Count {
* @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) }
@@ -107,7 +96,6 @@ object Count {
* @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),
@@ -123,7 +111,6 @@ object Count {
* @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/kotlin/src/main/kotlin/Custom.kt b/src/kotlin/src/main/kotlin/Custom.kt
index 265abd6..4d77ee5 100644
--- a/src/kotlin/src/main/kotlin/Custom.kt
+++ b/src/kotlin/src/main/kotlin/Custom.kt
@@ -2,7 +2,6 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.Configuration
-import solutions.bitbadger.documents.jvm.Parameters
import solutions.bitbadger.documents.jvm.Custom as JvmCustom
import java.sql.Connection
import java.sql.ResultSet
diff --git a/src/kotlin/src/main/kotlin/Definition.kt b/src/kotlin/src/main/kotlin/Definition.kt
index 8c2b2d9..23fa87e 100644
--- a/src/kotlin/src/main/kotlin/Definition.kt
+++ b/src/kotlin/src/main/kotlin/Definition.kt
@@ -1,4 +1,8 @@
+package solutions.bitbadger.documents.kotlin
+
+import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.DocumentIndex
+import solutions.bitbadger.documents.jvm.Definition as JvmDefinition
import java.sql.Connection
/**
@@ -13,10 +17,7 @@ object Definition {
* @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))
- }
+ JvmDefinition.ensureTable(tableName, conn)
/**
* Create a document table if necessary
@@ -24,7 +25,7 @@ object Definition {
* @param tableName The table whose existence should be ensured (may include schema)
*/
fun ensureTable(tableName: String) =
- Configuration.dbConn().use { ensureTable(tableName, it) }
+ JvmDefinition.ensureTable(tableName)
/**
* Create an index on field(s) within documents in the specified table if necessary
@@ -35,7 +36,7 @@ object Definition {
* @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))
+ JvmDefinition.ensureFieldIndex(tableName, indexName, fields, conn)
/**
* Create an index on field(s) within documents in the specified table if necessary
@@ -45,7 +46,7 @@ object Definition {
* @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) }
+ JvmDefinition.ensureFieldIndex(tableName, indexName, fields)
/**
* Create a document index on a table (PostgreSQL only)
@@ -56,7 +57,7 @@ object Definition {
* @throws DocumentException If called on a SQLite connection
*/
fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex, conn: Connection) =
- conn.customNonQuery(Definition.ensureDocumentIndexOn(tableName, indexType))
+ JvmDefinition.ensureDocumentIndex(tableName, indexType, conn)
/**
* Create a document index on a table (PostgreSQL only)
@@ -66,5 +67,5 @@ object Definition {
* @throws DocumentException If called on a SQLite connection
*/
fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex) =
- Configuration.dbConn().use { ensureDocumentIndex(tableName, indexType, it) }
+ JvmDefinition.ensureDocumentIndex(tableName, indexType)
}
diff --git a/src/kotlin/src/main/kotlin/Delete.kt b/src/kotlin/src/main/kotlin/Delete.kt
index e092b05..e670d48 100644
--- a/src/kotlin/src/main/kotlin/Delete.kt
+++ b/src/kotlin/src/main/kotlin/Delete.kt
@@ -1,7 +1,9 @@
-import solutions.bitbadger.documents.Field
-import solutions.bitbadger.documents.FieldMatch
-import solutions.bitbadger.documents.Parameter
-import solutions.bitbadger.documents.ParameterType
+package solutions.bitbadger.documents.kotlin
+
+import solutions.bitbadger.documents.*
+import solutions.bitbadger.documents.jvm.Delete as JvmDelete
+import solutions.bitbadger.documents.kotlin.extensions.*
+import solutions.bitbadger.documents.query.Delete
import java.sql.Connection
/**
@@ -17,10 +19,7 @@ object Delete {
* @param conn The connection on which the deletion should be executed
*/
fun byId(tableName: String, docId: TKey, conn: Connection) =
- conn.customNonQuery(
- Delete.byId(tableName, docId),
- Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id")))
- )
+ JvmDelete.byId(tableName, docId, conn)
/**
* Delete a document by its ID
@@ -29,7 +28,7 @@ object Delete {
* @param docId The ID of the document to be deleted
*/
fun byId(tableName: String, docId: TKey) =
- Configuration.dbConn().use { byId(tableName, docId, it) }
+ JvmDelete.byId(tableName, docId)
/**
* Delete documents using a field comparison
@@ -39,10 +38,8 @@ object Delete {
* @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(Delete.byFields(tableName, named, howMatched), Parameters.addFields(named))
- }
+ fun byFields(tableName: String, fields: Collection>, howMatched: FieldMatch? = null, conn: Connection) =
+ JvmDelete.byFields(tableName, fields, howMatched, conn)
/**
* Delete documents using a field comparison
@@ -52,7 +49,7 @@ object Delete {
* @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) }
+ JvmDelete.byFields(tableName, fields, howMatched)
/**
* Delete documents using a JSON containment query (PostgreSQL only)
@@ -84,7 +81,7 @@ object Delete {
* @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)))
+ JvmDelete.byJsonPath(tableName, path, conn)
/**
* Delete documents using a JSON Path match query (PostgreSQL only)
@@ -94,5 +91,5 @@ object Delete {
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String) =
- Configuration.dbConn().use { byJsonPath(tableName, path, it) }
+ JvmDelete.byJsonPath(tableName, path)
}
diff --git a/src/kotlin/src/main/kotlin/Document.kt b/src/kotlin/src/main/kotlin/Document.kt
new file mode 100644
index 0000000..2c2547e
--- /dev/null
+++ b/src/kotlin/src/main/kotlin/Document.kt
@@ -0,0 +1,114 @@
+package solutions.bitbadger.documents.kotlin
+
+import solutions.bitbadger.documents.AutoId
+import solutions.bitbadger.documents.Configuration
+import solutions.bitbadger.documents.Dialect
+import solutions.bitbadger.documents.Field
+import solutions.bitbadger.documents.extensions.customNonQuery
+import solutions.bitbadger.documents.query.Document
+import solutions.bitbadger.documents.query.Where
+import solutions.bitbadger.documents.query.statementWhere
+import java.sql.Connection
+
+/**
+ * Functions for manipulating documents
+ */
+object Document {
+
+ /**
+ * 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
+ * @param conn The connection on which the query should be executed
+ */
+ inline fun insert(tableName: String, document: TDoc, conn: Connection) {
+ val strategy = Configuration.autoIdStrategy
+ val query = if (strategy == AutoId.DISABLED) {
+ Document.insert(tableName)
+ } else {
+ val idField = Configuration.idField
+ val dialect = Configuration.dialect("Create auto-ID insert query")
+ val dataParam = if (AutoId.needsAutoId(strategy, document, idField)) {
+ when (dialect) {
+ Dialect.POSTGRESQL ->
+ when (strategy) {
+ AutoId.NUMBER -> "' || (SELECT coalesce(max(data->>'$idField')::numeric, 0) + 1 " +
+ "FROM $tableName) || '"
+ AutoId.UUID -> "\"${AutoId.generateUUID()}\""
+ AutoId.RANDOM_STRING -> "\"${AutoId.generateRandomString()}\""
+ else -> "\"' || (:data)->>'$idField' || '\""
+ }.let { ":data::jsonb || ('{\"$idField\":$it}')::jsonb" }
+
+ Dialect.SQLITE ->
+ when (strategy) {
+ AutoId.NUMBER -> "(SELECT coalesce(max(data->>'$idField'), 0) + 1 FROM $tableName)"
+ AutoId.UUID -> "'${AutoId.generateUUID()}'"
+ AutoId.RANDOM_STRING -> "'${AutoId.generateRandomString()}'"
+ else -> "(:data)->>'$idField'"
+ }.let { "json_set(:data, '$.$idField', $it)" }
+ }
+ } else {
+ ":data"
+ }
+
+ Document.insert(tableName).replace(":data", dataParam)
+ }
+ conn.customNonQuery(query, listOf(Parameters.json(":data", document)))
+ }
+
+ /**
+ * 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 insert(tableName: String, document: TDoc) =
+ Configuration.dbConn().use { insert(tableName, document, it) }
+
+ /**
+ * 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
+ * @param conn The connection on which the query should be executed
+ */
+ inline fun save(tableName: String, document: TDoc, conn: Connection) =
+ conn.customNonQuery(Document.save(tableName), listOf(Parameters.json(":data", document)))
+
+ /**
+ * Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
+ *
+ * @param tableName The table in which the document should be saved (may include schema)
+ * @param document The document to be saved
+ */
+ inline fun save(tableName: String, document: TDoc) =
+ Configuration.dbConn().use { save(tableName, document, it) }
+
+ /**
+ * 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
+ * @param conn The connection on which the query should be executed
+ */
+ inline fun update(tableName: String, docId: TKey, document: TDoc, conn: Connection) =
+ conn.customNonQuery(
+ statementWhere(Document.update(tableName), Where.byId(":id", docId)),
+ Parameters.addFields(
+ listOf(Field.equal(Configuration.idField, docId, ":id")),
+ mutableListOf(Parameters.json(":data", document))
+ )
+ )
+
+ /**
+ * 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 update(tableName: String, docId: TKey, document: TDoc) =
+ Configuration.dbConn().use { update(tableName, docId, document, it) }
+}
diff --git a/src/kotlin/src/main/kotlin/Exists.kt b/src/kotlin/src/main/kotlin/Exists.kt
index 5146ba7..e253bbb 100644
--- a/src/kotlin/src/main/kotlin/Exists.kt
+++ b/src/kotlin/src/main/kotlin/Exists.kt
@@ -1,7 +1,9 @@
-import solutions.bitbadger.documents.Field
-import solutions.bitbadger.documents.FieldMatch
-import solutions.bitbadger.documents.Parameter
-import solutions.bitbadger.documents.ParameterType
+package solutions.bitbadger.documents.kotlin
+
+import solutions.bitbadger.documents.*
+import solutions.bitbadger.documents.jvm.Exists as JvmExists
+import solutions.bitbadger.documents.kotlin.extensions.*
+import solutions.bitbadger.documents.query.Exists
import java.sql.Connection
/**
@@ -18,11 +20,7 @@ object Exists {
* @return True if the document exists, false if not
*/
fun byId(tableName: String, docId: TKey, conn: Connection) =
- conn.customScalar(
- Exists.byId(tableName, docId),
- Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))),
- Results::toExists
- )
+ JvmExists.byId(tableName, docId, conn)
/**
* Determine a document's existence by its ID
@@ -32,7 +30,7 @@ object Exists {
* @return True if the document exists, false if not
*/
fun byId(tableName: String, docId: TKey) =
- Configuration.dbConn().use { byId(tableName, docId, it) }
+ JvmExists.byId(tableName, docId)
/**
* Determine document existence using a field comparison
@@ -43,19 +41,8 @@ object Exists {
* @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(
- Exists.byFields(tableName, named, howMatched),
- Parameters.addFields(named),
- Results::toExists
- )
- }
+ fun byFields(tableName: String, fields: Collection>, howMatched: FieldMatch? = null, conn: Connection) =
+ JvmExists.byFields(tableName, fields, howMatched, conn)
/**
* Determine document existence using a field comparison
@@ -66,7 +53,7 @@ object Exists {
* @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) }
+ JvmExists.byFields(tableName, fields, howMatched)
/**
* Determine document existence using a JSON containment query (PostgreSQL only)
@@ -105,11 +92,7 @@ object Exists {
* @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
- )
+ JvmExists.byJsonPath(tableName, path, conn)
/**
* Determine document existence using a JSON Path match query (PostgreSQL only)
@@ -120,5 +103,5 @@ object Exists {
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String) =
- Configuration.dbConn().use { byJsonPath(tableName, path, it) }
+ JvmExists.byJsonPath(tableName, path)
}
diff --git a/src/kotlin/src/main/kotlin/Find.kt b/src/kotlin/src/main/kotlin/Find.kt
index d698790..a9ecf18 100644
--- a/src/kotlin/src/main/kotlin/Find.kt
+++ b/src/kotlin/src/main/kotlin/Find.kt
@@ -1,7 +1,9 @@
-import solutions.bitbadger.documents.Field
-import solutions.bitbadger.documents.FieldMatch
-import solutions.bitbadger.documents.Parameter
-import solutions.bitbadger.documents.ParameterType
+package solutions.bitbadger.documents.kotlin
+
+import solutions.bitbadger.documents.*
+import solutions.bitbadger.documents.kotlin.extensions.*
+import solutions.bitbadger.documents.query.Find
+import solutions.bitbadger.documents.query.orderBy
import java.sql.Connection
/**
@@ -17,7 +19,7 @@ object Find {
* @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) =
+ inline fun all(tableName: String, orderBy: Collection>? = null, conn: Connection) =
conn.customList(Find.all(tableName) + (orderBy?.let(::orderBy) ?: ""), mapFunc = Results::fromData)
/**
@@ -27,7 +29,7 @@ object Find {
* @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) =
+ inline fun all(tableName: String, orderBy: Collection>? = null) =
Configuration.dbConn().use { all(tableName, orderBy, it) }
/**
@@ -37,7 +39,7 @@ object Find {
* @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) =
+ inline fun all(tableName: String, conn: Connection) =
all(tableName, null, conn)
/**
@@ -48,7 +50,7 @@ object Find {
* @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) =
+ inline fun byId(tableName: String, docId: TKey, conn: Connection) =
conn.customSingle(
Find.byId(tableName, docId),
Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))),
@@ -62,7 +64,7 @@ object Find {
* @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) =
+ inline fun byId(tableName: String, docId: TKey) =
Configuration.dbConn().use { byId(tableName, docId, it) }
/**
@@ -75,7 +77,7 @@ object Find {
* @param conn The connection over which documents should be retrieved
* @return A list of documents matching the field comparison
*/
- inline fun byFields(
+ inline fun byFields(
tableName: String,
fields: Collection>,
howMatched: FieldMatch? = null,
@@ -99,7 +101,7 @@ object Find {
* @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(
+ inline fun byFields(
tableName: String,
fields: Collection>,
howMatched: FieldMatch? = null,
@@ -116,7 +118,7 @@ object Find {
* @param conn The connection over which documents should be retrieved
* @return A list of documents matching the field comparison
*/
- inline fun byFields(
+ inline fun byFields(
tableName: String,
fields: Collection>,
howMatched: FieldMatch? = null,
@@ -132,7 +134,7 @@ object Find {
* @param howMatched How the fields should be matched
* @return A list of documents matching the field comparison
*/
- inline fun byFields(
+ inline fun byFields(
tableName: String,
fields: Collection>,
howMatched: FieldMatch? = null
@@ -149,7 +151,7 @@ object Find {
* @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection
*/
- inline fun byContains(
+ inline fun byContains(
tableName: String,
criteria: TContains,
orderBy: Collection>? = null,
@@ -170,7 +172,7 @@ object Find {
* @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection
*/
- inline fun byContains(
+ inline fun byContains(
tableName: String,
criteria: TContains,
orderBy: Collection>? = null
@@ -186,7 +188,11 @@ object Find {
* @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) =
+ inline fun byContains(
+ tableName: String,
+ criteria: TContains,
+ conn: Connection
+ ) =
byContains(tableName, criteria, null, conn)
/**
@@ -197,7 +203,7 @@ object Find {
* @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) =
+ inline fun byContains(tableName: String, criteria: TContains) =
Configuration.dbConn().use { byContains(tableName, criteria, it) }
/**
@@ -210,7 +216,7 @@ object Find {
* @return A list of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection
*/
- inline fun byJsonPath(
+ inline fun byJsonPath(
tableName: String,
path: String,
orderBy: Collection>? = null,
@@ -231,7 +237,7 @@ object Find {
* @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) =
+ inline fun byJsonPath(tableName: String, path: String, orderBy: Collection>? = null) =
Configuration.dbConn().use { byJsonPath(tableName, path, orderBy, it) }
/**
@@ -243,7 +249,7 @@ object Find {
* @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) =
+ inline fun byJsonPath(tableName: String, path: String, conn: Connection) =
byJsonPath(tableName, path, null, conn)
/**
@@ -256,7 +262,7 @@ object Find {
* @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(
+ inline fun firstByFields(
tableName: String,
fields: Collection>,
howMatched: FieldMatch? = null,
@@ -280,7 +286,7 @@ object Find {
* @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(
+ inline fun firstByFields(
tableName: String,
fields: Collection>,
howMatched: FieldMatch? = null,
@@ -297,7 +303,7 @@ object Find {
* @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(
+ inline fun firstByFields(
tableName: String,
fields: Collection>,
howMatched: FieldMatch? = null,
@@ -315,7 +321,7 @@ object Find {
* @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(
+ inline fun firstByContains(
tableName: String,
criteria: TContains,
orderBy: Collection>? = null,
@@ -336,7 +342,11 @@ object Find {
* @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) =
+ inline fun firstByContains(
+ tableName: String,
+ criteria: TContains,
+ conn: Connection
+ ) =
firstByContains(tableName, criteria, null, conn)
/**
@@ -348,7 +358,11 @@ object Find {
* @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) =
+ inline fun firstByContains(
+ tableName: String,
+ criteria: TContains,
+ orderBy: Collection>? = null
+ ) =
Configuration.dbConn().use { firstByContains(tableName, criteria, orderBy, it) }
/**
@@ -361,7 +375,7 @@ object Find {
* @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(
+ inline fun firstByJsonPath(
tableName: String,
path: String,
orderBy: Collection>? = null,
@@ -382,7 +396,7 @@ object Find {
* @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) =
+ inline fun firstByJsonPath(tableName: String, path: String, conn: Connection) =
firstByJsonPath(tableName, path, null, conn)
/**
@@ -394,6 +408,10 @@ object Find {
* @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) =
+ inline fun firstByJsonPath(
+ tableName: String,
+ path: String,
+ orderBy: Collection>? = null
+ ) =
Configuration.dbConn().use { firstByJsonPath(tableName, path, orderBy, it) }
}
diff --git a/src/kotlin/src/main/kotlin/Parameters.kt b/src/kotlin/src/main/kotlin/Parameters.kt
index 7dae9e9..74bf62c 100644
--- a/src/kotlin/src/main/kotlin/Parameters.kt
+++ b/src/kotlin/src/main/kotlin/Parameters.kt
@@ -1,10 +1,8 @@
+package solutions.bitbadger.documents.kotlin
+
import solutions.bitbadger.documents.*
-import solutions.bitbadger.documents.ParameterName
-import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.jvm.Parameters as JvmParameters
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
@@ -19,17 +17,8 @@ object Parameters {
* @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
- }
- }
- }
+ fun nameFields(fields: Collection>): Collection> =
+ JvmParameters.nameFields(fields)
/**
* Create a parameter by encoding a JSON object
@@ -38,9 +27,8 @@ object Parameters {
* @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, Configuration.serializer.serialize(value))
+ inline fun json(name: String, value: T) =
+ Parameter(name, ParameterType.JSON, DocumentConfig.serialize(value))
/**
* Add field parameters to the given set of parameters
@@ -49,9 +37,8 @@ object Parameters {
* @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) }
+ JvmParameters.addFields(fields, existing)
/**
* Replace the parameter names in the query with question marks
@@ -60,9 +47,8 @@ object Parameters {
* @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, "?") }
+ JvmParameters.replaceNamesInQuery(query, parameters)
/**
* Apply the given parameters to the given query, returning a prepared statement
@@ -73,38 +59,8 @@ object Parameters {
* @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)
- }
- }
+ fun apply(conn: Connection, query: String, parameters: Collection>) =
+ JvmParameters.apply(conn, query, parameters)
/**
* Create parameters for field names to be removed from a document
@@ -114,17 +70,6 @@ object Parameters {
* @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()
- }
+ fun fieldNames(names: Collection, parameterName: String = ":name") =
+ JvmParameters.fieldNames(names, parameterName)
}
diff --git a/src/kotlin/src/main/kotlin/Patch.kt b/src/kotlin/src/main/kotlin/Patch.kt
index ea8ebc3..097eb2d 100644
--- a/src/kotlin/src/main/kotlin/Patch.kt
+++ b/src/kotlin/src/main/kotlin/Patch.kt
@@ -1,7 +1,8 @@
-import solutions.bitbadger.documents.Field
-import solutions.bitbadger.documents.FieldMatch
-import solutions.bitbadger.documents.Parameter
-import solutions.bitbadger.documents.ParameterType
+package solutions.bitbadger.documents.kotlin
+
+import solutions.bitbadger.documents.*
+import solutions.bitbadger.documents.kotlin.extensions.*
+import solutions.bitbadger.documents.query.Patch
import java.sql.Connection
/**
diff --git a/src/kotlin/src/main/kotlin/RemoveFields.kt b/src/kotlin/src/main/kotlin/RemoveFields.kt
index 9d40864..f1c7675 100644
--- a/src/kotlin/src/main/kotlin/RemoveFields.kt
+++ b/src/kotlin/src/main/kotlin/RemoveFields.kt
@@ -1,5 +1,9 @@
+package solutions.bitbadger.documents.kotlin
+
import solutions.bitbadger.documents.*
-import solutions.bitbadger.documents.common.*
+import solutions.bitbadger.documents.jvm.RemoveFields as JvmRemoveFields
+import solutions.bitbadger.documents.kotlin.extensions.*
+import solutions.bitbadger.documents.query.RemoveFields
import java.sql.Connection
/**
@@ -7,20 +11,6 @@ import java.sql.Connection
*/
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
*
@@ -29,16 +19,8 @@ object RemoveFields {
* @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(
- RemoveFields.byId(tableName, nameParams, docId),
- Parameters.addFields(
- listOf(Field.equal(Configuration.idField, docId, ":id")),
- translatePath(nameParams)
- )
- )
- }
+ fun byId(tableName: String, docId: TKey, toRemove: Collection, conn: Connection) =
+ JvmRemoveFields.byId(tableName, docId, toRemove, conn)
/**
* Remove fields from a document by its ID
@@ -48,7 +30,7 @@ object RemoveFields {
* @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) }
+ JvmRemoveFields.byId(tableName, docId, toRemove)
/**
* Remove fields from documents using a field comparison
@@ -65,14 +47,8 @@ object RemoveFields {
toRemove: Collection,
howMatched: FieldMatch? = null,
conn: Connection
- ) {
- val named = Parameters.nameFields(fields)
- val nameParams = Parameters.fieldNames(toRemove)
- conn.customNonQuery(
- RemoveFields.byFields(tableName, nameParams, named, howMatched),
- Parameters.addFields(named, translatePath(nameParams))
- )
- }
+ ) =
+ JvmRemoveFields.byFields(tableName, fields, toRemove, howMatched, conn)
/**
* Remove fields from documents using a field comparison
@@ -88,7 +64,7 @@ object RemoveFields {
toRemove: Collection,
howMatched: FieldMatch? = null
) =
- Configuration.dbConn().use { byFields(tableName, fields, toRemove, howMatched, it) }
+ JvmRemoveFields.byFields(tableName, fields, toRemove, howMatched)
/**
* Remove fields from documents using a JSON containment query (PostgreSQL only)
@@ -132,13 +108,8 @@ object RemoveFields {
* @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())
- )
- }
+ fun byJsonPath(tableName: String, path: String, toRemove: Collection, conn: Connection) =
+ JvmRemoveFields.byJsonPath(tableName, path, toRemove, conn)
/**
* Remove fields from documents using a JSON Path match query (PostgreSQL only)
@@ -149,5 +120,5 @@ object RemoveFields {
* @throws DocumentException If called on a SQLite connection
*/
fun byJsonPath(tableName: String, path: String, toRemove: Collection) =
- Configuration.dbConn().use { byJsonPath(tableName, path, toRemove, it) }
+ JvmRemoveFields.byJsonPath(tableName, path, toRemove)
}
diff --git a/src/kotlin/src/main/kotlin/Results.kt b/src/kotlin/src/main/kotlin/Results.kt
index 131dd66..791fb60 100644
--- a/src/kotlin/src/main/kotlin/Results.kt
+++ b/src/kotlin/src/main/kotlin/Results.kt
@@ -16,21 +16,18 @@ 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
+ * @return A function to create the constructed domain item
*/
- inline fun fromDocument(field: String): (ResultSet, Class) -> TDoc =
- { rs, _ -> Results.fromDocument(field, rs, TDoc::class.java) }
+ inline fun fromDocument(field: String): (ResultSet) -> TDoc =
+ { rs -> DocumentConfig.deserialize(rs.getString(field)) }
/**
* 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)
+ inline fun fromData(rs: ResultSet) =
+ fromDocument("data")(rs)
/**
* Create a list of items for the results of the given command, using the specified mapping function
@@ -59,7 +56,7 @@ object Results {
* @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) =
+ fun toCount(rs: ResultSet) =
when (Configuration.dialect()) {
Dialect.POSTGRESQL -> rs.getInt("it").toLong()
Dialect.SQLITE -> rs.getLong("it")
@@ -71,7 +68,7 @@ object Results {
* @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) =
+ fun toExists(rs: ResultSet) =
when (Configuration.dialect()) {
Dialect.POSTGRESQL -> rs.getBoolean("it")
Dialect.SQLITE -> toCount(rs) > 0L
diff --git a/src/kotlin/src/main/kotlin/ConnectionExtensions.kt b/src/kotlin/src/main/kotlin/extensions/Connection.kt
similarity index 94%
rename from src/kotlin/src/main/kotlin/ConnectionExtensions.kt
rename to src/kotlin/src/main/kotlin/extensions/Connection.kt
index 599cb22..d02e85b 100644
--- a/src/kotlin/src/main/kotlin/ConnectionExtensions.kt
+++ b/src/kotlin/src/main/kotlin/extensions/Connection.kt
@@ -1,10 +1,7 @@
-package solutions.bitbadger.documents.kotlin
+package solutions.bitbadger.documents.kotlin.extensions
-import solutions.bitbadger.documents.DocumentIndex
-import solutions.bitbadger.documents.Field
-import solutions.bitbadger.documents.FieldMatch
-import solutions.bitbadger.documents.Parameter
-import solutions.bitbadger.documents.java.jvm.Document
+import solutions.bitbadger.documents.*
+import solutions.bitbadger.documents.kotlin.*
import java.sql.Connection
import java.sql.ResultSet
@@ -18,8 +15,8 @@ import java.sql.ResultSet
* @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
+inline fun Connection.customList(
+ query: String, parameters: Collection> = listOf(), mapFunc: (ResultSet) -> TDoc
) = Custom.list(query, parameters, this, mapFunc)
/**
@@ -30,8 +27,8 @@ inline fun Connection.customList(
* @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
+inline fun Connection.customSingle(
+ query: String, parameters: Collection> = listOf(), mapFunc: (ResultSet) -> TDoc
) = Custom.single(query, parameters, this, mapFunc)
/**
@@ -54,7 +51,7 @@ fun Connection.customNonQuery(query: String, parameters: Collection
inline fun Connection.customScalar(
query: String,
parameters: Collection> = listOf(),
- noinline mapFunc: (ResultSet, Class) -> T
+ mapFunc: (ResultSet) -> T
) = Custom.scalar(query, parameters, this, mapFunc)
// ~~~ DEFINITION QUERIES ~~~
@@ -215,7 +212,7 @@ fun Connection.existsByJsonPath(tableName: String, path: String) =
* @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) =
+inline fun Connection.findAll(tableName: String, orderBy: Collection>? = null) =
Find.all(tableName, orderBy, this)
/**
@@ -225,7 +222,7 @@ inline fun Connection.findAll(tableName: String, orderBy: Collect
* @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) =
+inline fun Connection.findById(tableName: String, docId: TKey) =
Find.byId(tableName, docId, this)
/**
@@ -237,7 +234,7 @@ inline fun Connection.findById(tableName: String, docId: TK
* @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(
+inline fun Connection.findByFields(
tableName: String,
fields: Collection>,
howMatched: FieldMatch? = null,
@@ -254,7 +251,7 @@ inline fun Connection.findByFields(
* @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection
*/
-inline fun Connection.findByContains(
+inline fun Connection.findByContains(
tableName: String,
criteria: TContains,
orderBy: Collection>? = null
@@ -270,7 +267,7 @@ inline fun Connection.findByContains(
* @return A list of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection
*/
-inline fun Connection.findByJsonPath(
+inline fun Connection.findByJsonPath(
tableName: String,
path: String,
orderBy: Collection>? = null
@@ -286,7 +283,7 @@ inline fun Connection.findByJsonPath(
* @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(
+inline fun Connection.findFirstByFields(
tableName: String,
fields: Collection>,
howMatched: FieldMatch? = null,
@@ -303,7 +300,7 @@ inline fun Connection.findFirstByFields(
* @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(
+inline fun Connection.findFirstByContains(
tableName: String,
criteria: TContains,
orderBy: Collection>? = null
@@ -319,7 +316,7 @@ inline fun Connection.findFirstByContains(
* @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(
+inline fun Connection.findFirstByJsonPath(
tableName: String,
path: String,
orderBy: Collection>? = null
diff --git a/src/pom.xml b/src/pom.xml
index 96aa6fc..27748e6 100644
--- a/src/pom.xml
+++ b/src/pom.xml
@@ -46,6 +46,7 @@
jvm
+ kotlin