Initial Development #1
@ -98,4 +98,23 @@ object Parameters {
 | 
			
		||||
            throw DocumentException("Error creating query / binding parameters: ${ex.message}", ex)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create parameters for field names to be removed from a document
 | 
			
		||||
     *
 | 
			
		||||
     * @param names The names of the fields to be removed
 | 
			
		||||
     * @param parameterName The parameter name to use for the query
 | 
			
		||||
     * @return A list of parameters to use for building the query
 | 
			
		||||
     */
 | 
			
		||||
    fun fieldNames(names: Collection<String>, parameterName: String = ":name") =
 | 
			
		||||
        when (Configuration.dialect("generate field name parameters")) {
 | 
			
		||||
            Dialect.POSTGRESQL -> listOf(Parameter(parameterName, ParameterType.STRING, if (names.size == 1) {
 | 
			
		||||
                    names.elementAt(0)
 | 
			
		||||
                } else {
 | 
			
		||||
                    names.joinToString(",").let { "{$it}" }
 | 
			
		||||
                }))
 | 
			
		||||
            Dialect.SQLITE -> names.mapIndexed { index, name ->
 | 
			
		||||
                Parameter("$parameterName$index", ParameterType.STRING, name)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,7 @@ object Patch {
 | 
			
		||||
     * @param tableName The name of the table where the document is stored
 | 
			
		||||
     * @return A query to patch JSON documents by JSON containment
 | 
			
		||||
     */
 | 
			
		||||
    fun <TKey> byContains(tableName: String) =
 | 
			
		||||
    fun byContains(tableName: String) =
 | 
			
		||||
        statementWhere(patch(tableName), Where.jsonContains())
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -60,6 +60,6 @@ object Patch {
 | 
			
		||||
     * @param tableName The name of the table where the document is stored
 | 
			
		||||
     * @return A query to patch JSON documents by JSON path match
 | 
			
		||||
     */
 | 
			
		||||
    fun <TKey> byJsonPath(tableName: String) =
 | 
			
		||||
    fun byJsonPath(tableName: String) =
 | 
			
		||||
        statementWhere(patch(tableName), Where.jsonPathMatches())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										74
									
								
								src/main/kotlin/query/RemoveFields.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/main/kotlin/query/RemoveFields.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,74 @@
 | 
			
		||||
package solutions.bitbadger.documents.query
 | 
			
		||||
 | 
			
		||||
import solutions.bitbadger.documents.*
 | 
			
		||||
import solutions.bitbadger.documents.query.byFields as byFieldsBase
 | 
			
		||||
import solutions.bitbadger.documents.query.byId as byIdBase
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Functions to create queries to remove fields from documents
 | 
			
		||||
 */
 | 
			
		||||
object RemoveFields {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a query to remove fields based on the given parameters
 | 
			
		||||
     *
 | 
			
		||||
     * @param tableName The name of the table in which documents should have fields removed
 | 
			
		||||
     * @param toRemove The parameters for the fields to be removed
 | 
			
		||||
     * @return A query to remove fields from documents in the given table
 | 
			
		||||
     */
 | 
			
		||||
    private fun removeFields(tableName: String, toRemove: List<Parameter<String>>) =
 | 
			
		||||
        when (Configuration.dialect("generate field removal query")) {
 | 
			
		||||
            Dialect.POSTGRESQL -> "UPDATE $tableName SET data = data - ${toRemove[0].name}::text[]"
 | 
			
		||||
            Dialect.SQLITE -> toRemove.joinToString(", ") { it.name }.let {
 | 
			
		||||
                "UPDATE $tableName SET data = json_remove(data, $it)"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A query to patch (partially update) a JSON document by its ID
 | 
			
		||||
     *
 | 
			
		||||
     * @param tableName The name of the table where the document is stored
 | 
			
		||||
     * @param toRemove The parameters for the fields to be removed
 | 
			
		||||
     * @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
 | 
			
		||||
     */
 | 
			
		||||
    fun <TKey> byId(tableName: String, toRemove: List<Parameter<String>>, docId: TKey? = null) =
 | 
			
		||||
        byIdBase(removeFields(tableName, toRemove), docId)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A query to patch (partially update) a JSON document using field match criteria
 | 
			
		||||
     *
 | 
			
		||||
     * @param tableName The name of the table where the documents are stored
 | 
			
		||||
     * @param toRemove The parameters for the fields to be removed
 | 
			
		||||
     * @param fields The field criteria
 | 
			
		||||
     * @param howMatched How the fields should be matched (optional, defaults to `ALL`)
 | 
			
		||||
     * @return A query to patch JSON documents by field match criteria
 | 
			
		||||
     */
 | 
			
		||||
    fun byFields(
 | 
			
		||||
        tableName: String,
 | 
			
		||||
        toRemove: List<Parameter<String>>,
 | 
			
		||||
        fields: Collection<Field<*>>,
 | 
			
		||||
        howMatched: FieldMatch? = null
 | 
			
		||||
    ) =
 | 
			
		||||
        byFieldsBase(removeFields(tableName, toRemove), fields, howMatched)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A query to patch (partially update) a JSON document by JSON containment (PostgreSQL only)
 | 
			
		||||
     *
 | 
			
		||||
     * @param tableName The name of the table where the document is stored
 | 
			
		||||
     * @param toRemove The parameters for the fields to be removed
 | 
			
		||||
     * @return A query to patch JSON documents by JSON containment
 | 
			
		||||
     */
 | 
			
		||||
    fun byContains(tableName: String, toRemove: List<Parameter<String>>) =
 | 
			
		||||
        statementWhere(removeFields(tableName, toRemove), Where.jsonContains())
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A query to patch (partially update) a JSON document by JSON path match (PostgreSQL only)
 | 
			
		||||
     *
 | 
			
		||||
     * @param tableName The name of the table where the document is stored
 | 
			
		||||
     * @param toRemove The parameters for the fields to be removed
 | 
			
		||||
     * @return A query to patch JSON documents by JSON path match
 | 
			
		||||
     */
 | 
			
		||||
    fun byJsonPath(tableName: String, toRemove: List<Parameter<String>>) =
 | 
			
		||||
        statementWhere(removeFields(tableName, toRemove), Where.jsonPathMatches())
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package solutions.bitbadger.documents
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.AfterEach
 | 
			
		||||
import org.junit.jupiter.api.DisplayName
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertEquals
 | 
			
		||||
@ -12,6 +13,14 @@ import kotlin.test.assertSame
 | 
			
		||||
@DisplayName("Parameters")
 | 
			
		||||
class ParametersTest {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clear the connection string (resets Dialect)
 | 
			
		||||
     */
 | 
			
		||||
    @AfterEach
 | 
			
		||||
    fun cleanUp() {
 | 
			
		||||
        Configuration.dialectValue = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("nameFields works with no changes")
 | 
			
		||||
    fun nameFieldsNoChange() {
 | 
			
		||||
@ -47,4 +56,54 @@ class ParametersTest {
 | 
			
		||||
        assertEquals("SELECT data, data_ext FROM tbl WHERE data = ? AND data_ext = ? AND more_data = ?",
 | 
			
		||||
            Parameters.replaceNamesInQuery(query, parameters), "Parameters not replaced correctly")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("fieldNames generates a single parameter (PostgreSQL)")
 | 
			
		||||
    fun fieldNamesSinglePostgres() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.POSTGRESQL
 | 
			
		||||
        val nameParams = Parameters.fieldNames(listOf("test"))
 | 
			
		||||
        assertEquals(1, nameParams.size, "There should be one name parameter")
 | 
			
		||||
        assertEquals(":name", nameParams[0].name, "The parameter name is incorrect")
 | 
			
		||||
        assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect")
 | 
			
		||||
        assertEquals("test", nameParams[0].value, "The parameter value is incorrect")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("fieldNames generates multiple parameters (PostgreSQL)")
 | 
			
		||||
    fun fieldNamesMultiplePostgres() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.POSTGRESQL
 | 
			
		||||
        val nameParams = Parameters.fieldNames(listOf("test", "this", "today"))
 | 
			
		||||
        assertEquals(1, nameParams.size, "There should be one name parameter")
 | 
			
		||||
        assertEquals(":name", nameParams[0].name, "The parameter name is incorrect")
 | 
			
		||||
        assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect")
 | 
			
		||||
        assertEquals("{test,this,today}", nameParams[0].value, "The parameter value is incorrect")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("fieldNames generates a single parameter (SQLite)")
 | 
			
		||||
    fun fieldNamesSingleSQLite() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.SQLITE
 | 
			
		||||
        val nameParams = Parameters.fieldNames(listOf("test"))
 | 
			
		||||
        assertEquals(1, nameParams.size, "There should be one name parameter")
 | 
			
		||||
        assertEquals(":name0", nameParams[0].name, "The parameter name is incorrect")
 | 
			
		||||
        assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect")
 | 
			
		||||
        assertEquals("test", nameParams[0].value, "The parameter value is incorrect")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("fieldNames generates multiple parameters (SQLite)")
 | 
			
		||||
    fun fieldNamesMultipleSQLite() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.SQLITE
 | 
			
		||||
        val nameParams = Parameters.fieldNames(listOf("test", "this", "today"))
 | 
			
		||||
        assertEquals(3, nameParams.size, "There should be one name parameter")
 | 
			
		||||
        assertEquals(":name0", nameParams[0].name, "The first parameter name is incorrect")
 | 
			
		||||
        assertEquals(ParameterType.STRING, nameParams[0].type, "The first parameter type is incorrect")
 | 
			
		||||
        assertEquals("test", nameParams[0].value, "The first parameter value is incorrect")
 | 
			
		||||
        assertEquals(":name1", nameParams[1].name, "The second parameter name is incorrect")
 | 
			
		||||
        assertEquals(ParameterType.STRING, nameParams[1].type, "The second parameter type is incorrect")
 | 
			
		||||
        assertEquals("this", nameParams[1].value, "The second parameter value is incorrect")
 | 
			
		||||
        assertEquals(":name2", nameParams[2].name, "The third parameter name is incorrect")
 | 
			
		||||
        assertEquals(ParameterType.STRING, nameParams[2].type, "The third parameter type is incorrect")
 | 
			
		||||
        assertEquals("today", nameParams[2].value, "The third parameter value is incorrect")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										102
									
								
								src/test/kotlin/query/DeleteTest.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/test/kotlin/query/DeleteTest.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,102 @@
 | 
			
		||||
package solutions.bitbadger.documents.query
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.AfterEach
 | 
			
		||||
import org.junit.jupiter.api.DisplayName
 | 
			
		||||
import org.junit.jupiter.api.Test
 | 
			
		||||
import org.junit.jupiter.api.assertThrows
 | 
			
		||||
import solutions.bitbadger.documents.Configuration
 | 
			
		||||
import solutions.bitbadger.documents.Dialect
 | 
			
		||||
import solutions.bitbadger.documents.DocumentException
 | 
			
		||||
import solutions.bitbadger.documents.Field
 | 
			
		||||
import kotlin.test.assertEquals
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Unit tests for the `Delete` object
 | 
			
		||||
 */
 | 
			
		||||
@DisplayName("Delete (Query)")
 | 
			
		||||
class DeleteTest {
 | 
			
		||||
 | 
			
		||||
    /** Test table name */
 | 
			
		||||
    private val tbl = "test_table"
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clear the connection string (resets Dialect)
 | 
			
		||||
     */
 | 
			
		||||
    @AfterEach
 | 
			
		||||
    fun cleanUp() {
 | 
			
		||||
        Configuration.dialectValue = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("byId generates correctly (PostgreSQL)")
 | 
			
		||||
    fun byIdPostgres() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.POSTGRESQL
 | 
			
		||||
        assertEquals(
 | 
			
		||||
            "DELETE FROM $tbl WHERE data->>'id' = :id",
 | 
			
		||||
            Delete.byId<String>(tbl), "Delete query not constructed correctly"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("byId generates correctly (SQLite)")
 | 
			
		||||
    fun byIdSQLite() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.SQLITE
 | 
			
		||||
        assertEquals(
 | 
			
		||||
            "DELETE FROM $tbl WHERE data->>'id' = :id",
 | 
			
		||||
            Delete.byId<String>(tbl), "Delete query not constructed correctly"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("byFields generates correctly (PostgreSQL)")
 | 
			
		||||
    fun byFieldsPostgres() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.POSTGRESQL
 | 
			
		||||
        assertEquals(
 | 
			
		||||
            "DELETE FROM $tbl WHERE data->>'a' = :b", Delete.byFields(tbl, listOf(Field.equal("a", "", ":b"))),
 | 
			
		||||
            "Delete query not constructed correctly"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("byFields generates correctly (SQLite)")
 | 
			
		||||
    fun byFieldsSQLite() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.SQLITE
 | 
			
		||||
        assertEquals(
 | 
			
		||||
            "DELETE FROM $tbl WHERE data->>'a' = :b", Delete.byFields(tbl, listOf(Field.equal("a", "", ":b"))),
 | 
			
		||||
            "Delete query not constructed correctly"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("byContains generates correctly (PostgreSQL)")
 | 
			
		||||
    fun byContainsPostgres() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.POSTGRESQL
 | 
			
		||||
        assertEquals(
 | 
			
		||||
            "DELETE FROM $tbl WHERE data @> :criteria", Delete.byContains(tbl), "Delete query not constructed correctly"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("byContains fails (SQLite)")
 | 
			
		||||
    fun byContainsSQLite() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.SQLITE
 | 
			
		||||
        assertThrows<DocumentException> { Delete.byContains(tbl) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("byJsonPath generates correctly (PostgreSQL)")
 | 
			
		||||
    fun byJsonPathPostgres() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.POSTGRESQL
 | 
			
		||||
        assertEquals(
 | 
			
		||||
            "DELETE FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)", Delete.byJsonPath(tbl),
 | 
			
		||||
            "Delete query not constructed correctly"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("byJsonPath fails (SQLite)")
 | 
			
		||||
    fun byJsonPathSQLite() {
 | 
			
		||||
        Configuration.dialectValue = Dialect.SQLITE
 | 
			
		||||
        assertThrows<DocumentException> { Delete.byJsonPath(tbl) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -11,7 +11,7 @@ import solutions.bitbadger.documents.Field
 | 
			
		||||
import kotlin.test.assertEquals
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Unit tests for the `Exists` object
 | 
			
		||||
 * Unit tests for the `Find` object
 | 
			
		||||
 */
 | 
			
		||||
@DisplayName("Find (Query)")
 | 
			
		||||
class FindTest {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user