Reorg modules; complete impls/tests
This commit is contained in:
94
src/scala/pom.xml
Normal file
94
src/scala/pom.xml
Normal file
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>solutions.bitbadger</groupId>
|
||||
<artifactId>documents</artifactId>
|
||||
<version>4.0.0-alpha1-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>solutions.bitbadger.documents</groupId>
|
||||
<artifactId>scala</artifactId>
|
||||
|
||||
<name>${project.groupId}:${project.artifactId}</name>
|
||||
<description>Expose a document store interface for PostgreSQL and SQLite (Scala Library)</description>
|
||||
<url>https://bitbadger.solutions/open-source/relational-documents/jvm/</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.code.style>official</kotlin.code.style>
|
||||
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>${project.basedir}/src/main/scala</sourceDirectory>
|
||||
<testSourceDirectory>${project.basedir}/src/test/scala</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.alchim31.maven</groupId>
|
||||
<artifactId>scala-maven-plugin</artifactId>
|
||||
<version>4.9.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${surefire.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>${failsafe.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>solutions.bitbadger.documents</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.scala-lang</groupId>
|
||||
<artifactId>scala3-library_3</artifactId>
|
||||
<version>${scala.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
9
src/scala/scala.iml
Normal file
9
src/scala/scala.iml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="AdditionalModuleElements">
|
||||
<content url="file://$MODULE_DIR$" dumb="true">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/scala" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/scala" isTestSource="true" />
|
||||
</content>
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,105 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.{Field, FieldMatch}
|
||||
import solutions.bitbadger.documents.java.Count as CoreCount
|
||||
|
||||
import java.sql.Connection
|
||||
|
||||
import _root_.scala.jdk.CollectionConverters.*
|
||||
|
||||
/**
|
||||
* Functions to count documents
|
||||
*/
|
||||
object Count:
|
||||
|
||||
/**
|
||||
* Count all documents in the table
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be counted
|
||||
* @param conn The connection over which documents should be counted
|
||||
* @return A count of the documents in the table
|
||||
* @throws DocumentException If any dependent process does
|
||||
*/
|
||||
def all(tableName: String, conn: Connection): Long =
|
||||
CoreCount.all(tableName, conn)
|
||||
|
||||
/**
|
||||
* Count all documents in the table (creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be counted
|
||||
* @return A count of the documents in the table
|
||||
* @throws DocumentException If no connection string has been set
|
||||
*/
|
||||
def all(tableName: String): Long =
|
||||
CoreCount.all(tableName)
|
||||
|
||||
/**
|
||||
* Count documents using a field comparison
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be counted
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched (optional, default `ALL`)
|
||||
* @param conn The connection on which the deletion should be executed
|
||||
* @return A count of the matching documents in the table
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], conn: Connection): Long =
|
||||
CoreCount.byFields(tableName, fields.asJava, howMatched.orNull, conn)
|
||||
|
||||
/**
|
||||
* Count documents using a field comparison (creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be counted
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @return A count of the matching documents in the table
|
||||
* @throws DocumentException If no connection string has been set
|
||||
*/
|
||||
def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Long =
|
||||
CoreCount.byFields(tableName, fields.asJava, howMatched.orNull)
|
||||
|
||||
/**
|
||||
* Count documents using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @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 count should be executed
|
||||
* @return A count of the matching documents in the table
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byContains[TContains](tableName: String, criteria: TContains, conn: Connection): Long =
|
||||
CoreCount.byContains(tableName, criteria, conn)
|
||||
|
||||
/**
|
||||
* Count documents using a JSON containment query (PostgreSQL only; creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be counted
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @return A count of the matching documents in the table
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byContains[TContains](tableName: String, criteria: TContains): Long =
|
||||
CoreCount.byContains(tableName, criteria)
|
||||
|
||||
/**
|
||||
* Count documents using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @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 count should be executed
|
||||
* @return A count of the matching documents in the table
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath(tableName: String, path: String, conn: Connection): Long =
|
||||
CoreCount.byJsonPath(tableName, path, conn)
|
||||
|
||||
/**
|
||||
* Count documents using a JSON Path match query (PostgreSQL only; creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be counted
|
||||
* @param path The JSON path comparison to match
|
||||
* @return A count of the matching documents in the table
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath(tableName: String, path: String): Long =
|
||||
CoreCount.byJsonPath(tableName, path)
|
||||
@@ -0,0 +1,210 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.{Configuration, Parameter}
|
||||
|
||||
import java.sql.{Connection, ResultSet}
|
||||
import scala.reflect.ClassTag
|
||||
import scala.util.Using
|
||||
|
||||
object Custom:
|
||||
|
||||
/**
|
||||
* Execute a query that returns a list of results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param parameters Parameters to use for the query
|
||||
* @param conn The connection over which the query should be executed
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return A list of results for the given query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def list[TDoc](query: String, parameters: Seq[Parameter[?]], conn: Connection,
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Using(Parameters.apply(conn, query, parameters)) { stmt => Results.toCustomList[TDoc](stmt, mapFunc) }.get
|
||||
|
||||
/**
|
||||
* Execute a query that returns a list of results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param conn The connection over which the query should be executed
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return A list of results for the given query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def list[TDoc](query: String, conn: Connection,
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
list(query, List(), conn, mapFunc)
|
||||
|
||||
/**
|
||||
* Execute a query that returns a list of results (creates connection)
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param parameters Parameters to use for the query
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return A list of results for the given query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def list[TDoc](query: String, parameters: Seq[Parameter[?]],
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Using(Configuration.dbConn()) { conn => list[TDoc](query, parameters, conn, mapFunc) }.get
|
||||
|
||||
/**
|
||||
* Execute a query that returns a list of results (creates connection)
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return A list of results for the given query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def list[TDoc](query: String,
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
list(query, List(), mapFunc)
|
||||
|
||||
/**
|
||||
* Execute a query that returns one or no results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param parameters Parameters to use for the query
|
||||
* @param conn The connection over which the query should be executed
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return An `Option` value, with the document if one matches the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def single[TDoc](query: String, parameters: Seq[Parameter[?]], conn: Connection,
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
list[TDoc](s"$query LIMIT 1", parameters, conn, mapFunc).headOption
|
||||
|
||||
/**
|
||||
* Execute a query that returns one or no results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param conn The connection over which the query should be executed
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return An `Option` value, with the document if one matches the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def single[TDoc](query: String, conn: Connection,
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
list[TDoc](s"$query LIMIT 1", List(), conn, mapFunc).headOption
|
||||
|
||||
/**
|
||||
* Execute a query that returns one or no results (creates connection)
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param parameters Parameters to use for the query
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return An `Option` value, with the document if one matches the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def single[TDoc](query: String, parameters: Seq[Parameter[?]],
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Using(Configuration.dbConn()) { conn => single[TDoc](query, parameters, conn, mapFunc) }.get
|
||||
|
||||
/**
|
||||
* Execute a query that returns one or no results (creates connection)
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return An `Option` value, with the document if one matches the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def single[TDoc](query: String,
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
single[TDoc](query, List(), mapFunc)
|
||||
|
||||
/**
|
||||
* Execute a query that returns no results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param parameters Parameters to use for the query
|
||||
* @param conn The connection over which the query should be executed
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def nonQuery(query: String, parameters: Seq[Parameter[?]], conn: Connection): Unit =
|
||||
Using(Parameters.apply(conn, query, parameters)) { stmt => stmt.executeUpdate() }
|
||||
|
||||
/**
|
||||
* Execute a query that returns no results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param conn The connection over which the query should be executed
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def nonQuery(query: String, conn: Connection): Unit =
|
||||
nonQuery(query, List(), conn)
|
||||
|
||||
/**
|
||||
* Execute a query that returns no results (creates connection)
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param parameters Parameters to use for the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def nonQuery(query: String, parameters: Seq[Parameter[?]]): Unit =
|
||||
Using(Configuration.dbConn()) { conn => nonQuery(query, parameters, conn) }
|
||||
|
||||
/**
|
||||
* Execute a query that returns no results (creates connection)
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def nonQuery(query: String): Unit =
|
||||
nonQuery(query, List())
|
||||
|
||||
/**
|
||||
* Execute a query that returns a scalar result
|
||||
*
|
||||
* @param query The query to retrieve the result
|
||||
* @param parameters Parameters to use for the query
|
||||
* @param conn The connection over which the query should be executed
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return The scalar value from the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def scalar[T](query: String, parameters: Seq[Parameter[?]], conn: Connection,
|
||||
mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T =
|
||||
Using(Parameters.apply(conn, query, parameters)) { stmt =>
|
||||
Using(stmt.executeQuery()) { rs =>
|
||||
rs.next()
|
||||
mapFunc(rs, tag)
|
||||
}.get
|
||||
}.get
|
||||
|
||||
/**
|
||||
* Execute a query that returns a scalar result
|
||||
*
|
||||
* @param query The query to retrieve the result
|
||||
* @param conn The connection over which the query should be executed
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return The scalar value from the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def scalar[T](query: String, conn: Connection,
|
||||
mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T =
|
||||
scalar[T](query, List(), conn, mapFunc)
|
||||
|
||||
/**
|
||||
* Execute a query that returns a scalar result (creates connection)
|
||||
*
|
||||
* @param query The query to retrieve the result
|
||||
* @param parameters Parameters to use for the query
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return The scalar value from the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def scalar[T](query: String, parameters: Seq[Parameter[?]],
|
||||
mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T =
|
||||
Using(Configuration.dbConn()) { conn => scalar[T](query, parameters, conn, mapFunc) }.get
|
||||
|
||||
/**
|
||||
* Execute a query that returns a scalar result (creates connection)
|
||||
*
|
||||
* @param query The query to retrieve the result
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return The scalar value from the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def scalar[T](query: String,
|
||||
mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T =
|
||||
scalar[T](query, List(), mapFunc)
|
||||
@@ -0,0 +1,72 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.DocumentIndex
|
||||
import solutions.bitbadger.documents.java.Definition as CoreDefinition
|
||||
|
||||
import java.sql.Connection
|
||||
import _root_.scala.jdk.CollectionConverters.*
|
||||
|
||||
object Definition:
|
||||
|
||||
/**
|
||||
* Create a document table if necessary
|
||||
*
|
||||
* @param tableName The table whose existence should be ensured (may include schema)
|
||||
* @param conn The connection on which the query should be executed
|
||||
* @throws DocumentException If the dialect is not configured
|
||||
*/
|
||||
def ensureTable(tableName: String, conn: Connection): Unit =
|
||||
CoreDefinition.ensureTable(tableName, conn)
|
||||
|
||||
/**
|
||||
* Create a document table if necessary
|
||||
*
|
||||
* @param tableName The table whose existence should be ensured (may include schema)
|
||||
* @throws DocumentException If no connection string has been set
|
||||
*/
|
||||
def ensureTable(tableName: String): Unit =
|
||||
CoreDefinition.ensureTable(tableName)
|
||||
|
||||
/**
|
||||
* Create an index on field(s) within documents in the specified table if necessary
|
||||
*
|
||||
* @param tableName The table to be indexed (may include schema)
|
||||
* @param indexName The name of the index to create
|
||||
* @param fields One or more fields to be indexed
|
||||
* @param conn The connection on which the query should be executed
|
||||
* @throws DocumentException If any dependent process does
|
||||
*/
|
||||
def ensureFieldIndex(tableName: String, indexName: String, fields: Seq[String], conn: Connection): Unit =
|
||||
CoreDefinition.ensureFieldIndex(tableName, indexName, fields.asJava, conn)
|
||||
|
||||
/**
|
||||
* Create an index on field(s) within documents in the specified table if necessary
|
||||
*
|
||||
* @param tableName The table to be indexed (may include schema)
|
||||
* @param indexName The name of the index to create
|
||||
* @param fields One or more fields to be indexed
|
||||
* @throws DocumentException If no connection string has been set, or if any dependent process does
|
||||
*/
|
||||
def ensureFieldIndex(tableName: String, indexName: String, fields: Seq[String]): Unit =
|
||||
CoreDefinition.ensureFieldIndex(tableName, indexName, fields.asJava)
|
||||
|
||||
/**
|
||||
* Create a document index on a table (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table to be indexed (may include schema)
|
||||
* @param indexType The type of index to ensure
|
||||
* @param conn The connection on which the query should be executed
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def ensureDocumentIndex(tableName: String, indexType: DocumentIndex, conn: Connection): Unit =
|
||||
CoreDefinition.ensureDocumentIndex(tableName, indexType, conn)
|
||||
|
||||
/**
|
||||
* Create a document index on a table (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table to be indexed (may include schema)
|
||||
* @param indexType The type of index to ensure
|
||||
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def ensureDocumentIndex(tableName: String, indexType: DocumentIndex): Unit =
|
||||
CoreDefinition.ensureDocumentIndex(tableName, indexType)
|
||||
@@ -0,0 +1,98 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.{Field, FieldMatch}
|
||||
import solutions.bitbadger.documents.java.Delete as CoreDelete
|
||||
|
||||
import java.sql.Connection
|
||||
import _root_.scala.jdk.CollectionConverters.*
|
||||
|
||||
/**
|
||||
* Functions to delete documents
|
||||
*/
|
||||
object Delete:
|
||||
|
||||
/**
|
||||
* Delete a document by its ID
|
||||
*
|
||||
* @param tableName The name of the table from which documents should be deleted
|
||||
* @param docId The ID of the document to be deleted
|
||||
* @param conn The connection on which the deletion should be executed
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def byId[TKey](tableName: String, docId: TKey, conn: Connection): Unit =
|
||||
CoreDelete.byId(tableName, docId, conn)
|
||||
|
||||
/**
|
||||
* Delete a document by its ID (creates connection)
|
||||
*
|
||||
* @param tableName The name of the table from which documents should be deleted
|
||||
* @param docId The ID of the document to be deleted
|
||||
* @throws DocumentException If no connection string has been set
|
||||
*/
|
||||
def byId[TKey](tableName: String, docId: TKey): Unit =
|
||||
CoreDelete.byId(tableName, docId)
|
||||
|
||||
/**
|
||||
* Delete documents using a field comparison
|
||||
*
|
||||
* @param tableName The name of the table from which documents should be deleted
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @param conn The connection on which the deletion should be executed
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], conn: Connection): Unit =
|
||||
CoreDelete.byFields(tableName, fields.asJava, howMatched.orNull, conn)
|
||||
|
||||
/**
|
||||
* Delete documents using a field comparison (creates connection)
|
||||
*
|
||||
* @param tableName The name of the table from which documents should be deleted
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @throws DocumentException If no connection string has been set, or if parameters are invalid
|
||||
*/
|
||||
def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Unit =
|
||||
CoreDelete.byFields(tableName, fields.asJava, howMatched.orNull)
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
def byContains[TContains](tableName: String, criteria: TContains, conn: Connection): Unit =
|
||||
CoreDelete.byContains(tableName, criteria, conn)
|
||||
|
||||
/**
|
||||
* Delete documents using a JSON containment query (PostgreSQL only; creates connection)
|
||||
*
|
||||
* @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 no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def byContains[TContains](tableName: String, criteria: TContains): Unit =
|
||||
CoreDelete.byContains(tableName, criteria)
|
||||
|
||||
/**
|
||||
* Delete documents using a JSON Path match 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
|
||||
*/
|
||||
def byJsonPath(tableName: String, path: String, conn: Connection): Unit =
|
||||
CoreDelete.byJsonPath(tableName, path, conn)
|
||||
|
||||
/**
|
||||
* Delete documents using a JSON Path match query (PostgreSQL only; creates connection)
|
||||
*
|
||||
* @param tableName The name of the table from which documents should be deleted
|
||||
* @param path The JSON path comparison to match
|
||||
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath(tableName: String, path: String): Unit =
|
||||
CoreDelete.byJsonPath(tableName, path)
|
||||
@@ -0,0 +1,72 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.java.Document as CoreDocument
|
||||
|
||||
import java.sql.Connection
|
||||
|
||||
object Document:
|
||||
|
||||
/**
|
||||
* Insert a new document
|
||||
*
|
||||
* @param tableName The table into which the document should be inserted (may include schema)
|
||||
* @param document The document to be inserted
|
||||
* @param conn The connection on which the query should be executed
|
||||
* @throws DocumentException If IDs are misconfigured, or if the database command fails
|
||||
*/
|
||||
def insert[TDoc](tableName: String, document: TDoc, conn: Connection): Unit =
|
||||
CoreDocument.insert(tableName, document, conn)
|
||||
|
||||
/**
|
||||
* Insert a new document (creates connection)
|
||||
*
|
||||
* @param tableName The table into which the document should be inserted (may include schema)
|
||||
* @param document The document to be inserted
|
||||
* @throws DocumentException If IDs are misconfigured, or if the database command fails
|
||||
*/
|
||||
def insert[TDoc](tableName: String, document: TDoc): Unit =
|
||||
CoreDocument.insert(tableName, 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
|
||||
* @param conn The connection on which the query should be executed
|
||||
* @throws DocumentException If the database command fails
|
||||
*/
|
||||
def save[TDoc](tableName: String, document: TDoc, conn: Connection): Unit =
|
||||
CoreDocument.save(tableName, document, conn)
|
||||
|
||||
/**
|
||||
* Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert"; creates connection)
|
||||
*
|
||||
* @param tableName The table in which the document should be saved (may include schema)
|
||||
* @param document The document to be saved
|
||||
* @throws DocumentException If the database command fails
|
||||
*/
|
||||
def save[TDoc](tableName: String, document: TDoc): Unit =
|
||||
CoreDocument.save(tableName, 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
|
||||
* @param conn The connection on which the query should be executed
|
||||
* @throws DocumentException If no dialect has been configured, or if the database command fails
|
||||
*/
|
||||
def update[TKey, TDoc](tableName: String, docId: TKey, document: TDoc, conn: Connection): Unit =
|
||||
CoreDocument.update(tableName, docId, document, conn)
|
||||
|
||||
/**
|
||||
* Update (replace) a document by its ID (creates connection)
|
||||
*
|
||||
* @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
|
||||
* @throws DocumentException If no dialect has been configured, or if the database command fails
|
||||
*/
|
||||
def update[TKey, TDoc](tableName: String, docId: TKey, document: TDoc): Unit =
|
||||
CoreDocument.update(tableName, docId, document)
|
||||
@@ -0,0 +1,106 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.{Field, FieldMatch}
|
||||
import solutions.bitbadger.documents.java.Exists as CoreExists
|
||||
|
||||
import java.sql.Connection
|
||||
import _root_.scala.jdk.CollectionConverters.*
|
||||
|
||||
/**
|
||||
* Functions to determine whether documents exist
|
||||
*/
|
||||
object Exists:
|
||||
|
||||
/**
|
||||
* Determine a document's existence by its ID
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param docId The ID of the document to be checked
|
||||
* @param conn The connection on which the existence check should be executed
|
||||
* @return True if the document exists, false if not
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def byId[TKey](tableName: String, docId: TKey, conn: Connection): Boolean =
|
||||
CoreExists.byId(tableName, docId, conn)
|
||||
|
||||
/**
|
||||
* Determine a document's existence by its ID (creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param docId The ID of the document to be checked
|
||||
* @return True if the document exists, false if not
|
||||
* @throws DocumentException If no connection string has been set
|
||||
*/
|
||||
def byId[TKey](tableName: String, docId: TKey): Boolean =
|
||||
CoreExists.byId(tableName, docId)
|
||||
|
||||
/**
|
||||
* Determine document existence using a field comparison
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @param conn The connection on which the existence check should be executed
|
||||
* @return True if any matching documents exist, false if not
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], conn: Connection): Boolean =
|
||||
CoreExists.byFields(tableName, fields.asJava, howMatched.orNull, conn)
|
||||
|
||||
/**
|
||||
* Determine document existence using a field comparison (creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @return True if any matching documents exist, false if not
|
||||
* @throws DocumentException If no connection string has been set, or if parameters are invalid
|
||||
*/
|
||||
def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Boolean =
|
||||
CoreExists.byFields(tableName, fields.asJava, howMatched.orNull)
|
||||
|
||||
/**
|
||||
* Determine document existence using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @param conn The connection on which the existence check should be executed
|
||||
* @return True if any matching documents exist, false if not
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byContains[TContains](tableName: String, criteria: TContains, conn: Connection): Boolean =
|
||||
CoreExists.byContains(tableName, criteria, conn)
|
||||
|
||||
/**
|
||||
* Determine document existence using a JSON containment query (PostgreSQL only; creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @return True if any matching documents exist, false if not
|
||||
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def byContains[TContains](tableName: String, criteria: TContains): Boolean =
|
||||
CoreExists.byContains(tableName, criteria)
|
||||
|
||||
/**
|
||||
* Determine document existence using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param path The JSON path comparison to match
|
||||
* @param conn The connection on which the existence check should be executed
|
||||
* @return True if any matching documents exist, false if not
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath(tableName: String, path: String, conn: Connection): Boolean =
|
||||
CoreExists.byJsonPath(tableName, path, conn)
|
||||
|
||||
/**
|
||||
* Determine document existence using a JSON Path match query (PostgreSQL only; creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param path The JSON path comparison to match
|
||||
* @return True if any matching documents exist, false if not
|
||||
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath(tableName: String, path: String): Boolean =
|
||||
CoreExists.byJsonPath(tableName, path)
|
||||
@@ -0,0 +1,372 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.{Configuration, Field, FieldMatch, Parameter, ParameterType}
|
||||
import solutions.bitbadger.documents.query.{FindQuery, QueryUtils}
|
||||
|
||||
import java.sql.Connection
|
||||
import scala.reflect.ClassTag
|
||||
import scala.jdk.CollectionConverters.*
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* Functions to find and retrieve documents
|
||||
*/
|
||||
object Find:
|
||||
|
||||
/**
|
||||
* Retrieve all documents in the given table, ordering results by the optional given fields
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return A list of documents from the given table
|
||||
* @throws DocumentException If query execution fails
|
||||
*/
|
||||
def all[TDoc](tableName: String, orderBy: Seq[Field[?]], conn: Connection)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Custom.list[TDoc](FindQuery.all(tableName) + QueryUtils.orderBy(orderBy.asJava), conn, Results.fromData)
|
||||
|
||||
/**
|
||||
* Retrieve all documents in the given table, ordering results by the optional given fields
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return A list of documents from the given table
|
||||
* @throws DocumentException If query execution fails
|
||||
*/
|
||||
def all[TDoc](tableName: String, conn: Connection)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
all[TDoc](tableName, List(), conn)
|
||||
|
||||
/**
|
||||
* Retrieve all documents in the given table (creates connection)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return A list of documents from the given table
|
||||
* @throws DocumentException If no connection string has been set, or if query execution fails
|
||||
*/
|
||||
def all[TDoc](tableName: String, orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Using(Configuration.dbConn()) { conn => all[TDoc](tableName, orderBy, conn) }.get
|
||||
|
||||
/**
|
||||
* Retrieve a document by its ID
|
||||
*
|
||||
* @param tableName The table from which the document should be retrieved
|
||||
* @param docId The ID of the document to retrieve
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return An `Option` with the document if it is found
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def byId[TKey, TDoc](tableName: String, docId: TKey, conn: Connection)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Custom.single[TDoc](FindQuery.byId(tableName, docId),
|
||||
Parameters.addFields(Field.equal(Configuration.idField, docId, ":id") :: Nil).toSeq, conn, Results.fromData)
|
||||
|
||||
/**
|
||||
* Retrieve a document by its ID (creates connection
|
||||
*
|
||||
* @param tableName The table from which the document should be retrieved
|
||||
* @param docId The ID of the document to retrieve
|
||||
* @return An `Option` with the document if it is found
|
||||
* @throws DocumentException If no connection string has been set
|
||||
*/
|
||||
def byId[TKey, TDoc](tableName: String, docId: TKey)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Using(Configuration.dbConn()) { conn => byId[TKey, TDoc](tableName, docId, conn) }.get
|
||||
|
||||
/**
|
||||
* Retrieve documents using a field comparison, ordering results by the given fields
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return A list of documents matching the field comparison
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def byFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], orderBy: Seq[Field[?]],
|
||||
conn: Connection)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
val named = Parameters.nameFields(fields)
|
||||
Custom.list[TDoc](
|
||||
FindQuery.byFields(tableName, named.asJava, howMatched.orNull) + QueryUtils.orderBy(orderBy.asJava),
|
||||
Parameters.addFields(named).toSeq, conn, Results.fromData)
|
||||
|
||||
/**
|
||||
* Retrieve documents using a field comparison
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return A list of documents matching the field comparison
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def byFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], conn: Connection)
|
||||
(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
byFields[TDoc](tableName, fields, howMatched, List(), conn)
|
||||
|
||||
/**
|
||||
* Retrieve documents using a field comparison, ordering results by the given fields
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return A list of documents matching the field comparison
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def byFields[TDoc](tableName: String, fields: Seq[Field[?]], orderBy: Seq[Field[?]], conn: Connection)
|
||||
(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
byFields[TDoc](tableName, fields, None, orderBy, conn)
|
||||
|
||||
/**
|
||||
* Retrieve documents using a field comparison, ordering results by the given fields (creates connection)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return A list of documents matching the field comparison
|
||||
* @throws DocumentException If no connection string has been set, or if parameters are invalid
|
||||
*/
|
||||
def byFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None,
|
||||
orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Using(Configuration.dbConn()) { conn => byFields[TDoc](tableName, fields, howMatched, orderBy, conn) }.get
|
||||
|
||||
/**
|
||||
* Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return A list of documents matching the JSON containment query
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]],
|
||||
conn: Connection)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Custom.list[TDoc](FindQuery.byContains(tableName) + QueryUtils.orderBy(orderBy.asJava),
|
||||
Parameters.json(":criteria", criteria) :: Nil, conn, Results.fromData)
|
||||
|
||||
/**
|
||||
* Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return A list of documents matching the JSON containment query
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byContains[TDoc, TContains](tableName: String, criteria: TContains, conn: Connection)
|
||||
(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
byContains[TDoc, TContains](tableName, criteria, List(), conn)
|
||||
|
||||
/**
|
||||
* Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only; creates
|
||||
* connection)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return A list of documents matching the JSON containment query
|
||||
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def byContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]] = List())
|
||||
(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Using(Configuration.dbConn()) { conn => byContains[TDoc, TContains](tableName, criteria, orderBy, conn) }.get
|
||||
|
||||
/**
|
||||
* Retrieve documents using a JSON Path match query, ordering results by the given fields (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param path The JSON path comparison to match
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return A list of documents matching the JSON Path match query
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]], conn: Connection)
|
||||
(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Custom.list[TDoc](FindQuery.byJsonPath(tableName) + QueryUtils.orderBy(orderBy.asJava),
|
||||
Parameter(":path", ParameterType.STRING, path) :: Nil, conn, Results.fromData)
|
||||
|
||||
/**
|
||||
* Retrieve documents using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param path The JSON path comparison to match
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return A list of documents matching the JSON Path match query
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath[TDoc](tableName: String, path: String, conn: Connection)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
byJsonPath[TDoc](tableName, path, List(), conn)
|
||||
|
||||
/**
|
||||
* Retrieve documents using a JSON Path match query, ordering results by the given fields (PostgreSQL only; creates
|
||||
* connection)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param path The JSON path comparison to match
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return A list of documents matching the JSON Path match query
|
||||
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]] = List())
|
||||
(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Using(Configuration.dbConn()) { conn => byJsonPath[TDoc](tableName, path, orderBy, conn) }.get
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a field comparison and ordering fields
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return An `Option` with the first document matching the field comparison if found
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def firstByFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch],
|
||||
orderBy: Seq[Field[?]], conn: Connection)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
val named = Parameters.nameFields(fields)
|
||||
Custom.single[TDoc](
|
||||
FindQuery.byFields(tableName, named.asJava, howMatched.orNull) + QueryUtils.orderBy(orderBy.asJava),
|
||||
Parameters.addFields(named).toSeq, conn, Results.fromData)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a field comparison
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return An `Option` with the first document matching the field comparison if found
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def firstByFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], conn: Connection)
|
||||
(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
firstByFields[TDoc](tableName, fields, howMatched, List(), conn)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a field comparison and ordering fields
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return An `Option` with the first document matching the field comparison if found
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def firstByFields[TDoc](tableName: String, fields: Seq[Field[?]], orderBy: Seq[Field[?]], conn: Connection)
|
||||
(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
firstByFields[TDoc](tableName, fields, None, orderBy, conn)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a field comparison
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return An `Option` with the first document matching the field comparison if found
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def firstByFields[TDoc](tableName: String, fields: Seq[Field[?]], conn: Connection)
|
||||
(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
firstByFields[TDoc](tableName, fields, None, List(), conn)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a field comparison and optional ordering fields (creates connection)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return An `Option` with the first document matching the field comparison if found
|
||||
* @throws DocumentException If no connection string has been set, or if parameters are invalid
|
||||
*/
|
||||
def firstByFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None,
|
||||
orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Using(Configuration.dbConn()) { conn => firstByFields[TDoc](tableName, fields, howMatched, orderBy, conn) }.get
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a JSON containment query and ordering fields (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return An `Option` with the first document matching the JSON containment query if found
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def firstByContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]],
|
||||
conn: Connection)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Custom.single[TDoc](FindQuery.byContains(tableName) + QueryUtils.orderBy(orderBy.asJava),
|
||||
Parameters.json(":criteria", criteria) :: Nil, conn, Results.fromData)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return An `Option` with the first document matching the JSON containment query if found
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def firstByContains[TDoc, TContains](tableName: String, criteria: TContains, conn: Connection)
|
||||
(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
firstByContains[TDoc, TContains](tableName, criteria, List(), conn)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only; creates
|
||||
* connection)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return An `Option` with the first document matching the JSON containment query if found
|
||||
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def firstByContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]] = List())
|
||||
(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Using(Configuration.dbConn()) { conn => firstByContains[TDoc, TContains](tableName, criteria, orderBy, conn) }.get
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a JSON Path match query and ordering fields (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param path The JSON path comparison to match
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return An `Optional` item, with the first document matching the JSON Path match query if found
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def firstByJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]], conn: Connection)
|
||||
(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Custom.single[TDoc](FindQuery.byJsonPath(tableName) + QueryUtils.orderBy(orderBy.asJava),
|
||||
Parameter(":path", ParameterType.STRING, path) :: Nil, conn, Results.fromData)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param path The JSON path comparison to match
|
||||
* @param conn The connection over which documents should be retrieved
|
||||
* @return An `Option` with the first document matching the JSON Path match query if found
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def firstByJsonPath[TDoc](tableName: String, path: String, conn: Connection)
|
||||
(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
firstByJsonPath[TDoc](tableName, path, List(), conn)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only; creates
|
||||
* connection)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param path The JSON path comparison to match
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return An `Optional` item, with the first document matching the JSON Path match query if found
|
||||
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def firstByJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]] = List())
|
||||
(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Using(Configuration.dbConn()) { conn => firstByJsonPath[TDoc](tableName, path, orderBy, conn) }.get
|
||||
@@ -0,0 +1,87 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.{Field, Op, Parameter, ParameterName}
|
||||
import solutions.bitbadger.documents.java.Parameters as CoreParameters
|
||||
|
||||
import java.sql.{Connection, PreparedStatement}
|
||||
import java.util
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import _root_.scala.jdk.CollectionConverters.*
|
||||
|
||||
/**
|
||||
* Functions to assist with the creation and implementation of parameters for SQL queries
|
||||
*/
|
||||
object Parameters:
|
||||
|
||||
/**
|
||||
* Assign parameter names to any fields that do not have them assigned
|
||||
*
|
||||
* @param fields The collection of fields to be named
|
||||
* @return The collection of fields with parameter names assigned
|
||||
*/
|
||||
def nameFields(fields: Seq[Field[?]]): Seq[Field[?]] =
|
||||
val name = ParameterName()
|
||||
fields.map { it =>
|
||||
if ((it.getParameterName == null || it.getParameterName.isEmpty)
|
||||
&& !(Op.EXISTS :: Op.NOT_EXISTS :: Nil).contains(it.getComparison.getOp)) {
|
||||
it.withParameterName(name.derive(null))
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a parameter by encoding a JSON object
|
||||
*
|
||||
* @param name The parameter name
|
||||
* @param value The object to be encoded as JSON
|
||||
* @return A parameter with the value encoded
|
||||
*/
|
||||
def json[T](name: String, value: T): Parameter[String] =
|
||||
CoreParameters.json(name, value)
|
||||
|
||||
/**
|
||||
* Add field parameters to the given set of parameters
|
||||
*
|
||||
* @param fields The fields being compared in the query
|
||||
* @param existing Any existing parameters for the query (optional, defaults to empty collection)
|
||||
* @return A collection of parameters for the query
|
||||
*/
|
||||
def addFields(fields: Seq[Field[?]],
|
||||
existing: mutable.Buffer[Parameter[?]] = ListBuffer()): mutable.Buffer[Parameter[?]] =
|
||||
fields.foreach { it => it.appendParameter(new util.ArrayList[Parameter[?]]()).forEach(existing.append) }
|
||||
existing
|
||||
|
||||
/**
|
||||
* Replace the parameter names in the query with question marks
|
||||
*
|
||||
* @param query The query with named placeholders
|
||||
* @param parameters The parameters for the query
|
||||
* @return The query, with name parameters changed to `?`s
|
||||
*/
|
||||
def replaceNamesInQuery(query: String, parameters: Seq[Parameter[?]]): String =
|
||||
CoreParameters.replaceNamesInQuery(query, parameters.asJava)
|
||||
|
||||
/**
|
||||
* Apply the given parameters to the given query, returning a prepared statement
|
||||
*
|
||||
* @param conn The active JDBC connection
|
||||
* @param query The query
|
||||
* @param parameters The parameters for the query
|
||||
* @return A `PreparedStatement` with the parameter names replaced with `?` and parameter values bound
|
||||
* @throws DocumentException If parameter names are invalid or number value types are invalid
|
||||
*/
|
||||
def apply(conn: Connection, query: String, parameters: Seq[Parameter[?]]): PreparedStatement =
|
||||
CoreParameters.apply(conn, query, parameters.asJava)
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws DocumentException If the dialect has not been set
|
||||
*/
|
||||
def fieldNames(names: Seq[String], parameterName: String = ":name"): mutable.Buffer[Parameter[?]] =
|
||||
CoreParameters.fieldNames(names.asJava, parameterName).asScala.toBuffer
|
||||
@@ -0,0 +1,120 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.{Field, FieldMatch}
|
||||
import solutions.bitbadger.documents.java.Patch as CorePatch
|
||||
|
||||
import java.sql.Connection
|
||||
import _root_.scala.jdk.CollectionConverters.*
|
||||
|
||||
/**
|
||||
* Functions to patch (partially update) documents
|
||||
*/
|
||||
object Patch:
|
||||
|
||||
/**
|
||||
* Patch a document by its ID
|
||||
*
|
||||
* @param tableName The name of the table in which a document should be patched
|
||||
* @param docId The ID of the document to be patched
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @param conn The connection on which the update should be executed
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def byId[TKey, TPatch](tableName: String, docId: TKey, patch: TPatch, conn: Connection): Unit =
|
||||
CorePatch.byId(tableName, docId, patch, conn)
|
||||
|
||||
/**
|
||||
* Patch a document by its ID (creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which a document should be patched
|
||||
* @param docId The ID of the document to be patched
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @throws DocumentException If no connection string has been set
|
||||
*/
|
||||
def byId[TKey, TPatch](tableName: String, docId: TKey, patch: TPatch) =
|
||||
CorePatch.byId(tableName, docId, patch)
|
||||
|
||||
/**
|
||||
* Patch documents using a field comparison
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be patched
|
||||
* @param fields The fields which should be compared
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @param howMatched How the fields should be matched
|
||||
* @param conn The connection on which the update should be executed
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def byFields[TPatch](tableName: String, fields: Seq[Field[?]], patch: TPatch, howMatched: Option[FieldMatch],
|
||||
conn: Connection): Unit =
|
||||
CorePatch.byFields(tableName, fields.asJava, patch, howMatched.orNull, conn)
|
||||
|
||||
/**
|
||||
* Patch documents using a field comparison
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be patched
|
||||
* @param fields The fields which should be compared
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @param conn The connection on which the update should be executed
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def byFields[TPatch](tableName: String, fields: Seq[Field[?]], patch: TPatch, conn: Connection): Unit =
|
||||
byFields(tableName, fields, patch, None, conn)
|
||||
|
||||
/**
|
||||
* Patch documents using a field comparison (creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be patched
|
||||
* @param fields The fields which should be compared
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @param howMatched How the fields should be matched
|
||||
* @throws DocumentException If no connection string has been set, or if parameters are invalid
|
||||
*/
|
||||
def byFields[TPatch](tableName: String, fields: Seq[Field[?]], patch: TPatch,
|
||||
howMatched: Option[FieldMatch] = None): Unit =
|
||||
CorePatch.byFields(tableName, fields.asJava, patch, howMatched.orNull)
|
||||
|
||||
/**
|
||||
* Patch documents using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be patched
|
||||
* @param criteria The object against which JSON containment should be checked
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @param conn The connection on which the update should be executed
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byContains[TContains, TPatch](tableName: String, criteria: TContains, patch: TPatch, conn: Connection): Unit =
|
||||
CorePatch.byContains(tableName, criteria, patch, conn)
|
||||
|
||||
/**
|
||||
* Patch documents using a JSON containment query (PostgreSQL only; creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be patched
|
||||
* @param criteria The object against which JSON containment should be checked
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def byContains[TContains, TPatch](tableName: String, criteria: TContains, patch: TPatch): Unit =
|
||||
CorePatch.byContains(tableName, criteria, patch)
|
||||
|
||||
/**
|
||||
* Patch documents using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be patched
|
||||
* @param path The JSON path comparison to match
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @param conn The connection on which the update should be executed
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath[TPatch](tableName: String, path: String, patch: TPatch, conn: Connection): Unit =
|
||||
CorePatch.byJsonPath(tableName, path, patch, conn)
|
||||
|
||||
/**
|
||||
* Patch documents using a JSON Path match query (PostgreSQL only; creates connection)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be patched
|
||||
* @param path The JSON path comparison to match
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @throws DocumentException If no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath[TPatch](tableName: String, path: String, patch: TPatch): Unit =
|
||||
CorePatch.byJsonPath(tableName, path, patch)
|
||||
@@ -0,0 +1,120 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.{Field, FieldMatch}
|
||||
import solutions.bitbadger.documents.java.RemoveFields as CoreRemoveFields
|
||||
|
||||
import java.sql.Connection
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
/**
|
||||
* Functions to remove fields from documents
|
||||
*/
|
||||
object RemoveFields:
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def byId[TKey](tableName: String, docId: TKey, toRemove: Seq[String], conn: Connection): Unit =
|
||||
CoreRemoveFields.byId(tableName, docId, toRemove.asJava, conn)
|
||||
|
||||
/**
|
||||
* Remove fields from a document by its ID (creates connection)
|
||||
*
|
||||
* @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
|
||||
* @throws DocumentException If no connection string has been set
|
||||
*/
|
||||
def byId[TKey](tableName: String, docId: TKey, toRemove: Seq[String]): Unit =
|
||||
CoreRemoveFields.byId(tableName, docId, toRemove.asJava)
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def byFields(tableName: String, fields: Seq[Field[?]], toRemove: Seq[String], howMatched: Option[FieldMatch],
|
||||
conn: Connection): Unit =
|
||||
CoreRemoveFields.byFields(tableName, fields.asJava, toRemove.asJava, howMatched.orNull, conn)
|
||||
|
||||
/**
|
||||
* 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 conn The connection on which the update should be executed
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def byFields(tableName: String, fields: Seq[Field[?]], toRemove: Seq[String], conn: Connection): Unit =
|
||||
byFields(tableName, fields, toRemove, None, conn)
|
||||
|
||||
/**
|
||||
* Remove fields from documents using a field comparison (creates connection)
|
||||
*
|
||||
* @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
|
||||
* @throws DocumentException If no connection string has been set, or if parameters are invalid
|
||||
*/
|
||||
def byFields(tableName: String, fields: Seq[Field[?]], toRemove: Seq[String],
|
||||
howMatched: Option[FieldMatch] = None): Unit =
|
||||
CoreRemoveFields.byFields(tableName, fields.asJava, toRemove.asJava, howMatched.orNull)
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
def byContains[TContains](tableName: String, criteria: TContains, toRemove: Seq[String], conn: Connection): Unit =
|
||||
CoreRemoveFields.byContains(tableName, criteria, toRemove.asJava, conn)
|
||||
|
||||
/**
|
||||
* Remove fields from documents using a JSON containment query (PostgreSQL only; creates connection)
|
||||
*
|
||||
* @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 no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def byContains[TContains](tableName: String, criteria: TContains, toRemove: Seq[String]): Unit =
|
||||
CoreRemoveFields.byContains(tableName, criteria, toRemove.asJava)
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
def byJsonPath(tableName: String, path: String, toRemove: Seq[String], conn: Connection): Unit =
|
||||
CoreRemoveFields.byJsonPath(tableName, path, toRemove.asJava, conn)
|
||||
|
||||
/**
|
||||
* Remove fields from documents using a JSON Path match query (PostgreSQL only; creates connection)
|
||||
*
|
||||
* @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 no connection string has been set, or if called on a SQLite connection
|
||||
*/
|
||||
def byJsonPath(tableName: String, path: String, toRemove: Seq[String]): Unit =
|
||||
CoreRemoveFields.byJsonPath(tableName, path, toRemove.asJava)
|
||||
@@ -0,0 +1,77 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.java.Results as CoreResults
|
||||
|
||||
import java.sql.{PreparedStatement, ResultSet, SQLException}
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.reflect.ClassTag
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* Functions to manipulate results
|
||||
*/
|
||||
object Results:
|
||||
|
||||
/**
|
||||
* Create a domain item from a document, specifying the field in which the document is found
|
||||
*
|
||||
* @param field The field name containing the JSON document
|
||||
* @param rs A `ResultSet` set to the row with the document to be constructed
|
||||
* @param ignored The class tag (placeholder used for signature; implicit tag used for serialization)
|
||||
* @return The constructed domain item
|
||||
*/
|
||||
def fromDocument[TDoc](field: String, rs: ResultSet, ignored: ClassTag[TDoc])(implicit tag: ClassTag[TDoc]): TDoc =
|
||||
CoreResults.fromDocument(field, rs, tag.runtimeClass.asInstanceOf[Class[TDoc]])
|
||||
|
||||
/**
|
||||
* Create a domain item from a document
|
||||
*
|
||||
* @param rs A `ResultSet` set to the row with the document to be constructed
|
||||
* @param ignored The class tag (placeholder used for signature; implicit tag used for serialization)
|
||||
* @return The constructed domain item
|
||||
*/
|
||||
def fromData[TDoc](rs: ResultSet, ignored: ClassTag[TDoc])(implicit tag: ClassTag[TDoc]): TDoc =
|
||||
fromDocument[TDoc]("data", rs, tag)
|
||||
|
||||
/**
|
||||
* Create a list of items for the results of the given command, using the specified mapping function
|
||||
*
|
||||
* @param stmt The prepared statement to execute
|
||||
* @param mapFunc The mapping function from data reader to domain class instance
|
||||
* @return A list of items from the query's result
|
||||
* @throws DocumentException If there is a problem executing the query (unchecked)
|
||||
*/
|
||||
def toCustomList[TDoc](stmt: PreparedStatement,
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
try
|
||||
val buffer = ListBuffer[TDoc]()
|
||||
Using(stmt.executeQuery()) { rs =>
|
||||
while (rs.next()) {
|
||||
buffer.append(mapFunc(rs, tag))
|
||||
}
|
||||
}
|
||||
buffer.toList
|
||||
catch
|
||||
case ex: SQLException =>
|
||||
throw DocumentException("Error retrieving documents from query: ${ex.message}", ex)
|
||||
|
||||
/**
|
||||
* Extract a count from the first column
|
||||
*
|
||||
* @param rs A `ResultSet` set to the row with the count to retrieve
|
||||
* @return The count from the row
|
||||
* @throws DocumentException If the dialect has not been set (unchecked)
|
||||
*/
|
||||
def toCount(rs: ResultSet, tag: ClassTag[Long] = ClassTag.Long): Long =
|
||||
CoreResults.toCount(rs, Long.getClass)
|
||||
|
||||
/**
|
||||
* Extract a true/false value from the first column
|
||||
*
|
||||
* @param rs A `ResultSet` set to the row with the true/false value to retrieve
|
||||
* @return The true/false value from the row
|
||||
* @throws DocumentException If the dialect has not been set (unchecked)
|
||||
*/
|
||||
def toExists(rs: ResultSet, tag: ClassTag[Boolean] = ClassTag.Boolean): Boolean =
|
||||
CoreResults.toExists(rs, Boolean.getClass)
|
||||
@@ -0,0 +1,499 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
import solutions.bitbadger.documents.{DocumentIndex, Field, FieldMatch, Parameter}
|
||||
|
||||
import java.sql.{Connection, ResultSet}
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
package object extensions:
|
||||
|
||||
extension (conn: Connection)
|
||||
|
||||
// ~~~ CUSTOM QUERIES ~~~
|
||||
|
||||
/**
|
||||
* Execute a query that returns a list of results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param parameters Parameters to use for the query
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return A list of results for the given query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def customList[TDoc](query: String, parameters: Seq[Parameter[?]],
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Custom.list[TDoc](query, parameters, conn, mapFunc)
|
||||
|
||||
/**
|
||||
* Execute a query that returns a list of results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return A list of results for the given query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def customList[TDoc](query: String,
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Custom.list[TDoc](query, conn, mapFunc)
|
||||
|
||||
/**
|
||||
* Execute a query that returns one or no results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param parameters Parameters to use for the query
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return An optional document, filled if one matches the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def customSingle[TDoc](query: String, parameters: Seq[Parameter[?]],
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Custom.single[TDoc](query, parameters, conn, mapFunc)
|
||||
|
||||
/**
|
||||
* Execute a query that returns one or no results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return An optional document, filled if one matches the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def customSingle[TDoc](query: String,
|
||||
mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Custom.single[TDoc](query, conn, mapFunc)
|
||||
|
||||
/**
|
||||
* Execute a query that returns no results
|
||||
*
|
||||
* @param query The query to retrieve the results
|
||||
* @param parameters Parameters to use for the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def customNonQuery(query: String, parameters: Seq[Parameter[?]] = List()): Unit =
|
||||
Custom.nonQuery(query, parameters, conn)
|
||||
|
||||
/**
|
||||
* Execute a query that returns a scalar result
|
||||
*
|
||||
* @param query The query to retrieve the result
|
||||
* @param parameters Parameters to use for the query
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return The scalar value from the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def customScalar[T](query: String, parameters: Seq[Parameter[?]],
|
||||
mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T =
|
||||
Custom.scalar[T](query, parameters, conn, mapFunc)
|
||||
|
||||
/**
|
||||
* Execute a query that returns a scalar result
|
||||
*
|
||||
* @param query The query to retrieve the result
|
||||
* @param mapFunc The mapping function between the document and the domain item
|
||||
* @return The scalar value from the query
|
||||
* @throws DocumentException If parameters are invalid
|
||||
*/
|
||||
def customScalar[T](query: String,
|
||||
mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T =
|
||||
Custom.scalar[T](query, conn, mapFunc)
|
||||
|
||||
// ~~~ DEFINITION QUERIES ~~~
|
||||
|
||||
/**
|
||||
* Create a document table if necessary
|
||||
*
|
||||
* @param tableName The table whose existence should be ensured (may include schema)
|
||||
* @throws DocumentException If the dialect is not configured
|
||||
*/
|
||||
def ensureTable(tableName: String): Unit =
|
||||
Definition.ensureTable(tableName, conn)
|
||||
|
||||
/**
|
||||
* Create an index on field(s) within documents in the specified table if necessary
|
||||
*
|
||||
* @param tableName The table to be indexed (may include schema)
|
||||
* @param indexName The name of the index to create
|
||||
* @param fields One or more fields to be indexed<
|
||||
* @throws DocumentException If any dependent process does
|
||||
*/
|
||||
def ensureFieldIndex(tableName: String, indexName: String, fields: Seq[String]): Unit =
|
||||
Definition.ensureFieldIndex(tableName, indexName, fields, conn)
|
||||
|
||||
/**
|
||||
* Create a document index on a table (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table to be indexed (may include schema)
|
||||
* @param indexType The type of index to ensure
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def ensureDocumentIndex(tableName: String, indexType: DocumentIndex): Unit =
|
||||
Definition.ensureDocumentIndex (tableName, indexType, conn)
|
||||
|
||||
// ~~~ DOCUMENT MANIPULATION QUERIES ~~~
|
||||
|
||||
/**
|
||||
* Insert a new document
|
||||
*
|
||||
* @param tableName The table into which the document should be inserted (may include schema)
|
||||
* @param document The document to be inserted
|
||||
* @throws DocumentException If IDs are misconfigured, or if the database command fails
|
||||
*/
|
||||
def insert[TDoc](tableName: String, document: TDoc): Unit =
|
||||
Document.insert(tableName, document, conn)
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws DocumentException If the database command fails
|
||||
*/
|
||||
def save[TDoc](tableName: String, document: TDoc): Unit =
|
||||
Document.save(tableName, document, conn)
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws DocumentException If no dialect has been configured, or if the database command fails
|
||||
*/
|
||||
def update[TKey, TDoc](tableName: String, docId: TKey, document: TDoc): Unit =
|
||||
Document.update(tableName, docId, document, conn)
|
||||
|
||||
// ~~~ DOCUMENT COUNT QUERIES ~~~
|
||||
|
||||
/**
|
||||
* Count all documents in the table
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be counted
|
||||
* @return A count of the documents in the table
|
||||
* @throws DocumentException If any dependent process does
|
||||
*/
|
||||
def countAll(tableName: String): Long =
|
||||
Count.all(tableName, conn)
|
||||
|
||||
/**
|
||||
* Count documents using a field comparison
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be counted
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched (optional, default `ALL`)
|
||||
* @return A count of the matching documents in the table
|
||||
* @throws DocumentException If the dialect has not been configured
|
||||
*/
|
||||
def countByFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Long =
|
||||
Count.byFields(tableName, fields, howMatched, conn)
|
||||
|
||||
/**
|
||||
* Count documents using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be counted
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @return A count of the matching documents in the table
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def countByContains[TContains](tableName: String, criteria: TContains): Long =
|
||||
Count.byContains(tableName, criteria, conn)
|
||||
|
||||
/**
|
||||
* Count documents using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be counted
|
||||
* @param path The JSON path comparison to match
|
||||
* @return A count of the matching documents in the table
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def countByJsonPath(tableName: String, path: String): Long =
|
||||
Count.byJsonPath(tableName, path, conn)
|
||||
|
||||
// ~~~ DOCUMENT EXISTENCE QUERIES ~~~
|
||||
|
||||
/**
|
||||
* Determine a document's existence by its ID
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param docId The ID of the document to be checked
|
||||
* @return True if the document exists, false if not
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def existsById[TKey](tableName: String, docId: TKey): Boolean =
|
||||
Exists.byId(tableName, docId, conn)
|
||||
|
||||
/**
|
||||
* Determine document existence using a field comparison
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @return True if any matching documents exist, false if not
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def existsByFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Boolean =
|
||||
Exists.byFields(tableName, fields, howMatched, conn)
|
||||
|
||||
/**
|
||||
* Determine document existence using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @return True if any matching documents exist, false if not
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def existsByContains[TContains](tableName: String, criteria: TContains): Boolean =
|
||||
Exists.byContains(tableName, criteria, conn)
|
||||
|
||||
/**
|
||||
* Determine document existence using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param path The JSON path comparison to match
|
||||
* @return True if any matching documents exist, false if not
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def existsByJsonPath(tableName: String, path: String): Boolean =
|
||||
Exists.byJsonPath(tableName, path, conn)
|
||||
|
||||
// ~~~ DOCUMENT RETRIEVAL QUERIES ~~~
|
||||
|
||||
/**
|
||||
* Retrieve all documents in the given table, ordering results by the optional given fields
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return A list of documents from the given table
|
||||
* @throws DocumentException If query execution fails
|
||||
*/
|
||||
def findAll[TDoc](tableName: String, orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Find.all[TDoc](tableName, orderBy, conn)
|
||||
|
||||
/**
|
||||
* Retrieve a document by its ID
|
||||
*
|
||||
* @param tableName The table from which the document should be retrieved
|
||||
* @param docId The ID of the document to retrieve
|
||||
* @return The document if it is found, `None` otherwise
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def findById[TKey, TDoc](tableName: String, docId: TKey)(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Find.byId[TKey, TDoc](tableName, docId, conn)
|
||||
|
||||
/**
|
||||
* Retrieve documents using a field comparison, ordering results by the optional given fields
|
||||
*
|
||||
* @param tableName The table from which the document should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return A list of documents matching the field comparison
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def findByFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None,
|
||||
orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Find.byFields[TDoc](tableName, fields, howMatched, orderBy, conn)
|
||||
|
||||
/**
|
||||
* Retrieve documents using a JSON containment query, ordering results by the optional given fields (PostgreSQL
|
||||
* only)
|
||||
*
|
||||
* @param tableName The name of the table in which document existence should be checked
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return A list of documents matching the JSON containment query
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def findByContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]] = List())
|
||||
(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Find.byContains[TDoc, TContains](tableName, criteria, orderBy, conn)
|
||||
|
||||
/**
|
||||
* Retrieve documents using a JSON Path match query, ordering results by the optional given fields (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param path The JSON path comparison to match
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return A list of documents matching the JSON Path match query
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def findByJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]] = List())
|
||||
(implicit tag: ClassTag[TDoc]): List[TDoc] =
|
||||
Find.byJsonPath[TDoc](tableName, path, orderBy, conn)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a field comparison and optional ordering fields
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`)
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return The first document matching the field comparison, or `None` if no matches are found
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def findFirstByFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None,
|
||||
orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Find.firstByFields[TDoc](tableName, fields, howMatched, orderBy, conn)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param criteria The object for which JSON containment should be checked
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return The first document matching the JSON containment query, or `None` if no matches are found
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def findFirstByContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]] = List())
|
||||
(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Find.firstByContains[TDoc, TContains](tableName, criteria, orderBy, conn)
|
||||
|
||||
/**
|
||||
* Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The table from which documents should be retrieved
|
||||
* @param path The JSON path comparison to match
|
||||
* @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering)
|
||||
* @return The first document matching the JSON Path match query, or `None` if no matches are found
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def findFirstByJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]] = List())
|
||||
(implicit tag: ClassTag[TDoc]): Option[TDoc] =
|
||||
Find.firstByJsonPath[TDoc](tableName, path, orderBy, conn)
|
||||
|
||||
// ~~~ DOCUMENT PATCH (PARTIAL UPDATE) QUERIES ~~~
|
||||
|
||||
/**
|
||||
* Patch a document by its ID
|
||||
*
|
||||
* @param tableName The name of the table in which a document should be patched
|
||||
* @param docId The ID of the document to be patched
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def patchById[TKey, TPatch](tableName: String, docId: TKey, patch: TPatch): Unit =
|
||||
Patch.byId(tableName, docId, patch, conn)
|
||||
|
||||
/**
|
||||
* Patch documents using a field comparison
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be patched
|
||||
* @param fields The fields which should be compared
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @param howMatched How the fields should be matched
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def patchByFields[TPatch](tableName: String, fields: Seq[Field[?]], patch: TPatch,
|
||||
howMatched: Option[FieldMatch] = None): Unit =
|
||||
Patch.byFields(tableName, fields, patch, howMatched, conn)
|
||||
|
||||
/**
|
||||
* Patch documents using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be patched
|
||||
* @param criteria The object against which JSON containment should be checked
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def patchByContains[TContains, TPatch](tableName: String, criteria: TContains, patch: TPatch): Unit =
|
||||
Patch.byContains(tableName, criteria, patch, conn)
|
||||
|
||||
/**
|
||||
* Patch documents using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param tableName The name of the table in which documents should be patched
|
||||
* @param path The JSON path comparison to match
|
||||
* @param patch The object whose properties should be replaced in the document
|
||||
* @throws DocumentException If called on a SQLite connection
|
||||
*/
|
||||
def patchByJsonPath[TPatch](tableName: String, path: String, patch: TPatch): Unit =
|
||||
Patch.byJsonPath(tableName, path, patch, conn)
|
||||
|
||||
// ~~~ 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
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def removeFieldsById[TKey](tableName: String, docId: TKey, toRemove: Seq[String]): Unit =
|
||||
RemoveFields.byId(tableName, docId, toRemove, conn)
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def removeFieldsByFields(tableName: String, fields: Seq[Field[?]], toRemove: Seq[String],
|
||||
howMatched: Option[FieldMatch] = None): Unit =
|
||||
RemoveFields.byFields(tableName, fields, toRemove, howMatched, conn)
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
def removeFieldsByContains[TContains](tableName: String, criteria: TContains, toRemove: Seq[String]): Unit =
|
||||
RemoveFields.byContains(tableName, criteria, toRemove, conn)
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
def removeFieldsByJsonPath(tableName: String, path: String, toRemove: Seq[String]): Unit =
|
||||
RemoveFields.byJsonPath(tableName, path, toRemove, conn)
|
||||
|
||||
// ~~~ DOCUMENT DELETION QUERIES ~~~
|
||||
|
||||
/**
|
||||
* Delete a document by its ID
|
||||
*
|
||||
* @param tableName The name of the table from which documents should be deleted
|
||||
* @param docId The ID of the document to be deleted
|
||||
* @throws DocumentException If no dialect has been configured
|
||||
*/
|
||||
def deleteById[TKey](tableName: String, docId: TKey): Unit =
|
||||
Delete.byId(tableName, docId, conn)
|
||||
|
||||
/**
|
||||
* Delete documents using a field comparison
|
||||
*
|
||||
* @param tableName The name of the table from which documents should be deleted
|
||||
* @param fields The fields which should be compared
|
||||
* @param howMatched How the fields should be matched
|
||||
* @throws DocumentException If no dialect has been configured, or if parameters are invalid
|
||||
*/
|
||||
def deleteByFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Unit =
|
||||
Delete.byFields(tableName, fields, howMatched, conn)
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
def deleteByContains[TContains](tableName: String, criteria: TContains): Unit =
|
||||
Delete.byContains(tableName, criteria, conn)
|
||||
|
||||
/**
|
||||
* Delete documents using a JSON Path match 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
|
||||
*/
|
||||
def deleteByJsonPath(tableName: String, path: String): Unit =
|
||||
Delete.byJsonPath(tableName, path, conn)
|
||||
@@ -0,0 +1,126 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{AutoId, DocumentException}
|
||||
|
||||
@DisplayName("Scala | AutoId")
|
||||
class AutoIdTest:
|
||||
|
||||
@Test
|
||||
@DisplayName("Generates a UUID string")
|
||||
def generateUUID(): Unit =
|
||||
assertEquals(32, AutoId.generateUUID().length(), "The UUID should have been a 32-character string")
|
||||
|
||||
@Test
|
||||
@DisplayName("Generates a random hex character string of an even length")
|
||||
def generateRandomStringEven(): Unit =
|
||||
val result = AutoId.generateRandomString(8)
|
||||
assertEquals(8, result.length(), s"There should have been 8 characters in $result")
|
||||
|
||||
@Test
|
||||
@DisplayName("Generates a random hex character string of an odd length")
|
||||
def generateRandomStringOdd(): Unit =
|
||||
val result = AutoId.generateRandomString(11)
|
||||
assertEquals(11, result.length(), s"There should have been 11 characters in $result")
|
||||
|
||||
@Test
|
||||
@DisplayName("Generates different random hex character strings")
|
||||
def generateRandomStringIsRandom(): Unit =
|
||||
val result1 = AutoId.generateRandomString(16)
|
||||
val result2 = AutoId.generateRandomString(16)
|
||||
assertNotEquals(result1, result2, "There should have been 2 different strings generated")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId fails for null document")
|
||||
def needsAutoIdFailsForNullDocument(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => AutoId.needsAutoId(AutoId.DISABLED, null, "id"))
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId fails for missing ID property")
|
||||
def needsAutoIdFailsForMissingId(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => AutoId.needsAutoId(AutoId.UUID, IntIdClass(0), "Id"))
|
||||
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false if disabled")
|
||||
def needsAutoIdFalseIfDisabled(): Unit =
|
||||
assertFalse(AutoId.needsAutoId(AutoId.DISABLED, "", ""), "Disabled Auto ID should always return false")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for Number strategy and byte ID of 0")
|
||||
def needsAutoIdTrueForByteWithZero(): Unit =
|
||||
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(0), "id"), "Number Auto ID with 0 should return true")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for Number strategy and byte ID of non-0")
|
||||
def needsAutoIdFalseForByteWithNonZero(): Unit =
|
||||
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(77), "id"), "Number Auto ID with 77 should return false")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for Number strategy and short ID of 0")
|
||||
def needsAutoIdTrueForShortWithZero(): Unit =
|
||||
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(0), "id"), "Number Auto ID with 0 should return true")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for Number strategy and short ID of non-0")
|
||||
def needsAutoIdFalseForShortWithNonZero(): Unit =
|
||||
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(31), "id"), "Number Auto ID with 31 should return false")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for Number strategy and int ID of 0")
|
||||
def needsAutoIdTrueForIntWithZero(): Unit =
|
||||
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(0), "id"), "Number Auto ID with 0 should return true")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for Number strategy and int ID of non-0")
|
||||
def needsAutoIdFalseForIntWithNonZero(): Unit =
|
||||
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(6), "id"), "Number Auto ID with 6 should return false")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for Number strategy and long ID of 0")
|
||||
def needsAutoIdTrueForLongWithZero(): Unit =
|
||||
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(0), "id"), "Number Auto ID with 0 should return true")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for Number strategy and long ID of non-0")
|
||||
def needsAutoIdFalseForLongWithNonZero(): Unit =
|
||||
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(2), "id"), "Number Auto ID with 2 should return false")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId fails for Number strategy and non-number ID")
|
||||
def needsAutoIdFailsForNumberWithStringId(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => AutoId.needsAutoId(AutoId.NUMBER, StringIdClass(""), "id"))
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for UUID strategy and blank ID")
|
||||
def needsAutoIdTrueForUUIDWithBlank(): Unit =
|
||||
assertTrue(AutoId.needsAutoId(AutoId.UUID, StringIdClass(""), "id"), "UUID Auto ID with blank should return true")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for UUID strategy and non-blank ID")
|
||||
def needsAutoIdFalseForUUIDNotBlank(): Unit =
|
||||
assertFalse(AutoId.needsAutoId(AutoId.UUID, StringIdClass("howdy"), "id"),
|
||||
"UUID Auto ID with non-blank should return false")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId fails for UUID strategy and non-string ID")
|
||||
def needsAutoIdFailsForUUIDNonString(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => AutoId.needsAutoId(AutoId.UUID, IntIdClass(5), "id"))
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for Random String strategy and blank ID")
|
||||
def needsAutoIdTrueForRandomWithBlank(): Unit =
|
||||
assertTrue(AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass(""), "id"),
|
||||
"Random String Auto ID with blank should return true")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for Random String strategy and non-blank ID")
|
||||
def needsAutoIdFalseForRandomNotBlank(): Unit =
|
||||
assertFalse(AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass("full"), "id"),
|
||||
"Random String Auto ID with non-blank should return false")
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId fails for Random String strategy and non-string ID")
|
||||
def needsAutoIdFailsForRandomNonString(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id"))
|
||||
@@ -0,0 +1,3 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
class ByteIdClass(var id: Byte)
|
||||
@@ -0,0 +1,34 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{AutoId, Configuration, Dialect, DocumentException}
|
||||
|
||||
@DisplayName("Scala | Configuration")
|
||||
class ConfigurationTest:
|
||||
|
||||
@Test
|
||||
@DisplayName("Default ID field is `id`")
|
||||
def defaultIdField(): Unit =
|
||||
assertEquals("id", Configuration.idField, "Default ID field incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("Default Auto ID strategy is `DISABLED`")
|
||||
def defaultAutoId(): Unit =
|
||||
assertEquals(AutoId.DISABLED, Configuration.autoIdStrategy, "Default Auto ID strategy should be `disabled`")
|
||||
|
||||
@Test
|
||||
@DisplayName("Default ID string length should be 16")
|
||||
def defaultIdStringLength(): Unit =
|
||||
assertEquals(16, Configuration.idStringLength, "Default ID string length should be 16")
|
||||
|
||||
@Test
|
||||
@DisplayName("Dialect is derived from connection string")
|
||||
def dialectIsDerived(): Unit =
|
||||
try {
|
||||
assertThrows(classOf[DocumentException], () => Configuration.dialect())
|
||||
Configuration.setConnectionString("jdbc:postgresql:db")
|
||||
assertEquals(Dialect.POSTGRESQL, Configuration.dialect())
|
||||
} finally {
|
||||
Configuration.setConnectionString(null)
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{DocumentException, Field}
|
||||
import solutions.bitbadger.documents.query.CountQuery
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
@DisplayName("Scala | Query | CountQuery")
|
||||
class CountQueryTest:
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("all generates correctly")
|
||||
def all(): Unit =
|
||||
assertEquals(s"SELECT COUNT(*) AS it FROM $TEST_TABLE", CountQuery.all(TEST_TABLE),
|
||||
"Count query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | PostgreSQL")
|
||||
def byFieldsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0",
|
||||
CountQuery.byFields(TEST_TABLE, List(Field.equal("test", "", ":field0")).asJava),
|
||||
"Count query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | SQLite")
|
||||
def byFieldsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0",
|
||||
CountQuery.byFields(TEST_TABLE, List(Field.equal("test", "", ":field0")).asJava),
|
||||
"Count query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains generates correctly | PostgreSQL")
|
||||
def byContainsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data @> :criteria", CountQuery.byContains(TEST_TABLE),
|
||||
"Count query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails | SQLite")
|
||||
def byContainsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => CountQuery.byContains(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath generates correctly | PostgreSQL")
|
||||
def byJsonPathPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
|
||||
CountQuery.byJsonPath(TEST_TABLE), "Count query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails | SQLite")
|
||||
def byJsonPathSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => CountQuery.byJsonPath(TEST_TABLE))
|
||||
@@ -0,0 +1,104 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{Dialect, DocumentException, DocumentIndex}
|
||||
import solutions.bitbadger.documents.query.DefinitionQuery
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
@DisplayName("Scala | Query | DefinitionQuery")
|
||||
class DefinitionQueryTest:
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureTableFor generates correctly")
|
||||
def ensureTableFor(): Unit =
|
||||
assertEquals("CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)",
|
||||
DefinitionQuery.ensureTableFor("my.table", "JSONB"), "CREATE TABLE statement not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureTable generates correctly | PostgreSQL")
|
||||
def ensureTablePostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"CREATE TABLE IF NOT EXISTS $TEST_TABLE (data JSONB NOT NULL)",
|
||||
DefinitionQuery.ensureTable(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureTable generates correctly | SQLite")
|
||||
def ensureTableSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"CREATE TABLE IF NOT EXISTS $TEST_TABLE (data TEXT NOT NULL)",
|
||||
DefinitionQuery.ensureTable(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureTable fails when no dialect is set")
|
||||
def ensureTableFailsUnknown(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => DefinitionQuery.ensureTable(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureKey generates correctly with schema")
|
||||
def ensureKeyWithSchema(): Unit =
|
||||
assertEquals("CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'id'))",
|
||||
DefinitionQuery.ensureKey("test.table", Dialect.POSTGRESQL),
|
||||
"CREATE INDEX for key statement with schema not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureKey generates correctly without schema")
|
||||
def ensureKeyWithoutSchema(): Unit =
|
||||
assertEquals(s"CREATE UNIQUE INDEX IF NOT EXISTS idx_${TEST_TABLE}_key ON $TEST_TABLE ((data->>'id'))",
|
||||
DefinitionQuery.ensureKey(TEST_TABLE, Dialect.SQLITE),
|
||||
"CREATE INDEX for key statement without schema not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureIndexOn generates multiple fields and directions")
|
||||
def ensureIndexOnMultipleFields(): Unit =
|
||||
assertEquals("CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table " +
|
||||
"((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)",
|
||||
DefinitionQuery.ensureIndexOn("test.table", "gibberish", List("taco", "guac DESC", "salsa ASC").asJava,
|
||||
Dialect.POSTGRESQL),
|
||||
"CREATE INDEX for multiple field statement not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureIndexOn generates nested field | PostgreSQL")
|
||||
def ensureIndexOnNestedPostgres(): Unit =
|
||||
assertEquals(s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data#>>'{a,b,c}'))",
|
||||
DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", List("a.b.c").asJava, Dialect.POSTGRESQL),
|
||||
"CREATE INDEX for nested PostgreSQL field incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureIndexOn generates nested field | SQLite")
|
||||
def ensureIndexOnNestedSQLite(): Unit =
|
||||
assertEquals(s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data->'a'->'b'->>'c'))",
|
||||
DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", List("a.b.c").asJava, Dialect.SQLITE),
|
||||
"CREATE INDEX for nested SQLite field incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureDocumentIndexOn generates Full | PostgreSQL")
|
||||
def ensureDocumentIndexOnFullPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data)",
|
||||
DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL),
|
||||
"CREATE INDEX for full document index incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureDocumentIndexOn generates Optimized | PostgreSQL")
|
||||
def ensureDocumentIndexOnOptimizedPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(
|
||||
s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data jsonb_path_ops)",
|
||||
DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.OPTIMIZED),
|
||||
"CREATE INDEX for optimized document index incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureDocumentIndexOn fails | SQLite")
|
||||
def ensureDocumentIndexOnFailsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException],
|
||||
() => DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL))
|
||||
@@ -0,0 +1,74 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{DocumentException, Field}
|
||||
import solutions.bitbadger.documents.query.DeleteQuery
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
@DisplayName("Scala | Query | DeleteQuery")
|
||||
class DeleteQueryTest:
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates correctly | PostgreSQL")
|
||||
def byIdPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id", DeleteQuery.byId(TEST_TABLE),
|
||||
"Delete query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates correctly | SQLite")
|
||||
def byIdSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id", DeleteQuery.byId(TEST_TABLE),
|
||||
"Delete query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | PostgreSQL")
|
||||
def byFieldsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"DELETE FROM $TEST_TABLE WHERE data->>'a' = :b",
|
||||
DeleteQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b")).asJava),
|
||||
"Delete query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | SQLite")
|
||||
def byFieldsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"DELETE FROM $TEST_TABLE WHERE data->>'a' = :b",
|
||||
DeleteQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b")).asJava),
|
||||
"Delete query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains generates correctly | PostgreSQL")
|
||||
def byContainsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"DELETE FROM $TEST_TABLE WHERE data @> :criteria", DeleteQuery.byContains(TEST_TABLE),
|
||||
"Delete query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails | SQLite")
|
||||
def byContainsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => DeleteQuery.byContains(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath generates correctly | PostgreSQL")
|
||||
def byJsonPathPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"DELETE FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
|
||||
DeleteQuery.byJsonPath(TEST_TABLE), "Delete query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails | SQLite")
|
||||
def byJsonPathSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => DeleteQuery.byJsonPath(TEST_TABLE))
|
||||
@@ -0,0 +1,34 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{Dialect, DocumentException}
|
||||
|
||||
@DisplayName("Scala | Dialect")
|
||||
class DialectTest:
|
||||
|
||||
@Test
|
||||
@DisplayName("deriveFromConnectionString derives PostgreSQL correctly")
|
||||
def derivesPostgres(): Unit =
|
||||
assertEquals(Dialect.POSTGRESQL, Dialect.deriveFromConnectionString("jdbc:postgresql:db"),
|
||||
"Dialect should have been PostgreSQL")
|
||||
|
||||
@Test
|
||||
@DisplayName("deriveFromConnectionString derives SQLite correctly")
|
||||
def derivesSQLite(): Unit =
|
||||
assertEquals(Dialect.SQLITE, Dialect.deriveFromConnectionString("jdbc:sqlite:memory"),
|
||||
"Dialect should have been SQLite")
|
||||
|
||||
@Test
|
||||
@DisplayName("deriveFromConnectionString fails when the connection string is unknown")
|
||||
def deriveFailsWhenUnknown(): Unit =
|
||||
try {
|
||||
Dialect.deriveFromConnectionString("SQL Server")
|
||||
fail("Dialect derivation should have failed")
|
||||
} catch {
|
||||
case ex: DocumentException =>
|
||||
assertNotNull(ex.getMessage, "The exception message should not have been null")
|
||||
assertTrue(ex.getMessage.contains("[SQL Server]"),
|
||||
"The connection string should have been in the exception message")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import solutions.bitbadger.documents.DocumentIndex
|
||||
|
||||
@DisplayName("Scala | DocumentIndex")
|
||||
class DocumentIndexTest:
|
||||
|
||||
@Test
|
||||
@DisplayName("FULL uses proper SQL")
|
||||
def fullSQL(): Unit =
|
||||
assertEquals("", DocumentIndex.FULL.getSql, "The SQL for Full is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("OPTIMIZED uses proper SQL")
|
||||
def optimizedSQL(): Unit =
|
||||
assertEquals(" jsonb_path_ops", DocumentIndex.OPTIMIZED.getSql, "The SQL for Optimized is incorrect")
|
||||
@@ -0,0 +1,110 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{AutoId, Configuration, DocumentException}
|
||||
import solutions.bitbadger.documents.query.DocumentQuery
|
||||
|
||||
@DisplayName("Scala | Query | DocumentQuery")
|
||||
class DocumentQueryTest:
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("insert generates no auto ID | PostgreSQL")
|
||||
def insertNoAutoPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"INSERT INTO $TEST_TABLE VALUES (:data)", DocumentQuery.insert(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("insert generates no auto ID | SQLite")
|
||||
def insertNoAutoSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"INSERT INTO $TEST_TABLE VALUES (:data)", DocumentQuery.insert(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("insert generates auto number | PostgreSQL")
|
||||
def insertAutoNumberPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || ('{\"id\":' " +
|
||||
s"|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $TEST_TABLE) || '}')::jsonb)",
|
||||
DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER))
|
||||
|
||||
@Test
|
||||
@DisplayName("insert generates auto number | SQLite")
|
||||
def insertAutoNumberSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', " +
|
||||
s"(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $TEST_TABLE)))",
|
||||
DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER))
|
||||
|
||||
@Test
|
||||
@DisplayName("insert generates auto UUID | PostgreSQL")
|
||||
def insertAutoUUIDPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
|
||||
assertTrue(query.startsWith(s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\""),
|
||||
s"Query start not correct (actual: $query)")
|
||||
assertTrue(query.endsWith("\"}')"), "Query end not correct")
|
||||
|
||||
@Test
|
||||
@DisplayName("insert generates auto UUID | SQLite")
|
||||
def insertAutoUUIDSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
|
||||
assertTrue(query.startsWith(s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '"),
|
||||
s"Query start not correct (actual: $query)")
|
||||
assertTrue(query.endsWith("'))"), "Query end not correct")
|
||||
|
||||
@Test
|
||||
@DisplayName("insert generates auto random string | PostgreSQL")
|
||||
def insertAutoRandomPostgres(): Unit =
|
||||
try {
|
||||
ForceDialect.postgres()
|
||||
Configuration.idStringLength = 8
|
||||
val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
|
||||
assertTrue(query.startsWith(s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\""),
|
||||
s"Query start not correct (actual: $query)")
|
||||
assertTrue(query.endsWith("\"}')"), "Query end not correct")
|
||||
assertEquals(8, query.replace(s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\"", "")
|
||||
.replace("\"}')", "").length,
|
||||
"Random string length incorrect")
|
||||
} finally {
|
||||
Configuration.idStringLength = 16
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("insert generates auto random string | SQLite")
|
||||
def insertAutoRandomSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
|
||||
assertTrue(query.startsWith(s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '"),
|
||||
s"Query start not correct (actual: $query)")
|
||||
assertTrue(query.endsWith("'))"), "Query end not correct")
|
||||
assertEquals(Configuration.idStringLength,
|
||||
query.replace(s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '", "").replace("'))", "").length,
|
||||
"Random string length incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("insert fails when no dialect is set")
|
||||
def insertFailsUnknown(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => DocumentQuery.insert(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("save generates correctly")
|
||||
def save(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(
|
||||
s"INSERT INTO $TEST_TABLE VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data",
|
||||
DocumentQuery.save(TEST_TABLE), "INSERT ON CONFLICT UPDATE statement not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("update generates successfully")
|
||||
def update(): Unit =
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = :data", DocumentQuery.update(TEST_TABLE),
|
||||
"Update query not constructed correctly")
|
||||
@@ -0,0 +1,74 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{DocumentException, Field}
|
||||
import solutions.bitbadger.documents.query.ExistsQuery
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
@DisplayName("Scala | Query | ExistsQuery")
|
||||
class ExistsQueryTest:
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates correctly | PostgreSQL")
|
||||
def byIdPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it",
|
||||
ExistsQuery.byId(TEST_TABLE), "Exists query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates correctly | SQLite")
|
||||
def byIdSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it",
|
||||
ExistsQuery.byId(TEST_TABLE), "Exists query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | PostgreSQL")
|
||||
def byFieldsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE (data->>'it')::numeric = :test) AS it",
|
||||
ExistsQuery.byFields(TEST_TABLE, List(Field.equal("it", 7, ":test")).asJava),
|
||||
"Exists query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | SQLite")
|
||||
def byFieldsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'it' = :test) AS it",
|
||||
ExistsQuery.byFields(TEST_TABLE, List(Field.equal("it", 7, ":test")).asJava),
|
||||
"Exists query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains generates correctly | PostgreSQL")
|
||||
def byContainsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data @> :criteria) AS it",
|
||||
ExistsQuery.byContains(TEST_TABLE), "Exists query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails | SQLite")
|
||||
def byContainsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => ExistsQuery.byContains(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath generates correctly | PostgreSQL")
|
||||
def byJsonPathPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)) AS it",
|
||||
ExistsQuery.byJsonPath(TEST_TABLE), "Exists query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails | SQLite")
|
||||
def byJsonPathSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => ExistsQuery.byJsonPath(TEST_TABLE))
|
||||
@@ -0,0 +1,21 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import solutions.bitbadger.documents.FieldMatch
|
||||
|
||||
/**
|
||||
* Unit tests for the `FieldMatch` enum
|
||||
*/
|
||||
@DisplayName("Scala | FieldMatch")
|
||||
class FieldMatchTest:
|
||||
|
||||
@Test
|
||||
@DisplayName("ANY uses proper SQL")
|
||||
def any(): Unit =
|
||||
assertEquals("OR", FieldMatch.ANY.getSql, "ANY should use OR")
|
||||
|
||||
@Test
|
||||
@DisplayName("ALL uses proper SQL")
|
||||
def all(): Unit =
|
||||
assertEquals("AND", FieldMatch.ALL.getSql, "ALL should use AND")
|
||||
@@ -0,0 +1,537 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.*
|
||||
|
||||
import _root_.scala.jdk.CollectionConverters.*
|
||||
|
||||
@DisplayName("Scala | Field")
|
||||
class FieldTest:
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
// ~~~ INSTANCE METHODS ~~~
|
||||
|
||||
@Test
|
||||
@DisplayName("withParameterName fails for invalid name")
|
||||
def withParamNameFails(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => Field.equal("it", "").withParameterName("2424"))
|
||||
|
||||
@Test
|
||||
@DisplayName("withParameterName works with colon prefix")
|
||||
def withParamNameColon(): Unit =
|
||||
val field = Field.equal("abc", "22").withQualifier("me")
|
||||
val withParam = field.withParameterName(":test")
|
||||
assertNotSame(field, withParam, "A new Field instance should have been created")
|
||||
assertEquals(field.getName, withParam.getName, "Name should have been preserved")
|
||||
assertEquals(field.getComparison, withParam.getComparison, "Comparison should have been preserved")
|
||||
assertEquals(":test", withParam.getParameterName, "Parameter name not set correctly")
|
||||
assertEquals(field.getQualifier, withParam.getQualifier, "Qualifier should have been preserved")
|
||||
|
||||
@Test
|
||||
@DisplayName("withParameterName works with at-sign prefix")
|
||||
def withParamNameAtSign(): Unit =
|
||||
val field = Field.equal("def", "44")
|
||||
val withParam = field.withParameterName("@unit")
|
||||
assertNotSame(field, withParam, "A new Field instance should have been created")
|
||||
assertEquals(field.getName, withParam.getName, "Name should have been preserved")
|
||||
assertEquals(field.getComparison, withParam.getComparison, "Comparison should have been preserved")
|
||||
assertEquals("@unit", withParam.getParameterName, "Parameter name not set correctly")
|
||||
assertEquals(field.getQualifier, withParam.getQualifier, "Qualifier should have been preserved")
|
||||
|
||||
@Test
|
||||
@DisplayName("withQualifier sets qualifier correctly")
|
||||
def withQualifier(): Unit =
|
||||
val field = Field.equal("j", "k")
|
||||
val withQual = field.withQualifier("test")
|
||||
assertNotSame(field, withQual, "A new Field instance should have been created")
|
||||
assertEquals(field.getName, withQual.getName, "Name should have been preserved")
|
||||
assertEquals(field.getComparison, withQual.getComparison, "Comparison should have been preserved")
|
||||
assertEquals(field.getParameterName, withQual.getParameterName, "Parameter Name should have been preserved")
|
||||
assertEquals("test", withQual.getQualifier, "Qualifier not set correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("path generates for simple unqualified PostgreSQL field")
|
||||
def pathPostgresSimpleUnqualified(): Unit =
|
||||
assertEquals("data->>'SomethingCool'",
|
||||
Field.greaterOrEqual("SomethingCool", 18).path(Dialect.POSTGRESQL, FieldFormat.SQL), "Path not correct")
|
||||
|
||||
@Test
|
||||
@DisplayName("path generates for simple qualified PostgreSQL field")
|
||||
def pathPostgresSimpleQualified(): Unit =
|
||||
assertEquals("this.data->>'SomethingElse'",
|
||||
Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.POSTGRESQL, FieldFormat.SQL),
|
||||
"Path not correct")
|
||||
|
||||
@Test
|
||||
@DisplayName("path generates for nested unqualified PostgreSQL field")
|
||||
def pathPostgresNestedUnqualified(): Unit =
|
||||
assertEquals("data#>>'{My,Nested,Field}'",
|
||||
Field.equal("My.Nested.Field", "howdy").path(Dialect.POSTGRESQL, FieldFormat.SQL), "Path not correct")
|
||||
|
||||
@Test
|
||||
@DisplayName("path generates for nested qualified PostgreSQL field")
|
||||
def pathPostgresNestedQualified(): Unit =
|
||||
assertEquals("bird.data#>>'{Nest,Away}'",
|
||||
Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.POSTGRESQL, FieldFormat.SQL),
|
||||
"Path not correct")
|
||||
|
||||
@Test
|
||||
@DisplayName("path generates for simple unqualified SQLite field")
|
||||
def pathSQLiteSimpleUnqualified(): Unit =
|
||||
assertEquals("data->>'SomethingCool'",
|
||||
Field.greaterOrEqual("SomethingCool", 18).path(Dialect.SQLITE, FieldFormat.SQL), "Path not correct")
|
||||
|
||||
@Test
|
||||
@DisplayName("path generates for simple qualified SQLite field")
|
||||
def pathSQLiteSimpleQualified(): Unit =
|
||||
assertEquals("this.data->>'SomethingElse'",
|
||||
Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.SQLITE, FieldFormat.SQL),
|
||||
"Path not correct")
|
||||
|
||||
@Test
|
||||
@DisplayName("path generates for nested unqualified SQLite field")
|
||||
def pathSQLiteNestedUnqualified(): Unit =
|
||||
assertEquals("data->'My'->'Nested'->>'Field'",
|
||||
Field.equal("My.Nested.Field", "howdy").path(Dialect.SQLITE, FieldFormat.SQL), "Path not correct")
|
||||
|
||||
@Test
|
||||
@DisplayName("path generates for nested qualified SQLite field")
|
||||
def pathSQLiteNestedQualified(): Unit =
|
||||
assertEquals("bird.data->'Nest'->>'Away'",
|
||||
Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.SQLITE, FieldFormat.SQL),
|
||||
"Path not correct")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for exists w/o qualifier | PostgreSQL")
|
||||
def toWhereExistsNoQualPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->>'that_field' IS NOT NULL", Field.exists("that_field").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for exists w/o qualifier | SQLite")
|
||||
def toWhereExistsNoQualSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("data->>'that_field' IS NOT NULL", Field.exists("that_field").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for not-exists w/o qualifier | PostgreSQL")
|
||||
def toWhereNotExistsNoQualPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->>'a_field' IS NULL", Field.notExists("a_field").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for not-exists w/o qualifier | SQLite")
|
||||
def toWhereNotExistsNoQualSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("data->>'a_field' IS NULL", Field.notExists("a_field").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for BETWEEN w/o qualifier, numeric range | PostgreSQL")
|
||||
def toWhereBetweenNoQualNumericPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("(data->>'age')::numeric BETWEEN @agemin AND @agemax",
|
||||
Field.between("age", 13, 17, "@age").toWhere, "Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for BETWEEN w/o qualifier, alphanumeric range | PostgreSQL")
|
||||
def toWhereBetweenNoQualAlphaPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->>'city' BETWEEN :citymin AND :citymax",
|
||||
Field.between("city", "Atlanta", "Chicago", ":city").toWhere, "Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for BETWEEN w/o qualifier | SQLite")
|
||||
def toWhereBetweenNoQualSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("data->>'age' BETWEEN @agemin AND @agemax", Field.between("age", 13, 17, "@age").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for BETWEEN w/ qualifier, numeric range | PostgreSQL")
|
||||
def toWhereBetweenQualNumericPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("(test.data->>'age')::numeric BETWEEN @agemin AND @agemax",
|
||||
Field.between("age", 13, 17, "@age").withQualifier("test").toWhere, "Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for BETWEEN w/ qualifier, alphanumeric range | PostgreSQL")
|
||||
def toWhereBetweenQualAlphaPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("unit.data->>'city' BETWEEN :citymin AND :citymax",
|
||||
Field.between("city", "Atlanta", "Chicago", ":city").withQualifier("unit").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for BETWEEN w/ qualifier | SQLite")
|
||||
def toWhereBetweenQualSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("my.data->>'age' BETWEEN @agemin AND @agemax",
|
||||
Field.between("age", 13, 17, "@age").withQualifier("my").toWhere, "Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for IN/any, numeric values | PostgreSQL")
|
||||
def toWhereAnyNumericPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)",
|
||||
Field.any("even", List(2, 4, 6).asJava, ":nbr").toWhere, "Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for IN/any, alphanumeric values | PostgreSQL")
|
||||
def toWhereAnyAlphaPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->>'test' IN (:city_0, :city_1)",
|
||||
Field.any("test", List("Atlanta", "Chicago").asJava, ":city").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for IN/any | SQLite")
|
||||
def toWhereAnySQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("data->>'test' IN (:city_0, :city_1)",
|
||||
Field.any("test", List("Atlanta", "Chicago").asJava, ":city").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for inArray | PostgreSQL")
|
||||
def toWhereInArrayPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]",
|
||||
Field.inArray("even", "tbl", List(2, 4, 6, 8).asJava, ":it").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for inArray | SQLite")
|
||||
def toWhereInArraySQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("EXISTS (SELECT 1 FROM json_each(tbl.data, '$.test') WHERE value IN (:city_0, :city_1))",
|
||||
Field.inArray("test", "tbl", List("Atlanta", "Chicago").asJava, ":city").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for others w/o qualifier | PostgreSQL")
|
||||
def toWhereOtherNoQualPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->>'some_field' = :value", Field.equal("some_field", "", ":value").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates for others w/o qualifier | SQLite")
|
||||
def toWhereOtherNoQualSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("data->>'some_field' = :value", Field.equal("some_field", "", ":value").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates no-parameter w/ qualifier | PostgreSQL")
|
||||
def toWhereNoParamWithQualPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("test.data->>'no_field' IS NOT NULL", Field.exists("no_field").withQualifier("test").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates no-parameter w/ qualifier | SQLite")
|
||||
def toWhereNoParamWithQualSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("test.data->>'no_field' IS NOT NULL", Field.exists("no_field").withQualifier("test").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates parameter w/ qualifier | PostgreSQL")
|
||||
def toWhereParamWithQualPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("(q.data->>'le_field')::numeric <= :it",
|
||||
Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere, "Field WHERE clause not generated correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("toWhere generates parameter w/ qualifier | SQLite")
|
||||
def toWhereParamWithQualSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("q.data->>'le_field' <= :it",
|
||||
Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere,
|
||||
"Field WHERE clause not generated correctly")
|
||||
|
||||
// ~~~ STATIC CONSTRUCTOR TESTS ~~~
|
||||
|
||||
@Test
|
||||
@DisplayName("equal constructs a field w/o parameter name")
|
||||
def equalCtor(): Unit =
|
||||
val field = Field.equal("Test", 14)
|
||||
assertEquals("Test", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals(14, field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("equal constructs a field w/ parameter name")
|
||||
def equalParameterCtor(): Unit =
|
||||
val field = Field.equal("Test", 14, ":w")
|
||||
assertEquals("Test", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals(14, field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertEquals(":w", field.getParameterName, "Field parameter name not filled correctly")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("greater constructs a field w/o parameter name")
|
||||
def greaterCtor(): Unit =
|
||||
val field = Field.greater("Great", "night")
|
||||
assertEquals("Great", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.GREATER, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("night", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("greater constructs a field w/ parameter name")
|
||||
def greaterParameterCtor(): Unit =
|
||||
val field = Field.greater("Great", "night", ":yeah")
|
||||
assertEquals("Great", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.GREATER, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("night", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertEquals(":yeah", field.getParameterName, "Field parameter name not filled correctly")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("greaterOrEqual constructs a field w/o parameter name")
|
||||
def greaterOrEqualCtor(): Unit =
|
||||
val field = Field.greaterOrEqual("Nice", 88L)
|
||||
assertEquals("Nice", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.GREATER_OR_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals(88L, field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("greaterOrEqual constructs a field w/ parameter name")
|
||||
def greaterOrEqualParameterCtor(): Unit =
|
||||
val field = Field.greaterOrEqual("Nice", 88L, ":nice")
|
||||
assertEquals("Nice", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.GREATER_OR_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals(88L, field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertEquals(":nice", field.getParameterName, "Field parameter name not filled correctly")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("less constructs a field w/o parameter name")
|
||||
def lessCtor(): Unit =
|
||||
val field = Field.less("Lesser", "seven")
|
||||
assertEquals("Lesser", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.LESS, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("seven", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("less constructs a field w/ parameter name")
|
||||
def lessParameterCtor(): Unit =
|
||||
val field = Field.less("Lesser", "seven", ":max")
|
||||
assertEquals("Lesser", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.LESS, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("seven", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertEquals(":max", field.getParameterName, "Field parameter name not filled correctly")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("lessOrEqual constructs a field w/o parameter name")
|
||||
def lessOrEqualCtor(): Unit =
|
||||
val field = Field.lessOrEqual("Nobody", "KNOWS")
|
||||
assertEquals("Nobody", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.LESS_OR_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("KNOWS", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("lessOrEqual constructs a field w/ parameter name")
|
||||
def lessOrEqualParameterCtor(): Unit =
|
||||
val field = Field.lessOrEqual("Nobody", "KNOWS", ":nope")
|
||||
assertEquals("Nobody", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.LESS_OR_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("KNOWS", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertEquals(":nope", field.getParameterName, "Field parameter name not filled correctly")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("notEqual constructs a field w/o parameter name")
|
||||
def notEqualCtor(): Unit =
|
||||
val field = Field.notEqual("Park", "here")
|
||||
assertEquals("Park", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.NOT_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("here", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("notEqual constructs a field w/ parameter name")
|
||||
def notEqualParameterCtor(): Unit =
|
||||
val field = Field.notEqual("Park", "here", ":now")
|
||||
assertEquals("Park", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.NOT_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("here", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertEquals(":now", field.getParameterName, "Field parameter name not filled correctly")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("between constructs a field w/o parameter name")
|
||||
def betweenCtor(): Unit =
|
||||
val field = Field.between("Age", 18, 49)
|
||||
assertEquals("Age", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.BETWEEN, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals(18, field.getComparison.getValue.getFirst, "Field comparison min value not filled correctly")
|
||||
assertEquals(49, field.getComparison.getValue.getSecond, "Field comparison max value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("between constructs a field w/ parameter name")
|
||||
def betweenParameterCtor(): Unit =
|
||||
val field = Field.between("Age", 18, 49, ":limit")
|
||||
assertEquals("Age", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.BETWEEN, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals(18, field.getComparison.getValue.getFirst, "Field comparison min value not filled correctly")
|
||||
assertEquals(49, field.getComparison.getValue.getSecond, "Field comparison max value not filled correctly")
|
||||
assertEquals(":limit", field.getParameterName, "Field parameter name not filled correctly")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("any constructs a field w/o parameter name")
|
||||
def anyCtor(): Unit =
|
||||
val field = Field.any("Here", List(8, 16, 32).asJava)
|
||||
assertEquals("Here", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.IN, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals(List(8, 16, 32).asJava, field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("any constructs a field w/ parameter name")
|
||||
def anyParameterCtor(): Unit =
|
||||
val field = Field.any("Here", List(8, 16, 32).asJava, ":list")
|
||||
assertEquals("Here", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.IN, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals(List(8, 16, 32).asJava, field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertEquals(":list", field.getParameterName, "Field parameter name not filled correctly")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("inArray constructs a field w/o parameter name")
|
||||
def inArrayCtor(): Unit =
|
||||
val field = Field.inArray("ArrayField", "table", List("z").asJava)
|
||||
assertEquals("ArrayField", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.IN_ARRAY, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("table", field.getComparison.getValue.getFirst, "Field comparison table not filled correctly")
|
||||
assertEquals(List("z").asJava, field.getComparison.getValue.getSecond,
|
||||
"Field comparison values not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("inArray constructs a field w/ parameter name")
|
||||
def inArrayParameterCtor(): Unit =
|
||||
val field = Field.inArray("ArrayField", "table", List("z").asJava, ":a")
|
||||
assertEquals("ArrayField", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.IN_ARRAY, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("table", field.getComparison.getValue.getFirst, "Field comparison table not filled correctly")
|
||||
assertEquals(List("z").asJava, field.getComparison.getValue.getSecond,
|
||||
"Field comparison values not filled correctly")
|
||||
assertEquals(":a", field.getParameterName, "Field parameter name not filled correctly")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("exists constructs a field")
|
||||
def existsCtor(): Unit =
|
||||
val field = Field.exists("Groovy")
|
||||
assertEquals("Groovy", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.EXISTS, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("notExists constructs a field")
|
||||
def notExistsCtor(): Unit =
|
||||
val field = Field.notExists("Groovy")
|
||||
assertEquals("Groovy", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.NOT_EXISTS, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("named constructs a field")
|
||||
def namedCtor(): Unit =
|
||||
val field = Field.named("Tacos")
|
||||
assertEquals("Tacos", field.getName, "Field name not filled correctly")
|
||||
assertEquals(Op.EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
|
||||
assertEquals("", field.getComparison.getValue, "Field comparison value not filled correctly")
|
||||
assertNull(field.getParameterName, "The parameter name should have been null")
|
||||
assertNull(field.getQualifier, "The qualifier should have been null")
|
||||
|
||||
@Test
|
||||
@DisplayName("static constructors fail for invalid parameter name")
|
||||
def staticCtorsFailOnParamName(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => Field.equal("a", "b", "that ain't it, Jack..."))
|
||||
|
||||
@Test
|
||||
@DisplayName("nameToPath creates a simple PostgreSQL SQL name")
|
||||
def nameToPathPostgresSimpleSQL(): Unit =
|
||||
assertEquals("data->>'Simple'", Field.nameToPath("Simple", Dialect.POSTGRESQL, FieldFormat.SQL),
|
||||
"Path not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("nameToPath creates a simple SQLite SQL name")
|
||||
def nameToPathSQLiteSimpleSQL(): Unit =
|
||||
assertEquals("data->>'Simple'", Field.nameToPath("Simple", Dialect.SQLITE, FieldFormat.SQL),
|
||||
"Path not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("nameToPath creates a nested PostgreSQL SQL name")
|
||||
def nameToPathPostgresNestedSQL(): Unit =
|
||||
assertEquals("data#>>'{A,Long,Path,to,the,Property}'",
|
||||
Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.SQL),
|
||||
"Path not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("nameToPath creates a nested SQLite SQL name")
|
||||
def nameToPathSQLiteNestedSQL(): Unit =
|
||||
assertEquals("data->'A'->'Long'->'Path'->'to'->'the'->>'Property'",
|
||||
Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.SQL),
|
||||
"Path not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("nameToPath creates a simple PostgreSQL JSON name")
|
||||
def nameToPathPostgresSimpleJSON(): Unit =
|
||||
assertEquals("data->'Simple'", Field.nameToPath("Simple", Dialect.POSTGRESQL, FieldFormat.JSON),
|
||||
"Path not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("nameToPath creates a simple SQLite JSON name")
|
||||
def nameToPathSQLiteSimpleJSON(): Unit =
|
||||
assertEquals("data->'Simple'", Field.nameToPath("Simple", Dialect.SQLITE, FieldFormat.JSON),
|
||||
"Path not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("nameToPath creates a nested PostgreSQL JSON name")
|
||||
def nameToPathPostgresNestedJSON(): Unit =
|
||||
assertEquals("data#>'{A,Long,Path,to,the,Property}'",
|
||||
Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.JSON),
|
||||
"Path not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("nameToPath creates a nested SQLite JSON name")
|
||||
def nameToPathSQLiteNestedJSON(): Unit =
|
||||
assertEquals("data->'A'->'Long'->'Path'->'to'->'the'->'Property'",
|
||||
Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.JSON),
|
||||
"Path not constructed correctly")
|
||||
@@ -0,0 +1,79 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{DocumentException, Field}
|
||||
import solutions.bitbadger.documents.query.FindQuery
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
@DisplayName("Scala | Query | FindQuery")
|
||||
class FindQueryTest:
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("all generates correctly")
|
||||
def all(): Unit =
|
||||
assertEquals(s"SELECT data FROM $TEST_TABLE", FindQuery.all(TEST_TABLE), "Find query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates correctly | PostgreSQL")
|
||||
def byIdPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id", FindQuery.byId(TEST_TABLE),
|
||||
"Find query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates correctly | SQLite")
|
||||
def byIdSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id", FindQuery.byId(TEST_TABLE),
|
||||
"Find query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | PostgreSQL")
|
||||
def byFieldsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND (data->>'c')::numeric < :d",
|
||||
FindQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b"), Field.less("c", 14, ":d")).asJava),
|
||||
"Find query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | SQLite")
|
||||
def byFieldsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND data->>'c' < :d",
|
||||
FindQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b"), Field.less("c", 14, ":d")).asJava),
|
||||
"Find query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains generates correctly | PostgreSQL")
|
||||
def byContainsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT data FROM $TEST_TABLE WHERE data @> :criteria", FindQuery.byContains(TEST_TABLE),
|
||||
"Find query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails | SQLite")
|
||||
def byContainsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => FindQuery.byContains(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath generates correctly | PostgreSQL")
|
||||
def byJsonPathPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"SELECT data FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
|
||||
FindQuery.byJsonPath(TEST_TABLE), "Find query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails | SQLite")
|
||||
def byJsonPathSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => FindQuery.byJsonPath(TEST_TABLE))
|
||||
@@ -0,0 +1,17 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
|
||||
/**
|
||||
* These functions use a dummy connection string to force the given dialect for a given test
|
||||
*/
|
||||
object ForceDialect:
|
||||
|
||||
def postgres (): Unit =
|
||||
Configuration.setConnectionString(":postgresql:")
|
||||
|
||||
def sqlite (): Unit =
|
||||
Configuration.setConnectionString(":sqlite:")
|
||||
|
||||
def none (): Unit =
|
||||
Configuration.setConnectionString(null)
|
||||
@@ -0,0 +1,3 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
class IntIdClass(var id: Int)
|
||||
@@ -0,0 +1,3 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
class LongIdClass(var id: Long)
|
||||
@@ -0,0 +1,63 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import solutions.bitbadger.documents.Op
|
||||
|
||||
@DisplayName("Scala | Op")
|
||||
class OpTest:
|
||||
|
||||
@Test
|
||||
@DisplayName("EQUAL uses proper SQL")
|
||||
def equalSQL(): Unit =
|
||||
assertEquals("=", Op.EQUAL.getSql, "The SQL for equal is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("GREATER uses proper SQL")
|
||||
def greaterSQL(): Unit =
|
||||
assertEquals(">", Op.GREATER.getSql, "The SQL for greater is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("GREATER_OR_EQUAL uses proper SQL")
|
||||
def greaterOrEqualSQL(): Unit =
|
||||
assertEquals(">=", Op.GREATER_OR_EQUAL.getSql, "The SQL for greater-or-equal is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("LESS uses proper SQL")
|
||||
def lessSQL(): Unit =
|
||||
assertEquals("<", Op.LESS.getSql, "The SQL for less is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("LESS_OR_EQUAL uses proper SQL")
|
||||
def lessOrEqualSQL(): Unit =
|
||||
assertEquals("<=", Op.LESS_OR_EQUAL.getSql, "The SQL for less-or-equal is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("NOT_EQUAL uses proper SQL")
|
||||
def notEqualSQL(): Unit =
|
||||
assertEquals("<>", Op.NOT_EQUAL.getSql, "The SQL for not-equal is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("BETWEEN uses proper SQL")
|
||||
def betweenSQL(): Unit =
|
||||
assertEquals("BETWEEN", Op.BETWEEN.getSql, "The SQL for between is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("IN uses proper SQL")
|
||||
def inSQL(): Unit =
|
||||
assertEquals("IN", Op.IN.getSql, "The SQL for in is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("IN_ARRAY uses proper SQL")
|
||||
def inArraySQL(): Unit =
|
||||
assertEquals("??|", Op.IN_ARRAY.getSql, "The SQL for in-array is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("EXISTS uses proper SQL")
|
||||
def existsSQL(): Unit =
|
||||
assertEquals("IS NOT NULL", Op.EXISTS.getSql, "The SQL for exists is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("NOT_EXISTS uses proper SQL")
|
||||
def notExistsSQL(): Unit =
|
||||
assertEquals("IS NULL", Op.NOT_EXISTS.getSql, "The SQL for not-exists is incorrect")
|
||||
@@ -0,0 +1,24 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import solutions.bitbadger.documents.ParameterName
|
||||
|
||||
@DisplayName("Scala | ParameterName")
|
||||
class ParameterNameTest:
|
||||
|
||||
@Test
|
||||
@DisplayName("derive works when given existing names")
|
||||
def withExisting(): Unit =
|
||||
val names = ParameterName()
|
||||
assertEquals(":taco", names.derive(":taco"), "Name should have been :taco")
|
||||
assertEquals(":field0", names.derive(null), "Counter should not have advanced for named field")
|
||||
|
||||
@Test
|
||||
@DisplayName("derive works when given all anonymous fields")
|
||||
def allAnonymous(): Unit =
|
||||
val names = ParameterName()
|
||||
assertEquals(":field0", names.derive(null), "Anonymous field name should have been returned")
|
||||
assertEquals(":field1", names.derive(null), "Counter should have advanced from previous call")
|
||||
assertEquals(":field2", names.derive(null), "Counter should have advanced from previous call")
|
||||
assertEquals(":field3", names.derive(null), "Counter should have advanced from previous call")
|
||||
@@ -0,0 +1,29 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{DocumentException, Parameter, ParameterType}
|
||||
|
||||
@DisplayName("Scala | Parameter")
|
||||
class ParameterTest:
|
||||
|
||||
@Test
|
||||
@DisplayName("Construction with colon-prefixed name")
|
||||
def ctorWithColon(): Unit =
|
||||
val p = Parameter(":test", ParameterType.STRING, "ABC")
|
||||
assertEquals(":test", p.getName, "Parameter name was incorrect")
|
||||
assertEquals(ParameterType.STRING, p.getType, "Parameter type was incorrect")
|
||||
assertEquals("ABC", p.getValue, "Parameter value was incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("Construction with at-sign-prefixed name")
|
||||
def ctorWithAtSign(): Unit =
|
||||
val p = Parameter("@yo", ParameterType.NUMBER, null)
|
||||
assertEquals("@yo", p.getName, "Parameter name was incorrect")
|
||||
assertEquals(ParameterType.NUMBER, p.getType, "Parameter type was incorrect")
|
||||
assertNull(p.getValue, "Parameter value was incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("Construction fails with incorrect prefix")
|
||||
def ctorFailsForPrefix(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => Parameter("it", ParameterType.JSON, ""))
|
||||
@@ -0,0 +1,100 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{DocumentException, Field, Parameter, ParameterType}
|
||||
import solutions.bitbadger.documents.scala.Parameters
|
||||
|
||||
@DisplayName("Scala | Parameters")
|
||||
class ParametersTest:
|
||||
|
||||
/**
|
||||
* Reset the dialect
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("nameFields works with no changes")
|
||||
def nameFieldsNoChange(): Unit =
|
||||
val fields = Field.equal("a", "", ":test") :: Field.exists("q") :: Field.equal("b", "", ":me") :: Nil
|
||||
val named = Parameters.nameFields(fields).toList
|
||||
assertEquals(fields.size, named.size, "There should have been 3 fields in the list")
|
||||
assertSame(fields.head, named.head, "The first field should be the same")
|
||||
assertSame(fields(1), named(1), "The second field should be the same")
|
||||
assertSame(fields(2), named(2), "The third field should be the same")
|
||||
|
||||
@Test
|
||||
@DisplayName("nameFields works when changing fields")
|
||||
def nameFieldsChange(): Unit =
|
||||
val fields = Field.equal("a", "") :: Field.equal("e", "", ":hi") :: Field.equal("b", "") ::
|
||||
Field.notExists("z") :: Nil
|
||||
val named = Parameters.nameFields(fields).toList
|
||||
assertEquals(fields.size, named.size, "There should have been 4 fields in the list")
|
||||
assertNotSame(fields.head, named.head, "The first field should not be the same")
|
||||
assertEquals(":field0", named.head.getParameterName, "First parameter name incorrect")
|
||||
assertSame(fields(1), named(1), "The second field should be the same")
|
||||
assertNotSame(fields(2), named(2), "The third field should not be the same")
|
||||
assertEquals(":field1", named(2).getParameterName, "Third parameter name incorrect")
|
||||
assertSame(fields(3), named(3), "The fourth field should be the same")
|
||||
|
||||
@Test
|
||||
@DisplayName("replaceNamesInQuery replaces successfully")
|
||||
def replaceNamesInQuery(): Unit =
|
||||
val parameters = Parameter(":data", ParameterType.JSON, "{}") ::
|
||||
Parameter(":data_ext", ParameterType.STRING, "") :: Nil
|
||||
val query = "SELECT data, data_ext FROM tbl WHERE data = :data AND data_ext = :data_ext AND more_data = :data"
|
||||
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)")
|
||||
def fieldNamesSinglePostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
val nameParams = Parameters.fieldNames("test" :: Nil).toList
|
||||
assertEquals(1, nameParams.size, "There should be one name parameter")
|
||||
assertEquals(":name", nameParams.head.getName, "The parameter name is incorrect")
|
||||
assertEquals(ParameterType.STRING, nameParams.head.getType, "The parameter type is incorrect")
|
||||
assertEquals("{test}", nameParams.head.getValue, "The parameter value is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("fieldNames generates multiple parameters (PostgreSQL)")
|
||||
def fieldNamesMultiplePostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
val nameParams = Parameters.fieldNames("test" :: "this" :: "today" :: Nil).toList
|
||||
assertEquals(1, nameParams.size, "There should be one name parameter")
|
||||
assertEquals(":name", nameParams.head.getName, "The parameter name is incorrect")
|
||||
assertEquals(ParameterType.STRING, nameParams.head.getType, "The parameter type is incorrect")
|
||||
assertEquals("{test,this,today}", nameParams.head.getValue, "The parameter value is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("fieldNames generates a single parameter (SQLite)")
|
||||
def fieldNamesSingleSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
val nameParams = Parameters.fieldNames("test" :: Nil).toList
|
||||
assertEquals(1, nameParams.size, "There should be one name parameter")
|
||||
assertEquals(":name0", nameParams.head.getName, "The parameter name is incorrect")
|
||||
assertEquals(ParameterType.STRING, nameParams.head.getType, "The parameter type is incorrect")
|
||||
assertEquals("test", nameParams.head.getValue, "The parameter value is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("fieldNames generates multiple parameters (SQLite)")
|
||||
def fieldNamesMultipleSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
val nameParams = Parameters.fieldNames("test" :: "this" :: "today" :: Nil).toList
|
||||
assertEquals(3, nameParams.size, "There should be one name parameter")
|
||||
assertEquals(":name0", nameParams.head.getName, "The first parameter name is incorrect")
|
||||
assertEquals(ParameterType.STRING, nameParams.head.getType, "The first parameter type is incorrect")
|
||||
assertEquals("test", nameParams.head.getValue, "The first parameter value is incorrect")
|
||||
assertEquals(":name1", nameParams(1).getName, "The second parameter name is incorrect")
|
||||
assertEquals(ParameterType.STRING, nameParams(1).getType, "The second parameter type is incorrect")
|
||||
assertEquals("this", nameParams(1).getValue, "The second parameter value is incorrect")
|
||||
assertEquals(":name2", nameParams(2).getName, "The third parameter name is incorrect")
|
||||
assertEquals(ParameterType.STRING, nameParams(2).getType, "The third parameter type is incorrect")
|
||||
assertEquals("today", nameParams(2).getValue, "The third parameter value is incorrect")
|
||||
|
||||
@Test
|
||||
@DisplayName("fieldNames fails if dialect not set")
|
||||
def fieldNamesFails(): Unit =
|
||||
assertThrows(classOf[DocumentException], () => Parameters.fieldNames(List()))
|
||||
@@ -0,0 +1,72 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{DocumentException, Field}
|
||||
import solutions.bitbadger.documents.query.PatchQuery
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
@DisplayName("Scala | Query | PatchQuery")
|
||||
class PatchQueryTest:
|
||||
|
||||
/**
|
||||
* Reset the dialect
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates correctly | PostgreSQL")
|
||||
def byIdPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'id' = :id", PatchQuery.byId(TEST_TABLE),
|
||||
"Patch query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates correctly | SQLite")
|
||||
def byIdSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id",
|
||||
PatchQuery.byId(TEST_TABLE), "Patch query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | PostgreSQL")
|
||||
def byFieldsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'z' = :y",
|
||||
PatchQuery.byFields(TEST_TABLE, List(Field.equal("z", "", ":y")).asJava), "Patch query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | SQLite")
|
||||
def byFieldsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y",
|
||||
PatchQuery.byFields(TEST_TABLE, List(Field.equal("z", "", ":y")).asJava), "Patch query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains generates correctly | PostgreSQL")
|
||||
def byContainsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = data || :data WHERE data @> :criteria",
|
||||
PatchQuery.byContains(TEST_TABLE), "Patch query not constructed correctly" )
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails | SQLite")
|
||||
def byContainsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => PatchQuery.byContains(TEST_TABLE))
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath generates correctly | PostgreSQL")
|
||||
def byJsonPathPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)",
|
||||
PatchQuery.byJsonPath(TEST_TABLE), "Patch query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails | SQLite")
|
||||
def byJsonPathSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => PatchQuery.byJsonPath(TEST_TABLE))
|
||||
@@ -0,0 +1,136 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{Dialect, Field, FieldMatch}
|
||||
import solutions.bitbadger.documents.query.QueryUtils
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
@DisplayName("Scala | Query | Package Functions")
|
||||
class QueryUtilsTest:
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("statementWhere generates correctly")
|
||||
def statementWhere(): Unit =
|
||||
assertEquals("x WHERE y", QueryUtils.statementWhere("x", "y"), "Statements not combined correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates a numeric ID query | PostgreSQL")
|
||||
def byIdNumericPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("test WHERE (data->>'id')::numeric = :id", QueryUtils.byId("test", 9))
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates an alphanumeric ID query | PostgreSQL")
|
||||
def byIdAlphaPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("unit WHERE data->>'id' = :id", QueryUtils.byId("unit", "18"))
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates ID query | SQLite")
|
||||
def byIdSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("yo WHERE data->>'id' = :id", QueryUtils.byId("yo", 27))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates default field query | PostgreSQL")
|
||||
def byFieldsMultipleDefaultPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("this WHERE data->>'a' = :the_a AND (data->>'b')::numeric = :b_value",
|
||||
QueryUtils.byFields("this", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates default field query | SQLite")
|
||||
def byFieldsMultipleDefaultSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("this WHERE data->>'a' = :the_a AND data->>'b' = :b_value",
|
||||
QueryUtils.byFields("this", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates ANY field query | PostgreSQL")
|
||||
def byFieldsMultipleAnyPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value",
|
||||
QueryUtils.byFields("that", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava,
|
||||
FieldMatch.ANY))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates ANY field query | SQLite")
|
||||
def byFieldsMultipleAnySQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("that WHERE data->>'a' = :the_a OR data->>'b' = :b_value",
|
||||
QueryUtils.byFields("that", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava,
|
||||
FieldMatch.ANY))
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates for no fields")
|
||||
def orderByNone(): Unit =
|
||||
assertEquals("", QueryUtils.orderBy(List().asJava, Dialect.POSTGRESQL),
|
||||
"ORDER BY should have been blank (PostgreSQL)")
|
||||
assertEquals("", QueryUtils.orderBy(List().asJava, Dialect.SQLITE), "ORDER BY should have been blank (SQLite)")
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates single, no direction | PostgreSQL")
|
||||
def orderBySinglePostgres(): Unit =
|
||||
assertEquals(" ORDER BY data->>'TestField'",
|
||||
QueryUtils.orderBy(List(Field.named("TestField")).asJava, Dialect.POSTGRESQL),
|
||||
"ORDER BY not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates single, no direction | SQLite")
|
||||
def orderBySingleSQLite(): Unit =
|
||||
assertEquals(" ORDER BY data->>'TestField'",
|
||||
QueryUtils.orderBy(List(Field.named("TestField")).asJava, Dialect.SQLITE),
|
||||
"ORDER BY not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates multiple with direction | PostgreSQL")
|
||||
def orderByMultiplePostgres(): Unit =
|
||||
assertEquals(" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
|
||||
QueryUtils.orderBy(
|
||||
List(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")).asJava,
|
||||
Dialect.POSTGRESQL),
|
||||
"ORDER BY not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates multiple with direction | SQLite")
|
||||
def orderByMultipleSQLite(): Unit =
|
||||
assertEquals(" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
|
||||
QueryUtils.orderBy(
|
||||
List(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")).asJava,
|
||||
Dialect.SQLITE),
|
||||
"ORDER BY not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates numeric ordering | PostgreSQL")
|
||||
def orderByNumericPostgres(): Unit =
|
||||
assertEquals(" ORDER BY (data->>'Test')::numeric",
|
||||
QueryUtils.orderBy(List(Field.named("n:Test")).asJava, Dialect.POSTGRESQL), "ORDER BY not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates numeric ordering | SQLite")
|
||||
def orderByNumericSQLite(): Unit =
|
||||
assertEquals(" ORDER BY data->>'Test'", QueryUtils.orderBy(List(Field.named("n:Test")).asJava, Dialect.SQLITE),
|
||||
"ORDER BY not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates case-insensitive ordering | PostgreSQL")
|
||||
def orderByCIPostgres(): Unit =
|
||||
assertEquals(" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST",
|
||||
QueryUtils.orderBy(List(Field.named("i:Test.Field DESC NULLS FIRST")).asJava, Dialect.POSTGRESQL),
|
||||
"ORDER BY not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates case-insensitive ordering | SQLite")
|
||||
def orderByCISQLite(): Unit =
|
||||
assertEquals(" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST",
|
||||
QueryUtils.orderBy(List(Field.named("i:Test.Field ASC NULLS LAST")).asJava, Dialect.SQLITE),
|
||||
"ORDER BY not constructed correctly")
|
||||
@@ -0,0 +1,82 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{DocumentException, Field, Parameter, ParameterType}
|
||||
import solutions.bitbadger.documents.query.RemoveFieldsQuery
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
@DisplayName("Scala | Query | RemoveFieldsQuery")
|
||||
class RemoveFieldsQueryTest:
|
||||
|
||||
/**
|
||||
* Reset the dialect
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp(): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates correctly | PostgreSQL")
|
||||
def byIdPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data->>'id' = :id",
|
||||
RemoveFieldsQuery.byId(TEST_TABLE, List(Parameter(":name", ParameterType.STRING, "{a,z}")).asJava),
|
||||
"Remove Fields query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates correctly | SQLite")
|
||||
def byIdSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id",
|
||||
RemoveFieldsQuery.byId(TEST_TABLE,
|
||||
List(Parameter(":name0", ParameterType.STRING, "a"),Parameter(":name1", ParameterType.STRING, "z")).asJava),
|
||||
"Remove Field query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | PostgreSQL")
|
||||
def byFieldsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data->>'f' > :g",
|
||||
RemoveFieldsQuery.byFields(TEST_TABLE, List(Parameter(":name", ParameterType.STRING, "{b,c}")).asJava,
|
||||
List(Field.greater("f", "", ":g")).asJava),
|
||||
"Remove Field query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates correctly | SQLite")
|
||||
def byFieldsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g",
|
||||
RemoveFieldsQuery.byFields(TEST_TABLE,
|
||||
List(Parameter(":name0", ParameterType.STRING, "b"), Parameter(":name1", ParameterType.STRING, "c")).asJava,
|
||||
List(Field.greater("f", "", ":g")).asJava),
|
||||
"Remove Field query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains generates correctly | PostgreSQL")
|
||||
def byContainsPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data @> :criteria",
|
||||
RemoveFieldsQuery.byContains(TEST_TABLE, List(Parameter(":name", ParameterType.STRING, "{m,n}")).asJava),
|
||||
"Remove Field query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails | SQLite")
|
||||
def byContainsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => RemoveFieldsQuery.byContains(TEST_TABLE, List().asJava))
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath generates correctly | PostgreSQL")
|
||||
def byJsonPathPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals(s"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)",
|
||||
RemoveFieldsQuery.byJsonPath(TEST_TABLE, List(Parameter(":name", ParameterType.STRING, "{o,p}")).asJava),
|
||||
"Remove Field query not constructed correctly")
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails | SQLite")
|
||||
def byJsonPathSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => RemoveFieldsQuery.byJsonPath(TEST_TABLE, List().asJava))
|
||||
@@ -0,0 +1,3 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
class ShortIdClass(var id: Short)
|
||||
@@ -0,0 +1,3 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
class StringIdClass(var id: String)
|
||||
@@ -0,0 +1,141 @@
|
||||
package solutions.bitbadger.documents.scala.tests
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
|
||||
import solutions.bitbadger.documents.{DocumentException, Field, FieldMatch}
|
||||
import solutions.bitbadger.documents.query.Where
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
@DisplayName("Scala | Query | Where")
|
||||
class WhereTest:
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
def cleanUp (): Unit =
|
||||
ForceDialect.none()
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields is blank when given no fields")
|
||||
def byFieldsBlankIfEmpty(): Unit =
|
||||
assertEquals("", Where.byFields(List().asJava))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates one numeric field | PostgreSQL")
|
||||
def byFieldsOneFieldPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("(data->>'it')::numeric = :that", Where.byFields(List(Field.equal("it", 9, ":that")).asJava))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates one alphanumeric field | PostgreSQL")
|
||||
def byFieldsOneAlphaFieldPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->>'it' = :that", Where.byFields(List(Field.equal("it", "", ":that")).asJava))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates one field | SQLite")
|
||||
def byFieldsOneFieldSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("data->>'it' = :that", Where.byFields(List(Field.equal("it", "", ":that")).asJava))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates multiple fields w/ default match | PostgreSQL")
|
||||
def byFieldsMultipleDefaultPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->>'1' = :one AND (data->>'2')::numeric = :two AND data->>'3' = :three",
|
||||
Where.byFields(
|
||||
List(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")).asJava))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates multiple fields w/ default match | SQLite")
|
||||
def byFieldsMultipleDefaultSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("data->>'1' = :one AND data->>'2' = :two AND data->>'3' = :three",
|
||||
Where.byFields(
|
||||
List(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")).asJava))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates multiple fields w/ ANY match | PostgreSQL")
|
||||
def byFieldsMultipleAnyPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->>'1' = :one OR (data->>'2')::numeric = :two OR data->>'3' = :three",
|
||||
Where.byFields(
|
||||
List(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")).asJava,
|
||||
FieldMatch.ANY))
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields generates multiple fields w/ ANY match | SQLite")
|
||||
def byFieldsMultipleAnySQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("data->>'1' = :one OR data->>'2' = :two OR data->>'3' = :three",
|
||||
Where.byFields(
|
||||
List(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")).asJava,
|
||||
FieldMatch.ANY))
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates defaults for alphanumeric key | PostgreSQL")
|
||||
def byIdDefaultAlphaPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->>'id' = :id", Where.byId())
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates defaults for numeric key | PostgreSQL")
|
||||
def byIdDefaultNumericPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("(data->>'id')::numeric = :id", Where.byId(":id", 5))
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates defaults | SQLite")
|
||||
def byIdDefaultSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("data->>'id' = :id", Where.byId())
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates named ID | PostgreSQL")
|
||||
def byIdDefaultNamedPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data->>'id' = :key", Where.byId(":key"))
|
||||
|
||||
@Test
|
||||
@DisplayName("byId generates named ID | SQLite")
|
||||
def byIdDefaultNamedSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertEquals("data->>'id' = :key", Where.byId(":key"))
|
||||
|
||||
@Test
|
||||
@DisplayName("jsonContains generates defaults | PostgreSQL")
|
||||
def jsonContainsDefaultPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data @> :criteria", Where.jsonContains())
|
||||
|
||||
@Test
|
||||
@DisplayName("jsonContains generates named parameter | PostgreSQL")
|
||||
def jsonContainsNamedPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("data @> :it", Where.jsonContains(":it"))
|
||||
|
||||
@Test
|
||||
@DisplayName("jsonContains fails | SQLite")
|
||||
def jsonContainsFailsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => Where.jsonContains())
|
||||
|
||||
@Test
|
||||
@DisplayName("jsonPathMatches generates defaults | PostgreSQL")
|
||||
def jsonPathMatchDefaultPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("jsonb_path_exists(data, :path::jsonpath)", Where.jsonPathMatches())
|
||||
|
||||
@Test
|
||||
@DisplayName("jsonPathMatches generates named parameter | PostgreSQL")
|
||||
def jsonPathMatchNamedPostgres(): Unit =
|
||||
ForceDialect.postgres()
|
||||
assertEquals("jsonb_path_exists(data, :jp::jsonpath)", Where.jsonPathMatches(":jp"))
|
||||
|
||||
@Test
|
||||
@DisplayName("jsonPathMatches fails | SQLite")
|
||||
def jsonPathFailsSQLite(): Unit =
|
||||
ForceDialect.sqlite()
|
||||
assertThrows(classOf[DocumentException], () => Where.jsonPathMatches())
|
||||
@@ -0,0 +1,11 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
class ArrayDocument(val id: String = "", val values: List[String] = List())
|
||||
|
||||
object ArrayDocument:
|
||||
|
||||
/** A set of documents used for integration tests */
|
||||
val testDocuments: List[ArrayDocument] =
|
||||
ArrayDocument("first", "a" :: "b" :: "c" :: Nil) ::
|
||||
ArrayDocument("second", "c" :: "d" :: "e" :: Nil) ::
|
||||
ArrayDocument("third", "x" :: "y" :: "z" :: Nil) :: Nil
|
||||
@@ -0,0 +1,42 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
object CountFunctions:
|
||||
|
||||
def all(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should have been 5 documents in the table")
|
||||
|
||||
def byFieldsNumeric(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(3L, db.conn.countByFields(TEST_TABLE, Field.between("numValue", 10, 20) :: Nil),
|
||||
"There should have been 3 matching documents")
|
||||
|
||||
def byFieldsAlpha(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(1L, db.conn.countByFields(TEST_TABLE, Field.between("value", "aardvark", "apple") :: Nil),
|
||||
"There should have been 1 matching document")
|
||||
|
||||
def byContainsMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(2L, db.conn.countByContains(TEST_TABLE, Map.Map1("value", "purple")),
|
||||
"There should have been 2 matching documents")
|
||||
|
||||
def byContainsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(0L, db.conn.countByContains(TEST_TABLE, Map.Map1("value", "magenta")),
|
||||
"There should have been no matching documents")
|
||||
|
||||
def byJsonPathMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(2L, db.conn.countByJsonPath(TEST_TABLE, "$.numValue ? (@ < 5)"),
|
||||
"There should have been 2 matching documents")
|
||||
|
||||
def byJsonPathNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(0L, db.conn.countByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)"),
|
||||
"There should have been no matching documents")
|
||||
@@ -0,0 +1,52 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import solutions.bitbadger.documents.query.{CountQuery, DeleteQuery, FindQuery}
|
||||
import solutions.bitbadger.documents.scala.Results
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
import solutions.bitbadger.documents.{Configuration, Field, Parameter, ParameterType}
|
||||
|
||||
object CustomFunctions:
|
||||
|
||||
def listEmpty(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
db.conn.deleteByFields(TEST_TABLE, Field.exists(Configuration.idField) :: Nil)
|
||||
val result = db.conn.customList[JsonDocument](FindQuery.all(TEST_TABLE), Results.fromData)
|
||||
assertEquals(0, result.size, "There should have been no results")
|
||||
|
||||
def listAll(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val result = db.conn.customList[JsonDocument](FindQuery.all(TEST_TABLE), Results.fromData)
|
||||
assertEquals(5, result.size, "There should have been 5 results")
|
||||
|
||||
def singleNone(db: ThrowawayDatabase): Unit =
|
||||
assertTrue(db.conn.customSingle[JsonDocument](FindQuery.all(TEST_TABLE), Results.fromData).isEmpty,
|
||||
"There should not have been a document returned")
|
||||
|
||||
def singleOne(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertTrue(db.conn.customSingle[JsonDocument](FindQuery.all(TEST_TABLE), Results.fromData).isDefined,
|
||||
"There should have been a document returned")
|
||||
|
||||
def nonQueryChanges(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.customScalar[Long](CountQuery.all(TEST_TABLE), Results.toCount),
|
||||
"There should have been 5 documents in the table")
|
||||
db.conn.customNonQuery(s"DELETE FROM $TEST_TABLE")
|
||||
assertEquals(0L, db.conn.customScalar[Long](CountQuery.all(TEST_TABLE), Results.toCount),
|
||||
"There should have been no documents in the table")
|
||||
|
||||
def nonQueryNoChanges(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.customScalar[Long](CountQuery.all(TEST_TABLE), Results.toCount),
|
||||
"There should have been 5 documents in the table")
|
||||
db.conn.customNonQuery(DeleteQuery.byId(TEST_TABLE, "eighty-two"),
|
||||
Parameter(":id", ParameterType.STRING, "eighty-two") :: Nil)
|
||||
assertEquals(5L, db.conn.customScalar[Long](CountQuery.all(TEST_TABLE), Results.toCount),
|
||||
"There should still have been 5 documents in the table")
|
||||
|
||||
def scalar(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(3L, db.conn.customScalar[Long](s"SELECT 3 AS it FROM $TEST_TABLE LIMIT 1", Results.toCount),
|
||||
"The number 3 should have been returned")
|
||||
@@ -0,0 +1,36 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import solutions.bitbadger.documents.DocumentIndex
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
object DefinitionFunctions:
|
||||
|
||||
def ensureTable(db: ThrowawayDatabase): Unit =
|
||||
assertFalse(db.dbObjectExists("ensured"), "The 'ensured' table should not exist")
|
||||
assertFalse(db.dbObjectExists("idx_ensured_key"), "The PK index for the 'ensured' table should not exist")
|
||||
db.conn.ensureTable("ensured")
|
||||
assertTrue(db.dbObjectExists("ensured"), "The 'ensured' table should exist")
|
||||
assertTrue(db.dbObjectExists("idx_ensured_key"), "The PK index for the 'ensured' table should now exist")
|
||||
|
||||
def ensureFieldIndex(db: ThrowawayDatabase): Unit =
|
||||
assertFalse(db.dbObjectExists("idx_${TEST_TABLE}_test"), "The test index should not exist")
|
||||
db.conn.ensureFieldIndex(TEST_TABLE, "test", "id" :: "category" :: Nil)
|
||||
assertTrue(db.dbObjectExists(s"idx_${TEST_TABLE}_test"), "The test index should now exist")
|
||||
|
||||
def ensureDocumentIndexFull(db: ThrowawayDatabase): Unit =
|
||||
assertFalse(db.dbObjectExists("doc_table"), "The 'doc_table' table should not exist")
|
||||
db.conn.ensureTable("doc_table")
|
||||
assertTrue(db.dbObjectExists("doc_table"), "The 'doc_table' table should exist")
|
||||
assertFalse(db.dbObjectExists("idx_doc_table_document"), "The document index should not exist")
|
||||
db.conn.ensureDocumentIndex("doc_table", DocumentIndex.FULL)
|
||||
assertTrue(db.dbObjectExists("idx_doc_table_document"), "The document index should exist")
|
||||
|
||||
def ensureDocumentIndexOptimized(db: ThrowawayDatabase): Unit =
|
||||
assertFalse(db.dbObjectExists("doc_table"), "The 'doc_table' table should not exist")
|
||||
db.conn.ensureTable("doc_table")
|
||||
assertTrue(db.dbObjectExists("doc_table"), "The 'doc_table' table should exist")
|
||||
assertFalse(db.dbObjectExists("idx_doc_table_document"), "The document index should not exist")
|
||||
db.conn.ensureDocumentIndex("doc_table", DocumentIndex.OPTIMIZED)
|
||||
assertTrue(db.dbObjectExists("idx_doc_table_document"), "The document index should exist")
|
||||
@@ -0,0 +1,56 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
object DeleteFunctions:
|
||||
|
||||
def byIdMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
|
||||
db.conn.deleteById(TEST_TABLE, "four")
|
||||
assertEquals(4L, db.conn.countAll(TEST_TABLE), "There should now be 4 documents in the table")
|
||||
|
||||
def byIdNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
|
||||
db.conn.deleteById(TEST_TABLE, "negative four")
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table")
|
||||
|
||||
def byFieldsMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
|
||||
db.conn.deleteByFields(TEST_TABLE, Field.notEqual("value", "purple") :: Nil)
|
||||
assertEquals(2L, db.conn.countAll(TEST_TABLE), "There should now be 2 documents in the table")
|
||||
|
||||
def byFieldsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
|
||||
db.conn.deleteByFields(TEST_TABLE, Field.equal("value", "crimson") :: Nil)
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table")
|
||||
|
||||
def byContainsMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
|
||||
db.conn.deleteByContains(TEST_TABLE, Map.Map1("value", "purple"))
|
||||
assertEquals(3L, db.conn.countAll(TEST_TABLE), "There should now be 3 documents in the table")
|
||||
|
||||
def byContainsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
|
||||
db.conn.deleteByContains(TEST_TABLE, Map.Map1("target", "acquired"))
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table")
|
||||
|
||||
def byJsonPathMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
|
||||
db.conn.deleteByJsonPath(TEST_TABLE, "$.value ? (@ == \"purple\")")
|
||||
assertEquals(3L, db.conn.countAll(TEST_TABLE), "There should now be 3 documents in the table")
|
||||
|
||||
def byJsonPathNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table")
|
||||
db.conn.deleteByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)")
|
||||
assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table")
|
||||
@@ -0,0 +1,111 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import solutions.bitbadger.documents.{AutoId, Configuration, DocumentException, Field}
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
object DocumentFunctions:
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
|
||||
def insertDefault(db: ThrowawayDatabase): Unit =
|
||||
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
|
||||
val doc = JsonDocument("turkey", "", 0, SubDocument("gobble", "gobble"))
|
||||
db.conn.insert(TEST_TABLE, doc)
|
||||
val after = db.conn.findAll[JsonDocument](TEST_TABLE)
|
||||
assertEquals(1, after.size, "There should be one document in the table")
|
||||
assertEquals(doc, after.head, "The document should be what was inserted")
|
||||
|
||||
def insertDupe(db: ThrowawayDatabase): Unit =
|
||||
db.conn.insert(TEST_TABLE, JsonDocument("a", "", 0, null))
|
||||
assertThrows(classOf[DocumentException], () => db.conn.insert(TEST_TABLE, JsonDocument("a", "b", 22, null)),
|
||||
"Inserting a document with a duplicate key should have thrown an exception")
|
||||
|
||||
def insertNumAutoId(db: ThrowawayDatabase): Unit =
|
||||
try {
|
||||
Configuration.autoIdStrategy = AutoId.NUMBER
|
||||
Configuration.idField = "key"
|
||||
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
|
||||
|
||||
db.conn.insert(TEST_TABLE, NumIdDocument(0, "one"))
|
||||
db.conn.insert(TEST_TABLE, NumIdDocument(0, "two"))
|
||||
db.conn.insert(TEST_TABLE, NumIdDocument(77, "three"))
|
||||
db.conn.insert(TEST_TABLE, NumIdDocument(0, "four"))
|
||||
|
||||
val after = db.conn.findAll[NumIdDocument](TEST_TABLE, Field.named("key") :: Nil)
|
||||
assertEquals(4, after.size, "There should have been 4 documents returned")
|
||||
assertEquals("1|2|77|78", after.fold("") { (acc, item) => s"$acc|$item" }, "The IDs were not generated correctly")
|
||||
} finally {
|
||||
Configuration.autoIdStrategy = AutoId.DISABLED
|
||||
Configuration.idField = "id"
|
||||
}
|
||||
|
||||
def insertUUIDAutoId(db: ThrowawayDatabase): Unit =
|
||||
try {
|
||||
Configuration.autoIdStrategy = AutoId.UUID
|
||||
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
|
||||
|
||||
db.conn.insert(TEST_TABLE, JsonDocument(""))
|
||||
|
||||
val after = db.conn.findAll[JsonDocument](TEST_TABLE)
|
||||
assertEquals(1, after.size, "There should have been 1 document returned")
|
||||
assertEquals(32, after.head.id.length, "The ID was not generated correctly")
|
||||
} finally {
|
||||
Configuration.autoIdStrategy = AutoId.DISABLED
|
||||
}
|
||||
|
||||
def insertStringAutoId(db: ThrowawayDatabase): Unit =
|
||||
try {
|
||||
Configuration.autoIdStrategy = AutoId.RANDOM_STRING
|
||||
assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table")
|
||||
|
||||
db.conn.insert(TEST_TABLE, JsonDocument(""))
|
||||
|
||||
Configuration.idStringLength = 21
|
||||
db.conn.insert(TEST_TABLE, JsonDocument(""))
|
||||
|
||||
val after = db.conn.findAll[JsonDocument](TEST_TABLE)
|
||||
assertEquals(2, after.size, "There should have been 2 documents returned")
|
||||
assertEquals(16, after.head.id.length, "The first document's ID was not generated correctly")
|
||||
assertEquals(21, after(1).id.length, "The second document's ID was not generated correctly")
|
||||
} finally {
|
||||
Configuration.autoIdStrategy = AutoId.DISABLED
|
||||
Configuration.idStringLength = 16
|
||||
}
|
||||
|
||||
def saveMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
db.conn.save(TEST_TABLE, JsonDocument("two", numValue = 44))
|
||||
val tryDoc = db.conn.findById[String, JsonDocument](TEST_TABLE, "two")
|
||||
assertTrue(tryDoc.isDefined, "There should have been a document returned")
|
||||
val doc = tryDoc.get
|
||||
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")
|
||||
|
||||
def saveNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
db.conn.save(TEST_TABLE, JsonDocument("test", sub = SubDocument("a", "b")))
|
||||
assertTrue(db.conn.findById[String, JsonDocument](TEST_TABLE, "test").isDefined,
|
||||
"The test document should have been saved")
|
||||
|
||||
def updateMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
db.conn.update(TEST_TABLE, "one", JsonDocument("one", "howdy", 8, SubDocument("y", "z")))
|
||||
val tryDoc = db.conn.findById[String, JsonDocument](TEST_TABLE, "one")
|
||||
assertTrue(tryDoc.isDefined, "There should have been a document returned")
|
||||
val doc = tryDoc.get
|
||||
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")
|
||||
|
||||
def updateNoMatch(db: ThrowawayDatabase): Unit =
|
||||
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"))
|
||||
@@ -0,0 +1,46 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
object ExistsFunctions:
|
||||
|
||||
def byIdMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertTrue(db.conn.existsById(TEST_TABLE, "three"), "The document with ID \"three\" should exist")
|
||||
|
||||
def byIdNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.existsById(TEST_TABLE, "seven"), "The document with ID \"seven\" should not exist")
|
||||
|
||||
def byFieldsMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertTrue(db.conn.existsByFields(TEST_TABLE, Field.equal("numValue", 10) :: Nil),
|
||||
"Matching documents should have been found")
|
||||
|
||||
def byFieldsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.existsByFields(TEST_TABLE, Field.equal("nothing", "none") :: Nil),
|
||||
"No matching documents should have been found")
|
||||
|
||||
def byContainsMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertTrue(db.conn.existsByContains(TEST_TABLE, Map.Map1("value", "purple")),
|
||||
"Matching documents should have been found")
|
||||
|
||||
def byContainsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.existsByContains(TEST_TABLE, Map.Map1("value", "violet")),
|
||||
"Matching documents should not have been found")
|
||||
|
||||
def byJsonPathMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertTrue(db.conn.existsByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10)"),
|
||||
"Matching documents should have been found")
|
||||
|
||||
def byJsonPathNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.existsByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10.1)"),
|
||||
"Matching documents should not have been found")
|
||||
@@ -0,0 +1,217 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import solutions.bitbadger.documents.{Configuration, Field}
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
object FindFunctions:
|
||||
|
||||
/** Generate IDs as a pipe-delimited string */
|
||||
private def docIds(docs: List[JsonDocument]) =
|
||||
docs.map(_.id).reduce((ids, docId) => s"$ids|$docId")
|
||||
|
||||
def allDefault(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(5, db.conn.findAll[JsonDocument](TEST_TABLE).size, "There should have been 5 documents returned")
|
||||
|
||||
def allAscending(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val docs = db.conn.findAll[JsonDocument](TEST_TABLE, Field.named("id") :: Nil)
|
||||
assertEquals(5, docs.size, "There should have been 5 documents returned")
|
||||
assertEquals("five|four|one|three|two", docIds(docs), "The documents were not ordered correctly")
|
||||
|
||||
def allDescending(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val docs = db.conn.findAll[JsonDocument](TEST_TABLE, Field.named("id DESC") :: Nil)
|
||||
assertEquals(5, docs.size, "There should have been 5 documents returned")
|
||||
assertEquals("two|three|one|four|five", docIds(docs), "The documents were not ordered correctly")
|
||||
|
||||
def allNumOrder(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val docs = db.conn.findAll[JsonDocument](TEST_TABLE,
|
||||
Field.named("sub.foo NULLS LAST") :: Field.named("n:numValue") :: Nil)
|
||||
assertEquals(5, docs.size, "There should have been 5 documents returned")
|
||||
assertEquals("two|four|one|three|five", docIds(docs), "The documents were not ordered correctly")
|
||||
|
||||
def allEmpty(db: ThrowawayDatabase): Unit =
|
||||
assertEquals(0, db.conn.findAll[JsonDocument](TEST_TABLE).size, "There should have been no documents returned")
|
||||
|
||||
def byIdString(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val doc = db.conn.findById[String, JsonDocument](TEST_TABLE, "two")
|
||||
assertTrue(doc.isDefined, "The document should have been returned")
|
||||
assertEquals("two", doc.get.id, "An incorrect document was returned")
|
||||
|
||||
def byIdNumber(db: ThrowawayDatabase): Unit =
|
||||
Configuration.idField = "key"
|
||||
try {
|
||||
db.conn.insert(TEST_TABLE, NumIdDocument(18, "howdy"))
|
||||
val doc = db.conn.findById[Int, NumIdDocument](TEST_TABLE, 18)
|
||||
assertTrue(doc.isDefined, "The document should have been returned")
|
||||
} finally {
|
||||
Configuration.idField = "id"
|
||||
}
|
||||
|
||||
def byIdNotFound(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.findById[String, JsonDocument](TEST_TABLE, "x").isDefined,
|
||||
"There should have been no document returned")
|
||||
|
||||
def byFieldsMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val docs = db.conn.findByFields[JsonDocument](TEST_TABLE,
|
||||
Field.any("value", ("blue" :: "purple" :: Nil).asJava) :: Nil, orderBy = Field.exists("sub") :: Nil)
|
||||
assertEquals(1, docs.size, "There should have been a document returned")
|
||||
assertEquals("four", docs.head.id, "The incorrect document was returned")
|
||||
|
||||
def byFieldsMatchOrdered(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val docs = db.conn.findByFields[JsonDocument](TEST_TABLE, Field.equal("value", "purple") :: Nil,
|
||||
orderBy = Field.named("id") :: Nil)
|
||||
assertEquals(2, docs.size, "There should have been 2 documents returned")
|
||||
assertEquals("five|four", docIds(docs), "The documents were not ordered correctly")
|
||||
|
||||
def byFieldsMatchNumIn(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val docs = db.conn.findByFields[JsonDocument](TEST_TABLE,
|
||||
Field.any("numValue", (2 :: 4 :: 6 :: 8 :: Nil).asJava) :: Nil)
|
||||
assertEquals(1, docs.size, "There should have been a document returned")
|
||||
assertEquals("three", docs.head.id, "The incorrect document was returned")
|
||||
|
||||
def byFieldsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(0, db.conn.findByFields[JsonDocument](TEST_TABLE, Field.greater("numValue", 100) :: Nil).size,
|
||||
"There should have been no documents returned")
|
||||
|
||||
def byFieldsMatchInArray(db: ThrowawayDatabase): Unit =
|
||||
ArrayDocument.testDocuments.foreach { doc => db.conn.insert(TEST_TABLE, doc) }
|
||||
val docs = db.conn.findByFields[ArrayDocument](TEST_TABLE,
|
||||
Field.inArray("values", TEST_TABLE, ("c" :: Nil).asJava) :: Nil)
|
||||
assertEquals(2, docs.size, "There should have been two documents returned")
|
||||
assertTrue(("first" :: "second" :: Nil).contains(docs.head.id),
|
||||
s"An incorrect document was returned (${docs.head.id}")
|
||||
assertTrue(("first" :: "second" :: Nil).contains(docs(1).id), s"An incorrect document was returned (${docs(1).id})")
|
||||
|
||||
def byFieldsNoMatchInArray(db: ThrowawayDatabase): Unit =
|
||||
ArrayDocument.testDocuments.foreach { doc => db.conn.insert(TEST_TABLE, doc) }
|
||||
assertEquals(0,
|
||||
db.conn.findByFields[ArrayDocument](TEST_TABLE,
|
||||
Field.inArray("values", TEST_TABLE, ("j" :: Nil).asJava) :: Nil).size,
|
||||
"There should have been no documents returned")
|
||||
|
||||
def byContainsMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val docs = db.conn.findByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE, Map.Map1("value", "purple"))
|
||||
assertEquals(2, docs.size, "There should have been 2 documents returned")
|
||||
assertTrue(("four" :: "five" :: Nil).contains(docs.head.id),
|
||||
s"An incorrect document was returned (${docs.head.id})")
|
||||
assertTrue(("four" :: "five" :: Nil).contains(docs(1).id), s"An incorrect document was returned (${docs(1).id})")
|
||||
|
||||
def byContainsMatchOrdered(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val docs = db.conn.findByContains[JsonDocument, Map.Map1[String, Map.Map1[String, String]]](TEST_TABLE,
|
||||
Map.Map1("sub", Map.Map1("foo", "green")), Field.named("value") :: Nil)
|
||||
assertEquals(2, docs.size, "There should have been 2 documents returned")
|
||||
assertEquals("two|four", docIds(docs), "The documents were not ordered correctly")
|
||||
|
||||
def byContainsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(0,
|
||||
db.conn.findByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE, Map.Map1("value", "indigo")).size,
|
||||
"There should have been no documents returned")
|
||||
|
||||
def byJsonPathMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val docs = db.conn.findByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 10)")
|
||||
assertEquals(2, docs.size, "There should have been 2 documents returned")
|
||||
assertTrue(("four" :: "five" :: Nil).contains(docs.head.id),
|
||||
s"An incorrect document was returned (${docs.head.id})")
|
||||
assertTrue(("four" :: "five" :: Nil).contains(docs(1).id), s"An incorrect document was returned (${docs(1).id})")
|
||||
|
||||
def byJsonPathMatchOrdered(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val docs = db.conn.findByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 10)", Field.named("id") :: Nil)
|
||||
assertEquals(2, docs.size, "There should have been 2 documents returned")
|
||||
assertEquals("five|four", docIds(docs), "The documents were not ordered correctly")
|
||||
|
||||
def byJsonPathNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertEquals(0, db.conn.findByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 100)").size,
|
||||
"There should have been no documents returned")
|
||||
|
||||
def firstByFieldsMatchOne(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val doc = db.conn.findFirstByFields[JsonDocument](TEST_TABLE, Field.equal("value", "another") :: Nil)
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertEquals("two", doc.get.id, "The incorrect document was returned")
|
||||
|
||||
def firstByFieldsMatchMany(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val doc = db.conn.findFirstByFields[JsonDocument](TEST_TABLE, Field.equal("sub.foo", "green") :: Nil)
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertTrue(("two" :: "four" :: Nil).contains(doc.get.id), s"An incorrect document was returned (${doc.get.id})")
|
||||
|
||||
def firstByFieldsMatchOrdered(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val doc = db.conn.findFirstByFields[JsonDocument](TEST_TABLE, Field.equal("sub.foo", "green") :: Nil,
|
||||
orderBy = Field.named("n:numValue DESC") :: Nil)
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertEquals("four", doc.get.id, "An incorrect document was returned")
|
||||
|
||||
def firstByFieldsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.findFirstByFields[JsonDocument](TEST_TABLE, Field.equal("value", "absent") :: Nil).isDefined,
|
||||
"There should have been no document returned")
|
||||
|
||||
def firstByContainsMatchOne(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val doc = db.conn.findFirstByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE,
|
||||
Map.Map1("value", "FIRST!"))
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertEquals("one", doc.get.id, "An incorrect document was returned")
|
||||
|
||||
def firstByContainsMatchMany(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val doc = db.conn.findFirstByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE,
|
||||
Map.Map1("value", "purple"))
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertTrue(("four" :: "five" :: Nil).contains(doc.get.id), s"An incorrect document was returned (${doc.get.id})")
|
||||
|
||||
def firstByContainsMatchOrdered(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val doc = db.conn.findFirstByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE,
|
||||
Map.Map1("value", "purple"), Field.named("sub.bar NULLS FIRST") :: Nil)
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertEquals("five", doc.get.id, "An incorrect document was returned")
|
||||
|
||||
def firstByContainsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.findFirstByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE,
|
||||
Map.Map1("value", "indigo")).isDefined, "There should have been no document returned")
|
||||
|
||||
def firstByJsonPathMatchOne(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val doc = db.conn.findFirstByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ == 10)")
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertEquals("two", doc.get.id, "An incorrect document was returned")
|
||||
|
||||
def firstByJsonPathMatchMany(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val doc = db.conn.findFirstByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 10)")
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertTrue(("four" :: "five" :: Nil).contains(doc.get.id), s"An incorrect document was returned (${doc.get.id})")
|
||||
|
||||
def firstByJsonPathMatchOrdered(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val doc = db.conn.findFirstByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 10)",
|
||||
Field.named("id DESC") :: Nil)
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertEquals("four", doc.get.id, "An incorrect document was returned")
|
||||
|
||||
def firstByJsonPathNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.findFirstByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 100)").isDefined,
|
||||
"There should have been no document returned")
|
||||
@@ -0,0 +1,17 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import solutions.bitbadger.documents.DocumentSerializer
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
|
||||
/**
|
||||
* A JSON serializer using Jackson's default options
|
||||
*/
|
||||
class JacksonDocumentSerializer extends DocumentSerializer:
|
||||
|
||||
private val mapper = ObjectMapper()
|
||||
|
||||
override def serialize[TDoc](document: TDoc): String =
|
||||
mapper.writeValueAsString(document)
|
||||
|
||||
override def deserialize[TDoc](json: String, clazz: Class[TDoc]): TDoc =
|
||||
mapper.readValue(json, clazz)
|
||||
@@ -0,0 +1,19 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import solutions.bitbadger.documents.scala.extensions.insert
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
class JsonDocument(val id: String = "", val value: String = "", val numValue: Int = 0, val sub: SubDocument = null)
|
||||
|
||||
object JsonDocument:
|
||||
|
||||
/** Documents to use for testing */
|
||||
private val testDocuments = List(
|
||||
JsonDocument("one", "FIRST!", 0, null),
|
||||
JsonDocument("two", "another", 10, SubDocument("green", "blue")),
|
||||
JsonDocument("three", "", 4, null),
|
||||
JsonDocument("four", "purple", 17, SubDocument("green", "red")),
|
||||
JsonDocument("five", "purple", 18, null))
|
||||
|
||||
def load(db: ThrowawayDatabase, tableName: String = TEST_TABLE): Unit =
|
||||
testDocuments.foreach { it => db.conn.insert(tableName, it) }
|
||||
@@ -0,0 +1,3 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
class NumIdDocument(val key: Int = 0, val text: String = "")
|
||||
@@ -0,0 +1,65 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
object PatchFunctions:
|
||||
|
||||
def byIdMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
db.conn.patchById(TEST_TABLE, "one", Map.Map1("numValue", 44))
|
||||
val doc = db.conn.findById[String, JsonDocument](TEST_TABLE, "one")
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertEquals("one", doc.get.id, "An incorrect document was returned")
|
||||
assertEquals(44, doc.get.numValue, "The document was not patched")
|
||||
|
||||
def byIdNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.existsById(TEST_TABLE, "forty-seven"), "Document with ID \"forty-seven\" should not exist")
|
||||
db.conn.patchById(TEST_TABLE, "forty-seven", Map.Map1("foo", "green")) // no exception = pass
|
||||
|
||||
def byFieldsMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
db.conn.patchByFields(TEST_TABLE, Field.equal("value", "purple") :: Nil, Map.Map1("numValue", 77))
|
||||
assertEquals(2L, db.conn.countByFields(TEST_TABLE, Field.equal("numValue", 77) :: Nil),
|
||||
"There should have been 2 documents with numeric value 77")
|
||||
|
||||
def byFieldsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val fields = Field.equal("value", "burgundy") :: Nil
|
||||
assertFalse(db.conn.existsByFields(TEST_TABLE, fields), "There should be no documents with value of \"burgundy\"")
|
||||
db.conn.patchByFields(TEST_TABLE, fields, Map.Map1("foo", "green")) // no exception = pass
|
||||
|
||||
def byContainsMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val contains = Map.Map1("value", "another")
|
||||
db.conn.patchByContains(TEST_TABLE, contains, Map.Map1("numValue", 12))
|
||||
val doc = db.conn.findFirstByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE, contains)
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertEquals("two", doc.get.id, "The incorrect document was returned")
|
||||
assertEquals(12, doc.get.numValue, "The document was not updated")
|
||||
|
||||
def byContainsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val contains = Map.Map1("value", "updated")
|
||||
assertFalse(db.conn.existsByContains(TEST_TABLE, contains), "There should be no matching documents")
|
||||
db.conn.patchByContains(TEST_TABLE, contains, Map.Map1("sub.foo", "green")) // no exception = pass
|
||||
|
||||
def byJsonPathMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val path = "$.numValue ? (@ > 10)"
|
||||
db.conn.patchByJsonPath(TEST_TABLE, path, Map.Map1("value", "blue"))
|
||||
val docs = db.conn.findByJsonPath[JsonDocument](TEST_TABLE, path)
|
||||
assertEquals(2, docs.size, "There should have been two documents returned")
|
||||
docs.foreach { doc =>
|
||||
assertTrue(("four" :: "five" :: Nil).contains(doc.id), s"An incorrect document was returned (${doc.id})")
|
||||
assertEquals("blue", doc.value, s"The value for ID ${doc.id} was incorrect")
|
||||
}
|
||||
|
||||
def byJsonPathNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val path = "$.numValue ? (@ > 100)"
|
||||
assertFalse(db.conn.existsByJsonPath(TEST_TABLE, path), "There should be no documents with numeric values over 100")
|
||||
db.conn.patchByJsonPath(TEST_TABLE, path, Map.Map1("value", "blue")) // no exception = pass
|
||||
@@ -0,0 +1,45 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import solutions.bitbadger.documents.{Configuration, Parameter, ParameterType}
|
||||
import solutions.bitbadger.documents.scala.Results
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
import java.sql.Connection
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* A wrapper for a throwaway PostgreSQL database
|
||||
*/
|
||||
class PgDB extends ThrowawayDatabase:
|
||||
|
||||
Configuration.setConnectionString(PgDB.connString("postgres"))
|
||||
Using(Configuration.dbConn()) { conn => conn.customNonQuery(s"CREATE DATABASE $dbName") }
|
||||
|
||||
Configuration.setConnectionString(PgDB.connString(dbName))
|
||||
|
||||
override val conn: Connection = Configuration.dbConn()
|
||||
|
||||
conn.ensureTable(TEST_TABLE)
|
||||
|
||||
override def close(): Unit =
|
||||
conn.close()
|
||||
Configuration.setConnectionString(PgDB.connString("postgres"))
|
||||
Using(Configuration.dbConn()) { conn => conn.customNonQuery(s"DROP DATABASE $dbName") }
|
||||
Configuration.setConnectionString(null)
|
||||
|
||||
override def dbObjectExists(name: String): Boolean =
|
||||
conn.customScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = :name) AS it",
|
||||
Parameter(":name", ParameterType.STRING, name) :: Nil, Results.toExists)
|
||||
|
||||
|
||||
object PgDB:
|
||||
|
||||
/**
|
||||
* Create a connection string for the given database
|
||||
*
|
||||
* @param database The database to which the library should connect
|
||||
* @return The connection string for the database
|
||||
*/
|
||||
private def connString(database: String): String =
|
||||
s"jdbc:postgresql://localhost/$database?user=postgres&password=postgres"
|
||||
@@ -0,0 +1,43 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
@DisplayName("Scala | PostgreSQL: Count")
|
||||
class PostgreSQLCountIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("all counts all documents")
|
||||
def all(): Unit =
|
||||
Using(PgDB()) { db => CountFunctions.all(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields counts documents by a numeric value")
|
||||
def byFieldsNumeric(): Unit =
|
||||
Using(PgDB()) { db => CountFunctions.byFieldsNumeric(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields counts documents by a alphanumeric value")
|
||||
def byFieldsAlpha(): Unit =
|
||||
Using(PgDB()) { db => CountFunctions.byFieldsAlpha(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains counts documents when matches are found")
|
||||
def byContainsMatch(): Unit =
|
||||
Using(PgDB()) { db => CountFunctions.byContainsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains counts documents when no matches are found")
|
||||
def byContainsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => CountFunctions.byContainsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath counts documents when matches are found")
|
||||
def byJsonPathMatch(): Unit =
|
||||
Using(PgDB()) { db => CountFunctions.byJsonPathMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath counts documents when no matches are found")
|
||||
def byJsonPathNoMatch(): Unit =
|
||||
Using(PgDB()) { db => CountFunctions.byJsonPathNoMatch(db) }
|
||||
@@ -0,0 +1,43 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
@DisplayName("Scala | PostgreSQL: Custom")
|
||||
class PostgreSQLCustomIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("list succeeds with empty list")
|
||||
def listEmpty(): Unit =
|
||||
Using(PgDB()) { db => CustomFunctions.listEmpty(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("list succeeds with a non-empty list")
|
||||
def listAll(): Unit =
|
||||
Using(PgDB()) { db => CustomFunctions.listAll(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("single succeeds when document not found")
|
||||
def singleNone(): Unit =
|
||||
Using(PgDB()) { db => CustomFunctions.singleNone(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("single succeeds when a document is found")
|
||||
def singleOne(): Unit =
|
||||
Using(PgDB()) { db => CustomFunctions.singleOne(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("nonQuery makes changes")
|
||||
def nonQueryChanges(): Unit =
|
||||
Using(PgDB()) { db => CustomFunctions.nonQueryChanges(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("nonQuery makes no changes when where clause matches nothing")
|
||||
def nonQueryNoChanges(): Unit =
|
||||
Using(PgDB()) { db => CustomFunctions.nonQueryNoChanges(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("scalar succeeds")
|
||||
def scalar(): Unit =
|
||||
Using(PgDB()) { db => CustomFunctions.scalar(db) }
|
||||
@@ -0,0 +1,31 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | PostgreSQL: Definition")
|
||||
class PostgreSQLDefinitionIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureTable creates table and index")
|
||||
def ensureTable(): Unit =
|
||||
Using(PgDB()) { db => DefinitionFunctions.ensureTable(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureFieldIndex creates an index")
|
||||
def ensureFieldIndex(): Unit =
|
||||
Using(PgDB()) { db => DefinitionFunctions.ensureFieldIndex(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureDocumentIndex creates a full index")
|
||||
def ensureDocumentIndexFull(): Unit =
|
||||
Using(PgDB()) { db => DefinitionFunctions.ensureDocumentIndexFull(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureDocumentIndex creates an optimized index")
|
||||
def ensureDocumentIndexOptimized(): Unit =
|
||||
Using(PgDB()) { db => DefinitionFunctions.ensureDocumentIndexOptimized(db) }
|
||||
@@ -0,0 +1,51 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | PostgreSQL: Delete")
|
||||
class PostgreSQLDeleteIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("byId deletes a matching ID")
|
||||
def byIdMatch(): Unit =
|
||||
Using(PgDB()) { db => DeleteFunctions.byIdMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId succeeds when no ID matches")
|
||||
def byIdNoMatch(): Unit =
|
||||
Using(PgDB()) { db => DeleteFunctions.byIdNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields deletes matching documents")
|
||||
def byFieldsMatch(): Unit =
|
||||
Using(PgDB()) { db => DeleteFunctions.byFieldsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when no documents match")
|
||||
def byFieldsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => DeleteFunctions.byFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains deletes matching documents")
|
||||
def byContainsMatch(): Unit =
|
||||
Using(PgDB()) { db => DeleteFunctions.byContainsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains succeeds when no documents match")
|
||||
def byContainsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => DeleteFunctions.byContainsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath deletes matching documents")
|
||||
def byJsonPathMatch(): Unit =
|
||||
Using(PgDB()) { db => DeleteFunctions.byJsonPathMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath succeeds when no documents match")
|
||||
def byJsonPathNoMatch(): Unit =
|
||||
Using(PgDB()) { db => DeleteFunctions.byJsonPathNoMatch(db) }
|
||||
@@ -0,0 +1,56 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | PostgreSQL: Document")
|
||||
class PostgreSQLDocumentIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("insert works with default values")
|
||||
def insertDefault(): Unit =
|
||||
Using(PgDB()) { db => DocumentFunctions.insertDefault(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("insert fails with duplicate key")
|
||||
def insertDupe(): Unit =
|
||||
Using(PgDB()) { db => DocumentFunctions.insertDupe(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("insert succeeds with numeric auto IDs")
|
||||
def insertNumAutoId(): Unit =
|
||||
Using(PgDB()) { db => DocumentFunctions.insertNumAutoId(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("insert succeeds with UUID auto ID")
|
||||
def insertUUIDAutoId(): Unit =
|
||||
Using(PgDB()) { db => DocumentFunctions.insertUUIDAutoId(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("insert succeeds with random string auto ID")
|
||||
def insertStringAutoId(): Unit =
|
||||
Using(PgDB()) { db => DocumentFunctions.insertStringAutoId(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("save updates an existing document")
|
||||
def saveMatch(): Unit =
|
||||
Using(PgDB()) { db => DocumentFunctions.saveMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("save inserts a new document")
|
||||
def saveNoMatch(): Unit =
|
||||
Using(PgDB()) { db => DocumentFunctions.saveNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("update replaces an existing document")
|
||||
def updateMatch(): Unit =
|
||||
Using(PgDB()) { db => DocumentFunctions.updateMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("update succeeds when no document exists")
|
||||
def updateNoMatch(): Unit =
|
||||
Using(PgDB()) { db => DocumentFunctions.updateNoMatch(db) }
|
||||
@@ -0,0 +1,51 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | PostgreSQL: Exists")
|
||||
class PostgreSQLExistsIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("byId returns true when a document matches the ID")
|
||||
def byIdMatch(): Unit =
|
||||
Using(PgDB()) { db => ExistsFunctions.byIdMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId returns false when no document matches the ID")
|
||||
def byIdNoMatch(): Unit =
|
||||
Using(PgDB()) { db => ExistsFunctions.byIdNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields returns true when documents match")
|
||||
def byFieldsMatch(): Unit =
|
||||
Using(PgDB()) { db => ExistsFunctions.byFieldsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields returns false when no documents match")
|
||||
def byFieldsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => ExistsFunctions.byFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains returns true when documents match")
|
||||
def byContainsMatch(): Unit =
|
||||
Using(PgDB()) { db => ExistsFunctions.byContainsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains returns false when no documents match")
|
||||
def byContainsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => ExistsFunctions.byContainsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath returns true when documents match")
|
||||
def byJsonPathMatch(): Unit =
|
||||
Using(PgDB()) { db => ExistsFunctions.byJsonPathMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath returns false when no documents match")
|
||||
def byJsonPathNoMatch(): Unit =
|
||||
Using(PgDB()) { db => ExistsFunctions.byJsonPathNoMatch(db) }
|
||||
@@ -0,0 +1,172 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* PostgreSQL integration tests for the `Find` object / `find*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | PostgreSQL: Find")
|
||||
class PostgreSQLFindIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("all retrieves all documents")
|
||||
def allDefault(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.allDefault(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("all sorts data ascending")
|
||||
def allAscending(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.allAscending(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("all sorts data descending")
|
||||
def allDescending(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.allDescending(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("all sorts data numerically")
|
||||
def allNumOrder(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.allNumOrder(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("all succeeds with an empty table")
|
||||
def allEmpty(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.allEmpty(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId retrieves a document via a string ID")
|
||||
def byIdString(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byIdString(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId retrieves a document via a numeric ID")
|
||||
def byIdNumber(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byIdNumber(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId returns null when a matching ID is not found")
|
||||
def byIdNotFound(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byIdNotFound(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields retrieves matching documents")
|
||||
def byFieldsMatch(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byFieldsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields retrieves ordered matching documents")
|
||||
def byFieldsMatchOrdered(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byFieldsMatchOrdered(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields retrieves matching documents with a numeric IN clause")
|
||||
def byFieldsMatchNumIn(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byFieldsMatchNumIn(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when no documents match")
|
||||
def byFieldsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison")
|
||||
def byFieldsMatchInArray(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byFieldsMatchInArray(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison")
|
||||
def byFieldsNoMatchInArray(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byFieldsNoMatchInArray(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains retrieves matching documents")
|
||||
def byContainsMatch(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byContainsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains retrieves ordered matching documents")
|
||||
def byContainsMatchOrdered(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byContainsMatchOrdered(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains succeeds when no documents match")
|
||||
def byContainsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byContainsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath retrieves matching documents")
|
||||
def byJsonPathMatch(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byJsonPathMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath retrieves ordered matching documents")
|
||||
def byJsonPathMatchOrdered(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byJsonPathMatchOrdered(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath succeeds when no documents match")
|
||||
def byJsonPathNoMatch(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.byJsonPathNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByFields retrieves a matching document")
|
||||
def firstByFieldsMatchOne(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByFieldsMatchOne(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByFields retrieves a matching document among many")
|
||||
def firstByFieldsMatchMany(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByFieldsMatchMany(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByFields retrieves a matching document among many (ordered)")
|
||||
def firstByFieldsMatchOrdered(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByFieldsMatchOrdered(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByFields returns null when no document matches")
|
||||
def firstByFieldsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByContains retrieves a matching document")
|
||||
def firstByContainsMatchOne(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByContainsMatchOne(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByContains retrieves a matching document among many")
|
||||
def firstByContainsMatchMany(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByContainsMatchMany(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByContains retrieves a matching document among many (ordered)")
|
||||
def firstByContainsMatchOrdered(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByContainsMatchOrdered(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByContains returns null when no document matches")
|
||||
def firstByContainsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByContainsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByJsonPath retrieves a matching document")
|
||||
def firstByJsonPathMatchOne(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByJsonPathMatchOne(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByJsonPath retrieves a matching document among many")
|
||||
def firstByJsonPathMatchMany(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByJsonPathMatchMany(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByJsonPath retrieves a matching document among many (ordered)")
|
||||
def firstByJsonPathMatchOrdered(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByJsonPathMatchOrdered(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByJsonPath returns null when no document matches")
|
||||
def firstByJsonPathNoMatch(): Unit =
|
||||
Using(PgDB()) { db => FindFunctions.firstByJsonPathNoMatch(db) }
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | PostgreSQL: Patch")
|
||||
class PostgreSQLPatchIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("byId patches an existing document")
|
||||
def byIdMatch(): Unit =
|
||||
Using(PgDB()) { db => PatchFunctions. byIdMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId succeeds for a non-existent document")
|
||||
def byIdNoMatch(): Unit =
|
||||
Using(PgDB()) { db => PatchFunctions. byIdNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields patches matching document")
|
||||
def byFieldsMatch(): Unit =
|
||||
Using(PgDB()) { db => PatchFunctions. byFieldsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when no documents match")
|
||||
def byFieldsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => PatchFunctions. byFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains patches matching document")
|
||||
def byContainsMatch(): Unit =
|
||||
Using(PgDB()) { db => PatchFunctions. byContainsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains succeeds when no documents match")
|
||||
def byContainsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => PatchFunctions. byContainsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath patches matching document")
|
||||
def byJsonPathMatch(): Unit =
|
||||
Using(PgDB()) { db => PatchFunctions. byJsonPathMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath succeeds when no documents match")
|
||||
def byJsonPathNoMatch(): Unit =
|
||||
Using(PgDB()) { db => PatchFunctions. byJsonPathNoMatch(db) }
|
||||
@@ -0,0 +1,71 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | PostgreSQL: RemoveFields")
|
||||
class PostgreSQLRemoveFieldsIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("byId removes fields from an existing document")
|
||||
def byIdMatchFields(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byIdMatchFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId succeeds when fields do not exist on an existing document")
|
||||
def byIdMatchNoFields(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byIdMatchNoFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId succeeds when no document exists")
|
||||
def byIdNoMatch(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byIdNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields removes fields from matching documents")
|
||||
def byFieldsMatchFields(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byFieldsMatchFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when fields do not exist on matching documents")
|
||||
def byFieldsMatchNoFields(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byFieldsMatchNoFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when no matching documents exist")
|
||||
def byFieldsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains removes fields from matching documents")
|
||||
def byContainsMatchFields(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byContainsMatchFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains succeeds when fields do not exist on matching documents")
|
||||
def byContainsMatchNoFields(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byContainsMatchNoFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains succeeds when no matching documents exist")
|
||||
def byContainsNoMatch(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byContainsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath removes fields from matching documents")
|
||||
def byJsonPathMatchFields(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byJsonPathMatchFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath succeeds when fields do not exist on matching documents")
|
||||
def byJsonPathMatchNoFields(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byJsonPathMatchNoFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath succeeds when no matching documents exist")
|
||||
def byJsonPathNoMatch(): Unit =
|
||||
Using(PgDB()) { db => RemoveFieldsFunctions. byJsonPathNoMatch(db) }
|
||||
@@ -0,0 +1,92 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
object RemoveFieldsFunctions:
|
||||
|
||||
def byIdMatchFields(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
db.conn.removeFieldsById(TEST_TABLE, "two", "sub" :: "value" :: Nil)
|
||||
val doc = db.conn.findById[String, JsonDocument](TEST_TABLE, "two")
|
||||
assertTrue(doc.isDefined, "There should have been a document returned")
|
||||
assertEquals("", doc.get.value, "The value should have been empty")
|
||||
assertNull(doc.get.sub, "The sub-document should have been removed")
|
||||
|
||||
def byIdMatchNoFields(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.existsByFields(TEST_TABLE, Field.exists("a_field_that_does_not_exist") :: Nil))
|
||||
db.conn.removeFieldsById(TEST_TABLE, "one", "a_field_that_does_not_exist" :: Nil) // no exception = pass
|
||||
|
||||
def byIdNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.existsById(TEST_TABLE, "fifty"))
|
||||
db.conn.removeFieldsById(TEST_TABLE, "fifty", "sub" :: Nil) // no exception = pass
|
||||
|
||||
def byFieldsMatchFields(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val fields = Field.equal("numValue", 17) :: Nil
|
||||
db.conn.removeFieldsByFields(TEST_TABLE, fields, "sub" :: Nil)
|
||||
val doc = db.conn.findFirstByFields[JsonDocument](TEST_TABLE, fields)
|
||||
assertTrue(doc.isDefined, "The document should have been returned")
|
||||
assertEquals("four", doc.get.id, "An incorrect document was returned")
|
||||
assertNull(doc.get.sub, "The sub-document should have been removed")
|
||||
|
||||
def byFieldsMatchNoFields(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.existsByFields(TEST_TABLE, Field.exists("nada") :: Nil))
|
||||
db.conn.removeFieldsByFields(TEST_TABLE, Field.equal("numValue", 17) :: Nil, "nada" :: Nil) // no exn = pass
|
||||
|
||||
def byFieldsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val fields = Field.notEqual("missing", "nope") :: Nil
|
||||
assertFalse(db.conn.existsByFields(TEST_TABLE, fields))
|
||||
db.conn.removeFieldsByFields(TEST_TABLE, fields, "value" :: Nil) // no exception = pass
|
||||
|
||||
def byContainsMatchFields(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val criteria = Map.Map1("sub", Map.Map1("foo", "green"))
|
||||
db.conn.removeFieldsByContains(TEST_TABLE, criteria, "value" :: Nil)
|
||||
val docs = db.conn.findByContains[JsonDocument, Map.Map1[String, Map.Map1[String, String]]](TEST_TABLE, criteria)
|
||||
assertEquals(2, docs.size, "There should have been 2 documents returned")
|
||||
docs.foreach { doc =>
|
||||
assertTrue(("two" :: "four" :: Nil).contains(doc.id), s"An incorrect document was returned (${doc.id})")
|
||||
assertEquals("", doc.value, "The value should have been empty")
|
||||
}
|
||||
|
||||
def byContainsMatchNoFields(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.existsByFields(TEST_TABLE, Field.exists("invalid_field") :: Nil))
|
||||
db.conn.removeFieldsByContains(TEST_TABLE, Map.Map1("sub", Map.Map1("foo", "green")), "invalid_field" :: Nil)
|
||||
// no exception = pass
|
||||
|
||||
def byContainsNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val contains = Map.Map1("value", "substantial")
|
||||
assertFalse(db.conn.existsByContains(TEST_TABLE, contains))
|
||||
db.conn.removeFieldsByContains(TEST_TABLE, contains, "numValue" :: Nil)
|
||||
|
||||
def byJsonPathMatchFields(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val path = "$.value ? (@ == \"purple\")"
|
||||
db.conn.removeFieldsByJsonPath(TEST_TABLE, path, "sub" :: Nil)
|
||||
val docs = db.conn.findByJsonPath[JsonDocument](TEST_TABLE, path)
|
||||
assertEquals(2, docs.size, "There should have been 2 documents returned")
|
||||
docs.foreach { doc =>
|
||||
assertTrue(("four" :: "five" :: Nil).contains(doc.id), s"An incorrect document was returned (${doc.id})")
|
||||
assertNull(doc.sub, "The sub-document should have been removed")
|
||||
}
|
||||
|
||||
def byJsonPathMatchNoFields(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
assertFalse(db.conn.existsByFields(TEST_TABLE, Field.exists("submarine") :: Nil))
|
||||
db.conn.removeFieldsByJsonPath(TEST_TABLE, "$.value ? (@ == \"purple\")", "submarine" :: Nil) // no exn = pass
|
||||
|
||||
def byJsonPathNoMatch(db: ThrowawayDatabase): Unit =
|
||||
JsonDocument.load(db)
|
||||
val path = "$.value ? (@ == \"mauve\")"
|
||||
assertFalse(db.conn.existsByJsonPath(TEST_TABLE, path))
|
||||
db.conn.removeFieldsByJsonPath(TEST_TABLE, path, "value" :: Nil) // no exception = pass
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
@DisplayName("Scala | SQLite: Count")
|
||||
class SQLiteCountIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("all counts all documents")
|
||||
def all(): Unit =
|
||||
Using(SQLiteDB()) { db => CountFunctions.all(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields counts documents by a numeric value")
|
||||
def byFieldsNumeric(): Unit =
|
||||
Using(SQLiteDB()) { db => CountFunctions.byFieldsNumeric(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields counts documents by a alphanumeric value")
|
||||
def byFieldsAlpha(): Unit =
|
||||
Using(SQLiteDB()) { db => CountFunctions.byFieldsAlpha(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails")
|
||||
def byContainsMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => CountFunctions.byContainsMatch(db)) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails")
|
||||
def byJsonPathMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => CountFunctions.byJsonPathMatch(db)) }
|
||||
@@ -0,0 +1,43 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
@DisplayName("Scala | SQLite: Custom")
|
||||
class SQLiteCustomIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("list succeeds with empty list")
|
||||
def listEmpty(): Unit =
|
||||
Using(SQLiteDB()) { db => CustomFunctions.listEmpty(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("list succeeds with a non-empty list")
|
||||
def listAll(): Unit =
|
||||
Using(SQLiteDB()) { db => CustomFunctions.listAll(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("single succeeds when document not found")
|
||||
def singleNone(): Unit =
|
||||
Using(SQLiteDB()) { db => CustomFunctions.singleNone(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("single succeeds when a document is found")
|
||||
def singleOne(): Unit =
|
||||
Using(SQLiteDB()) { db => CustomFunctions.singleOne(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("nonQuery makes changes")
|
||||
def nonQueryChanges(): Unit =
|
||||
Using(SQLiteDB()) { db => CustomFunctions.nonQueryChanges(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("nonQuery makes no changes when where clause matches nothing")
|
||||
def nonQueryNoChanges(): Unit =
|
||||
Using(SQLiteDB()) { db => CustomFunctions.nonQueryNoChanges(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("scalar succeeds")
|
||||
def scalar(): Unit =
|
||||
Using(SQLiteDB()) { db => CustomFunctions.scalar(db) }
|
||||
@@ -0,0 +1,30 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import solutions.bitbadger.documents.{Configuration, Parameter, ParameterType}
|
||||
import solutions.bitbadger.documents.scala.Results
|
||||
import solutions.bitbadger.documents.scala.extensions.*
|
||||
import solutions.bitbadger.documents.scala.tests.TEST_TABLE
|
||||
|
||||
import java.io.File
|
||||
import java.sql.Connection
|
||||
|
||||
/**
|
||||
* A wrapper for a throwaway SQLite database
|
||||
*/
|
||||
class SQLiteDB extends ThrowawayDatabase:
|
||||
|
||||
Configuration.setConnectionString(s"jdbc:sqlite:$dbName.db")
|
||||
|
||||
override val conn: Connection = Configuration.dbConn()
|
||||
|
||||
conn.ensureTable(TEST_TABLE)
|
||||
|
||||
override def close(): Unit =
|
||||
conn.close()
|
||||
File(s"$dbName.db").delete()
|
||||
Configuration.setConnectionString(null)
|
||||
|
||||
override def dbObjectExists(name: String): Boolean =
|
||||
conn.customScalar[Boolean]("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE name = :name) AS it",
|
||||
Parameter(":name", ParameterType.STRING, name) :: Nil, Results.toExists)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* SQLite integration tests for the `Definition` object / `ensure*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | SQLite: Definition")
|
||||
class SQLiteDefinitionIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureTable creates table and index")
|
||||
def ensureTable(): Unit =
|
||||
Using(SQLiteDB()) { db => DefinitionFunctions.ensureTable(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureFieldIndex creates an index")
|
||||
def ensureFieldIndex(): Unit =
|
||||
Using(SQLiteDB()) { db => DefinitionFunctions.ensureFieldIndex(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("ensureDocumentIndex creates a full index")
|
||||
def ensureDocumentIndexFull(): Unit =
|
||||
Using(SQLiteDB()) { db =>
|
||||
assertThrows(classOf[DocumentException], () => DefinitionFunctions.ensureDocumentIndexFull(db))
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | SQLite: Delete")
|
||||
class SQLiteDeleteIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("byId deletes a matching ID")
|
||||
def byIdMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => DeleteFunctions.byIdMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId succeeds when no ID matches")
|
||||
def byIdNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => DeleteFunctions.byIdNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields deletes matching documents")
|
||||
def byFieldsMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => DeleteFunctions.byFieldsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when no documents match")
|
||||
def byFieldsNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => DeleteFunctions.byFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails")
|
||||
def byContainsFails(): Unit =
|
||||
Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => DeleteFunctions.byContainsMatch(db)) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails")
|
||||
def byJsonPathFails(): Unit =
|
||||
Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => DeleteFunctions.byJsonPathMatch(db)) }
|
||||
@@ -0,0 +1,56 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | SQLite: Document")
|
||||
class SQLiteDocumentIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("insert works with default values")
|
||||
def insertDefault(): Unit =
|
||||
Using(SQLiteDB()) { db => DocumentFunctions.insertDefault(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("insert fails with duplicate key")
|
||||
def insertDupe(): Unit =
|
||||
Using(SQLiteDB()) { db => DocumentFunctions.insertDupe(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("insert succeeds with numeric auto IDs")
|
||||
def insertNumAutoId(): Unit =
|
||||
Using(SQLiteDB()) { db => DocumentFunctions.insertNumAutoId(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("insert succeeds with UUID auto ID")
|
||||
def insertUUIDAutoId(): Unit =
|
||||
Using(SQLiteDB()) { db => DocumentFunctions.insertUUIDAutoId(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("insert succeeds with random string auto ID")
|
||||
def insertStringAutoId(): Unit =
|
||||
Using(SQLiteDB()) { db => DocumentFunctions.insertStringAutoId(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("save updates an existing document")
|
||||
def saveMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => DocumentFunctions.saveMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("save inserts a new document")
|
||||
def saveNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => DocumentFunctions.saveNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("update replaces an existing document")
|
||||
def updateMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => DocumentFunctions.updateMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("update succeeds when no document exists")
|
||||
def updateNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => DocumentFunctions.updateNoMatch(db) }
|
||||
@@ -0,0 +1,43 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | SQLite: Exists")
|
||||
class SQLiteExistsIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("byId returns true when a document matches the ID")
|
||||
def byIdMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => ExistsFunctions.byIdMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId returns false when no document matches the ID")
|
||||
def byIdNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => ExistsFunctions.byIdNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields returns true when documents match")
|
||||
def byFieldsMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => ExistsFunctions.byFieldsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields returns false when no documents match")
|
||||
def byFieldsNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => ExistsFunctions.byFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails")
|
||||
def byContainsFails(): Unit =
|
||||
Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => ExistsFunctions.byContainsMatch(db)) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails")
|
||||
def byJsonPathFails(): Unit =
|
||||
Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => ExistsFunctions.byJsonPathMatch(db)) }
|
||||
@@ -0,0 +1,127 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* SQLite integration tests for the `Find` object / `find*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | SQLite: Find")
|
||||
class SQLiteFindIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("all retrieves all documents")
|
||||
def allDefault(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.allDefault(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("all sorts data ascending")
|
||||
def allAscending(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.allAscending(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("all sorts data descending")
|
||||
def allDescending(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.allDescending(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("all sorts data numerically")
|
||||
def allNumOrder(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.allNumOrder(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("all succeeds with an empty table")
|
||||
def allEmpty(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.allEmpty(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId retrieves a document via a string ID")
|
||||
def byIdString(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.byIdString(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId retrieves a document via a numeric ID")
|
||||
def byIdNumber(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.byIdNumber(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId returns null when a matching ID is not found")
|
||||
def byIdNotFound(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.byIdNotFound(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields retrieves matching documents")
|
||||
def byFieldsMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.byFieldsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields retrieves ordered matching documents")
|
||||
def byFieldsMatchOrdered(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.byFieldsMatchOrdered(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields retrieves matching documents with a numeric IN clause")
|
||||
def byFieldsMatchNumIn(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.byFieldsMatchNumIn(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when no documents match")
|
||||
def byFieldsNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.byFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison")
|
||||
def byFieldsMatchInArray(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.byFieldsMatchInArray(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison")
|
||||
def byFieldsNoMatchInArray(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.byFieldsNoMatchInArray(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails")
|
||||
def byContainsFails(): Unit =
|
||||
Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => FindFunctions.byContainsMatch(db)) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails")
|
||||
def byJsonPathFails(): Unit =
|
||||
Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => FindFunctions.byJsonPathMatch(db)) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByFields retrieves a matching document")
|
||||
def firstByFieldsMatchOne(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.firstByFieldsMatchOne(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByFields retrieves a matching document among many")
|
||||
def firstByFieldsMatchMany(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.firstByFieldsMatchMany(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByFields retrieves a matching document among many (ordered)")
|
||||
def firstByFieldsMatchOrdered(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.firstByFieldsMatchOrdered(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByFields returns null when no document matches")
|
||||
def firstByFieldsNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => FindFunctions.firstByFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByContains fails")
|
||||
def firstByContainsFails(): Unit =
|
||||
Using(SQLiteDB()) { db =>
|
||||
assertThrows(classOf[DocumentException], () => FindFunctions.firstByContainsMatchOne(db))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("firstByJsonPath fails")
|
||||
def firstByJsonPathFails(): Unit =
|
||||
Using(SQLiteDB()) { db =>
|
||||
assertThrows(classOf[DocumentException], () => FindFunctions.firstByJsonPathMatchOne(db))
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | SQLite: Patch")
|
||||
class SQLitePatchIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("byId patches an existing document")
|
||||
def byIdMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => PatchFunctions.byIdMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId succeeds for a non-existent document")
|
||||
def byIdNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => PatchFunctions.byIdNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields patches matching document")
|
||||
def byFieldsMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => PatchFunctions.byFieldsMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when no documents match")
|
||||
def byFieldsNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => PatchFunctions.byFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails")
|
||||
def byContainsFails(): Unit =
|
||||
Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => PatchFunctions.byContainsMatch(db)) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails")
|
||||
def byJsonPathFails(): Unit =
|
||||
Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => PatchFunctions.byJsonPathMatch(db)) }
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
/**
|
||||
* SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
|
||||
*/
|
||||
@DisplayName("Scala | SQLite: RemoveFields")
|
||||
class SQLiteRemoveFieldsIT:
|
||||
|
||||
@Test
|
||||
@DisplayName("byId removes fields from an existing document")
|
||||
def byIdMatchFields(): Unit =
|
||||
Using(SQLiteDB()) { db => RemoveFieldsFunctions.byIdMatchFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId succeeds when fields do not exist on an existing document")
|
||||
def byIdMatchNoFields(): Unit =
|
||||
Using(SQLiteDB()) { db => RemoveFieldsFunctions.byIdMatchNoFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byId succeeds when no document exists")
|
||||
def byIdNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => RemoveFieldsFunctions.byIdNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields removes fields from matching documents")
|
||||
def byFieldsMatchFields(): Unit =
|
||||
Using(SQLiteDB()) { db => RemoveFieldsFunctions.byFieldsMatchFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when fields do not exist on matching documents")
|
||||
def byFieldsMatchNoFields(): Unit =
|
||||
Using(SQLiteDB()) { db => RemoveFieldsFunctions.byFieldsMatchNoFields(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byFields succeeds when no matching documents exist")
|
||||
def byFieldsNoMatch(): Unit =
|
||||
Using(SQLiteDB()) { db => RemoveFieldsFunctions.byFieldsNoMatch(db) }
|
||||
|
||||
@Test
|
||||
@DisplayName("byContains fails")
|
||||
def byContainsFails(): Unit =
|
||||
Using(SQLiteDB()) { db =>
|
||||
assertThrows(classOf[DocumentException], () => RemoveFieldsFunctions.byContainsMatchFields(db))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("byJsonPath fails")
|
||||
def byJsonPathFails(): Unit =
|
||||
Using(SQLiteDB()) { db =>
|
||||
assertThrows(classOf[DocumentException], () => RemoveFieldsFunctions.byJsonPathMatchFields(db))
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
class SubDocument(val foo: String = "", val bar: String = "")
|
||||
@@ -0,0 +1,28 @@
|
||||
package solutions.bitbadger.documents.scala.tests.integration
|
||||
|
||||
import solutions.bitbadger.documents.AutoId
|
||||
import solutions.bitbadger.documents.java.DocumentConfig
|
||||
|
||||
import java.sql.Connection
|
||||
|
||||
/**
|
||||
* Common trait for PostgreSQL and SQLite throwaway databases
|
||||
*/
|
||||
trait ThrowawayDatabase extends AutoCloseable:
|
||||
|
||||
/** The database connection for the throwaway database */
|
||||
def conn: Connection
|
||||
|
||||
/**
|
||||
* Determine if a database object exists
|
||||
*
|
||||
* @param name The name of the object whose existence should be checked
|
||||
* @return True if the object exists, false if not
|
||||
*/
|
||||
def dbObjectExists(name: String): Boolean
|
||||
|
||||
/** The name for the throwaway database */
|
||||
val dbName = s"throwaway_${AutoId.generateRandomString(8)}"
|
||||
|
||||
// Use a Jackson-based document serializer for testing
|
||||
DocumentConfig.setSerializer(JacksonDocumentSerializer())
|
||||
@@ -0,0 +1,6 @@
|
||||
package solutions.bitbadger.documents.scala
|
||||
|
||||
package object tests {
|
||||
|
||||
def TEST_TABLE = "test_table"
|
||||
}
|
||||
Reference in New Issue
Block a user