Initial Development #1

Merged
danieljsummers merged 88 commits from v1-rc into main 2025-04-16 01:29:20 +00:00
15 changed files with 260 additions and 266 deletions
Showing only changes of commit 5497238fb1 - Show all commits

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>solutions.bitbadger.documents</groupId> <groupId>solutions.bitbadger.documents</groupId>
<artifactId>java</artifactId> <artifactId>jvm</artifactId>
<version>4.0.0-alpha1-SNAPSHOT</version> <version>4.0.0-alpha1-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
@ -16,7 +16,7 @@
</parent> </parent>
<name>${project.groupId}:${project.artifactId}</name> <name>${project.groupId}:${project.artifactId}</name>
<description>Expose a document store interface for PostgreSQL and SQLite (Java Library)</description> <description>Expose a document store interface for PostgreSQL and SQLite (Standard JVM Library)</description>
<url>https://bitbadger.solutions/open-source/relational-documents/jvm/</url> <url>https://bitbadger.solutions/open-source/relational-documents/jvm/</url>
<scm> <scm>

View File

@ -28,16 +28,10 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>solutions.bitbadger.documents</groupId> <groupId>solutions.bitbadger.documents</groupId>
<artifactId>common</artifactId> <artifactId>jvm</artifactId>
<version>4.0.0-alpha1-SNAPSHOT</version>
<scope>system</scope> <scope>system</scope>
<systemPath>${project.basedir}/../common/target/common-4.0.0-alpha1-SNAPSHOT.jar</systemPath> <systemPath>${project.basedir}/../jvm/target/jvm-4.0.0-alpha1-SNAPSHOT.jar</systemPath>
<type>jar</type>
</dependency>
<dependency>
<groupId>solutions.bitbadger.documents</groupId>
<artifactId>java</artifactId>
<scope>system</scope>
<systemPath>${project.basedir}/../java/target/java-4.0.0-alpha1-SNAPSHOT.jar</systemPath>
<type>jar</type> <type>jar</type>
</dependency> </dependency>
</dependencies> </dependencies>
@ -57,11 +51,6 @@
<goals> <goals>
<goal>compile</goal> <goal>compile</goal>
</goals> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution> </execution>
<execution> <execution>
<id>test-compile</id> <id>test-compile</id>
@ -69,12 +58,6 @@
<goals> <goals>
<goal>test-compile</goal> <goal>test-compile</goal>
</goals> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/java</sourceDir>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution> </execution>
</executions> </executions>
<configuration> <configuration>

View File

@ -1,10 +1,7 @@
package solutions.bitbadger.documents.kotlin package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.Parameter
import solutions.bitbadger.documents.ParameterType
import solutions.bitbadger.documents.query.Count import solutions.bitbadger.documents.query.Count
import java.sql.Connection import java.sql.Connection
@ -21,7 +18,6 @@ object Count {
* @param conn The connection over which documents should be counted * @param conn The connection over which documents should be counted
* @return A count of the documents in the table * @return A count of the documents in the table
*/ */
@JvmStatic
fun all(tableName: String, conn: Connection) = fun all(tableName: String, conn: Connection) =
conn.customScalar(Count.all(tableName), mapFunc = Results::toCount) 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 * @param tableName The name of the table in which documents should be counted
* @return A count of the documents in the table * @return A count of the documents in the table
*/ */
@JvmStatic
fun all(tableName: String) = fun all(tableName: String) =
Configuration.dbConn().use { all(tableName, it) } Configuration.dbConn().use { all(tableName, it) }
@ -44,8 +39,6 @@ object Count {
* @param conn The connection on which the deletion should be executed * @param conn The connection on which the deletion should be executed
* @return A count of the matching documents in the table * @return A count of the matching documents in the table
*/ */
@JvmStatic
@JvmOverloads
fun byFields( fun byFields(
tableName: String, tableName: String,
fields: Collection<Field<*>>, fields: Collection<Field<*>>,
@ -68,8 +61,6 @@ object Count {
* @param howMatched How the fields should be matched * @param howMatched How the fields should be matched
* @return A count of the matching documents in the table * @return A count of the matching documents in the table
*/ */
@JvmStatic
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
Configuration.dbConn().use { byFields(tableName, fields, howMatched, it) } Configuration.dbConn().use { byFields(tableName, fields, howMatched, it) }
@ -82,7 +73,6 @@ object Count {
* @return A count of the matching documents in the table * @return A count of the matching documents in the table
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
@JvmStatic
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) = inline fun <reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) =
conn.customScalar(Count.byContains(tableName), listOf(Parameters.json(":criteria", criteria)), Results::toCount) conn.customScalar(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 * @return A count of the matching documents in the table
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
@JvmStatic
inline fun <reified TContains> byContains(tableName: String, criteria: TContains) = inline fun <reified TContains> byContains(tableName: String, criteria: TContains) =
Configuration.dbConn().use { byContains(tableName, criteria, it) } Configuration.dbConn().use { byContains(tableName, criteria, it) }
@ -107,7 +96,6 @@ object Count {
* @return A count of the matching documents in the table * @return A count of the matching documents in the table
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
@JvmStatic
fun byJsonPath(tableName: String, path: String, conn: Connection) = fun byJsonPath(tableName: String, path: String, conn: Connection) =
conn.customScalar( conn.customScalar(
Count.byJsonPath(tableName), Count.byJsonPath(tableName),
@ -123,7 +111,6 @@ object Count {
* @return A count of the matching documents in the table * @return A count of the matching documents in the table
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
@JvmStatic
fun byJsonPath(tableName: String, path: String) = fun byJsonPath(tableName: String, path: String) =
Configuration.dbConn().use { byJsonPath(tableName, path, it) } Configuration.dbConn().use { byJsonPath(tableName, path, it) }
} }

View File

@ -2,7 +2,6 @@ package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.jvm.Parameters
import solutions.bitbadger.documents.jvm.Custom as JvmCustom import solutions.bitbadger.documents.jvm.Custom as JvmCustom
import java.sql.Connection import java.sql.Connection
import java.sql.ResultSet import java.sql.ResultSet

View File

@ -1,4 +1,8 @@
package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.DocumentIndex import solutions.bitbadger.documents.DocumentIndex
import solutions.bitbadger.documents.jvm.Definition as JvmDefinition
import java.sql.Connection import java.sql.Connection
/** /**
@ -13,10 +17,7 @@ object Definition {
* @param conn The connection on which the query should be executed * @param conn The connection on which the query should be executed
*/ */
fun ensureTable(tableName: String, conn: Connection) = fun ensureTable(tableName: String, conn: Connection) =
Configuration.dialect("ensure $tableName exists").let { JvmDefinition.ensureTable(tableName, conn)
conn.customNonQuery(Definition.ensureTable(tableName, it))
conn.customNonQuery(Definition.ensureKey(tableName, it))
}
/** /**
* Create a document table if necessary * Create a document table if necessary
@ -24,7 +25,7 @@ object Definition {
* @param tableName The table whose existence should be ensured (may include schema) * @param tableName The table whose existence should be ensured (may include schema)
*/ */
fun ensureTable(tableName: String) = 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 * 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 * @param conn The connection on which the query should be executed
*/ */
fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>, conn: Connection) = fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>, 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 * 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< * @param fields One or more fields to be indexed<
*/ */
fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>) = fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection<String>) =
Configuration.dbConn().use { ensureFieldIndex(tableName, indexName, fields, it) } JvmDefinition.ensureFieldIndex(tableName, indexName, fields)
/** /**
* Create a document index on a table (PostgreSQL only) * Create a document index on a table (PostgreSQL only)
@ -56,7 +57,7 @@ object Definition {
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex, conn: 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) * Create a document index on a table (PostgreSQL only)
@ -66,5 +67,5 @@ object Definition {
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex) = fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex) =
Configuration.dbConn().use { ensureDocumentIndex(tableName, indexType, it) } JvmDefinition.ensureDocumentIndex(tableName, indexType)
} }

View File

@ -1,7 +1,9 @@
import solutions.bitbadger.documents.Field package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.Parameter import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.ParameterType import solutions.bitbadger.documents.jvm.Delete as JvmDelete
import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Delete
import java.sql.Connection import java.sql.Connection
/** /**
@ -17,10 +19,7 @@ object Delete {
* @param conn The connection on which the deletion should be executed * @param conn The connection on which the deletion should be executed
*/ */
fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) = fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) =
conn.customNonQuery( JvmDelete.byId(tableName, docId, conn)
Delete.byId(tableName, docId),
Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id")))
)
/** /**
* Delete a document by its ID * Delete a document by its ID
@ -29,7 +28,7 @@ object Delete {
* @param docId The ID of the document to be deleted * @param docId The ID of the document to be deleted
*/ */
fun <TKey> byId(tableName: String, docId: TKey) = fun <TKey> byId(tableName: String, docId: TKey) =
Configuration.dbConn().use { byId(tableName, docId, it) } JvmDelete.byId(tableName, docId)
/** /**
* Delete documents using a field comparison * Delete documents using a field comparison
@ -39,10 +38,8 @@ object Delete {
* @param howMatched How the fields should be matched * @param howMatched How the fields should be matched
* @param conn The connection on which the deletion should be executed * @param conn The connection on which the deletion should be executed
*/ */
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null, conn: Connection) { fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null, conn: Connection) =
val named = Parameters.nameFields(fields) JvmDelete.byFields(tableName, fields, howMatched, conn)
conn.customNonQuery(Delete.byFields(tableName, named, howMatched), Parameters.addFields(named))
}
/** /**
* Delete documents using a field comparison * Delete documents using a field comparison
@ -52,7 +49,7 @@ object Delete {
* @param howMatched How the fields should be matched * @param howMatched How the fields should be matched
*/ */
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, 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) * Delete documents using a JSON containment query (PostgreSQL only)
@ -84,7 +81,7 @@ object Delete {
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
fun byJsonPath(tableName: String, path: String, conn: 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) * Delete documents using a JSON Path match query (PostgreSQL only)
@ -94,5 +91,5 @@ object Delete {
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
fun byJsonPath(tableName: String, path: String) = fun byJsonPath(tableName: String, path: String) =
Configuration.dbConn().use { byJsonPath(tableName, path, it) } JvmDelete.byJsonPath(tableName, path)
} }

View File

@ -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 <reified TDoc> 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 <reified TDoc> 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 <reified TDoc> 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 <reified TDoc> 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 <TKey, reified TDoc> 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 <TKey, reified TDoc> update(tableName: String, docId: TKey, document: TDoc) =
Configuration.dbConn().use { update(tableName, docId, document, it) }
}

View File

@ -1,7 +1,9 @@
import solutions.bitbadger.documents.Field package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.Parameter import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.ParameterType import solutions.bitbadger.documents.jvm.Exists as JvmExists
import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Exists
import java.sql.Connection import java.sql.Connection
/** /**
@ -18,11 +20,7 @@ object Exists {
* @return True if the document exists, false if not * @return True if the document exists, false if not
*/ */
fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) = fun <TKey> byId(tableName: String, docId: TKey, conn: Connection) =
conn.customScalar( JvmExists.byId(tableName, docId, conn)
Exists.byId(tableName, docId),
Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))),
Results::toExists
)
/** /**
* Determine a document's existence by its ID * Determine a document's existence by its ID
@ -32,7 +30,7 @@ object Exists {
* @return True if the document exists, false if not * @return True if the document exists, false if not
*/ */
fun <TKey> byId(tableName: String, docId: TKey) = fun <TKey> byId(tableName: String, docId: TKey) =
Configuration.dbConn().use { byId(tableName, docId, it) } JvmExists.byId(tableName, docId)
/** /**
* Determine document existence using a field comparison * 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 * @param conn The connection on which the existence check should be executed
* @return True if any matching documents exist, false if not * @return True if any matching documents exist, false if not
*/ */
fun byFields( fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null, conn: Connection) =
tableName: String, JvmExists.byFields(tableName, fields, howMatched, conn)
fields: Collection<Field<*>>,
howMatched: FieldMatch? = null,
conn: Connection
): Boolean {
val named = Parameters.nameFields(fields)
return conn.customScalar(
Exists.byFields(tableName, named, howMatched),
Parameters.addFields(named),
Results::toExists
)
}
/** /**
* Determine document existence using a field comparison * Determine document existence using a field comparison
@ -66,7 +53,7 @@ object Exists {
* @return True if any matching documents exist, false if not * @return True if any matching documents exist, false if not
*/ */
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, 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) * Determine document existence using a JSON containment query (PostgreSQL only)
@ -105,11 +92,7 @@ object Exists {
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
fun byJsonPath(tableName: String, path: String, conn: Connection) = fun byJsonPath(tableName: String, path: String, conn: Connection) =
conn.customScalar( JvmExists.byJsonPath(tableName, path, conn)
Exists.byJsonPath(tableName),
listOf(Parameter(":path", ParameterType.STRING, path)),
Results::toExists
)
/** /**
* Determine document existence using a JSON Path match query (PostgreSQL only) * 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 * @throws DocumentException If called on a SQLite connection
*/ */
fun byJsonPath(tableName: String, path: String) = fun byJsonPath(tableName: String, path: String) =
Configuration.dbConn().use { byJsonPath(tableName, path, it) } JvmExists.byJsonPath(tableName, path)
} }

View File

@ -1,7 +1,9 @@
import solutions.bitbadger.documents.Field package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.Parameter import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.ParameterType import solutions.bitbadger.documents.kotlin.extensions.*
import solutions.bitbadger.documents.query.Find
import solutions.bitbadger.documents.query.orderBy
import java.sql.Connection import java.sql.Connection
/** /**
@ -17,7 +19,7 @@ object Find {
* @param conn The connection over which documents should be retrieved * @param conn The connection over which documents should be retrieved
* @return A list of documents from the given table * @return A list of documents from the given table
*/ */
inline fun <reified TDoc> all(tableName: String, orderBy: Collection<Field<*>>? = null, conn: Connection) = inline fun <reified TDoc : Any> all(tableName: String, orderBy: Collection<Field<*>>? = null, conn: Connection) =
conn.customList<TDoc>(Find.all(tableName) + (orderBy?.let(::orderBy) ?: ""), mapFunc = Results::fromData) conn.customList<TDoc>(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) * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents from the given table * @return A list of documents from the given table
*/ */
inline fun <reified TDoc> all(tableName: String, orderBy: Collection<Field<*>>? = null) = inline fun <reified TDoc : Any> all(tableName: String, orderBy: Collection<Field<*>>? = null) =
Configuration.dbConn().use { all<TDoc>(tableName, orderBy, it) } Configuration.dbConn().use { all<TDoc>(tableName, orderBy, it) }
/** /**
@ -37,7 +39,7 @@ object Find {
* @param conn The connection over which documents should be retrieved * @param conn The connection over which documents should be retrieved
* @return A list of documents from the given table * @return A list of documents from the given table
*/ */
inline fun <reified TDoc> all(tableName: String, conn: Connection) = inline fun <reified TDoc : Any> all(tableName: String, conn: Connection) =
all<TDoc>(tableName, null, conn) all<TDoc>(tableName, null, conn)
/** /**
@ -48,7 +50,7 @@ object Find {
* @param conn The connection over which documents should be retrieved * @param conn The connection over which documents should be retrieved
* @return The document if it is found, `null` otherwise * @return The document if it is found, `null` otherwise
*/ */
inline fun <TKey, reified TDoc> byId(tableName: String, docId: TKey, conn: Connection) = inline fun <TKey, reified TDoc : Any> byId(tableName: String, docId: TKey, conn: Connection) =
conn.customSingle<TDoc>( conn.customSingle<TDoc>(
Find.byId(tableName, docId), Find.byId(tableName, docId),
Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))), Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))),
@ -62,7 +64,7 @@ object Find {
* @param docId The ID of the document to retrieve * @param docId The ID of the document to retrieve
* @return The document if it is found, `null` otherwise * @return The document if it is found, `null` otherwise
*/ */
inline fun <TKey, reified TDoc> byId(tableName: String, docId: TKey) = inline fun <TKey, reified TDoc : Any> byId(tableName: String, docId: TKey) =
Configuration.dbConn().use { byId<TKey, TDoc>(tableName, docId, it) } Configuration.dbConn().use { byId<TKey, TDoc>(tableName, docId, it) }
/** /**
@ -75,7 +77,7 @@ object Find {
* @param conn The connection over which documents should be retrieved * @param conn The connection over which documents should be retrieved
* @return A list of documents matching the field comparison * @return A list of documents matching the field comparison
*/ */
inline fun <reified TDoc> byFields( inline fun <reified TDoc : Any> byFields(
tableName: String, tableName: String,
fields: Collection<Field<*>>, fields: Collection<Field<*>>,
howMatched: FieldMatch? = null, howMatched: FieldMatch? = null,
@ -99,7 +101,7 @@ object Find {
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents matching the field comparison * @return A list of documents matching the field comparison
*/ */
inline fun <reified TDoc> byFields( inline fun <reified TDoc : Any> byFields(
tableName: String, tableName: String,
fields: Collection<Field<*>>, fields: Collection<Field<*>>,
howMatched: FieldMatch? = null, howMatched: FieldMatch? = null,
@ -116,7 +118,7 @@ object Find {
* @param conn The connection over which documents should be retrieved * @param conn The connection over which documents should be retrieved
* @return A list of documents matching the field comparison * @return A list of documents matching the field comparison
*/ */
inline fun <reified TDoc> byFields( inline fun <reified TDoc : Any> byFields(
tableName: String, tableName: String,
fields: Collection<Field<*>>, fields: Collection<Field<*>>,
howMatched: FieldMatch? = null, howMatched: FieldMatch? = null,
@ -132,7 +134,7 @@ object Find {
* @param howMatched How the fields should be matched * @param howMatched How the fields should be matched
* @return A list of documents matching the field comparison * @return A list of documents matching the field comparison
*/ */
inline fun <reified TDoc> byFields( inline fun <reified TDoc : Any> byFields(
tableName: String, tableName: String,
fields: Collection<Field<*>>, fields: Collection<Field<*>>,
howMatched: FieldMatch? = null howMatched: FieldMatch? = null
@ -149,7 +151,7 @@ object Find {
* @return A list of documents matching the JSON containment query * @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc, reified TContains> byContains( inline fun <reified TDoc : Any, reified TContains> byContains(
tableName: String, tableName: String,
criteria: TContains, criteria: TContains,
orderBy: Collection<Field<*>>? = null, orderBy: Collection<Field<*>>? = null,
@ -170,7 +172,7 @@ object Find {
* @return A list of documents matching the JSON containment query * @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc, reified TContains> byContains( inline fun <reified TDoc : Any, reified TContains> byContains(
tableName: String, tableName: String,
criteria: TContains, criteria: TContains,
orderBy: Collection<Field<*>>? = null orderBy: Collection<Field<*>>? = null
@ -186,7 +188,11 @@ object Find {
* @return A list of documents matching the JSON containment query * @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc, reified TContains> byContains(tableName: String, criteria: TContains, conn: Connection) = inline fun <reified TDoc : Any, reified TContains> byContains(
tableName: String,
criteria: TContains,
conn: Connection
) =
byContains<TDoc, TContains>(tableName, criteria, null, conn) byContains<TDoc, TContains>(tableName, criteria, null, conn)
/** /**
@ -197,7 +203,7 @@ object Find {
* @return A list of documents matching the JSON containment query * @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc, reified TContains> byContains(tableName: String, criteria: TContains) = inline fun <reified TDoc : Any, reified TContains> byContains(tableName: String, criteria: TContains) =
Configuration.dbConn().use { byContains<TDoc, TContains>(tableName, criteria, it) } Configuration.dbConn().use { byContains<TDoc, TContains>(tableName, criteria, it) }
/** /**
@ -210,7 +216,7 @@ object Find {
* @return A list of documents matching the JSON Path match query * @return A list of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc> byJsonPath( inline fun <reified TDoc : Any> byJsonPath(
tableName: String, tableName: String,
path: String, path: String,
orderBy: Collection<Field<*>>? = null, orderBy: Collection<Field<*>>? = null,
@ -231,7 +237,7 @@ object Find {
* @return A list of documents matching the JSON Path match query * @return A list of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc> byJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null) = inline fun <reified TDoc : Any> byJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null) =
Configuration.dbConn().use { byJsonPath<TDoc>(tableName, path, orderBy, it) } Configuration.dbConn().use { byJsonPath<TDoc>(tableName, path, orderBy, it) }
/** /**
@ -243,7 +249,7 @@ object Find {
* @return A list of documents matching the JSON Path match query * @return A list of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc> byJsonPath(tableName: String, path: String, conn: Connection) = inline fun <reified TDoc : Any> byJsonPath(tableName: String, path: String, conn: Connection) =
byJsonPath<TDoc>(tableName, path, null, conn) byJsonPath<TDoc>(tableName, path, null, conn)
/** /**
@ -256,7 +262,7 @@ object Find {
* @param conn The connection over which documents should be retrieved * @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 * @return The first document matching the field comparison, or `null` if no matches are found
*/ */
inline fun <reified TDoc> firstByFields( inline fun <reified TDoc : Any> firstByFields(
tableName: String, tableName: String,
fields: Collection<Field<*>>, fields: Collection<Field<*>>,
howMatched: FieldMatch? = null, howMatched: FieldMatch? = null,
@ -280,7 +286,7 @@ object Find {
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) * @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 * @return The first document matching the field comparison, or `null` if no matches are found
*/ */
inline fun <reified TDoc> firstByFields( inline fun <reified TDoc : Any> firstByFields(
tableName: String, tableName: String,
fields: Collection<Field<*>>, fields: Collection<Field<*>>,
howMatched: FieldMatch? = null, howMatched: FieldMatch? = null,
@ -297,7 +303,7 @@ object Find {
* @param conn The connection over which documents should be retrieved * @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 * @return The first document matching the field comparison, or `null` if no matches are found
*/ */
inline fun <reified TDoc> firstByFields( inline fun <reified TDoc : Any> firstByFields(
tableName: String, tableName: String,
fields: Collection<Field<*>>, fields: Collection<Field<*>>,
howMatched: FieldMatch? = null, 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 * @return The first document matching the JSON containment query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc, reified TContains> firstByContains( inline fun <reified TDoc : Any, reified TContains> firstByContains(
tableName: String, tableName: String,
criteria: TContains, criteria: TContains,
orderBy: Collection<Field<*>>? = null, orderBy: Collection<Field<*>>? = null,
@ -336,7 +342,11 @@ object Find {
* @return The first document matching the JSON containment query, or `null` if no matches are found * @return The first document matching the JSON containment query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc, reified TContains> firstByContains(tableName: String, criteria: TContains, conn: Connection) = inline fun <reified TDoc : Any, reified TContains> firstByContains(
tableName: String,
criteria: TContains,
conn: Connection
) =
firstByContains<TDoc, TContains>(tableName, criteria, null, conn) firstByContains<TDoc, TContains>(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 * @return The first document matching the JSON containment query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc, reified TContains> firstByContains(tableName: String, criteria: TContains, orderBy: Collection<Field<*>>? = null) = inline fun <reified TDoc : Any, reified TContains> firstByContains(
tableName: String,
criteria: TContains,
orderBy: Collection<Field<*>>? = null
) =
Configuration.dbConn().use { firstByContains<TDoc, TContains>(tableName, criteria, orderBy, it) } Configuration.dbConn().use { firstByContains<TDoc, TContains>(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 * @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 * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc> firstByJsonPath( inline fun <reified TDoc : Any> firstByJsonPath(
tableName: String, tableName: String,
path: String, path: String,
orderBy: Collection<Field<*>>? = null, orderBy: Collection<Field<*>>? = null,
@ -382,7 +396,7 @@ object Find {
* @return The first document matching the JSON Path match query, or `null` if no matches are found * @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 * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc> firstByJsonPath(tableName: String, path: String, conn: Connection) = inline fun <reified TDoc : Any> firstByJsonPath(tableName: String, path: String, conn: Connection) =
firstByJsonPath<TDoc>(tableName, path, null, conn) firstByJsonPath<TDoc>(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 * @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 * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc> firstByJsonPath(tableName: String, path: String, orderBy: Collection<Field<*>>? = null) = inline fun <reified TDoc : Any> firstByJsonPath(
tableName: String,
path: String,
orderBy: Collection<Field<*>>? = null
) =
Configuration.dbConn().use { firstByJsonPath<TDoc>(tableName, path, orderBy, it) } Configuration.dbConn().use { firstByJsonPath<TDoc>(tableName, path, orderBy, it) }
} }

View File

@ -1,10 +1,8 @@
package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.ParameterName import solutions.bitbadger.documents.jvm.Parameters as JvmParameters
import solutions.bitbadger.documents.common.*
import java.sql.Connection 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 * 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 * @param fields The collection of fields to be named
* @return The collection of fields with parameter names assigned * @return The collection of fields with parameter names assigned
*/ */
@JvmStatic fun nameFields(fields: Collection<Field<*>>): Collection<Field<*>> =
fun nameFields(fields: Collection<Field<*>>): Collection<Field<*>> { JvmParameters.nameFields(fields)
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 * Create a parameter by encoding a JSON object
@ -38,9 +27,8 @@ object Parameters {
* @param value The object to be encoded as JSON * @param value The object to be encoded as JSON
* @return A parameter with the value encoded * @return A parameter with the value encoded
*/ */
@JvmStatic inline fun <reified T> json(name: String, value: T) =
fun <T> json(name: String, value: T) = Parameter(name, ParameterType.JSON, DocumentConfig.serialize(value))
Parameter(name, ParameterType.JSON, Configuration.serializer.serialize(value))
/** /**
* Add field parameters to the given set of parameters * 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) * @param existing Any existing parameters for the query (optional, defaults to empty collection)
* @return A collection of parameters for the query * @return A collection of parameters for the query
*/ */
@JvmStatic
fun addFields(fields: Collection<Field<*>>, existing: MutableCollection<Parameter<*>> = mutableListOf()) = fun addFields(fields: Collection<Field<*>>, existing: MutableCollection<Parameter<*>> = mutableListOf()) =
fields.fold(existing) { acc, field -> field.appendParameter(acc) } JvmParameters.addFields(fields, existing)
/** /**
* Replace the parameter names in the query with question marks * Replace the parameter names in the query with question marks
@ -60,9 +47,8 @@ object Parameters {
* @param parameters The parameters for the query * @param parameters The parameters for the query
* @return The query, with name parameters changed to `?`s * @return The query, with name parameters changed to `?`s
*/ */
@JvmStatic
fun replaceNamesInQuery(query: String, parameters: Collection<Parameter<*>>) = fun replaceNamesInQuery(query: String, parameters: Collection<Parameter<*>>) =
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 * 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 * @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 If parameter names are invalid or number value types are invalid
*/ */
@Throws(DocumentException::class) fun apply(conn: Connection, query: String, parameters: Collection<Parameter<*>>) =
@JvmStatic JvmParameters.apply(conn, query, parameters)
fun apply(conn: Connection, query: String, parameters: Collection<Parameter<*>>): PreparedStatement {
if (parameters.isEmpty()) return try {
conn.prepareStatement(query)
} catch (ex: SQLException) {
throw DocumentException("Error preparing no-parameter query: ${ex.message}", ex)
}
val replacements = mutableListOf<Pair<Int, Parameter<*>>>()
parameters.sortedByDescending { it.name.length }.forEach {
var startPos = query.indexOf(it.name)
while (startPos > -1) {
replacements.add(Pair(startPos, it))
startPos = query.indexOf(it.name, startPos + it.name.length + 1)
}
}
return try {
replaceNamesInQuery(query, parameters)
//.also(::println)
.let { conn.prepareStatement(it) }
.also { stmt ->
replacements.sortedBy { it.first }
.map { it.second }
.forEachIndexed { index, param -> param.bind(stmt, index + 1) }
}
} catch (ex: SQLException) {
throw DocumentException("Error creating query / binding parameters: ${ex.message}", ex)
}
}
/** /**
* Create parameters for field names to be removed from a document * 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 * @return A list of parameters to use for building the query
* @throws DocumentException If the dialect has not been set * @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class) fun fieldNames(names: Collection<String>, parameterName: String = ":name") =
@JvmStatic JvmParameters.fieldNames(names, parameterName)
@JvmOverloads
fun fieldNames(names: Collection<String>, parameterName: String = ":name"): MutableCollection<Parameter<*>> =
when (Configuration.dialect("generate field name parameters")) {
Dialect.POSTGRESQL -> mutableListOf(
Parameter(parameterName, ParameterType.STRING, names.joinToString(",").let { "{$it}" })
)
Dialect.SQLITE -> names.mapIndexed { index, name ->
Parameter("$parameterName$index", ParameterType.STRING, name)
}.toMutableList()
}
} }

View File

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

View File

@ -1,5 +1,9 @@
package solutions.bitbadger.documents.kotlin
import solutions.bitbadger.documents.* 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 import java.sql.Connection
/** /**
@ -7,20 +11,6 @@ import java.sql.Connection
*/ */
object RemoveFields { object RemoveFields {
/**
* Translate field paths to JSON paths for SQLite queries
*
* @param parameters The parameters for the specified fields
* @return The parameters for the specified fields, translated if used for SQLite
*/
private fun translatePath(parameters: MutableCollection<Parameter<*>>): MutableCollection<Parameter<*>> {
val dialect = Configuration.dialect("remove fields")
return when (dialect) {
Dialect.POSTGRESQL -> parameters
Dialect.SQLITE -> parameters.map { Parameter(it.name, it.type, "$.${it.value}") }.toMutableList()
}
}
/** /**
* Remove fields from a document by its ID * 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 toRemove The names of the fields to be removed
* @param conn The connection on which the update should be executed * @param conn The connection on which the update should be executed
*/ */
fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>, conn: Connection) { fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>, conn: Connection) =
val nameParams = Parameters.fieldNames(toRemove) JvmRemoveFields.byId(tableName, docId, toRemove, conn)
conn.customNonQuery(
RemoveFields.byId(tableName, nameParams, docId),
Parameters.addFields(
listOf(Field.equal(Configuration.idField, docId, ":id")),
translatePath(nameParams)
)
)
}
/** /**
* Remove fields from a document by its ID * Remove fields from a document by its ID
@ -48,7 +30,7 @@ object RemoveFields {
* @param toRemove The names of the fields to be removed * @param toRemove The names of the fields to be removed
*/ */
fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>) = fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>) =
Configuration.dbConn().use { byId(tableName, docId, toRemove, it) } JvmRemoveFields.byId(tableName, docId, toRemove)
/** /**
* Remove fields from documents using a field comparison * Remove fields from documents using a field comparison
@ -65,14 +47,8 @@ object RemoveFields {
toRemove: Collection<String>, toRemove: Collection<String>,
howMatched: FieldMatch? = null, howMatched: FieldMatch? = null,
conn: Connection conn: Connection
) { ) =
val named = Parameters.nameFields(fields) JvmRemoveFields.byFields(tableName, fields, toRemove, howMatched, conn)
val nameParams = Parameters.fieldNames(toRemove)
conn.customNonQuery(
RemoveFields.byFields(tableName, nameParams, named, howMatched),
Parameters.addFields(named, translatePath(nameParams))
)
}
/** /**
* Remove fields from documents using a field comparison * Remove fields from documents using a field comparison
@ -88,7 +64,7 @@ object RemoveFields {
toRemove: Collection<String>, toRemove: Collection<String>,
howMatched: FieldMatch? = null 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) * 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 * @param conn The connection on which the update should be executed
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>, conn: Connection) { fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>, conn: Connection) =
val nameParams = Parameters.fieldNames(toRemove) JvmRemoveFields.byJsonPath(tableName, path, toRemove, conn)
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) * 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 * @throws DocumentException If called on a SQLite connection
*/ */
fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>) = fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>) =
Configuration.dbConn().use { byJsonPath(tableName, path, toRemove, it) } JvmRemoveFields.byJsonPath(tableName, path, toRemove)
} }

View File

@ -16,21 +16,18 @@ object Results {
* Create a domain item from a document, specifying the field in which the document is found * 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 field The field name containing the JSON document
* @param rs A `ResultSet` set to the row with the document to be constructed * @return A function to create the constructed domain item
* @return The constructed domain item
*/ */
inline fun <reified TDoc> fromDocument(field: String): (ResultSet, Class<TDoc>) -> TDoc = inline fun <reified TDoc> fromDocument(field: String): (ResultSet) -> TDoc =
{ rs, _ -> Results.fromDocument(field, rs, TDoc::class.java) } { rs -> DocumentConfig.deserialize(rs.getString(field)) }
/** /**
* Create a domain item from a document * 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 * @return The constructed domain item
*/ */
inline fun <reified TDoc> fromData(rs: ResultSet, clazz: Class<TDoc> = TDoc::class.java) = inline fun <reified TDoc> fromData(rs: ResultSet) =
Results.fromDocument("data", rs, TDoc::class.java) fromDocument<TDoc>("data")(rs)
/** /**
* Create a list of items for the results of the given command, using the specified mapping function * 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 * @param rs A `ResultSet` set to the row with the count to retrieve
* @return The count from the row * @return The count from the row
*/ */
fun toCount(rs: ResultSet, clazz: Class<Long> = Long::class.java) = fun toCount(rs: ResultSet) =
when (Configuration.dialect()) { when (Configuration.dialect()) {
Dialect.POSTGRESQL -> rs.getInt("it").toLong() Dialect.POSTGRESQL -> rs.getInt("it").toLong()
Dialect.SQLITE -> rs.getLong("it") 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 * @param rs A `ResultSet` set to the row with the true/false value to retrieve
* @return The true/false value from the row * @return The true/false value from the row
*/ */
fun toExists(rs: ResultSet, clazz: Class<Boolean> = Boolean::class.java) = fun toExists(rs: ResultSet) =
when (Configuration.dialect()) { when (Configuration.dialect()) {
Dialect.POSTGRESQL -> rs.getBoolean("it") Dialect.POSTGRESQL -> rs.getBoolean("it")
Dialect.SQLITE -> toCount(rs) > 0L Dialect.SQLITE -> toCount(rs) > 0L

View File

@ -1,10 +1,7 @@
package solutions.bitbadger.documents.kotlin package solutions.bitbadger.documents.kotlin.extensions
import solutions.bitbadger.documents.DocumentIndex import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.kotlin.*
import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.Parameter
import solutions.bitbadger.documents.java.jvm.Document
import java.sql.Connection import java.sql.Connection
import java.sql.ResultSet import java.sql.ResultSet
@ -18,8 +15,8 @@ import java.sql.ResultSet
* @param mapFunc The mapping function between the document and the domain item * @param mapFunc The mapping function between the document and the domain item
* @return A list of results for the given query * @return A list of results for the given query
*/ */
inline fun <reified TDoc> Connection.customList( inline fun <reified TDoc : Any> Connection.customList(
query: String, parameters: Collection<Parameter<*>> = listOf(), noinline mapFunc: (ResultSet, Class<TDoc>) -> TDoc query: String, parameters: Collection<Parameter<*>> = listOf(), mapFunc: (ResultSet) -> TDoc
) = Custom.list(query, parameters, this, mapFunc) ) = Custom.list(query, parameters, this, mapFunc)
/** /**
@ -30,8 +27,8 @@ inline fun <reified TDoc> Connection.customList(
* @param mapFunc The mapping function between the document and the domain item * @param mapFunc The mapping function between the document and the domain item
* @return The document if one matches the query, `null` otherwise * @return The document if one matches the query, `null` otherwise
*/ */
inline fun <reified TDoc> Connection.customSingle( inline fun <reified TDoc : Any> Connection.customSingle(
query: String, parameters: Collection<Parameter<*>> = listOf(), noinline mapFunc: (ResultSet, Class<TDoc>) -> TDoc query: String, parameters: Collection<Parameter<*>> = listOf(), mapFunc: (ResultSet) -> TDoc
) = Custom.single(query, parameters, this, mapFunc) ) = Custom.single(query, parameters, this, mapFunc)
/** /**
@ -54,7 +51,7 @@ fun Connection.customNonQuery(query: String, parameters: Collection<Parameter<*>
inline fun <reified T : Any> Connection.customScalar( inline fun <reified T : Any> Connection.customScalar(
query: String, query: String,
parameters: Collection<Parameter<*>> = listOf(), parameters: Collection<Parameter<*>> = listOf(),
noinline mapFunc: (ResultSet, Class<T>) -> T mapFunc: (ResultSet) -> T
) = Custom.scalar(query, parameters, this, mapFunc) ) = Custom.scalar(query, parameters, this, mapFunc)
// ~~~ DEFINITION QUERIES ~~~ // ~~~ 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) * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents from the given table * @return A list of documents from the given table
*/ */
inline fun <reified TDoc> Connection.findAll(tableName: String, orderBy: Collection<Field<*>>? = null) = inline fun <reified TDoc : Any> Connection.findAll(tableName: String, orderBy: Collection<Field<*>>? = null) =
Find.all<TDoc>(tableName, orderBy, this) Find.all<TDoc>(tableName, orderBy, this)
/** /**
@ -225,7 +222,7 @@ inline fun <reified TDoc> Connection.findAll(tableName: String, orderBy: Collect
* @param docId The ID of the document to retrieve * @param docId The ID of the document to retrieve
* @return The document if it is found, `null` otherwise * @return The document if it is found, `null` otherwise
*/ */
inline fun <TKey, reified TDoc> Connection.findById(tableName: String, docId: TKey) = inline fun <TKey, reified TDoc : Any> Connection.findById(tableName: String, docId: TKey) =
Find.byId<TKey, TDoc>(tableName, docId, this) Find.byId<TKey, TDoc>(tableName, docId, this)
/** /**
@ -237,7 +234,7 @@ inline fun <TKey, reified TDoc> Connection.findById(tableName: String, docId: TK
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
* @return A list of documents matching the field comparison * @return A list of documents matching the field comparison
*/ */
inline fun <reified TDoc> Connection.findByFields( inline fun <reified TDoc : Any> Connection.findByFields(
tableName: String, tableName: String,
fields: Collection<Field<*>>, fields: Collection<Field<*>>,
howMatched: FieldMatch? = null, howMatched: FieldMatch? = null,
@ -254,7 +251,7 @@ inline fun <reified TDoc> Connection.findByFields(
* @return A list of documents matching the JSON containment query * @return A list of documents matching the JSON containment query
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc, reified TContains> Connection.findByContains( inline fun <reified TDoc : Any, reified TContains> Connection.findByContains(
tableName: String, tableName: String,
criteria: TContains, criteria: TContains,
orderBy: Collection<Field<*>>? = null orderBy: Collection<Field<*>>? = null
@ -270,7 +267,7 @@ inline fun <reified TDoc, reified TContains> Connection.findByContains(
* @return A list of documents matching the JSON Path match query * @return A list of documents matching the JSON Path match query
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc> Connection.findByJsonPath( inline fun <reified TDoc : Any> Connection.findByJsonPath(
tableName: String, tableName: String,
path: String, path: String,
orderBy: Collection<Field<*>>? = null orderBy: Collection<Field<*>>? = null
@ -286,7 +283,7 @@ inline fun <reified TDoc> Connection.findByJsonPath(
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) * @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 * @return The first document matching the field comparison, or `null` if no matches are found
*/ */
inline fun <reified TDoc> Connection.findFirstByFields( inline fun <reified TDoc : Any> Connection.findFirstByFields(
tableName: String, tableName: String,
fields: Collection<Field<*>>, fields: Collection<Field<*>>,
howMatched: FieldMatch? = null, howMatched: FieldMatch? = null,
@ -303,7 +300,7 @@ inline fun <reified TDoc> Connection.findFirstByFields(
* @return The first document matching the JSON containment query, or `null` if no matches are found * @return The first document matching the JSON containment query, or `null` if no matches are found
* @throws DocumentException If called on a SQLite connection * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc, reified TContains> Connection.findFirstByContains( inline fun <reified TDoc : Any, reified TContains> Connection.findFirstByContains(
tableName: String, tableName: String,
criteria: TContains, criteria: TContains,
orderBy: Collection<Field<*>>? = null orderBy: Collection<Field<*>>? = null
@ -319,7 +316,7 @@ inline fun <reified TDoc, reified TContains> Connection.findFirstByContains(
* @return The first document matching the JSON Path match query, or `null` if no matches are found * @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 * @throws DocumentException If called on a SQLite connection
*/ */
inline fun <reified TDoc> Connection.findFirstByJsonPath( inline fun <reified TDoc : Any> Connection.findFirstByJsonPath(
tableName: String, tableName: String,
path: String, path: String,
orderBy: Collection<Field<*>>? = null orderBy: Collection<Field<*>>? = null

View File

@ -46,6 +46,7 @@
<modules> <modules>
<module>jvm</module> <module>jvm</module>
<module>kotlin</module>
</modules> </modules>
<dependencies> <dependencies>