diff --git a/src/integration-test/kotlin/common/Delete.kt b/src/integration-test/kotlin/common/Delete.kt new file mode 100644 index 0000000..09b9f26 --- /dev/null +++ b/src/integration-test/kotlin/common/Delete.kt @@ -0,0 +1,66 @@ +package solutions.bitbadger.documents.common + +import solutions.bitbadger.documents.* +import kotlin.test.assertEquals + +/** + * Integration tests for the `Delete` object + */ +object Delete { + + fun byIdMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteById(TEST_TABLE, "four") + assertEquals(4, db.conn.countAll(TEST_TABLE), "There should now be 4 documents in the table") + } + + fun byIdNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteById(TEST_TABLE, "negative four") + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + } + + fun byFieldsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByFields(TEST_TABLE, listOf(Field.notEqual("value", "purple"))) + assertEquals(2, db.conn.countAll(TEST_TABLE), "There should now be 2 documents in the table") + } + + fun byFieldsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByFields(TEST_TABLE, listOf(Field.equal("value", "crimson"))) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + } + + fun byContainsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByContains(TEST_TABLE, mapOf("value" to "purple")) + assertEquals(3, db.conn.countAll(TEST_TABLE), "There should now be 3 documents in the table") + } + + fun byContainsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByContains(TEST_TABLE, mapOf("target" to "acquired")) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + } + + fun byJsonPathMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByJsonPath(TEST_TABLE, "$.value ? (@ == \"purple\")") + assertEquals(3, db.conn.countAll(TEST_TABLE), "There should now be 3 documents in the table") + } + + fun byJsonPathNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)") + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + } +} diff --git a/src/integration-test/kotlin/postgresql/DeleteIT.kt b/src/integration-test/kotlin/postgresql/DeleteIT.kt new file mode 100644 index 0000000..6504711 --- /dev/null +++ b/src/integration-test/kotlin/postgresql/DeleteIT.kt @@ -0,0 +1,49 @@ +package solutions.bitbadger.documents.postgresql + +import org.junit.jupiter.api.DisplayName +import solutions.bitbadger.documents.common.Delete +import kotlin.test.Test + +@DisplayName("PostgreSQL - Delete") +class DeleteIT { + + @Test + @DisplayName("byId deletes a matching ID") + fun byIdMatch() = + PgDB().use(Delete::byIdMatch) + + @Test + @DisplayName("byId succeeds when no ID matches") + fun byIdNoMatch() = + PgDB().use(Delete::byIdNoMatch) + + @Test + @DisplayName("byFields deletes matching documents") + fun byFieldsMatch() = + PgDB().use(Delete::byFieldsMatch) + + @Test + @DisplayName("byFields succeeds when no documents match") + fun byFieldsNoMatch() = + PgDB().use(Delete::byFieldsNoMatch) + + @Test + @DisplayName("byContains deletes matching documents") + fun byContainsMatch() = + PgDB().use(Delete::byContainsMatch) + + @Test + @DisplayName("byContains succeeds when no documents match") + fun byContainsNoMatch() = + PgDB().use(Delete::byContainsNoMatch) + + @Test + @DisplayName("byJsonPath deletes matching documents") + fun byJsonPathMatch() = + PgDB().use(Delete::byJsonPathMatch) + + @Test + @DisplayName("byJsonPath succeeds when no documents match") + fun byJsonPathNoMatch() = + PgDB().use(Delete::byJsonPathNoMatch) +} diff --git a/src/integration-test/kotlin/sqlite/DeleteIT.kt b/src/integration-test/kotlin/sqlite/DeleteIT.kt new file mode 100644 index 0000000..3a08270 --- /dev/null +++ b/src/integration-test/kotlin/sqlite/DeleteIT.kt @@ -0,0 +1,43 @@ +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.Delete +import kotlin.test.Test + +@DisplayName("SQLite - Delete") +class DeleteIT { + + @Test + @DisplayName("byId deletes a matching ID") + fun byIdMatch() = + SQLiteDB().use(Delete::byIdMatch) + + @Test + @DisplayName("byId succeeds when no ID matches") + fun byIdNoMatch() = + SQLiteDB().use(Delete::byIdNoMatch) + + @Test + @DisplayName("byFields deletes matching documents") + fun byFieldsMatch() = + SQLiteDB().use(Delete::byFieldsMatch) + + @Test + @DisplayName("byFields succeeds when no documents match") + fun byFieldsNoMatch() = + SQLiteDB().use(Delete::byFieldsNoMatch) + + @Test + @DisplayName("byContains fails") + fun byContainsMatch() { + assertThrows { SQLiteDB().use(Delete::byContainsMatch) } + } + + @Test + @DisplayName("byJsonPath fails") + fun byJsonPathMatch() { + assertThrows { SQLiteDB().use(Delete::byJsonPathMatch) } + } +} diff --git a/src/main/kotlin/ConnectionExtensions.kt b/src/main/kotlin/ConnectionExtensions.kt index 2d4edf3..b347437 100644 --- a/src/main/kotlin/ConnectionExtensions.kt +++ b/src/main/kotlin/ConnectionExtensions.kt @@ -165,7 +165,7 @@ inline fun Connection.findAll(tableName: String, orderBy: Collect * @param tableName The name of the table from which documents should be deleted * @param docId The ID of the document to be deleted */ -fun Connection.byId(tableName: String, docId: TKey) = +fun Connection.deleteById(tableName: String, docId: TKey) = Delete.byId(tableName, docId, this) /** @@ -177,3 +177,23 @@ fun Connection.byId(tableName: String, docId: TKey) = */ fun Connection.deleteByFields(tableName: String, fields: Collection>, howMatched: FieldMatch? = null) = Delete.byFields(tableName, fields, howMatched, this) + +/** + * Delete documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table from which documents should be deleted + * @param criteria The object for which JSON containment should be checked + * @throws DocumentException If called on a SQLite connection + */ +inline fun Connection.deleteByContains(tableName: String, criteria: T) = + Delete.byContains(tableName, criteria, this) + +/** + * Delete documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table from which documents should be deleted + * @param path The JSON path comparison to match + * @throws DocumentException If called on a SQLite connection + */ +fun Connection.deleteByJsonPath(tableName: String, path: String) = + Delete.byJsonPath(tableName, path, this) diff --git a/src/main/kotlin/Count.kt b/src/main/kotlin/Count.kt index ddf3010..2a3433a 100644 --- a/src/main/kotlin/Count.kt +++ b/src/main/kotlin/Count.kt @@ -66,7 +66,7 @@ object Count { * * @param tableName The name of the table in which documents should be counted * @param criteria The object for which JSON containment should be checked - * @param conn The connection on which the deletion should be executed + * @param conn The connection on which the count should be executed * @return A count of the matching documents in the table * @throws DocumentException If called on a SQLite connection */ @@ -89,7 +89,7 @@ object Count { * * @param tableName The name of the table in which documents should be counted * @param path The JSON path comparison to match - * @param conn The connection on which the deletion should be executed + * @param conn The connection on which the count should be executed * @return A count of the matching documents in the table * @throws DocumentException If called on a SQLite connection */ diff --git a/src/main/kotlin/Delete.kt b/src/main/kotlin/Delete.kt index d9002b0..6d8f771 100644 --- a/src/main/kotlin/Delete.kt +++ b/src/main/kotlin/Delete.kt @@ -18,7 +18,7 @@ object Delete { fun byId(tableName: String, docId: TKey, conn: Connection) = conn.customNonQuery( Delete.byId(tableName, docId), - Parameters.addFields(listOf(Field.equal(Configuration.idField, docId))) + Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))) ) /** @@ -52,4 +52,46 @@ object Delete { */ fun byFields(tableName: String, fields: Collection>, howMatched: FieldMatch? = null) = Configuration.dbConn().use { byFields(tableName, fields, howMatched, it) } + + /** + * Delete documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table from which documents should be deleted + * @param criteria The object for which JSON containment should be checked + * @param conn The connection on which the deletion should be executed + * @throws DocumentException If called on a SQLite connection + */ + inline fun byContains(tableName: String, criteria: T, conn: Connection) = + conn.customNonQuery(Delete.byContains(tableName), listOf(Parameters.json(":criteria", criteria))) + + /** + * Delete documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table from which documents should be deleted + * @param criteria The object for which JSON containment should be checked + * @throws DocumentException If called on a SQLite connection + */ + inline fun byContains(tableName: String, criteria: T) = + Configuration.dbConn().use { byContains(tableName, criteria, it) } + + /** + * Delete documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table from which documents should be deleted + * @param path The JSON path comparison to match + * @param conn The connection on which the deletion should be executed + * @throws DocumentException If called on a SQLite connection + */ + fun byJsonPath(tableName: String, path: String, conn: Connection) = + conn.customNonQuery(Delete.byJsonPath(tableName), listOf(Parameter(":path", ParameterType.STRING, path))) + + /** + * Delete documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table from which documents should be deleted + * @param path The JSON path comparison to match + * @throws DocumentException If called on a SQLite connection + */ + fun byJsonPath(tableName: String, path: String) = + Configuration.dbConn().use { byJsonPath(tableName, path, it) } }