Initial Development #1
@ -23,13 +23,10 @@ data class ArrayDocument(val id: String, val values: List<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class JsonDocument(val id: String, val value: String, val numValue: Int, val sub: SubDocument?) {
|
data class JsonDocument(val id: String, val value: String = "", val numValue: Int = 0, val sub: SubDocument? = null) {
|
||||||
companion object {
|
companion object {
|
||||||
/** An empty JsonDocument */
|
|
||||||
val emptyDoc = JsonDocument("", "", 0, null)
|
|
||||||
|
|
||||||
/** Documents to use for testing */
|
/** Documents to use for testing */
|
||||||
val testDocuments = listOf(
|
private val testDocuments = listOf(
|
||||||
JsonDocument("one", "FIRST!", 0, null),
|
JsonDocument("one", "FIRST!", 0, null),
|
||||||
JsonDocument("two", "another", 10, SubDocument("green", "blue")),
|
JsonDocument("two", "another", 10, SubDocument("green", "blue")),
|
||||||
JsonDocument("three", "", 4, null),
|
JsonDocument("three", "", 4, null),
|
||||||
|
@ -56,7 +56,7 @@ object Document {
|
|||||||
Configuration.autoIdStrategy = AutoId.UUID
|
Configuration.autoIdStrategy = AutoId.UUID
|
||||||
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
|
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
|
||||||
|
|
||||||
db.conn.insert(TEST_TABLE, JsonDocument.emptyDoc)
|
db.conn.insert(TEST_TABLE, JsonDocument(""))
|
||||||
|
|
||||||
val after = db.conn.findAll<JsonDocument>(TEST_TABLE)
|
val after = db.conn.findAll<JsonDocument>(TEST_TABLE)
|
||||||
assertEquals(1, after.size, "There should have been 1 document returned")
|
assertEquals(1, after.size, "There should have been 1 document returned")
|
||||||
@ -71,10 +71,10 @@ object Document {
|
|||||||
Configuration.autoIdStrategy = AutoId.RANDOM_STRING
|
Configuration.autoIdStrategy = AutoId.RANDOM_STRING
|
||||||
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
|
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
|
||||||
|
|
||||||
db.conn.insert(TEST_TABLE, JsonDocument.emptyDoc)
|
db.conn.insert(TEST_TABLE, JsonDocument(""))
|
||||||
|
|
||||||
Configuration.idStringLength = 21
|
Configuration.idStringLength = 21
|
||||||
db.conn.insert(TEST_TABLE, JsonDocument.emptyDoc)
|
db.conn.insert(TEST_TABLE, JsonDocument(""))
|
||||||
|
|
||||||
val after = db.conn.findAll<JsonDocument>(TEST_TABLE)
|
val after = db.conn.findAll<JsonDocument>(TEST_TABLE)
|
||||||
assertEquals(2, after.size, "There should have been 2 documents returned")
|
assertEquals(2, after.size, "There should have been 2 documents returned")
|
||||||
|
@ -7,7 +7,7 @@ import kotlin.test.assertNotNull
|
|||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests for the `Find` object
|
* Integration tests for the `Patch` object
|
||||||
*/
|
*/
|
||||||
object Patch {
|
object Patch {
|
||||||
|
|
||||||
|
106
src/integration-test/kotlin/common/RemoveFields.kt
Normal file
106
src/integration-test/kotlin/common/RemoveFields.kt
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package solutions.bitbadger.documents.common
|
||||||
|
|
||||||
|
import solutions.bitbadger.documents.*
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for the `RemoveFields` object
|
||||||
|
*/
|
||||||
|
object RemoveFields {
|
||||||
|
|
||||||
|
fun byIdMatchFields(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
db.conn.removeFieldsById(TEST_TABLE, "two", listOf("sub", "value"))
|
||||||
|
val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "two")
|
||||||
|
assertNotNull(doc, "There should have been a document returned")
|
||||||
|
assertEquals("", doc.value, "The value should have been empty")
|
||||||
|
assertNull(doc.sub, "The sub-document should have been removed")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byIdMatchNoFields(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
assertFalse { db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("a_field_that_does_not_exist"))) }
|
||||||
|
db.conn.removeFieldsById(TEST_TABLE, "one", listOf("a_field_that_does_not_exist")) // no exception = pass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byIdNoMatch(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
assertFalse { db.conn.existsById(TEST_TABLE, "fifty") }
|
||||||
|
db.conn.removeFieldsById(TEST_TABLE, "fifty", listOf("sub")) // no exception = pass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byFieldsMatchFields(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
val fields = listOf(Field.equal("numValue", 17))
|
||||||
|
db.conn.removeFieldsByFields(TEST_TABLE, fields, listOf("sub"))
|
||||||
|
val doc = db.conn.findFirstByFields<JsonDocument>(TEST_TABLE, fields)
|
||||||
|
assertNotNull(doc, "The document should have been returned")
|
||||||
|
assertEquals("four", doc.id, "An incorrect document was returned")
|
||||||
|
assertNull(doc.sub, "The sub-document should have been removed")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byFieldsMatchNoFields(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
assertFalse { db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("nada"))) }
|
||||||
|
db.conn.removeFieldsByFields(TEST_TABLE, listOf(Field.equal("numValue", 17)), listOf("nada")) // no exn = pass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byFieldsNoMatch(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
val fields = listOf(Field.notEqual("missing", "nope"))
|
||||||
|
assertFalse { db.conn.existsByFields(TEST_TABLE, fields) }
|
||||||
|
db.conn.removeFieldsByFields(TEST_TABLE, fields, listOf("value")) // no exception = pass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byContainsMatchFields(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
val criteria = mapOf("sub" to mapOf("foo" to "green"))
|
||||||
|
db.conn.removeFieldsByContains(TEST_TABLE, criteria, listOf("value"))
|
||||||
|
val docs = db.conn.findByContains<JsonDocument, Map<String, Map<String, String>>>(TEST_TABLE, criteria)
|
||||||
|
assertEquals(2, docs.size, "There should have been 2 documents returned")
|
||||||
|
docs.forEach {
|
||||||
|
assertTrue(listOf("two", "four").contains(it.id), "An incorrect document was returned (${it.id})")
|
||||||
|
assertEquals("", it.value, "The value should have been empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byContainsMatchNoFields(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
assertFalse { db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("invalid_field"))) }
|
||||||
|
db.conn.removeFieldsByContains(TEST_TABLE, mapOf("sub" to mapOf("foo" to "green")), listOf("invalid_field"))
|
||||||
|
// no exception = pass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byContainsNoMatch(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
val contains = mapOf("value" to "substantial")
|
||||||
|
assertFalse { db.conn.existsByContains(TEST_TABLE, contains) }
|
||||||
|
db.conn.removeFieldsByContains(TEST_TABLE, contains, listOf("numValue"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byJsonPathMatchFields(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
val path = "$.value ? (@ == \"purple\")"
|
||||||
|
db.conn.removeFieldsByJsonPath(TEST_TABLE, path, listOf("sub"))
|
||||||
|
val docs = db.conn.findByJsonPath<JsonDocument>(TEST_TABLE, path)
|
||||||
|
assertEquals(2, docs.size, "There should have been 2 documents returned")
|
||||||
|
docs.forEach {
|
||||||
|
assertTrue(listOf("four", "five").contains(it.id), "An incorrect document was returned (${it.id})")
|
||||||
|
assertNull(it.sub, "The sub-document should have been removed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byJsonPathMatchNoFields(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
assertFalse { db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("submarine"))) }
|
||||||
|
db.conn.removeFieldsByJsonPath(TEST_TABLE, "$.value ? (@ == \"purple\")", listOf("submarine")) // no exn = pass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun byJsonPathNoMatch(db: ThrowawayDatabase) {
|
||||||
|
JsonDocument.load(db)
|
||||||
|
val path = "$.value ? (@ == \"mauve\")"
|
||||||
|
assertFalse { db.conn.existsByJsonPath(TEST_TABLE, path) }
|
||||||
|
db.conn.removeFieldsByJsonPath(TEST_TABLE, path, listOf("value")) // no exception = pass
|
||||||
|
}
|
||||||
|
}
|
72
src/integration-test/kotlin/postgresql/RemoveFieldsIT.kt
Normal file
72
src/integration-test/kotlin/postgresql/RemoveFieldsIT.kt
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package solutions.bitbadger.documents.postgresql
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import solutions.bitbadger.documents.common.RemoveFields
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
|
||||||
|
*/
|
||||||
|
@DisplayName("PostgreSQL - RemoveFields")
|
||||||
|
class RemoveFieldsIT {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byId removes fields from an existing document")
|
||||||
|
fun byIdMatchFields() =
|
||||||
|
PgDB().use(RemoveFields::byIdMatchFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byId succeeds when fields do not exist on an existing document")
|
||||||
|
fun byIdMatchNoFields() =
|
||||||
|
PgDB().use(RemoveFields::byIdMatchNoFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byId succeeds when no document exists")
|
||||||
|
fun byIdNoMatch() =
|
||||||
|
PgDB().use(RemoveFields::byIdNoMatch)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byFields removes fields from matching documents")
|
||||||
|
fun byFieldsMatchFields() =
|
||||||
|
PgDB().use(RemoveFields::byFieldsMatchFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byFields succeeds when fields do not exist on matching documents")
|
||||||
|
fun byFieldsMatchNoFields() =
|
||||||
|
PgDB().use(RemoveFields::byFieldsMatchNoFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byFields succeeds when no matching documents exist")
|
||||||
|
fun byFieldsNoMatch() =
|
||||||
|
PgDB().use(RemoveFields::byFieldsNoMatch)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byContains removes fields from matching documents")
|
||||||
|
fun byContainsMatchFields() =
|
||||||
|
PgDB().use(RemoveFields::byContainsMatchFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byContains succeeds when fields do not exist on matching documents")
|
||||||
|
fun byContainsMatchNoFields() =
|
||||||
|
PgDB().use(RemoveFields::byContainsMatchNoFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byContains succeeds when no matching documents exist")
|
||||||
|
fun byContainsNoMatch() =
|
||||||
|
PgDB().use(RemoveFields::byContainsNoMatch)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byJsonPath removes fields from matching documents")
|
||||||
|
fun byJsonPathMatchFields() =
|
||||||
|
PgDB().use(RemoveFields::byJsonPathMatchFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byJsonPath succeeds when fields do not exist on matching documents")
|
||||||
|
fun byJsonPathMatchNoFields() =
|
||||||
|
PgDB().use(RemoveFields::byJsonPathMatchNoFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byJsonPath succeeds when no matching documents exist")
|
||||||
|
fun byJsonPathNoMatch() =
|
||||||
|
PgDB().use(RemoveFields::byJsonPathNoMatch)
|
||||||
|
}
|
56
src/integration-test/kotlin/sqlite/RemoveFieldsIT.kt
Normal file
56
src/integration-test/kotlin/sqlite/RemoveFieldsIT.kt
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package solutions.bitbadger.documents.sqlite
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
|
import solutions.bitbadger.documents.DocumentException
|
||||||
|
import solutions.bitbadger.documents.common.RemoveFields
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
|
||||||
|
*/
|
||||||
|
@DisplayName("SQLite - RemoveFields")
|
||||||
|
class RemoveFieldsIT {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byId removes fields from an existing document")
|
||||||
|
fun byIdMatchFields() =
|
||||||
|
SQLiteDB().use(RemoveFields::byIdMatchFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byId succeeds when fields do not exist on an existing document")
|
||||||
|
fun byIdMatchNoFields() =
|
||||||
|
SQLiteDB().use(RemoveFields::byIdMatchNoFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byId succeeds when no document exists")
|
||||||
|
fun byIdNoMatch() =
|
||||||
|
SQLiteDB().use(RemoveFields::byIdNoMatch)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byFields removes fields from matching documents")
|
||||||
|
fun byFieldsMatchFields() =
|
||||||
|
SQLiteDB().use(RemoveFields::byFieldsMatchFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byFields succeeds when fields do not exist on matching documents")
|
||||||
|
fun byFieldsMatchNoFields() =
|
||||||
|
SQLiteDB().use(RemoveFields::byFieldsMatchNoFields)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byFields succeeds when no matching documents exist")
|
||||||
|
fun byFieldsNoMatch() =
|
||||||
|
SQLiteDB().use(RemoveFields::byFieldsNoMatch)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byContains fails")
|
||||||
|
fun byContainsFails() {
|
||||||
|
assertThrows<DocumentException> { SQLiteDB().use(RemoveFields::byContainsMatchFields) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("byJsonPath fails")
|
||||||
|
fun byJsonPathFails() {
|
||||||
|
assertThrows<DocumentException> { SQLiteDB().use(RemoveFields::byJsonPathMatchFields) }
|
||||||
|
}
|
||||||
|
}
|
@ -13,8 +13,9 @@ object Configuration {
|
|||||||
* https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/json.md for all configuration options
|
* https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/json.md for all configuration options
|
||||||
*/
|
*/
|
||||||
var json = Json {
|
var json = Json {
|
||||||
encodeDefaults = true
|
encodeDefaults = true
|
||||||
explicitNulls = false
|
explicitNulls = false
|
||||||
|
coerceInputValues = true
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The field in which a document's ID is stored */
|
/** The field in which a document's ID is stored */
|
||||||
|
@ -356,6 +356,60 @@ inline fun <reified TContains, reified TPatch> Connection.patchByContains(
|
|||||||
inline fun <reified TPatch> Connection.patchByJsonPath(tableName: String, path: String, patch: TPatch) =
|
inline fun <reified TPatch> Connection.patchByJsonPath(tableName: String, path: String, patch: TPatch) =
|
||||||
Patch.byJsonPath(tableName, path, patch, this)
|
Patch.byJsonPath(tableName, path, patch, this)
|
||||||
|
|
||||||
|
// ~~~ DOCUMENT FIELD REMOVAL QUERIES ~~~
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from a document by its ID
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which the document's fields should be removed
|
||||||
|
* @param docId The ID of the document to have fields removed
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
*/
|
||||||
|
fun <TKey> Connection.removeFieldsById(tableName: String, docId: TKey, toRemove: Collection<String>) =
|
||||||
|
RemoveFields.byId(tableName, docId, toRemove, this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from documents using a field comparison
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which document fields should be removed
|
||||||
|
* @param fields The fields which should be compared
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
* @param howMatched How the fields should be matched
|
||||||
|
*/
|
||||||
|
fun Connection.removeFieldsByFields(
|
||||||
|
tableName: String,
|
||||||
|
fields: Collection<Field<*>>,
|
||||||
|
toRemove: Collection<String>,
|
||||||
|
howMatched: FieldMatch? = null
|
||||||
|
) =
|
||||||
|
RemoveFields.byFields(tableName, fields, toRemove, howMatched, this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from documents using a JSON containment query (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which document fields should be removed
|
||||||
|
* @param criteria The object against which JSON containment should be checked
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
* @throws DocumentException If called on a SQLite connection
|
||||||
|
*/
|
||||||
|
inline fun <reified TContains> Connection.removeFieldsByContains(
|
||||||
|
tableName: String,
|
||||||
|
criteria: TContains,
|
||||||
|
toRemove: Collection<String>
|
||||||
|
) =
|
||||||
|
RemoveFields.byContains(tableName, criteria, toRemove, this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from documents using a JSON Path match query (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which document fields should be removed
|
||||||
|
* @param path The JSON path comparison to match
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
* @throws DocumentException If called on a SQLite connection
|
||||||
|
*/
|
||||||
|
fun Connection.removeFieldsByJsonPath(tableName: String, path: String, toRemove: Collection<String>) =
|
||||||
|
RemoveFields.byJsonPath(tableName, path, toRemove, this)
|
||||||
|
|
||||||
// ~~~ DOCUMENT DELETION QUERIES ~~~
|
// ~~~ DOCUMENT DELETION QUERIES ~~~
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,14 +105,14 @@ object Parameters {
|
|||||||
* @param parameterName The parameter name to use for the query
|
* @param parameterName The parameter name to use for the query
|
||||||
* @return A list of parameters to use for building the query
|
* @return A list of parameters to use for building the query
|
||||||
*/
|
*/
|
||||||
fun fieldNames(names: Collection<String>, parameterName: String = ":name") =
|
fun fieldNames(names: Collection<String>, parameterName: String = ":name"): MutableCollection<Parameter<*>> =
|
||||||
when (Configuration.dialect("generate field name parameters")) {
|
when (Configuration.dialect("generate field name parameters")) {
|
||||||
Dialect.POSTGRESQL -> listOf(
|
Dialect.POSTGRESQL -> mutableListOf(
|
||||||
Parameter(parameterName, ParameterType.STRING, names.joinToString(",").let { "{$it}" })
|
Parameter(parameterName, ParameterType.STRING, names.joinToString(",").let { "{$it}" })
|
||||||
)
|
)
|
||||||
|
|
||||||
Dialect.SQLITE -> names.mapIndexed { index, name ->
|
Dialect.SQLITE -> names.mapIndexed { index, name ->
|
||||||
Parameter("$parameterName$index", ParameterType.STRING, name)
|
Parameter("$parameterName$index", ParameterType.STRING, name)
|
||||||
}
|
}.toMutableList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
154
src/main/kotlin/RemoveFields.kt
Normal file
154
src/main/kotlin/RemoveFields.kt
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package solutions.bitbadger.documents
|
||||||
|
|
||||||
|
import solutions.bitbadger.documents.query.RemoveFields
|
||||||
|
import java.sql.Connection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions to remove fields from documents
|
||||||
|
*/
|
||||||
|
object RemoveFields {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate field paths to JSON paths for SQLite queries
|
||||||
|
*
|
||||||
|
* @param parameters The parameters for the specified fields
|
||||||
|
* @return The parameters for the specified fields, translated if used for SQLite
|
||||||
|
*/
|
||||||
|
private fun translatePath(parameters: MutableCollection<Parameter<*>>): MutableCollection<Parameter<*>> {
|
||||||
|
val dialect = Configuration.dialect("remove fields")
|
||||||
|
return when (dialect) {
|
||||||
|
Dialect.POSTGRESQL -> parameters
|
||||||
|
Dialect.SQLITE -> parameters.map { Parameter(it.name, it.type, "$.${it.value}") }.toMutableList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from a document by its ID
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which the document's fields should be removed
|
||||||
|
* @param docId The ID of the document to have fields removed
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
* @param conn The connection on which the update should be executed
|
||||||
|
*/
|
||||||
|
fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>, conn: Connection) {
|
||||||
|
val nameParams = Parameters.fieldNames(toRemove)
|
||||||
|
conn.customNonQuery(
|
||||||
|
RemoveFields.byId(tableName, nameParams, docId),
|
||||||
|
Parameters.addFields(
|
||||||
|
listOf(Field.equal(Configuration.idField, docId, ":id")),
|
||||||
|
translatePath(nameParams)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from a document by its ID
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which the document's fields should be removed
|
||||||
|
* @param docId The ID of the document to have fields removed
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
*/
|
||||||
|
fun <TKey> byId(tableName: String, docId: TKey, toRemove: Collection<String>) =
|
||||||
|
Configuration.dbConn().use { byId(tableName, docId, toRemove, it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from documents using a field comparison
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which document fields should be removed
|
||||||
|
* @param fields The fields which should be compared
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
* @param howMatched How the fields should be matched
|
||||||
|
* @param conn The connection on which the update should be executed
|
||||||
|
*/
|
||||||
|
fun byFields(
|
||||||
|
tableName: String,
|
||||||
|
fields: Collection<Field<*>>,
|
||||||
|
toRemove: Collection<String>,
|
||||||
|
howMatched: FieldMatch? = null,
|
||||||
|
conn: Connection
|
||||||
|
) {
|
||||||
|
val named = Parameters.nameFields(fields)
|
||||||
|
val nameParams = Parameters.fieldNames(toRemove)
|
||||||
|
conn.customNonQuery(
|
||||||
|
RemoveFields.byFields(tableName, nameParams, named, howMatched),
|
||||||
|
Parameters.addFields(named, translatePath(nameParams))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from documents using a field comparison
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which document fields should be removed
|
||||||
|
* @param fields The fields which should be compared
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
* @param howMatched How the fields should be matched
|
||||||
|
*/
|
||||||
|
fun byFields(
|
||||||
|
tableName: String,
|
||||||
|
fields: Collection<Field<*>>,
|
||||||
|
toRemove: Collection<String>,
|
||||||
|
howMatched: FieldMatch? = null
|
||||||
|
) =
|
||||||
|
Configuration.dbConn().use { byFields(tableName, fields, toRemove, howMatched, it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from documents using a JSON containment query (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which document fields should be removed
|
||||||
|
* @param criteria The object against which JSON containment should be checked
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
* @param conn The connection on which the update should be executed
|
||||||
|
* @throws DocumentException If called on a SQLite connection
|
||||||
|
*/
|
||||||
|
inline fun <reified TContains> byContains(
|
||||||
|
tableName: String,
|
||||||
|
criteria: TContains,
|
||||||
|
toRemove: Collection<String>,
|
||||||
|
conn: Connection
|
||||||
|
) {
|
||||||
|
val nameParams = Parameters.fieldNames(toRemove)
|
||||||
|
conn.customNonQuery(
|
||||||
|
RemoveFields.byContains(tableName, nameParams),
|
||||||
|
listOf(Parameters.json(":criteria", criteria), *nameParams.toTypedArray())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from documents using a JSON containment query (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which document fields should be removed
|
||||||
|
* @param criteria The object against which JSON containment should be checked
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
* @throws DocumentException If called on a SQLite connection
|
||||||
|
*/
|
||||||
|
inline fun <reified TContains> byContains(tableName: String, criteria: TContains, toRemove: Collection<String>) =
|
||||||
|
Configuration.dbConn().use { byContains(tableName, criteria, toRemove, it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from documents using a JSON Path match query (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which document fields should be removed
|
||||||
|
* @param path The JSON path comparison to match
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
* @param conn The connection on which the update should be executed
|
||||||
|
* @throws DocumentException If called on a SQLite connection
|
||||||
|
*/
|
||||||
|
fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>, conn: Connection) {
|
||||||
|
val nameParams = Parameters.fieldNames(toRemove)
|
||||||
|
conn.customNonQuery(
|
||||||
|
RemoveFields.byJsonPath(tableName, nameParams),
|
||||||
|
listOf(Parameter(":path", ParameterType.STRING, path), *nameParams.toTypedArray())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove fields from documents using a JSON Path match query (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The name of the table in which document fields should be removed
|
||||||
|
* @param path The JSON path comparison to match
|
||||||
|
* @param toRemove The names of the fields to be removed
|
||||||
|
* @throws DocumentException If called on a SQLite connection
|
||||||
|
*/
|
||||||
|
fun byJsonPath(tableName: String, path: String, toRemove: Collection<String>) =
|
||||||
|
Configuration.dbConn().use { byJsonPath(tableName, path, toRemove, it) }
|
||||||
|
}
|
@ -16,9 +16,9 @@ object RemoveFields {
|
|||||||
* @param toRemove The parameters for the fields to be removed
|
* @param toRemove The parameters for the fields to be removed
|
||||||
* @return A query to remove fields from documents in the given table
|
* @return A query to remove fields from documents in the given table
|
||||||
*/
|
*/
|
||||||
private fun removeFields(tableName: String, toRemove: List<Parameter<String>>) =
|
private fun removeFields(tableName: String, toRemove: Collection<Parameter<*>>) =
|
||||||
when (Configuration.dialect("generate field removal query")) {
|
when (Configuration.dialect("generate field removal query")) {
|
||||||
Dialect.POSTGRESQL -> "UPDATE $tableName SET data = data - ${toRemove[0].name}::text[]"
|
Dialect.POSTGRESQL -> "UPDATE $tableName SET data = data - ${toRemove.elementAt(0).name}::text[]"
|
||||||
Dialect.SQLITE -> toRemove.joinToString(", ") { it.name }.let {
|
Dialect.SQLITE -> toRemove.joinToString(", ") { it.name }.let {
|
||||||
"UPDATE $tableName SET data = json_remove(data, $it)"
|
"UPDATE $tableName SET data = json_remove(data, $it)"
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ object RemoveFields {
|
|||||||
* @param docId The ID of the document to be updated (optional, used for type checking)
|
* @param docId The ID of the document to be updated (optional, used for type checking)
|
||||||
* @return A query to patch a JSON document by its ID
|
* @return A query to patch a JSON document by its ID
|
||||||
*/
|
*/
|
||||||
fun <TKey> byId(tableName: String, toRemove: List<Parameter<String>>, docId: TKey? = null) =
|
fun <TKey> byId(tableName: String, toRemove: Collection<Parameter<*>>, docId: TKey? = null) =
|
||||||
byIdBase(removeFields(tableName, toRemove), docId)
|
byIdBase(removeFields(tableName, toRemove), docId)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,7 +46,7 @@ object RemoveFields {
|
|||||||
*/
|
*/
|
||||||
fun byFields(
|
fun byFields(
|
||||||
tableName: String,
|
tableName: String,
|
||||||
toRemove: List<Parameter<String>>,
|
toRemove: Collection<Parameter<*>>,
|
||||||
fields: Collection<Field<*>>,
|
fields: Collection<Field<*>>,
|
||||||
howMatched: FieldMatch? = null
|
howMatched: FieldMatch? = null
|
||||||
) =
|
) =
|
||||||
@ -59,7 +59,7 @@ object RemoveFields {
|
|||||||
* @param toRemove The parameters for the fields to be removed
|
* @param toRemove The parameters for the fields to be removed
|
||||||
* @return A query to patch JSON documents by JSON containment
|
* @return A query to patch JSON documents by JSON containment
|
||||||
*/
|
*/
|
||||||
fun byContains(tableName: String, toRemove: List<Parameter<String>>) =
|
fun byContains(tableName: String, toRemove: Collection<Parameter<*>>) =
|
||||||
statementWhere(removeFields(tableName, toRemove), Where.jsonContains())
|
statementWhere(removeFields(tableName, toRemove), Where.jsonContains())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,6 +69,6 @@ object RemoveFields {
|
|||||||
* @param toRemove The parameters for the fields to be removed
|
* @param toRemove The parameters for the fields to be removed
|
||||||
* @return A query to patch JSON documents by JSON path match
|
* @return A query to patch JSON documents by JSON path match
|
||||||
*/
|
*/
|
||||||
fun byJsonPath(tableName: String, toRemove: List<Parameter<String>>) =
|
fun byJsonPath(tableName: String, toRemove: Collection<Parameter<*>>) =
|
||||||
statementWhere(removeFields(tableName, toRemove), Where.jsonPathMatches())
|
statementWhere(removeFields(tableName, toRemove), Where.jsonPathMatches())
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ class ConfigurationTest {
|
|||||||
fun defaultJsonOptions() {
|
fun defaultJsonOptions() {
|
||||||
assertTrue(Configuration.json.configuration.encodeDefaults, "Encode Defaults should have been set")
|
assertTrue(Configuration.json.configuration.encodeDefaults, "Encode Defaults should have been set")
|
||||||
assertFalse(Configuration.json.configuration.explicitNulls, "Explicit Nulls should not have been set")
|
assertFalse(Configuration.json.configuration.explicitNulls, "Explicit Nulls should not have been set")
|
||||||
|
assertTrue(Configuration.json.configuration.coerceInputValues, "Coerce Input Values should have been set")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -62,7 +62,7 @@ class ParametersTest {
|
|||||||
@DisplayName("fieldNames generates a single parameter (PostgreSQL)")
|
@DisplayName("fieldNames generates a single parameter (PostgreSQL)")
|
||||||
fun fieldNamesSinglePostgres() {
|
fun fieldNamesSinglePostgres() {
|
||||||
Configuration.dialectValue = Dialect.POSTGRESQL
|
Configuration.dialectValue = Dialect.POSTGRESQL
|
||||||
val nameParams = Parameters.fieldNames(listOf("test"))
|
val nameParams = Parameters.fieldNames(listOf("test")).toList()
|
||||||
assertEquals(1, nameParams.size, "There should be one name parameter")
|
assertEquals(1, nameParams.size, "There should be one name parameter")
|
||||||
assertEquals(":name", nameParams[0].name, "The parameter name is incorrect")
|
assertEquals(":name", nameParams[0].name, "The parameter name is incorrect")
|
||||||
assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect")
|
assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect")
|
||||||
@ -73,7 +73,7 @@ class ParametersTest {
|
|||||||
@DisplayName("fieldNames generates multiple parameters (PostgreSQL)")
|
@DisplayName("fieldNames generates multiple parameters (PostgreSQL)")
|
||||||
fun fieldNamesMultiplePostgres() {
|
fun fieldNamesMultiplePostgres() {
|
||||||
Configuration.dialectValue = Dialect.POSTGRESQL
|
Configuration.dialectValue = Dialect.POSTGRESQL
|
||||||
val nameParams = Parameters.fieldNames(listOf("test", "this", "today"))
|
val nameParams = Parameters.fieldNames(listOf("test", "this", "today")).toList()
|
||||||
assertEquals(1, nameParams.size, "There should be one name parameter")
|
assertEquals(1, nameParams.size, "There should be one name parameter")
|
||||||
assertEquals(":name", nameParams[0].name, "The parameter name is incorrect")
|
assertEquals(":name", nameParams[0].name, "The parameter name is incorrect")
|
||||||
assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect")
|
assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect")
|
||||||
@ -84,7 +84,7 @@ class ParametersTest {
|
|||||||
@DisplayName("fieldNames generates a single parameter (SQLite)")
|
@DisplayName("fieldNames generates a single parameter (SQLite)")
|
||||||
fun fieldNamesSingleSQLite() {
|
fun fieldNamesSingleSQLite() {
|
||||||
Configuration.dialectValue = Dialect.SQLITE
|
Configuration.dialectValue = Dialect.SQLITE
|
||||||
val nameParams = Parameters.fieldNames(listOf("test"))
|
val nameParams = Parameters.fieldNames(listOf("test")).toList()
|
||||||
assertEquals(1, nameParams.size, "There should be one name parameter")
|
assertEquals(1, nameParams.size, "There should be one name parameter")
|
||||||
assertEquals(":name0", nameParams[0].name, "The parameter name is incorrect")
|
assertEquals(":name0", nameParams[0].name, "The parameter name is incorrect")
|
||||||
assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect")
|
assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect")
|
||||||
@ -95,7 +95,7 @@ class ParametersTest {
|
|||||||
@DisplayName("fieldNames generates multiple parameters (SQLite)")
|
@DisplayName("fieldNames generates multiple parameters (SQLite)")
|
||||||
fun fieldNamesMultipleSQLite() {
|
fun fieldNamesMultipleSQLite() {
|
||||||
Configuration.dialectValue = Dialect.SQLITE
|
Configuration.dialectValue = Dialect.SQLITE
|
||||||
val nameParams = Parameters.fieldNames(listOf("test", "this", "today"))
|
val nameParams = Parameters.fieldNames(listOf("test", "this", "today")).toList()
|
||||||
assertEquals(3, nameParams.size, "There should be one name parameter")
|
assertEquals(3, nameParams.size, "There should be one name parameter")
|
||||||
assertEquals(":name0", nameParams[0].name, "The first parameter name is incorrect")
|
assertEquals(":name0", nameParams[0].name, "The first parameter name is incorrect")
|
||||||
assertEquals(ParameterType.STRING, nameParams[0].type, "The first parameter type is incorrect")
|
assertEquals(ParameterType.STRING, nameParams[0].type, "The first parameter type is incorrect")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user