Initial Development #1
@ -96,7 +96,7 @@ object Query {
|
|||||||
* @param dataType The type of data for the column (`JSON`, `JSONB`, etc.)
|
* @param dataType The type of data for the column (`JSON`, `JSONB`, etc.)
|
||||||
* @return A query to create a document table
|
* @return A query to create a document table
|
||||||
*/
|
*/
|
||||||
fun ensureTableFor(tableName: String, dataType: String): String =
|
fun ensureTableFor(tableName: String, dataType: String) =
|
||||||
"CREATE TABLE IF NOT EXISTS $tableName (data $dataType NOT NULL)"
|
"CREATE TABLE IF NOT EXISTS $tableName (data $dataType NOT NULL)"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,7 +146,7 @@ object Query {
|
|||||||
* @param dialect The SQL dialect to use when creating this index
|
* @param dialect The SQL dialect to use when creating this index
|
||||||
* @return A query to create the key index
|
* @return A query to create the key index
|
||||||
*/
|
*/
|
||||||
fun ensureKey(tableName: String, dialect: Dialect): String =
|
fun ensureKey(tableName: String, dialect: Dialect) =
|
||||||
ensureIndexOn(tableName, "key", listOf(Configuration.idField), dialect).replace("INDEX", "UNIQUE INDEX")
|
ensureIndexOn(tableName, "key", listOf(Configuration.idField), dialect).replace("INDEX", "UNIQUE INDEX")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,37 +184,162 @@ object Query {
|
|||||||
* @param tableName The table into which to save (may include schema)
|
* @param tableName The table into which to save (may include schema)
|
||||||
* @return A query to save a document
|
* @return A query to save a document
|
||||||
*/
|
*/
|
||||||
fun save(tableName: String): String =
|
fun save(tableName: String) =
|
||||||
insert(tableName, AutoId.DISABLED) +
|
insert(tableName, AutoId.DISABLED) +
|
||||||
" ON CONFLICT ((data->>'${Configuration.idField}')) DO UPDATE SET data = EXCLUDED.data"
|
" ON CONFLICT ((data->>'${Configuration.idField}')) DO UPDATE SET data = EXCLUDED.data"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query to count documents in a table (this query has no `WHERE` clause)
|
* Functions to count documents
|
||||||
*
|
|
||||||
* @param tableName The table in which to count documents (may include schema)
|
|
||||||
* @return A query to count documents
|
|
||||||
*/
|
*/
|
||||||
fun count(tableName: String): String =
|
object Count {
|
||||||
"SELECT COUNT(*) AS it FROM $tableName"
|
|
||||||
|
/**
|
||||||
|
* Query to count all documents in a table
|
||||||
|
*
|
||||||
|
* @param tableName The table in which to count documents (may include schema)
|
||||||
|
* @return A query to count documents
|
||||||
|
*/
|
||||||
|
fun all(tableName: String) =
|
||||||
|
"SELECT COUNT(*) AS it FROM $tableName"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to count documents matching the given fields
|
||||||
|
*
|
||||||
|
* @param tableName The table in which to count documents (may include schema)
|
||||||
|
* @param fields The field comparisons for the count
|
||||||
|
* @param howMatched How fields should be compared (optional, defaults to ALL)
|
||||||
|
* @return A query to count documents matching the given fields
|
||||||
|
*/
|
||||||
|
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
|
||||||
|
statementWhere(all(tableName), Where.byFields(fields, howMatched))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to count documents via JSON containment (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The table in which to count documents (may include schema)
|
||||||
|
* @return A query to count documents via JSON containment
|
||||||
|
*/
|
||||||
|
fun byContains(tableName: String) =
|
||||||
|
statementWhere(all(tableName), Where.jsonContains())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to count documents via a JSON path match (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The table in which to count documents (may include schema)
|
||||||
|
* @return A query to count documents via a JSON path match
|
||||||
|
*/
|
||||||
|
fun byJsonPath(tableName: String) =
|
||||||
|
statementWhere(all(tableName), Where.jsonPathMatches())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query to check for document existence in a table
|
* Functions to check for document existence
|
||||||
*
|
|
||||||
* @param tableName The table in which existence should be checked (may include schema)
|
|
||||||
* @param where The `WHERE` clause with the existence criteria
|
|
||||||
* @return A query to check document existence
|
|
||||||
*/
|
*/
|
||||||
fun exists(tableName: String, where: String): String =
|
object Exists {
|
||||||
"SELECT EXISTS (SELECT 1 FROM $tableName WHERE $where) AS it"
|
|
||||||
|
/**
|
||||||
|
* Query to check for document existence in a table
|
||||||
|
*
|
||||||
|
* @param tableName The table in which existence should be checked (may include schema)
|
||||||
|
* @param where The `WHERE` clause with the existence criteria
|
||||||
|
* @return A query to check document existence
|
||||||
|
*/
|
||||||
|
private fun exists(tableName: String, where: String) =
|
||||||
|
"SELECT EXISTS (SELECT 1 FROM $tableName WHERE $where) AS it"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to check for document existence by ID
|
||||||
|
*
|
||||||
|
* @param tableName The table in which existence should be checked (may include schema)
|
||||||
|
* @param docId The ID of the document (optional, used for type checking)
|
||||||
|
* @return A query to determine document existence by ID
|
||||||
|
*/
|
||||||
|
fun <TKey> byId(tableName: String, docId: TKey?) =
|
||||||
|
exists(tableName, Where.byId(docId = docId))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to check for document existence matching the given fields
|
||||||
|
*
|
||||||
|
* @param tableName The table in which existence should be checked (may include schema)
|
||||||
|
* @param fields The field comparisons for the existence check
|
||||||
|
* @param howMatched How fields should be compared (optional, defaults to ALL)
|
||||||
|
* @return A query to determine document existence for the given fields
|
||||||
|
*/
|
||||||
|
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch?) =
|
||||||
|
exists(tableName, Where.byFields(fields, howMatched))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to check for document existence via JSON containment (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The table in which existence should be checked (may include schema)
|
||||||
|
* @return A query to determine document existence via JSON containment
|
||||||
|
*/
|
||||||
|
fun byContains(tableName: String) =
|
||||||
|
exists(tableName, Where.jsonContains())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to check for document existence via a JSON path match (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The table in which existence should be checked (may include schema)
|
||||||
|
* @return A query to determine document existence via a JSON path match
|
||||||
|
*/
|
||||||
|
fun byJsonPath(tableName: String) =
|
||||||
|
exists(tableName, Where.jsonPathMatches())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query to select documents from a table (this query has no `WHERE` clause)
|
* Functions to retrieve documents
|
||||||
*
|
|
||||||
* @param tableName The table from which documents should be found (may include schema)
|
|
||||||
* @return A query to retrieve documents
|
|
||||||
*/
|
*/
|
||||||
fun find(tableName: String): String =
|
object Find {
|
||||||
"SELECT data FROM $tableName"
|
|
||||||
|
/**
|
||||||
|
* Query to retrieve all documents from a table
|
||||||
|
*
|
||||||
|
* @param tableName The table from which documents should be retrieved (may include schema)
|
||||||
|
* @return A query to retrieve documents
|
||||||
|
*/
|
||||||
|
fun all(tableName: String) =
|
||||||
|
"SELECT data FROM $tableName"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to retrieve a document by its ID
|
||||||
|
*
|
||||||
|
* @param tableName The table from which documents should be retrieved (may include schema)
|
||||||
|
* @param docId The ID of the document (optional, used for type checking)
|
||||||
|
* @return A query to retrieve a document by its ID
|
||||||
|
*/
|
||||||
|
fun <TKey> byId(tableName: String, docId: TKey?) =
|
||||||
|
statementWhere(all(tableName), Where.byId(docId = docId))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to retrieve documents matching the given fields
|
||||||
|
*
|
||||||
|
* @param tableName The table from which documents should be retrieved (may include schema)
|
||||||
|
* @param fields The field comparisons for matching documents to retrieve
|
||||||
|
* @param howMatched How fields should be compared (optional, defaults to ALL)
|
||||||
|
* @return A query to retrieve documents matching the given fields
|
||||||
|
*/
|
||||||
|
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch?) =
|
||||||
|
statementWhere(all(tableName), Where.byFields(fields, howMatched))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to retrieve documents via JSON containment (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The table from which documents should be retrieved (may include schema)
|
||||||
|
* @return A query to retrieve documents via JSON containment
|
||||||
|
*/
|
||||||
|
fun byContains(tableName: String) =
|
||||||
|
statementWhere(all(tableName), Where.jsonContains())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to retrieve documents via a JSON path match (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The table from which documents should be retrieved (may include schema)
|
||||||
|
* @return A query to retrieve documents via a JSON path match
|
||||||
|
*/
|
||||||
|
fun byJsonPath(tableName: String) =
|
||||||
|
statementWhere(all(tableName), Where.jsonPathMatches())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query to update (replace) a document (this query has no `WHERE` clause)
|
* Query to update (replace) a document (this query has no `WHERE` clause)
|
||||||
@ -222,7 +347,7 @@ object Query {
|
|||||||
* @param tableName The table in which documents should be replaced (may include schema)
|
* @param tableName The table in which documents should be replaced (may include schema)
|
||||||
* @return A query to update documents
|
* @return A query to update documents
|
||||||
*/
|
*/
|
||||||
fun update(tableName: String): String =
|
fun update(tableName: String) =
|
||||||
"UPDATE $tableName SET data = :data"
|
"UPDATE $tableName SET data = :data"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,13 +411,59 @@ object Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query to delete documents from a table (this query has no `WHERE` clause)
|
* Functions to delete documents
|
||||||
*
|
|
||||||
* @param tableName The table in which documents should be deleted (may include schema)
|
|
||||||
* @return A query to delete documents
|
|
||||||
*/
|
*/
|
||||||
fun delete(tableName: String): String =
|
object Delete {
|
||||||
"DELETE FROM $tableName"
|
|
||||||
|
/**
|
||||||
|
* Query to delete documents from a table
|
||||||
|
*
|
||||||
|
* @param tableName The table in which documents should be deleted (may include schema)
|
||||||
|
* @param where The WHERE clause for the delete statement
|
||||||
|
* @return A query to delete documents
|
||||||
|
*/
|
||||||
|
private fun delete(tableName: String, where: String) =
|
||||||
|
statementWhere("DELETE FROM $tableName", where)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to delete a document by its ID
|
||||||
|
*
|
||||||
|
* @param tableName The table from which documents should be deleted (may include schema)
|
||||||
|
* @param docId The ID of the document (optional, used for type checking)
|
||||||
|
* @return A query to delete a document by its ID
|
||||||
|
*/
|
||||||
|
fun <TKey> byId(tableName: String, docId: TKey?) =
|
||||||
|
delete(tableName, Where.byId(docId = docId))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to delete documents matching the given fields
|
||||||
|
*
|
||||||
|
* @param tableName The table from which documents should be deleted (may include schema)
|
||||||
|
* @param fields The field comparisons for documents to be deleted
|
||||||
|
* @param howMatched How fields should be compared (optional, defaults to ALL)
|
||||||
|
* @return A query to delete documents matching for the given fields
|
||||||
|
*/
|
||||||
|
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch?) =
|
||||||
|
delete(tableName, Where.byFields(fields, howMatched))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to delete documents via JSON containment (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The table from which documents should be deleted (may include schema)
|
||||||
|
* @return A query to delete documents via JSON containment
|
||||||
|
*/
|
||||||
|
fun byContains(tableName: String) =
|
||||||
|
delete(tableName, Where.jsonContains())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to delete documents via a JSON path match (PostgreSQL only)
|
||||||
|
*
|
||||||
|
* @param tableName The table from which documents should be deleted (may include schema)
|
||||||
|
* @return A query to delete documents via a JSON path match
|
||||||
|
*/
|
||||||
|
fun byJsonPath(tableName: String) =
|
||||||
|
delete(tableName, Where.jsonPathMatches())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an `ORDER BY` clause for the given fields
|
* Create an `ORDER BY` clause for the given fields
|
||||||
|
@ -399,30 +399,78 @@ class QueryTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("count generates correctly")
|
@DisplayName("Count.all generates correctly")
|
||||||
fun count() =
|
fun countAll() =
|
||||||
assertEquals("SELECT COUNT(*) AS it FROM $tbl", Query.count(tbl), "Count query not constructed correctly")
|
assertEquals("SELECT COUNT(*) AS it FROM $tbl", Query.Count.all(tbl), "Count query not constructed correctly")
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("exists generates correctly")
|
@DisplayName("Count.byFields generates correctly (PostgreSQL)")
|
||||||
fun exists() =
|
fun countByFieldsPostgres() {
|
||||||
assertEquals("SELECT EXISTS (SELECT 1 FROM $tbl WHERE turkey) AS it", Query.exists(tbl, "turkey"),
|
Configuration.connectionString = pg
|
||||||
"Exists query not constructed correctly")
|
assertEquals("SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0",
|
||||||
|
Query.Count.byFields(tbl, listOf(Field.equal("test", "", ":field0"))),
|
||||||
|
"Count query not constructed correctly")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("find generates correctly")
|
@DisplayName("Count.byFields generates correctly (PostgreSQL)")
|
||||||
fun find() =
|
fun countByFieldsSQLite() {
|
||||||
assertEquals("SELECT data FROM $tbl", Query.find(tbl), "Find query not constructed correctly")
|
Configuration.connectionString = lite
|
||||||
|
assertEquals("SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0",
|
||||||
|
Query.Count.byFields(tbl, listOf(Field.equal("test", "", ":field0"))),
|
||||||
|
"Count query not constructed correctly")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Count.byContains generates correctly (PostgreSQL)")
|
||||||
|
fun countByContainsPostgres() {
|
||||||
|
Configuration.connectionString = pg
|
||||||
|
assertEquals("SELECT COUNT(*) AS it FROM $tbl WHERE data @> :criteria", Query.Count.byContains(tbl),
|
||||||
|
"Count query not constructed correctly")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Count.byContains fails (SQLite)")
|
||||||
|
fun countByContainsSQLite() {
|
||||||
|
Configuration.connectionString = lite
|
||||||
|
assertThrows<DocumentException> { Query.Count.byContains(tbl) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Count.byJsonPath generates correctly (PostgreSQL)")
|
||||||
|
fun countByJsonPathPostgres() {
|
||||||
|
Configuration.connectionString = pg
|
||||||
|
assertEquals("SELECT COUNT(*) AS it FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)",
|
||||||
|
Query.Count.byJsonPath(tbl), "Count query not constructed correctly")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Count.byJsonPath fails (SQLite)")
|
||||||
|
fun countByJsonPathSQLite() {
|
||||||
|
Configuration.connectionString = lite
|
||||||
|
assertThrows<DocumentException> { Query.Count.byJsonPath(tbl) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// @DisplayName("exists generates correctly")
|
||||||
|
// fun exists() =
|
||||||
|
// assertEquals("SELECT EXISTS (SELECT 1 FROM $tbl WHERE turkey) AS it", Query.exists(tbl, "turkey"),
|
||||||
|
// "Exists query not constructed correctly")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Find.all generates correctly")
|
||||||
|
fun findAll() =
|
||||||
|
assertEquals("SELECT data FROM $tbl", Query.Find.all(tbl), "Find query not constructed correctly")
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("update generates successfully")
|
@DisplayName("update generates successfully")
|
||||||
fun update() =
|
fun update() =
|
||||||
assertEquals("UPDATE $tbl SET data = :data", Query.update(tbl), "Update query not constructed correctly")
|
assertEquals("UPDATE $tbl SET data = :data", Query.update(tbl), "Update query not constructed correctly")
|
||||||
|
|
||||||
@Test
|
// @Test
|
||||||
@DisplayName("delete generates successfully")
|
// @DisplayName("delete generates successfully")
|
||||||
fun delete() =
|
// fun delete() =
|
||||||
assertEquals("DELETE FROM $tbl", Query.delete(tbl), "Delete query not constructed correctly")
|
// assertEquals("DELETE FROM $tbl", Query.delete(tbl), "Delete query not constructed correctly")
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("orderBy generates for no fields")
|
@DisplayName("orderBy generates for no fields")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user