Initial Development #1
@ -1,8 +1,7 @@
|
||||
package solutions.bitbadger.documents.common
|
||||
|
||||
import solutions.bitbadger.documents.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.fail
|
||||
import kotlin.test.*
|
||||
|
||||
/**
|
||||
* Integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
|
||||
@ -85,4 +84,44 @@ object Document {
|
||||
Configuration.idStringLength = 16
|
||||
}
|
||||
}
|
||||
|
||||
fun saveMatch(db: ThrowawayDatabase) {
|
||||
JsonDocument.load(db)
|
||||
db.conn.save(TEST_TABLE, JsonDocument("two", numValue = 44))
|
||||
val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "two")
|
||||
assertNotNull(doc, "There should have been a document returned")
|
||||
assertEquals("two", doc.id, "An incorrect document was returned")
|
||||
assertEquals("", doc.value, "The \"value\" field was not updated")
|
||||
assertEquals(44, doc.numValue, "The \"numValue\" field was not updated")
|
||||
assertNull(doc.sub, "The \"sub\" field was not updated")
|
||||
}
|
||||
|
||||
fun saveNoMatch(db: ThrowawayDatabase) {
|
||||
JsonDocument.load(db)
|
||||
db.conn.save(TEST_TABLE, JsonDocument("test", sub = SubDocument("a", "b")))
|
||||
assertNotNull(
|
||||
db.conn.findById<String, JsonDocument>(TEST_TABLE, "test"),
|
||||
"The test document should have been saved"
|
||||
)
|
||||
}
|
||||
|
||||
fun updateMatch(db: ThrowawayDatabase) {
|
||||
JsonDocument.load(db)
|
||||
db.conn.update(TEST_TABLE, "one", JsonDocument("one", "howdy", 8, SubDocument("y", "z")))
|
||||
val doc = db.conn.findById<String, JsonDocument>(TEST_TABLE, "one")
|
||||
assertNotNull(doc, "There should have been a document returned")
|
||||
assertEquals("one", doc.id, "An incorrect document was returned")
|
||||
assertEquals("howdy", doc.value, "The \"value\" field was not updated")
|
||||
assertEquals(8, doc.numValue, "The \"numValue\" field was not updated")
|
||||
assertNotNull(doc.sub, "The sub-document should not be null")
|
||||
assertEquals("y", doc.sub.foo, "The sub-document \"foo\" field was not updated")
|
||||
assertEquals("z", doc.sub.bar, "The sub-document \"bar\" field was not updated")
|
||||
}
|
||||
|
||||
fun updateNoMatch(db: ThrowawayDatabase) {
|
||||
JsonDocument.load(db)
|
||||
assertFalse { db.conn.existsById(TEST_TABLE, "two-hundred") }
|
||||
db.conn.update(TEST_TABLE, "two-hundred", JsonDocument("two-hundred", numValue = 200))
|
||||
assertFalse { db.conn.existsById(TEST_TABLE, "two-hundred") }
|
||||
}
|
||||
}
|
||||
|
@ -34,4 +34,24 @@ class DocumentIT {
|
||||
@DisplayName("insert succeeds with random string auto ID")
|
||||
fun insertStringAutoId() =
|
||||
PgDB().use(Document::insertStringAutoId)
|
||||
|
||||
@Test
|
||||
@DisplayName("save updates an existing document")
|
||||
fun saveMatch() =
|
||||
PgDB().use(Document::saveMatch)
|
||||
|
||||
@Test
|
||||
@DisplayName("save inserts a new document")
|
||||
fun saveNoMatch() =
|
||||
PgDB().use(Document::saveNoMatch)
|
||||
|
||||
@Test
|
||||
@DisplayName("update replaces an existing document")
|
||||
fun updateMatch() =
|
||||
PgDB().use(Document::updateMatch)
|
||||
|
||||
@Test
|
||||
@DisplayName("update succeeds when no document exists")
|
||||
fun updateNoMatch() =
|
||||
PgDB().use(Document::updateNoMatch)
|
||||
}
|
||||
|
@ -34,4 +34,24 @@ class DocumentIT {
|
||||
@DisplayName("insert succeeds with random string auto ID")
|
||||
fun insertStringAutoId() =
|
||||
SQLiteDB().use(Document::insertStringAutoId)
|
||||
|
||||
@Test
|
||||
@DisplayName("save updates an existing document")
|
||||
fun saveMatch() =
|
||||
SQLiteDB().use(Document::saveMatch)
|
||||
|
||||
@Test
|
||||
@DisplayName("save inserts a new document")
|
||||
fun saveNoMatch() =
|
||||
SQLiteDB().use(Document::saveNoMatch)
|
||||
|
||||
@Test
|
||||
@DisplayName("update replaces an existing document")
|
||||
fun updateMatch() =
|
||||
SQLiteDB().use(Document::updateMatch)
|
||||
|
||||
@Test
|
||||
@DisplayName("update succeeds when no document exists")
|
||||
fun updateNoMatch() =
|
||||
SQLiteDB().use(Document::updateNoMatch)
|
||||
}
|
||||
|
@ -93,6 +93,25 @@ fun Connection.ensureDocumentIndex(tableName: String, indexType: DocumentIndex)
|
||||
inline fun <reified TDoc> Connection.insert(tableName: String, document: TDoc) =
|
||||
Document.insert(tableName, document, this)
|
||||
|
||||
/**
|
||||
* 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> Connection.save(tableName: String, document: TDoc) =
|
||||
Document.save(tableName, document, this)
|
||||
|
||||
/**
|
||||
* 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> Connection.update(tableName: String, docId: TKey, document: TDoc) =
|
||||
Document.update(tableName, docId, document, this)
|
||||
|
||||
// ~~~ DOCUMENT COUNT QUERIES ~~~
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,8 @@ package solutions.bitbadger.documents
|
||||
|
||||
import java.sql.Connection
|
||||
import solutions.bitbadger.documents.query.Document
|
||||
import solutions.bitbadger.documents.query.Where
|
||||
import solutions.bitbadger.documents.query.statementWhere
|
||||
|
||||
/**
|
||||
* Functions for manipulating documents
|
||||
@ -20,24 +22,25 @@ object Document {
|
||||
val query = if (strategy == AutoId.DISABLED) {
|
||||
Document.insert(tableName)
|
||||
} else {
|
||||
val idField = Configuration.idField
|
||||
val dialect = Configuration.dialect("Create auto-ID insert query")
|
||||
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.NUMBER -> "' || (SELECT coalesce(max(data->>'$idField')::numeric, 0) + 1 " +
|
||||
"FROM $tableName) || '"
|
||||
AutoId.UUID -> "\"${AutoId.generateUUID()}\""
|
||||
AutoId.RANDOM_STRING -> "\"${AutoId.generateRandomString()}\""
|
||||
else -> "\"' || (:data)->>'$idField' || '\""
|
||||
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.NUMBER -> "(SELECT coalesce(max(data->>'$idField'), 0) + 1 FROM $tableName)"
|
||||
AutoId.UUID -> "'${AutoId.generateUUID()}'"
|
||||
AutoId.RANDOM_STRING -> "'${AutoId.generateRandomString()}'"
|
||||
else -> "(:data)->>'$idField'"
|
||||
else -> "(:data)->>'$idField'"
|
||||
}.let { "json_set(:data, '$.$idField', $it)" }
|
||||
}
|
||||
} else {
|
||||
@ -57,4 +60,50 @@ object Document {
|
||||
*/
|
||||
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) }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user