Version 4 #6
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -396,3 +396,4 @@ FodyWeavers.xsd
|
||||||
|
|
||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
*.sln.iml
|
*.sln.iml
|
||||||
|
**/.idea
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
namespace BitBadger.Documents
|
namespace BitBadger.Documents
|
||||||
|
|
||||||
|
open System.Security.Cryptography
|
||||||
|
|
||||||
/// The types of logical operations available for JSON fields
|
/// The types of logical operations available for JSON fields
|
||||||
[<Struct>]
|
[<Struct>]
|
||||||
type Op =
|
type Op =
|
||||||
|
@ -35,6 +37,12 @@ type Op =
|
||||||
| NEX -> "IS NULL"
|
| NEX -> "IS NULL"
|
||||||
|
|
||||||
|
|
||||||
|
/// The dialect in which a command should be rendered
|
||||||
|
[<Struct>]
|
||||||
|
type Dialect =
|
||||||
|
| PostgreSQL
|
||||||
|
| SQLite
|
||||||
|
|
||||||
/// Criteria for a field WHERE clause
|
/// Criteria for a field WHERE clause
|
||||||
type Field = {
|
type Field = {
|
||||||
/// The name of the field
|
/// The name of the field
|
||||||
|
@ -45,43 +53,166 @@ type Field = {
|
||||||
|
|
||||||
/// The value of the field
|
/// The value of the field
|
||||||
Value: obj
|
Value: obj
|
||||||
|
|
||||||
|
/// The name of the parameter for this field
|
||||||
|
ParameterName: string option
|
||||||
|
|
||||||
|
/// The table qualifier for this field
|
||||||
|
Qualifier: string option
|
||||||
} with
|
} with
|
||||||
|
|
||||||
/// Create an equals (=) field criterion
|
/// Create an equals (=) field criterion
|
||||||
static member EQ name (value: obj) =
|
static member EQ name (value: obj) =
|
||||||
{ Name = name; Op = EQ; Value = value }
|
{ Name = name; Op = EQ; Value = value; ParameterName = None; Qualifier = None }
|
||||||
|
|
||||||
/// Create a greater than (>) field criterion
|
/// Create a greater than (>) field criterion
|
||||||
static member GT name (value: obj) =
|
static member GT name (value: obj) =
|
||||||
{ Name = name; Op = GT; Value = value }
|
{ Name = name; Op = GT; Value = value; ParameterName = None; Qualifier = None }
|
||||||
|
|
||||||
/// Create a greater than or equal to (>=) field criterion
|
/// Create a greater than or equal to (>=) field criterion
|
||||||
static member GE name (value: obj) =
|
static member GE name (value: obj) =
|
||||||
{ Name = name; Op = GE; Value = value }
|
{ Name = name; Op = GE; Value = value; ParameterName = None; Qualifier = None }
|
||||||
|
|
||||||
/// Create a less than (<) field criterion
|
/// Create a less than (<) field criterion
|
||||||
static member LT name (value: obj) =
|
static member LT name (value: obj) =
|
||||||
{ Name = name; Op = LT; Value = value }
|
{ Name = name; Op = LT; Value = value; ParameterName = None; Qualifier = None }
|
||||||
|
|
||||||
/// Create a less than or equal to (<=) field criterion
|
/// Create a less than or equal to (<=) field criterion
|
||||||
static member LE name (value: obj) =
|
static member LE name (value: obj) =
|
||||||
{ Name = name; Op = LE; Value = value }
|
{ Name = name; Op = LE; Value = value; ParameterName = None; Qualifier = None }
|
||||||
|
|
||||||
/// Create a not equals (<>) field criterion
|
/// Create a not equals (<>) field criterion
|
||||||
static member NE name (value: obj) =
|
static member NE name (value: obj) =
|
||||||
{ Name = name; Op = NE; Value = value }
|
{ Name = name; Op = NE; Value = value; ParameterName = None; Qualifier = None }
|
||||||
|
|
||||||
/// Create a BETWEEN field criterion
|
/// Create a BETWEEN field criterion
|
||||||
static member BT name (min: obj) (max: obj) =
|
static member BT name (min: obj) (max: obj) =
|
||||||
{ Name = name; Op = BT; Value = [ min; max ] }
|
{ Name = name; Op = BT; Value = [ min; max ]; ParameterName = None; Qualifier = None }
|
||||||
|
|
||||||
/// Create an exists (IS NOT NULL) field criterion
|
/// Create an exists (IS NOT NULL) field criterion
|
||||||
static member EX name =
|
static member EX name =
|
||||||
{ Name = name; Op = EX; Value = obj () }
|
{ Name = name; Op = EX; Value = obj (); ParameterName = None; Qualifier = None }
|
||||||
|
|
||||||
/// Create a not exists (IS NULL) field criterion
|
/// Create a not exists (IS NULL) field criterion
|
||||||
static member NEX name =
|
static member NEX name =
|
||||||
{ Name = name; Op = NEX; Value = obj () }
|
{ Name = name; Op = NEX; Value = obj (); ParameterName = None; Qualifier = None }
|
||||||
|
|
||||||
|
/// Transform a field name (a.b.c) to a path for the given SQL dialect
|
||||||
|
static member NameToPath (name: string) dialect =
|
||||||
|
let path =
|
||||||
|
if name.Contains '.' then
|
||||||
|
match dialect with
|
||||||
|
| PostgreSQL -> "#>>'{" + String.concat "," (name.Split '.') + "}'"
|
||||||
|
| SQLite -> "->>'" + String.concat "'->>'" (name.Split '.') + "'"
|
||||||
|
else $"->>'{name}'"
|
||||||
|
$"data{path}"
|
||||||
|
|
||||||
|
/// Create a field with a given name, but no other properties filled (op will be EQ, value will be "")
|
||||||
|
static member Named name =
|
||||||
|
{ Name = name; Op = EQ; Value = ""; ParameterName = None; Qualifier = None }
|
||||||
|
|
||||||
|
/// Specify the name of the parameter for this field
|
||||||
|
member this.WithParameterName name =
|
||||||
|
{ this with ParameterName = Some name }
|
||||||
|
|
||||||
|
/// Specify a qualifier (alias) for the table from which this field will be referenced
|
||||||
|
member this.WithQualifier alias =
|
||||||
|
{ this with Qualifier = Some alias }
|
||||||
|
|
||||||
|
/// Get the qualified path to the field
|
||||||
|
member this.Path dialect =
|
||||||
|
(this.Qualifier |> Option.map (fun q -> $"{q}.") |> Option.defaultValue "") + Field.NameToPath this.Name dialect
|
||||||
|
|
||||||
|
|
||||||
|
/// How fields should be matched
|
||||||
|
[<Struct>]
|
||||||
|
type FieldMatch =
|
||||||
|
/// Any field matches (OR)
|
||||||
|
| Any
|
||||||
|
/// All fields match (AND)
|
||||||
|
| All
|
||||||
|
|
||||||
|
/// The SQL value implementing each matching strategy
|
||||||
|
override this.ToString() =
|
||||||
|
match this with Any -> "OR" | All -> "AND"
|
||||||
|
|
||||||
|
|
||||||
|
/// Derive parameter names (each instance wraps a counter to uniquely name anonymous fields)
|
||||||
|
type ParameterName() =
|
||||||
|
/// The counter for the next field value
|
||||||
|
let mutable currentIdx = -1
|
||||||
|
|
||||||
|
/// Return the specified name for the parameter, or an anonymous parameter name if none is specified
|
||||||
|
member this.Derive paramName =
|
||||||
|
match paramName with
|
||||||
|
| Some it -> it
|
||||||
|
| None ->
|
||||||
|
currentIdx <- currentIdx + 1
|
||||||
|
$"@field{currentIdx}"
|
||||||
|
|
||||||
|
#if NET6_0
|
||||||
|
open System.Text
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Automatically-generated document ID strategies
|
||||||
|
[<Struct>]
|
||||||
|
type AutoId =
|
||||||
|
/// No automatic IDs will be generated
|
||||||
|
| Disabled
|
||||||
|
/// Generate a MAX-plus-1 numeric value for documents
|
||||||
|
| Number
|
||||||
|
/// Generate a GUID for each document (as a lowercase, no-dashes, 32-character string)
|
||||||
|
| Guid
|
||||||
|
/// Generate a random string of hexadecimal characters for each document
|
||||||
|
| RandomString
|
||||||
|
with
|
||||||
|
/// Generate a GUID string
|
||||||
|
static member GenerateGuid () =
|
||||||
|
System.Guid.NewGuid().ToString "N"
|
||||||
|
|
||||||
|
/// Generate a string of random hexadecimal characters
|
||||||
|
static member GenerateRandomString (length: int) =
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
RandomNumberGenerator.GetHexString(length, lowercase = true)
|
||||||
|
#else
|
||||||
|
RandomNumberGenerator.GetBytes((length / 2) + 1)
|
||||||
|
|> Array.fold (fun (str: StringBuilder) byt -> str.Append(byt.ToString "x2")) (StringBuilder length)
|
||||||
|
|> function it -> it.Length <- length; it.ToString()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Does the given document need an automatic ID generated?
|
||||||
|
static member NeedsAutoId<'T> strategy (document: 'T) idProp =
|
||||||
|
match strategy with
|
||||||
|
| Disabled -> false
|
||||||
|
| _ ->
|
||||||
|
let prop = document.GetType().GetProperty idProp
|
||||||
|
if isNull prop then invalidOp $"{idProp} not found in document"
|
||||||
|
else
|
||||||
|
match strategy with
|
||||||
|
| Number ->
|
||||||
|
if prop.PropertyType = typeof<int8> then
|
||||||
|
let value = prop.GetValue document :?> int8
|
||||||
|
value = int8 0
|
||||||
|
elif prop.PropertyType = typeof<int16> then
|
||||||
|
let value = prop.GetValue document :?> int16
|
||||||
|
value = int16 0
|
||||||
|
elif prop.PropertyType = typeof<int> then
|
||||||
|
let value = prop.GetValue document :?> int
|
||||||
|
value = 0
|
||||||
|
elif prop.PropertyType = typeof<int64> then
|
||||||
|
let value = prop.GetValue document :?> int64
|
||||||
|
value = int64 0
|
||||||
|
else invalidOp "Document ID was not a number; cannot auto-generate a Number ID"
|
||||||
|
| Guid | RandomString ->
|
||||||
|
if prop.PropertyType = typeof<string> then
|
||||||
|
let value =
|
||||||
|
prop.GetValue document
|
||||||
|
|> Option.ofObj
|
||||||
|
|> Option.map (fun it -> it :?> string)
|
||||||
|
|> Option.defaultValue ""
|
||||||
|
value = ""
|
||||||
|
else invalidOp "Document ID was not a string; cannot auto-generate GUID or random string"
|
||||||
|
| Disabled -> false
|
||||||
|
|
||||||
|
|
||||||
/// The required document serialization implementation
|
/// The required document serialization implementation
|
||||||
|
@ -135,7 +266,7 @@ module Configuration =
|
||||||
serializerValue
|
serializerValue
|
||||||
|
|
||||||
/// The serialized name of the ID field for documents
|
/// The serialized name of the ID field for documents
|
||||||
let mutable idFieldValue = "Id"
|
let mutable private idFieldValue = "Id"
|
||||||
|
|
||||||
/// Specify the name of the ID field for documents
|
/// Specify the name of the ID field for documents
|
||||||
[<CompiledName "UseIdField">]
|
[<CompiledName "UseIdField">]
|
||||||
|
@ -147,15 +278,41 @@ module Configuration =
|
||||||
let idField () =
|
let idField () =
|
||||||
idFieldValue
|
idFieldValue
|
||||||
|
|
||||||
|
/// The automatic ID strategy used by the library
|
||||||
|
let mutable private autoIdValue = Disabled
|
||||||
|
|
||||||
|
/// Specify the automatic ID generation strategy used by the library
|
||||||
|
[<CompiledName "UseAutoIdStrategy">]
|
||||||
|
let useAutoIdStrategy it =
|
||||||
|
autoIdValue <- it
|
||||||
|
|
||||||
|
/// Retrieve the currently configured automatic ID generation strategy
|
||||||
|
[<CompiledName "AutoIdStrategy">]
|
||||||
|
let autoIdStrategy () =
|
||||||
|
autoIdValue
|
||||||
|
|
||||||
|
/// The length of automatically generated random strings
|
||||||
|
let mutable private idStringLengthValue = 16
|
||||||
|
|
||||||
|
/// Specify the length of automatically generated random strings
|
||||||
|
[<CompiledName "UseIdStringLength">]
|
||||||
|
let useIdStringLength length =
|
||||||
|
idStringLengthValue <- length
|
||||||
|
|
||||||
|
/// Retrieve the currently configured length of automatically generated random strings
|
||||||
|
[<CompiledName "IdStringLength">]
|
||||||
|
let idStringLength () =
|
||||||
|
idStringLengthValue
|
||||||
|
|
||||||
|
|
||||||
/// Query construction functions
|
/// Query construction functions
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Query =
|
module Query =
|
||||||
|
|
||||||
/// Create a SELECT clause to retrieve the document data from the given table
|
/// Combine a query (select, update, etc.) and a WHERE clause
|
||||||
[<CompiledName "SelectFromTable">]
|
[<CompiledName "StatementWhere">]
|
||||||
let selectFromTable tableName =
|
let statementWhere statement where =
|
||||||
$"SELECT data FROM %s{tableName}"
|
$"%s{statement} WHERE %s{where}"
|
||||||
|
|
||||||
/// Queries to define tables and indexes
|
/// Queries to define tables and indexes
|
||||||
module Definition =
|
module Definition =
|
||||||
|
@ -172,7 +329,7 @@ module Query =
|
||||||
|
|
||||||
/// SQL statement to create an index on one or more fields in a JSON document
|
/// SQL statement to create an index on one or more fields in a JSON document
|
||||||
[<CompiledName "EnsureIndexOn">]
|
[<CompiledName "EnsureIndexOn">]
|
||||||
let ensureIndexOn tableName indexName (fields: string seq) =
|
let ensureIndexOn tableName indexName (fields: string seq) dialect =
|
||||||
let _, tbl = splitSchemaAndTable tableName
|
let _, tbl = splitSchemaAndTable tableName
|
||||||
let jsonFields =
|
let jsonFields =
|
||||||
fields
|
fields
|
||||||
|
@ -180,14 +337,14 @@ module Query =
|
||||||
let parts = it.Split ' '
|
let parts = it.Split ' '
|
||||||
let fieldName = if Array.length parts = 1 then it else parts[0]
|
let fieldName = if Array.length parts = 1 then it else parts[0]
|
||||||
let direction = if Array.length parts < 2 then "" else $" {parts[1]}"
|
let direction = if Array.length parts < 2 then "" else $" {parts[1]}"
|
||||||
$"(data ->> '{fieldName}'){direction}")
|
$"({Field.NameToPath fieldName dialect}){direction}")
|
||||||
|> String.concat ", "
|
|> String.concat ", "
|
||||||
$"CREATE INDEX IF NOT EXISTS idx_{tbl}_%s{indexName} ON {tableName} ({jsonFields})"
|
$"CREATE INDEX IF NOT EXISTS idx_{tbl}_%s{indexName} ON {tableName} ({jsonFields})"
|
||||||
|
|
||||||
/// SQL statement to create a key index for a document table
|
/// SQL statement to create a key index for a document table
|
||||||
[<CompiledName "EnsureKey">]
|
[<CompiledName "EnsureKey">]
|
||||||
let ensureKey tableName =
|
let ensureKey tableName dialect =
|
||||||
(ensureIndexOn tableName "key" [ Configuration.idField () ]).Replace("INDEX", "UNIQUE INDEX")
|
(ensureIndexOn tableName "key" [ Configuration.idField () ] dialect).Replace("INDEX", "UNIQUE INDEX")
|
||||||
|
|
||||||
/// Query to insert a document
|
/// Query to insert a document
|
||||||
[<CompiledName "Insert">]
|
[<CompiledName "Insert">]
|
||||||
|
@ -201,3 +358,53 @@ module Query =
|
||||||
"INSERT INTO %s VALUES (@data) ON CONFLICT ((data->>'%s')) DO UPDATE SET data = EXCLUDED.data"
|
"INSERT INTO %s VALUES (@data) ON CONFLICT ((data->>'%s')) DO UPDATE SET data = EXCLUDED.data"
|
||||||
tableName (Configuration.idField ())
|
tableName (Configuration.idField ())
|
||||||
|
|
||||||
|
/// Query to count documents in a table (no WHERE clause)
|
||||||
|
[<CompiledName "Count">]
|
||||||
|
let count tableName =
|
||||||
|
$"SELECT COUNT(*) AS it FROM %s{tableName}"
|
||||||
|
|
||||||
|
/// Query to check for document existence in a table
|
||||||
|
[<CompiledName "Exists">]
|
||||||
|
let exists tableName where =
|
||||||
|
$"SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE %s{where}) AS it"
|
||||||
|
|
||||||
|
/// Query to select documents from a table (no WHERE clause)
|
||||||
|
[<CompiledName "Find">]
|
||||||
|
let find tableName =
|
||||||
|
$"SELECT data FROM %s{tableName}"
|
||||||
|
|
||||||
|
/// Query to update a document (no WHERE clause)
|
||||||
|
[<CompiledName "Update">]
|
||||||
|
let update tableName =
|
||||||
|
$"UPDATE %s{tableName} SET data = @data"
|
||||||
|
|
||||||
|
/// Query to delete documents from a table (no WHERE clause)
|
||||||
|
[<CompiledName "Delete">]
|
||||||
|
let delete tableName =
|
||||||
|
$"DELETE FROM %s{tableName}"
|
||||||
|
|
||||||
|
/// Create a SELECT clause to retrieve the document data from the given table
|
||||||
|
[<CompiledName "SelectFromTable">]
|
||||||
|
[<System.Obsolete "Use Find instead">]
|
||||||
|
let selectFromTable tableName =
|
||||||
|
find tableName
|
||||||
|
|
||||||
|
/// Create an ORDER BY clause for the given fields
|
||||||
|
[<CompiledName "OrderBy">]
|
||||||
|
let orderBy fields dialect =
|
||||||
|
if Seq.isEmpty fields then ""
|
||||||
|
else
|
||||||
|
fields
|
||||||
|
|> Seq.map (fun it ->
|
||||||
|
if it.Name.Contains ' ' then
|
||||||
|
let parts = it.Name.Split ' '
|
||||||
|
{ it with Name = parts[0] }, Some $" {parts[1]}"
|
||||||
|
else it, None)
|
||||||
|
|> Seq.map (fun (field, direction) ->
|
||||||
|
match dialect, field.Name.StartsWith "n:" with
|
||||||
|
| PostgreSQL, true -> $"({ { field with Name = field.Name[2..] }.Path PostgreSQL})::numeric"
|
||||||
|
| SQLite, true -> { field with Name = field.Name[2..] }.Path SQLite
|
||||||
|
| _, _ -> field.Path dialect
|
||||||
|
|> function path -> path + defaultArg direction "")
|
||||||
|
|> String.concat ", "
|
||||||
|
|> function it -> $" ORDER BY {it}"
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||||
<AssemblyVersion>3.1.0.0</AssemblyVersion>
|
<AssemblyVersion>4.0.0.0</AssemblyVersion>
|
||||||
<FileVersion>3.1.0.0</FileVersion>
|
<FileVersion>4.0.0.0</FileVersion>
|
||||||
<VersionPrefix>3.1.0</VersionPrefix>
|
<VersionPrefix>4.0.0</VersionPrefix>
|
||||||
<PackageReleaseNotes>Add BT (between) operator; drop .NET 7 support</PackageReleaseNotes>
|
<VersionSuffix>rc1</VersionSuffix>
|
||||||
|
<PackageReleaseNotes>Change ByField to ByFields; support dot-access to nested document fields; add Find*Ordered functions/methods; see project site for breaking changes and compatibility</PackageReleaseNotes>
|
||||||
<Authors>danieljsummers</Authors>
|
<Authors>danieljsummers</Authors>
|
||||||
<Company>Bit Badger Solutions</Company>
|
<Company>Bit Badger Solutions</Company>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Library.fs" />
|
<Compile Include="Library.fs" />
|
||||||
<Compile Include="Extensions.fs" />
|
<Compile Include="Extensions.fs" />
|
||||||
|
<Compile Include="Compat.fs" />
|
||||||
<None Include="README.md" Pack="true" PackagePath="\" />
|
<None Include="README.md" Pack="true" PackagePath="\" />
|
||||||
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
270
src/Postgres/Compat.fs
Normal file
270
src/Postgres/Compat.fs
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
namespace BitBadger.Documents.Postgres.Compat
|
||||||
|
|
||||||
|
open BitBadger.Documents
|
||||||
|
open BitBadger.Documents.Postgres
|
||||||
|
|
||||||
|
[<AutoOpen>]
|
||||||
|
module Parameters =
|
||||||
|
|
||||||
|
/// Create a JSON field parameter
|
||||||
|
[<CompiledName "AddField">]
|
||||||
|
[<System.Obsolete "Use addFieldParams (F#) / AddFields (C#) instead ~ will be removed in v4.1">]
|
||||||
|
let addFieldParam name field parameters =
|
||||||
|
addFieldParams [ { field with ParameterName = Some name } ] parameters
|
||||||
|
|
||||||
|
/// Append JSON field name parameters for the given field names to the given parameters
|
||||||
|
[<CompiledName "FieldName">]
|
||||||
|
[<System.Obsolete "Use fieldNameParams (F#) / FieldNames (C#) instead ~ will be removed in v4.1">]
|
||||||
|
let fieldNameParam fieldNames =
|
||||||
|
fieldNameParams fieldNames
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Query =
|
||||||
|
|
||||||
|
/// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
|
||||||
|
[<CompiledName "WhereByField">]
|
||||||
|
[<System.Obsolete "Use WhereByFields instead ~ will be removed in v4.1">]
|
||||||
|
let whereByField field paramName =
|
||||||
|
Query.whereByFields Any [ { field with ParameterName = Some paramName } ]
|
||||||
|
|
||||||
|
|
||||||
|
module WithProps =
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Count =
|
||||||
|
|
||||||
|
/// Count matching documents using a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field sqlProps =
|
||||||
|
WithProps.Count.byFields tableName Any [ field ] sqlProps
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Exists =
|
||||||
|
|
||||||
|
/// Determine if a document exists using a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field sqlProps =
|
||||||
|
WithProps.Exists.byFields tableName Any [ field ] sqlProps
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Find =
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "FSharpByField">]
|
||||||
|
[<System.Obsolete "Use byFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField<'TDoc> tableName field sqlProps =
|
||||||
|
WithProps.Find.byFields<'TDoc> tableName Any [ field ] sqlProps
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison (->> =)
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let ByField<'TDoc>(tableName, field, sqlProps) =
|
||||||
|
WithProps.Find.ByFields<'TDoc>(tableName, Any, Seq.singleton field, sqlProps)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found
|
||||||
|
[<CompiledName "FSharpFirstByField">]
|
||||||
|
[<System.Obsolete "Use firstByFields instead ~ will be removed in v4.1">]
|
||||||
|
let firstByField<'TDoc> tableName field sqlProps =
|
||||||
|
WithProps.Find.firstByFields<'TDoc> tableName Any [ field ] sqlProps
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
|
||||||
|
[<System.Obsolete "Use FirstByFields instead ~ will be removed in v4.1">]
|
||||||
|
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, sqlProps) =
|
||||||
|
WithProps.Find.FirstByFields<'TDoc>(tableName, Any, Seq.singleton field, sqlProps)
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Patch =
|
||||||
|
|
||||||
|
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field (patch: 'TPatch) sqlProps =
|
||||||
|
WithProps.Patch.byFields tableName Any [ field ] patch sqlProps
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module RemoveFields =
|
||||||
|
|
||||||
|
/// Remove fields from documents via a comparison on a JSON field in the document
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field fieldNames sqlProps =
|
||||||
|
WithProps.RemoveFields.byFields tableName Any [ field ] fieldNames sqlProps
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Delete =
|
||||||
|
|
||||||
|
/// Delete documents by matching a JSON field comparison query (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field sqlProps =
|
||||||
|
WithProps.Delete.byFields tableName Any [ field ] sqlProps
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Count =
|
||||||
|
|
||||||
|
/// Count matching documents using a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field =
|
||||||
|
Count.byFields tableName Any [ field ]
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Exists =
|
||||||
|
|
||||||
|
/// Determine if a document exists using a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field =
|
||||||
|
Exists.byFields tableName Any [ field ]
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Find =
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "FSharpByField">]
|
||||||
|
[<System.Obsolete "Use byFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField<'TDoc> tableName field =
|
||||||
|
Find.byFields<'TDoc> tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison (->> =)
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let ByField<'TDoc>(tableName, field) =
|
||||||
|
Find.ByFields<'TDoc>(tableName, Any, Seq.singleton field)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found
|
||||||
|
[<CompiledName "FSharpFirstByField">]
|
||||||
|
[<System.Obsolete "Use firstByFields instead ~ will be removed in v4.1">]
|
||||||
|
let firstByField<'TDoc> tableName field =
|
||||||
|
Find.firstByFields<'TDoc> tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
|
||||||
|
[<System.Obsolete "Use FirstByFields instead ~ will be removed in v4.1">]
|
||||||
|
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
|
||||||
|
Find.FirstByFields<'TDoc>(tableName, Any, Seq.singleton field)
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Patch =
|
||||||
|
|
||||||
|
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field (patch: 'TPatch) =
|
||||||
|
Patch.byFields tableName Any [ field ] patch
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module RemoveFields =
|
||||||
|
|
||||||
|
/// Remove fields from documents via a comparison on a JSON field in the document
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field fieldNames =
|
||||||
|
RemoveFields.byFields tableName Any [ field ] fieldNames
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Delete =
|
||||||
|
|
||||||
|
/// Delete documents by matching a JSON field comparison query (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field =
|
||||||
|
Delete.byFields tableName Any [ field ]
|
||||||
|
|
||||||
|
|
||||||
|
open Npgsql
|
||||||
|
|
||||||
|
/// F# Extensions for the NpgsqlConnection type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module Extensions =
|
||||||
|
|
||||||
|
type NpgsqlConnection with
|
||||||
|
|
||||||
|
/// Count matching documents using a JSON field comparison query (->> =)
|
||||||
|
[<System.Obsolete "Use countByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.countByField tableName field =
|
||||||
|
conn.countByFields tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Determine if documents exist using a JSON field comparison query (->> =)
|
||||||
|
[<System.Obsolete "Use existsByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.existsByField tableName field =
|
||||||
|
conn.existsByFields tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison query (->> =)
|
||||||
|
[<System.Obsolete "Use findByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.findByField<'TDoc> tableName field =
|
||||||
|
conn.findByFields<'TDoc> tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
|
||||||
|
[<System.Obsolete "Use findFirstByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.findFirstByField<'TDoc> tableName field =
|
||||||
|
conn.findFirstByFields<'TDoc> tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||||
|
[<System.Obsolete "Use patchByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.patchByField tableName field (patch: 'TPatch) =
|
||||||
|
conn.patchByFields tableName Any [ field ] patch
|
||||||
|
|
||||||
|
/// Remove fields from documents via a comparison on a JSON field in the document
|
||||||
|
[<System.Obsolete "Use removeFieldsByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.removeFieldsByField tableName field fieldNames =
|
||||||
|
conn.removeFieldsByFields tableName Any [ field ] fieldNames
|
||||||
|
|
||||||
|
/// Delete documents by matching a JSON field comparison query (->> =)
|
||||||
|
[<System.Obsolete "Use deleteByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.deleteByField tableName field =
|
||||||
|
conn.deleteByFields tableName Any [ field ]
|
||||||
|
|
||||||
|
|
||||||
|
open System.Runtime.CompilerServices
|
||||||
|
open Npgsql.FSharp
|
||||||
|
|
||||||
|
type NpgsqlConnectionCSharpCompatExtensions =
|
||||||
|
|
||||||
|
/// Count matching documents using a JSON field comparison query (->> =)
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use CountByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline CountByField(conn, tableName, field) =
|
||||||
|
WithProps.Count.byFields tableName Any [ field ] (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Determine if documents exist using a JSON field comparison query (->> =)
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use ExistsByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline ExistsByField(conn, tableName, field) =
|
||||||
|
WithProps.Exists.byFields tableName Any [ field ] (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison query (->> =)
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use FindByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline FindByField<'TDoc>(conn, tableName, field) =
|
||||||
|
WithProps.Find.ByFields<'TDoc>(tableName, Any, [ field ], Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use FindFirstByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
|
||||||
|
WithProps.Find.FirstByFields<'TDoc>(tableName, Any, [ field ], Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use PatchByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline PatchByField(conn, tableName, field, patch: 'TPatch) =
|
||||||
|
WithProps.Patch.byFields tableName Any [ field ] patch (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Remove fields from documents via a comparison on a JSON field in the document
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use RemoveFieldsByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
|
||||||
|
WithProps.RemoveFields.byFields tableName Any [ field ] fieldNames (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Delete documents by matching a JSON field comparison query (->> =)
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use DeleteByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline DeleteByField(conn, tableName, field) =
|
||||||
|
WithProps.Delete.byFields tableName Any [ field ] (Sql.existingConnection conn)
|
|
@ -50,8 +50,8 @@ module Extensions =
|
||||||
WithProps.Count.all tableName (Sql.existingConnection conn)
|
WithProps.Count.all tableName (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Count matching documents using a JSON field comparison query (->> =)
|
/// Count matching documents using a JSON field comparison query (->> =)
|
||||||
member conn.countByField tableName field =
|
member conn.countByFields tableName howMatched fields =
|
||||||
WithProps.Count.byField tableName field (Sql.existingConnection conn)
|
WithProps.Count.byFields tableName howMatched fields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Count matching documents using a JSON containment query (@>)
|
/// Count matching documents using a JSON containment query (@>)
|
||||||
member conn.countByContains tableName criteria =
|
member conn.countByContains tableName criteria =
|
||||||
|
@ -66,8 +66,8 @@ module Extensions =
|
||||||
WithProps.Exists.byId tableName docId (Sql.existingConnection conn)
|
WithProps.Exists.byId tableName docId (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Determine if documents exist using a JSON field comparison query (->> =)
|
/// Determine if documents exist using a JSON field comparison query (->> =)
|
||||||
member conn.existsByField tableName field =
|
member conn.existsByFields tableName howMatched fields =
|
||||||
WithProps.Exists.byField tableName field (Sql.existingConnection conn)
|
WithProps.Exists.byFields tableName howMatched fields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Determine if documents exist using a JSON containment query (@>)
|
/// Determine if documents exist using a JSON containment query (@>)
|
||||||
member conn.existsByContains tableName criteria =
|
member conn.existsByContains tableName criteria =
|
||||||
|
@ -81,34 +81,68 @@ module Extensions =
|
||||||
member conn.findAll<'TDoc> tableName =
|
member conn.findAll<'TDoc> tableName =
|
||||||
WithProps.Find.all<'TDoc> tableName (Sql.existingConnection conn)
|
WithProps.Find.all<'TDoc> tableName (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve all documents in the given table ordered by the given fields in the document
|
||||||
|
member conn.findAllOrdered<'TDoc> tableName orderFields =
|
||||||
|
WithProps.Find.allOrdered<'TDoc> tableName orderFields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve a document by its ID; returns None if not found
|
/// Retrieve a document by its ID; returns None if not found
|
||||||
member conn.findById<'TKey, 'TDoc> tableName docId =
|
member conn.findById<'TKey, 'TDoc> tableName docId =
|
||||||
WithProps.Find.byId<'TKey, 'TDoc> tableName docId (Sql.existingConnection conn)
|
WithProps.Find.byId<'TKey, 'TDoc> tableName docId (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve documents matching a JSON field comparison query (->> =)
|
/// Retrieve documents matching a JSON field comparison query (->> =)
|
||||||
member conn.findByField<'TDoc> tableName field =
|
member conn.findByFields<'TDoc> tableName howMatched fields =
|
||||||
WithProps.Find.byField<'TDoc> tableName field (Sql.existingConnection conn)
|
WithProps.Find.byFields<'TDoc> tableName howMatched fields (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison query (->> =) ordered by the given fields in the
|
||||||
|
/// document
|
||||||
|
member conn.findByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
|
||||||
|
WithProps.Find.byFieldsOrdered<'TDoc>
|
||||||
|
tableName howMatched queryFields orderFields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve documents matching a JSON containment query (@>)
|
/// Retrieve documents matching a JSON containment query (@>)
|
||||||
member conn.findByContains<'TDoc> tableName (criteria: obj) =
|
member conn.findByContains<'TDoc> tableName (criteria: obj) =
|
||||||
WithProps.Find.byContains<'TDoc> tableName criteria (Sql.existingConnection conn)
|
WithProps.Find.byContains<'TDoc> tableName criteria (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
|
||||||
|
member conn.findByContainsOrdered<'TDoc> tableName (criteria: obj) orderFields =
|
||||||
|
WithProps.Find.byContainsOrdered<'TDoc> tableName criteria orderFields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve documents matching a JSON Path match query (@?)
|
/// Retrieve documents matching a JSON Path match query (@?)
|
||||||
member conn.findByJsonPath<'TDoc> tableName jsonPath =
|
member conn.findByJsonPath<'TDoc> tableName jsonPath =
|
||||||
WithProps.Find.byJsonPath<'TDoc> tableName jsonPath (Sql.existingConnection conn)
|
WithProps.Find.byJsonPath<'TDoc> tableName jsonPath (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
|
||||||
|
member conn.findByJsonPathOrdered<'TDoc> tableName jsonPath orderFields =
|
||||||
|
WithProps.Find.byJsonPathOrdered<'TDoc> tableName jsonPath orderFields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
|
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
|
||||||
member conn.findFirstByField<'TDoc> tableName field =
|
member conn.findFirstByFields<'TDoc> tableName howMatched fields =
|
||||||
WithProps.Find.firstByField<'TDoc> tableName field (Sql.existingConnection conn)
|
WithProps.Find.firstByFields<'TDoc> tableName howMatched fields (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison query (->> =) ordered by the given fields in
|
||||||
|
/// the document; returns None if not found
|
||||||
|
member conn.findFirstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
|
||||||
|
WithProps.Find.firstByFieldsOrdered<'TDoc>
|
||||||
|
tableName howMatched queryFields orderFields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
|
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
|
||||||
member conn.findFirstByContains<'TDoc> tableName (criteria: obj) =
|
member conn.findFirstByContains<'TDoc> tableName (criteria: obj) =
|
||||||
WithProps.Find.firstByContains<'TDoc> tableName criteria (Sql.existingConnection conn)
|
WithProps.Find.firstByContains<'TDoc> tableName criteria (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the
|
||||||
|
/// document; returns None if not found
|
||||||
|
member conn.findFirstByContainsOrdered<'TDoc> tableName (criteria: obj) orderFields =
|
||||||
|
WithProps.Find.firstByContainsOrdered<'TDoc> tableName criteria orderFields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
|
/// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
|
||||||
member conn.findFirstByJsonPath<'TDoc> tableName jsonPath =
|
member conn.findFirstByJsonPath<'TDoc> tableName jsonPath =
|
||||||
WithProps.Find.firstByJsonPath<'TDoc> tableName jsonPath (Sql.existingConnection conn)
|
WithProps.Find.firstByJsonPath<'TDoc> tableName jsonPath (Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the
|
||||||
|
/// document; returns None if not found
|
||||||
|
member conn.findFirstByJsonPathOrdered<'TDoc> tableName jsonPath orderFields =
|
||||||
|
WithProps.Find.firstByJsonPathOrdered<'TDoc> tableName jsonPath orderFields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Update an entire document by its ID
|
/// Update an entire document by its ID
|
||||||
member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) =
|
member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) =
|
||||||
WithProps.Update.byId tableName docId document (Sql.existingConnection conn)
|
WithProps.Update.byId tableName docId document (Sql.existingConnection conn)
|
||||||
|
@ -122,8 +156,8 @@ module Extensions =
|
||||||
WithProps.Patch.byId tableName docId patch (Sql.existingConnection conn)
|
WithProps.Patch.byId tableName docId patch (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||||
member conn.patchByField tableName field (patch: 'TPatch) =
|
member conn.patchByFields tableName howMatched fields (patch: 'TPatch) =
|
||||||
WithProps.Patch.byField tableName field patch (Sql.existingConnection conn)
|
WithProps.Patch.byFields tableName howMatched fields patch (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Patch documents using a JSON containment query in the WHERE clause (@>)
|
/// Patch documents using a JSON containment query in the WHERE clause (@>)
|
||||||
member conn.patchByContains tableName (criteria: 'TCriteria) (patch: 'TPatch) =
|
member conn.patchByContains tableName (criteria: 'TCriteria) (patch: 'TPatch) =
|
||||||
|
@ -137,9 +171,9 @@ module Extensions =
|
||||||
member conn.removeFieldsById tableName (docId: 'TKey) fieldNames =
|
member conn.removeFieldsById tableName (docId: 'TKey) fieldNames =
|
||||||
WithProps.RemoveFields.byId tableName docId fieldNames (Sql.existingConnection conn)
|
WithProps.RemoveFields.byId tableName docId fieldNames (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Remove fields from documents via a comparison on a JSON field in the document
|
/// Remove fields from documents via a comparison on JSON fields in the document
|
||||||
member conn.removeFieldsByField tableName field fieldNames =
|
member conn.removeFieldsByFields tableName howMatched fields fieldNames =
|
||||||
WithProps.RemoveFields.byField tableName field fieldNames (Sql.existingConnection conn)
|
WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Remove fields from documents via a JSON containment query (@>)
|
/// Remove fields from documents via a JSON containment query (@>)
|
||||||
member conn.removeFieldsByContains tableName (criteria: 'TContains) fieldNames =
|
member conn.removeFieldsByContains tableName (criteria: 'TContains) fieldNames =
|
||||||
|
@ -153,9 +187,8 @@ module Extensions =
|
||||||
member conn.deleteById tableName (docId: 'TKey) =
|
member conn.deleteById tableName (docId: 'TKey) =
|
||||||
WithProps.Delete.byId tableName docId (Sql.existingConnection conn)
|
WithProps.Delete.byId tableName docId (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Delete documents by matching a JSON field comparison query (->> =)
|
member conn.deleteByFields tableName howMatched fields =
|
||||||
member conn.deleteByField tableName field =
|
WithProps.Delete.byFields tableName howMatched fields (Sql.existingConnection conn)
|
||||||
WithProps.Delete.byField tableName field (Sql.existingConnection conn)
|
|
||||||
|
|
||||||
/// Delete documents by matching a JSON containment query (@>)
|
/// Delete documents by matching a JSON containment query (@>)
|
||||||
member conn.deleteByContains tableName (criteria: 'TContains) =
|
member conn.deleteByContains tableName (criteria: 'TContains) =
|
||||||
|
@ -225,8 +258,8 @@ type NpgsqlConnectionCSharpExtensions =
|
||||||
|
|
||||||
/// Count matching documents using a JSON field comparison query (->> =)
|
/// Count matching documents using a JSON field comparison query (->> =)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline CountByField(conn, tableName, field) =
|
static member inline CountByFields(conn, tableName, howMatched, fields) =
|
||||||
WithProps.Count.byField tableName field (Sql.existingConnection conn)
|
WithProps.Count.byFields tableName howMatched fields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Count matching documents using a JSON containment query (@>)
|
/// Count matching documents using a JSON containment query (@>)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
|
@ -245,8 +278,8 @@ type NpgsqlConnectionCSharpExtensions =
|
||||||
|
|
||||||
/// Determine if documents exist using a JSON field comparison query (->> =)
|
/// Determine if documents exist using a JSON field comparison query (->> =)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline ExistsByField(conn, tableName, field) =
|
static member inline ExistsByFields(conn, tableName, howMatched, fields) =
|
||||||
WithProps.Exists.byField tableName field (Sql.existingConnection conn)
|
WithProps.Exists.byFields tableName howMatched fields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Determine if documents exist using a JSON containment query (@>)
|
/// Determine if documents exist using a JSON containment query (@>)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
|
@ -263,6 +296,11 @@ type NpgsqlConnectionCSharpExtensions =
|
||||||
static member inline FindAll<'TDoc>(conn, tableName) =
|
static member inline FindAll<'TDoc>(conn, tableName) =
|
||||||
WithProps.Find.All<'TDoc>(tableName, Sql.existingConnection conn)
|
WithProps.Find.All<'TDoc>(tableName, Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve all documents in the given table ordered by the given fields in the document
|
||||||
|
[<Extension>]
|
||||||
|
static member inline FindAllOrdered<'TDoc>(conn, tableName, orderFields) =
|
||||||
|
WithProps.Find.AllOrdered<'TDoc>(tableName, orderFields, Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve a document by its ID; returns None if not found
|
/// Retrieve a document by its ID; returns None if not found
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindById<'TKey, 'TDoc when 'TDoc: null>(conn, tableName, docId: 'TKey) =
|
static member inline FindById<'TKey, 'TDoc when 'TDoc: null>(conn, tableName, docId: 'TKey) =
|
||||||
|
@ -270,34 +308,71 @@ type NpgsqlConnectionCSharpExtensions =
|
||||||
|
|
||||||
/// Retrieve documents matching a JSON field comparison query (->> =)
|
/// Retrieve documents matching a JSON field comparison query (->> =)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindByField<'TDoc>(conn, tableName, field) =
|
static member inline FindByFields<'TDoc>(conn, tableName, howMatched, fields) =
|
||||||
WithProps.Find.ByField<'TDoc>(tableName, field, Sql.existingConnection conn)
|
WithProps.Find.ByFields<'TDoc>(tableName, howMatched, fields, Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison query (->> =) ordered by the given fields in the document
|
||||||
|
[<Extension>]
|
||||||
|
static member inline FindByFieldsOrdered<'TDoc>(conn, tableName, howMatched, queryFields, orderFields) =
|
||||||
|
WithProps.Find.ByFieldsOrdered<'TDoc>(
|
||||||
|
tableName, howMatched, queryFields, orderFields, Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve documents matching a JSON containment query (@>)
|
/// Retrieve documents matching a JSON containment query (@>)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindByContains<'TDoc>(conn, tableName, criteria: obj) =
|
static member inline FindByContains<'TDoc>(conn, tableName, criteria: obj) =
|
||||||
WithProps.Find.ByContains<'TDoc>(tableName, criteria, Sql.existingConnection conn)
|
WithProps.Find.ByContains<'TDoc>(tableName, criteria, Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
|
||||||
|
[<Extension>]
|
||||||
|
static member inline FindByContainsOrdered<'TDoc>(conn, tableName, criteria: obj, orderFields) =
|
||||||
|
WithProps.Find.ByContainsOrdered<'TDoc>(tableName, criteria, orderFields, Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve documents matching a JSON Path match query (@?)
|
/// Retrieve documents matching a JSON Path match query (@?)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindByJsonPath<'TDoc>(conn, tableName, jsonPath) =
|
static member inline FindByJsonPath<'TDoc>(conn, tableName, jsonPath) =
|
||||||
WithProps.Find.ByJsonPath<'TDoc>(tableName, jsonPath, Sql.existingConnection conn)
|
WithProps.Find.ByJsonPath<'TDoc>(tableName, jsonPath, Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
|
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
|
static member inline FindByJsonPathOrdered<'TDoc>(conn, tableName, jsonPath, orderFields) =
|
||||||
WithProps.Find.FirstByField<'TDoc>(tableName, field, Sql.existingConnection conn)
|
WithProps.Find.ByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
|
||||||
|
[<Extension>]
|
||||||
|
static member inline FindFirstByFields<'TDoc when 'TDoc: null>(conn, tableName, howMatched, fields) =
|
||||||
|
WithProps.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison query (->> =) ordered by the given fields in the
|
||||||
|
/// document; returns null if not found
|
||||||
|
[<Extension>]
|
||||||
|
static member inline FindFirstByFieldsOrdered<'TDoc when 'TDoc: null>(
|
||||||
|
conn, tableName, howMatched, queryFields, orderFields) =
|
||||||
|
WithProps.Find.FirstByFieldsOrdered<'TDoc>(
|
||||||
|
tableName, howMatched, queryFields, orderFields, Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
|
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindFirstByContains<'TDoc when 'TDoc: null>(conn, tableName, criteria: obj) =
|
static member inline FindFirstByContains<'TDoc when 'TDoc: null>(conn, tableName, criteria: obj) =
|
||||||
WithProps.Find.FirstByContains<'TDoc>(tableName, criteria, Sql.existingConnection conn)
|
WithProps.Find.FirstByContains<'TDoc>(tableName, criteria, Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the document;
|
||||||
|
/// returns None if not found
|
||||||
|
[<Extension>]
|
||||||
|
static member inline FindFirstByContainsOrdered<'TDoc when 'TDoc: null>(
|
||||||
|
conn, tableName, criteria: obj, orderFields) =
|
||||||
|
WithProps.Find.FirstByContainsOrdered<'TDoc>(tableName, criteria, orderFields, Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
|
/// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindFirstByJsonPath<'TDoc when 'TDoc: null>(conn, tableName, jsonPath) =
|
static member inline FindFirstByJsonPath<'TDoc when 'TDoc: null>(conn, tableName, jsonPath) =
|
||||||
WithProps.Find.FirstByJsonPath<'TDoc>(tableName, jsonPath, Sql.existingConnection conn)
|
WithProps.Find.FirstByJsonPath<'TDoc>(tableName, jsonPath, Sql.existingConnection conn)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the document;
|
||||||
|
/// returns None if not found
|
||||||
|
[<Extension>]
|
||||||
|
static member inline FindFirstByJsonPathOrdered<'TDoc when 'TDoc: null>(conn, tableName, jsonPath, orderFields) =
|
||||||
|
WithProps.Find.FirstByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Update an entire document by its ID
|
/// Update an entire document by its ID
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline UpdateById(conn, tableName, docId: 'TKey, document: 'TDoc) =
|
static member inline UpdateById(conn, tableName, docId: 'TKey, document: 'TDoc) =
|
||||||
|
@ -315,8 +390,8 @@ type NpgsqlConnectionCSharpExtensions =
|
||||||
|
|
||||||
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline PatchByField(conn, tableName, field, patch: 'TPatch) =
|
static member inline PatchByFields(conn, tableName, howMatched, fields, patch: 'TPatch) =
|
||||||
WithProps.Patch.byField tableName field patch (Sql.existingConnection conn)
|
WithProps.Patch.byFields tableName howMatched fields patch (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Patch documents using a JSON containment query in the WHERE clause (@>)
|
/// Patch documents using a JSON containment query in the WHERE clause (@>)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
|
@ -331,22 +406,22 @@ type NpgsqlConnectionCSharpExtensions =
|
||||||
/// Remove fields from a document by the document's ID
|
/// Remove fields from a document by the document's ID
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline RemoveFieldsById(conn, tableName, docId: 'TKey, fieldNames) =
|
static member inline RemoveFieldsById(conn, tableName, docId: 'TKey, fieldNames) =
|
||||||
WithProps.RemoveFields.ById(tableName, docId, fieldNames, Sql.existingConnection conn)
|
WithProps.RemoveFields.byId tableName docId fieldNames (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Remove fields from documents via a comparison on a JSON field in the document
|
/// Remove fields from documents via a comparison on JSON fields in the document
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
|
static member inline RemoveFieldsByFields(conn, tableName, howMatched, fields, fieldNames) =
|
||||||
WithProps.RemoveFields.ByField(tableName, field, fieldNames, Sql.existingConnection conn)
|
WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Remove fields from documents via a JSON containment query (@>)
|
/// Remove fields from documents via a JSON containment query (@>)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline RemoveFieldsByContains(conn, tableName, criteria: 'TContains, fieldNames) =
|
static member inline RemoveFieldsByContains(conn, tableName, criteria: 'TContains, fieldNames) =
|
||||||
WithProps.RemoveFields.ByContains(tableName, criteria, fieldNames, Sql.existingConnection conn)
|
WithProps.RemoveFields.byContains tableName criteria fieldNames (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Remove fields from documents via a JSON Path match query (@?)
|
/// Remove fields from documents via a JSON Path match query (@?)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline RemoveFieldsByJsonPath(conn, tableName, jsonPath, fieldNames) =
|
static member inline RemoveFieldsByJsonPath(conn, tableName, jsonPath, fieldNames) =
|
||||||
WithProps.RemoveFields.ByJsonPath(tableName, jsonPath, fieldNames, Sql.existingConnection conn)
|
WithProps.RemoveFields.byJsonPath tableName jsonPath fieldNames (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Delete a document by its ID
|
/// Delete a document by its ID
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
|
@ -355,8 +430,8 @@ type NpgsqlConnectionCSharpExtensions =
|
||||||
|
|
||||||
/// Delete documents by matching a JSON field comparison query (->> =)
|
/// Delete documents by matching a JSON field comparison query (->> =)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline DeleteByField(conn, tableName, field) =
|
static member inline DeleteByFields(conn, tableName, howMatched, fields) =
|
||||||
WithProps.Delete.byField tableName field (Sql.existingConnection conn)
|
WithProps.Delete.byFields tableName howMatched fields (Sql.existingConnection conn)
|
||||||
|
|
||||||
/// Delete documents by matching a JSON containment query (@>)
|
/// Delete documents by matching a JSON containment query (@>)
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,6 +8,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Library.fs" />
|
<Compile Include="Library.fs" />
|
||||||
<Compile Include="Extensions.fs" />
|
<Compile Include="Extensions.fs" />
|
||||||
|
<Compile Include="Compat.fs" />
|
||||||
<None Include="README.md" Pack="true" PackagePath="\" />
|
<None Include="README.md" Pack="true" PackagePath="\" />
|
||||||
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
269
src/Sqlite/Compat.fs
Normal file
269
src/Sqlite/Compat.fs
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
namespace BitBadger.Documents.Sqlite.Compat
|
||||||
|
|
||||||
|
open BitBadger.Documents
|
||||||
|
open BitBadger.Documents.Sqlite
|
||||||
|
|
||||||
|
[<AutoOpen>]
|
||||||
|
module Parameters =
|
||||||
|
|
||||||
|
/// Create a JSON field parameter
|
||||||
|
[<CompiledName "AddField">]
|
||||||
|
[<System.Obsolete "Use addFieldParams (F#) / AddFields (C#) instead ~ will be removed in v4.1">]
|
||||||
|
let addFieldParam name field parameters =
|
||||||
|
addFieldParams [ { field with ParameterName = Some name } ] parameters
|
||||||
|
|
||||||
|
/// Append JSON field name parameters for the given field names to the given parameters
|
||||||
|
[<CompiledName "FieldName">]
|
||||||
|
[<System.Obsolete "Use fieldNameParams (F#) / FieldNames (C#) instead ~ will be removed in v4.1">]
|
||||||
|
let fieldNameParam fieldNames =
|
||||||
|
fieldNameParams fieldNames
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Query =
|
||||||
|
|
||||||
|
/// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
|
||||||
|
[<CompiledName "WhereByField">]
|
||||||
|
[<System.Obsolete "Use WhereByFields instead ~ will be removed in v4.1">]
|
||||||
|
let whereByField field paramName =
|
||||||
|
Query.whereByFields Any [ { field with ParameterName = Some paramName } ]
|
||||||
|
|
||||||
|
|
||||||
|
module WithConn =
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Count =
|
||||||
|
|
||||||
|
/// Count matching documents using a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field conn =
|
||||||
|
WithConn.Count.byFields tableName Any [ field ] conn
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Exists =
|
||||||
|
|
||||||
|
/// Determine if a document exists using a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field conn =
|
||||||
|
WithConn.Exists.byFields tableName Any [ field ] conn
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Find =
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "FSharpByField">]
|
||||||
|
[<System.Obsolete "Use byFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField<'TDoc> tableName field conn =
|
||||||
|
WithConn.Find.byFields<'TDoc> tableName Any [ field ] conn
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison (->> =)
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let ByField<'TDoc>(tableName, field, conn) =
|
||||||
|
WithConn.Find.ByFields<'TDoc>(tableName, Any, Seq.singleton field, conn)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found
|
||||||
|
[<CompiledName "FSharpFirstByField">]
|
||||||
|
[<System.Obsolete "Use firstByFields instead ~ will be removed in v4.1">]
|
||||||
|
let firstByField<'TDoc> tableName field conn =
|
||||||
|
WithConn.Find.firstByFields<'TDoc> tableName Any [ field ] conn
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
|
||||||
|
[<System.Obsolete "Use FirstByFields instead ~ will be removed in v4.1">]
|
||||||
|
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) =
|
||||||
|
WithConn.Find.FirstByFields<'TDoc>(tableName, Any, Seq.singleton field, conn)
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Patch =
|
||||||
|
|
||||||
|
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field (patch: 'TPatch) conn =
|
||||||
|
WithConn.Patch.byFields tableName Any [ field ] patch conn
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module RemoveFields =
|
||||||
|
|
||||||
|
/// Remove fields from documents via a comparison on a JSON field in the document
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field fieldNames conn =
|
||||||
|
WithConn.RemoveFields.byFields tableName Any [ field ] fieldNames conn
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Delete =
|
||||||
|
|
||||||
|
/// Delete documents by matching a JSON field comparison query (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field conn =
|
||||||
|
WithConn.Delete.byFields tableName Any [ field ] conn
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Count =
|
||||||
|
|
||||||
|
/// Count matching documents using a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field =
|
||||||
|
Count.byFields tableName Any [ field ]
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Exists =
|
||||||
|
|
||||||
|
/// Determine if a document exists using a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field =
|
||||||
|
Exists.byFields tableName Any [ field ]
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Find =
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison (->> =)
|
||||||
|
[<CompiledName "FSharpByField">]
|
||||||
|
[<System.Obsolete "Use byFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField<'TDoc> tableName field =
|
||||||
|
Find.byFields<'TDoc> tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison (->> =)
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let ByField<'TDoc>(tableName, field) =
|
||||||
|
Find.ByFields<'TDoc>(tableName, Any, Seq.singleton field)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found
|
||||||
|
[<CompiledName "FSharpFirstByField">]
|
||||||
|
[<System.Obsolete "Use firstByFields instead ~ will be removed in v4.1">]
|
||||||
|
let firstByField<'TDoc> tableName field =
|
||||||
|
Find.firstByFields<'TDoc> tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
|
||||||
|
[<System.Obsolete "Use FirstByFields instead ~ will be removed in v4.1">]
|
||||||
|
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
|
||||||
|
Find.FirstByFields<'TDoc>(tableName, Any, Seq.singleton field)
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Patch =
|
||||||
|
|
||||||
|
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field (patch: 'TPatch) =
|
||||||
|
Patch.byFields tableName Any [ field ] patch
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module RemoveFields =
|
||||||
|
|
||||||
|
/// Remove fields from documents via a comparison on a JSON field in the document
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field fieldNames =
|
||||||
|
RemoveFields.byFields tableName Any [ field ] fieldNames
|
||||||
|
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Delete =
|
||||||
|
|
||||||
|
/// Delete documents by matching a JSON field comparison query (->> =)
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
|
||||||
|
let byField tableName field =
|
||||||
|
Delete.byFields tableName Any [ field ]
|
||||||
|
|
||||||
|
|
||||||
|
open Microsoft.Data.Sqlite
|
||||||
|
|
||||||
|
/// F# Extensions for the NpgsqlConnection type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module Extensions =
|
||||||
|
|
||||||
|
type SqliteConnection with
|
||||||
|
|
||||||
|
/// Count matching documents using a JSON field comparison query (->> =)
|
||||||
|
[<System.Obsolete "Use countByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.countByField tableName field =
|
||||||
|
conn.countByFields tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Determine if documents exist using a JSON field comparison query (->> =)
|
||||||
|
[<System.Obsolete "Use existsByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.existsByField tableName field =
|
||||||
|
conn.existsByFields tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison query (->> =)
|
||||||
|
[<System.Obsolete "Use findByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.findByField<'TDoc> tableName field =
|
||||||
|
conn.findByFields<'TDoc> tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
|
||||||
|
[<System.Obsolete "Use findFirstByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.findFirstByField<'TDoc> tableName field =
|
||||||
|
conn.findFirstByFields<'TDoc> tableName Any [ field ]
|
||||||
|
|
||||||
|
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||||
|
[<System.Obsolete "Use patchByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.patchByField tableName field (patch: 'TPatch) =
|
||||||
|
conn.patchByFields tableName Any [ field ] patch
|
||||||
|
|
||||||
|
/// Remove fields from documents via a comparison on a JSON field in the document
|
||||||
|
[<System.Obsolete "Use removeFieldsByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.removeFieldsByField tableName field fieldNames =
|
||||||
|
conn.removeFieldsByFields tableName Any [ field ] fieldNames
|
||||||
|
|
||||||
|
/// Delete documents by matching a JSON field comparison query (->> =)
|
||||||
|
[<System.Obsolete "Use deleteByFields instead ~ will be removed in v4.1">]
|
||||||
|
member conn.deleteByField tableName field =
|
||||||
|
conn.deleteByFields tableName Any [ field ]
|
||||||
|
|
||||||
|
|
||||||
|
open System.Runtime.CompilerServices
|
||||||
|
|
||||||
|
type SqliteConnectionCSharpCompatExtensions =
|
||||||
|
|
||||||
|
/// Count matching documents using a JSON field comparison query (->> =)
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use CountByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline CountByField(conn, tableName, field) =
|
||||||
|
WithConn.Count.byFields tableName Any [ field ] conn
|
||||||
|
|
||||||
|
/// Determine if documents exist using a JSON field comparison query (->> =)
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use ExistsByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline ExistsByField(conn, tableName, field) =
|
||||||
|
WithConn.Exists.byFields tableName Any [ field ] conn
|
||||||
|
|
||||||
|
/// Retrieve documents matching a JSON field comparison query (->> =)
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use FindByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline FindByField<'TDoc>(conn, tableName, field) =
|
||||||
|
WithConn.Find.ByFields<'TDoc>(tableName, Any, [ field ], conn)
|
||||||
|
|
||||||
|
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use FindFirstByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
|
||||||
|
WithConn.Find.FirstByFields<'TDoc>(tableName, Any, [ field ], conn)
|
||||||
|
|
||||||
|
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use PatchByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline PatchByField(conn, tableName, field, patch: 'TPatch) =
|
||||||
|
WithConn.Patch.byFields tableName Any [ field ] patch conn
|
||||||
|
|
||||||
|
/// Remove fields from documents via a comparison on a JSON field in the document
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use RemoveFieldsByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
|
||||||
|
WithConn.RemoveFields.byFields tableName Any [ field ] fieldNames conn
|
||||||
|
|
||||||
|
/// Delete documents by matching a JSON field comparison query (->> =)
|
||||||
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use DeleteByFields instead ~ will be removed in v4.1">]
|
||||||
|
static member inline DeleteByField(conn, tableName, field) =
|
||||||
|
WithConn.Delete.byFields tableName Any [ field ] conn
|
|
@ -1,5 +1,6 @@
|
||||||
namespace BitBadger.Documents.Sqlite
|
namespace BitBadger.Documents.Sqlite
|
||||||
|
|
||||||
|
open BitBadger.Documents
|
||||||
open Microsoft.Data.Sqlite
|
open Microsoft.Data.Sqlite
|
||||||
|
|
||||||
/// F# extensions for the SqliteConnection type
|
/// F# extensions for the SqliteConnection type
|
||||||
|
@ -34,43 +35,56 @@ module Extensions =
|
||||||
|
|
||||||
/// Insert a new document
|
/// Insert a new document
|
||||||
member conn.insert<'TDoc> tableName (document: 'TDoc) =
|
member conn.insert<'TDoc> tableName (document: 'TDoc) =
|
||||||
WithConn.insert<'TDoc> tableName document conn
|
WithConn.Document.insert<'TDoc> tableName document conn
|
||||||
|
|
||||||
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
|
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
|
||||||
member conn.save<'TDoc> tableName (document: 'TDoc) =
|
member conn.save<'TDoc> tableName (document: 'TDoc) =
|
||||||
WithConn.save tableName document conn
|
WithConn.Document.save tableName document conn
|
||||||
|
|
||||||
/// Count all documents in a table
|
/// Count all documents in a table
|
||||||
member conn.countAll tableName =
|
member conn.countAll tableName =
|
||||||
WithConn.Count.all tableName conn
|
WithConn.Count.all tableName conn
|
||||||
|
|
||||||
/// Count matching documents using a comparison on a JSON field
|
/// Count matching documents using a comparison on JSON fields
|
||||||
member conn.countByField tableName field =
|
member conn.countByFields tableName howMatched fields =
|
||||||
WithConn.Count.byField tableName field conn
|
WithConn.Count.byFields tableName howMatched fields conn
|
||||||
|
|
||||||
/// Determine if a document exists for the given ID
|
/// Determine if a document exists for the given ID
|
||||||
member conn.existsById tableName (docId: 'TKey) =
|
member conn.existsById tableName (docId: 'TKey) =
|
||||||
WithConn.Exists.byId tableName docId conn
|
WithConn.Exists.byId tableName docId conn
|
||||||
|
|
||||||
/// Determine if a document exists using a comparison on a JSON field
|
/// Determine if a document exists using a comparison on JSON fields
|
||||||
member conn.existsByField tableName field =
|
member conn.existsByFields tableName howMatched fields =
|
||||||
WithConn.Exists.byField tableName field conn
|
WithConn.Exists.byFields tableName howMatched fields conn
|
||||||
|
|
||||||
/// Retrieve all documents in the given table
|
/// Retrieve all documents in the given table
|
||||||
member conn.findAll<'TDoc> tableName =
|
member conn.findAll<'TDoc> tableName =
|
||||||
WithConn.Find.all<'TDoc> tableName conn
|
WithConn.Find.all<'TDoc> tableName conn
|
||||||
|
|
||||||
|
/// Retrieve all documents in the given table ordered by the given fields in the document
|
||||||
|
member conn.findAllOrdered<'TDoc> tableName orderFields =
|
||||||
|
WithConn.Find.allOrdered<'TDoc> tableName orderFields conn
|
||||||
|
|
||||||
/// Retrieve a document by its ID
|
/// Retrieve a document by its ID
|
||||||
member conn.findById<'TKey, 'TDoc> tableName (docId: 'TKey) =
|
member conn.findById<'TKey, 'TDoc> tableName (docId: 'TKey) =
|
||||||
WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn
|
WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field
|
/// Retrieve documents via a comparison on JSON fields
|
||||||
member conn.findByField<'TDoc> tableName field =
|
member conn.findByFields<'TDoc> tableName howMatched fields =
|
||||||
WithConn.Find.byField<'TDoc> tableName field conn
|
WithConn.Find.byFields<'TDoc> tableName howMatched fields conn
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field, returning only the first result
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
|
||||||
member conn.findFirstByField<'TDoc> tableName field =
|
member conn.findByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
|
||||||
WithConn.Find.firstByField<'TDoc> tableName field conn
|
WithConn.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields, returning only the first result
|
||||||
|
member conn.findFirstByFields<'TDoc> tableName howMatched fields =
|
||||||
|
WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning
|
||||||
|
/// only the first result
|
||||||
|
member conn.findFirstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
|
||||||
|
WithConn.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
|
||||||
|
|
||||||
/// Update an entire document by its ID
|
/// Update an entire document by its ID
|
||||||
member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) =
|
member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) =
|
||||||
|
@ -84,25 +98,25 @@ module Extensions =
|
||||||
member conn.patchById tableName (docId: 'TKey) (patch: 'TPatch) =
|
member conn.patchById tableName (docId: 'TKey) (patch: 'TPatch) =
|
||||||
WithConn.Patch.byId tableName docId patch conn
|
WithConn.Patch.byId tableName docId patch conn
|
||||||
|
|
||||||
/// Patch documents using a comparison on a JSON field
|
/// Patch documents using a comparison on JSON fields
|
||||||
member conn.patchByField tableName field (patch: 'TPatch) =
|
member conn.patchByFields tableName howMatched fields (patch: 'TPatch) =
|
||||||
WithConn.Patch.byField tableName field patch conn
|
WithConn.Patch.byFields tableName howMatched fields patch conn
|
||||||
|
|
||||||
/// Remove fields from a document by the document's ID
|
/// Remove fields from a document by the document's ID
|
||||||
member conn.removeFieldsById tableName (docId: 'TKey) fieldNames =
|
member conn.removeFieldsById tableName (docId: 'TKey) fieldNames =
|
||||||
WithConn.RemoveFields.byId tableName docId fieldNames conn
|
WithConn.RemoveFields.byId tableName docId fieldNames conn
|
||||||
|
|
||||||
/// Remove a field from a document via a comparison on a JSON field in the document
|
/// Remove a field from a document via a comparison on JSON fields in the document
|
||||||
member conn.removeFieldsByField tableName field fieldNames =
|
member conn.removeFieldsByFields tableName howMatched fields fieldNames =
|
||||||
WithConn.RemoveFields.byField tableName field fieldNames conn
|
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn
|
||||||
|
|
||||||
/// Delete a document by its ID
|
/// Delete a document by its ID
|
||||||
member conn.deleteById tableName (docId: 'TKey) =
|
member conn.deleteById tableName (docId: 'TKey) =
|
||||||
WithConn.Delete.byId tableName docId conn
|
WithConn.Delete.byId tableName docId conn
|
||||||
|
|
||||||
/// Delete documents by matching a comparison on a JSON field
|
/// Delete documents by matching a comparison on JSON fields
|
||||||
member conn.deleteByField tableName field =
|
member conn.deleteByFields tableName howMatched fields =
|
||||||
WithConn.Delete.byField tableName field conn
|
WithConn.Delete.byFields tableName howMatched fields conn
|
||||||
|
|
||||||
|
|
||||||
open System.Runtime.CompilerServices
|
open System.Runtime.CompilerServices
|
||||||
|
@ -145,52 +159,69 @@ type SqliteConnectionCSharpExtensions =
|
||||||
/// Insert a new document
|
/// Insert a new document
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline Insert<'TDoc>(conn, tableName, document: 'TDoc) =
|
static member inline Insert<'TDoc>(conn, tableName, document: 'TDoc) =
|
||||||
WithConn.insert<'TDoc> tableName document conn
|
WithConn.Document.insert<'TDoc> tableName document conn
|
||||||
|
|
||||||
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
|
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline Save<'TDoc>(conn, tableName, document: 'TDoc) =
|
static member inline Save<'TDoc>(conn, tableName, document: 'TDoc) =
|
||||||
WithConn.save<'TDoc> tableName document conn
|
WithConn.Document.save<'TDoc> tableName document conn
|
||||||
|
|
||||||
/// Count all documents in a table
|
/// Count all documents in a table
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline CountAll(conn, tableName) =
|
static member inline CountAll(conn, tableName) =
|
||||||
WithConn.Count.all tableName conn
|
WithConn.Count.all tableName conn
|
||||||
|
|
||||||
/// Count matching documents using a comparison on a JSON field
|
/// Count matching documents using a comparison on JSON fields
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline CountByField(conn, tableName, field) =
|
static member inline CountByFields(conn, tableName, howMatched, fields) =
|
||||||
WithConn.Count.byField tableName field conn
|
WithConn.Count.byFields tableName howMatched fields conn
|
||||||
|
|
||||||
/// Determine if a document exists for the given ID
|
/// Determine if a document exists for the given ID
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline ExistsById<'TKey>(conn, tableName, docId: 'TKey) =
|
static member inline ExistsById<'TKey>(conn, tableName, docId: 'TKey) =
|
||||||
WithConn.Exists.byId tableName docId conn
|
WithConn.Exists.byId tableName docId conn
|
||||||
|
|
||||||
/// Determine if a document exists using a comparison on a JSON field
|
/// Determine if a document exists using a comparison on JSON fields
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline ExistsByField(conn, tableName, field) =
|
static member inline ExistsByFields(conn, tableName, howMatched, fields) =
|
||||||
WithConn.Exists.byField tableName field conn
|
WithConn.Exists.byFields tableName howMatched fields conn
|
||||||
|
|
||||||
/// Retrieve all documents in the given table
|
/// Retrieve all documents in the given table
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindAll<'TDoc>(conn, tableName) =
|
static member inline FindAll<'TDoc>(conn, tableName) =
|
||||||
WithConn.Find.All<'TDoc>(tableName, conn)
|
WithConn.Find.All<'TDoc>(tableName, conn)
|
||||||
|
|
||||||
|
/// Retrieve all documents in the given table ordered by the given fields in the document
|
||||||
|
[<Extension>]
|
||||||
|
static member inline FindAllOrdered<'TDoc>(conn, tableName, orderFields) =
|
||||||
|
WithConn.Find.AllOrdered<'TDoc>(tableName, orderFields, conn)
|
||||||
|
|
||||||
/// Retrieve a document by its ID
|
/// Retrieve a document by its ID
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindById<'TKey, 'TDoc when 'TDoc: null>(conn, tableName, docId: 'TKey) =
|
static member inline FindById<'TKey, 'TDoc when 'TDoc: null>(conn, tableName, docId: 'TKey) =
|
||||||
WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
|
WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field
|
/// Retrieve documents via a comparison on JSON fields
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindByField<'TDoc>(conn, tableName, field) =
|
static member inline FindByFields<'TDoc>(conn, tableName, howMatched, fields) =
|
||||||
WithConn.Find.ByField<'TDoc>(tableName, field, conn)
|
WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn)
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field, returning only the first result
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
|
static member inline FindByFieldsOrdered<'TDoc>(conn, tableName, howMatched, queryFields, orderFields) =
|
||||||
WithConn.Find.FirstByField<'TDoc>(tableName, field, conn)
|
WithConn.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields, returning only the first result
|
||||||
|
[<Extension>]
|
||||||
|
static member inline FindFirstByFields<'TDoc when 'TDoc: null>(conn, tableName, howMatched, fields) =
|
||||||
|
WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn)
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning only
|
||||||
|
/// the first result
|
||||||
|
[<Extension>]
|
||||||
|
static member inline FindFirstByFieldsOrdered<'TDoc when 'TDoc: null>(
|
||||||
|
conn, tableName, howMatched, queryFields, orderFields) =
|
||||||
|
WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
|
||||||
|
|
||||||
/// Update an entire document by its ID
|
/// Update an entire document by its ID
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
|
@ -207,27 +238,33 @@ type SqliteConnectionCSharpExtensions =
|
||||||
static member inline PatchById<'TKey, 'TPatch>(conn, tableName, docId: 'TKey, patch: 'TPatch) =
|
static member inline PatchById<'TKey, 'TPatch>(conn, tableName, docId: 'TKey, patch: 'TPatch) =
|
||||||
WithConn.Patch.byId tableName docId patch conn
|
WithConn.Patch.byId tableName docId patch conn
|
||||||
|
|
||||||
/// Patch documents using a comparison on a JSON field
|
/// Patch documents using a comparison on JSON fields
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline PatchByField<'TPatch>(conn, tableName, field, patch: 'TPatch) =
|
static member inline PatchByFields<'TPatch>(conn, tableName, howMatched, fields, patch: 'TPatch) =
|
||||||
WithConn.Patch.byField tableName field patch conn
|
WithConn.Patch.byFields tableName howMatched fields patch conn
|
||||||
|
|
||||||
/// Remove fields from a document by the document's ID
|
/// Remove fields from a document by the document's ID
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline RemoveFieldsById<'TKey>(conn, tableName, docId: 'TKey, fieldNames) =
|
static member inline RemoveFieldsById<'TKey>(conn, tableName, docId: 'TKey, fieldNames) =
|
||||||
WithConn.RemoveFields.ById(tableName, docId, fieldNames, conn)
|
WithConn.RemoveFields.byId tableName docId fieldNames conn
|
||||||
|
|
||||||
/// Remove fields from documents via a comparison on a JSON field in the document
|
/// Remove fields from documents via a comparison on JSON fields in the document
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
|
static member inline RemoveFieldsByFields(conn, tableName, howMatched, fields, fieldNames) =
|
||||||
WithConn.RemoveFields.ByField(tableName, field, fieldNames, conn)
|
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn
|
||||||
|
|
||||||
/// Delete a document by its ID
|
/// Delete a document by its ID
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
static member inline DeleteById<'TKey>(conn, tableName, docId: 'TKey) =
|
static member inline DeleteById<'TKey>(conn, tableName, docId: 'TKey) =
|
||||||
WithConn.Delete.byId tableName docId conn
|
WithConn.Delete.byId tableName docId conn
|
||||||
|
|
||||||
|
/// Delete documents by matching a comparison on JSON fields
|
||||||
|
[<Extension>]
|
||||||
|
static member inline DeleteByFields(conn, tableName, howMatched, fields) =
|
||||||
|
WithConn.Delete.byFields tableName howMatched fields conn
|
||||||
|
|
||||||
/// Delete documents by matching a comparison on a JSON field
|
/// Delete documents by matching a comparison on a JSON field
|
||||||
[<Extension>]
|
[<Extension>]
|
||||||
|
[<System.Obsolete "Use DeleteByFields instead; will be removed in v4">]
|
||||||
static member inline DeleteByField(conn, tableName, field) =
|
static member inline DeleteByField(conn, tableName, field) =
|
||||||
WithConn.Delete.byField tableName field conn
|
conn.DeleteByFields(tableName, Any, [ field ])
|
||||||
|
|
|
@ -31,20 +31,47 @@ module Configuration =
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Query =
|
module Query =
|
||||||
|
|
||||||
/// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
|
/// Create a WHERE clause fragment to implement a comparison on fields in a JSON document
|
||||||
[<CompiledName "WhereByField">]
|
[<CompiledName "WhereByFields">]
|
||||||
let whereByField field paramName =
|
let whereByFields (howMatched: FieldMatch) fields =
|
||||||
let theRest =
|
let name = ParameterName()
|
||||||
match field.Op with
|
fields
|
||||||
| EX | NEX -> ""
|
|> Seq.map (fun it ->
|
||||||
| BT -> $" {paramName}min AND {paramName}max"
|
match it.Op with
|
||||||
| _ -> $" %s{paramName}"
|
| EX | NEX -> $"{it.Path SQLite} {it.Op}"
|
||||||
$"data->>'{field.Name}' {field.Op}{theRest}"
|
| BT ->
|
||||||
|
let p = name.Derive it.ParameterName
|
||||||
|
$"{it.Path SQLite} {it.Op} {p}min AND {p}max"
|
||||||
|
| _ -> $"{it.Path SQLite} {it.Op} {name.Derive it.ParameterName}")
|
||||||
|
|> String.concat $" {howMatched} "
|
||||||
|
|
||||||
/// Create a WHERE clause fragment to implement an ID-based query
|
/// Create a WHERE clause fragment to implement an ID-based query
|
||||||
[<CompiledName "WhereById">]
|
[<CompiledName "WhereById">]
|
||||||
let whereById paramName =
|
let whereById paramName =
|
||||||
whereByField (Field.EQ (Configuration.idField ()) 0) paramName
|
whereByFields Any [ { Field.EQ (Configuration.idField ()) 0 with ParameterName = Some paramName } ]
|
||||||
|
|
||||||
|
/// Create an UPDATE statement to patch documents
|
||||||
|
[<CompiledName "Patch">]
|
||||||
|
let patch tableName =
|
||||||
|
$"UPDATE %s{tableName} SET data = json_patch(data, json(@data))"
|
||||||
|
|
||||||
|
/// Create an UPDATE statement to remove fields from documents
|
||||||
|
[<CompiledName "RemoveFields">]
|
||||||
|
let removeFields tableName (parameters: SqliteParameter seq) =
|
||||||
|
let paramNames = parameters |> Seq.map _.ParameterName |> String.concat ", "
|
||||||
|
$"UPDATE %s{tableName} SET data = json_remove(data, {paramNames})"
|
||||||
|
|
||||||
|
/// Create a query by a document's ID
|
||||||
|
[<CompiledName "ById">]
|
||||||
|
let byId<'TKey> statement (docId: 'TKey) =
|
||||||
|
Query.statementWhere
|
||||||
|
statement
|
||||||
|
(whereByFields Any [ { Field.EQ (Configuration.idField ()) docId with ParameterName = Some "@id" } ])
|
||||||
|
|
||||||
|
/// Create a query on JSON fields
|
||||||
|
[<CompiledName "ByFields">]
|
||||||
|
let byFields statement howMatched fields =
|
||||||
|
Query.statementWhere statement (whereByFields howMatched fields)
|
||||||
|
|
||||||
/// Data definition
|
/// Data definition
|
||||||
module Definition =
|
module Definition =
|
||||||
|
@ -54,106 +81,6 @@ module Query =
|
||||||
let ensureTable name =
|
let ensureTable name =
|
||||||
Query.Definition.ensureTableFor name "TEXT"
|
Query.Definition.ensureTableFor name "TEXT"
|
||||||
|
|
||||||
/// Query to update a document
|
|
||||||
[<CompiledName "Update">]
|
|
||||||
let update tableName =
|
|
||||||
$"""UPDATE %s{tableName} SET data = @data WHERE {whereById "@id"}"""
|
|
||||||
|
|
||||||
/// Queries for counting documents
|
|
||||||
module Count =
|
|
||||||
|
|
||||||
/// Query to count all documents in a table
|
|
||||||
[<CompiledName "All">]
|
|
||||||
let all tableName =
|
|
||||||
$"SELECT COUNT(*) AS it FROM %s{tableName}"
|
|
||||||
|
|
||||||
/// Query to count matching documents using a text comparison on a JSON field
|
|
||||||
[<CompiledName "ByField">]
|
|
||||||
let byField tableName field =
|
|
||||||
$"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereByField field "@field"}"""
|
|
||||||
|
|
||||||
/// Queries for determining document existence
|
|
||||||
module Exists =
|
|
||||||
|
|
||||||
/// Query to determine if a document exists for the given ID
|
|
||||||
[<CompiledName "ById">]
|
|
||||||
let byId tableName =
|
|
||||||
$"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereById "@id"}) AS it"""
|
|
||||||
|
|
||||||
/// Query to determine if documents exist using a comparison on a JSON field
|
|
||||||
[<CompiledName "ByField">]
|
|
||||||
let byField tableName field =
|
|
||||||
$"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereByField field "@field"}) AS it"""
|
|
||||||
|
|
||||||
/// Queries for retrieving documents
|
|
||||||
module Find =
|
|
||||||
|
|
||||||
/// Query to retrieve a document by its ID
|
|
||||||
[<CompiledName "ById">]
|
|
||||||
let byId tableName =
|
|
||||||
$"""{Query.selectFromTable tableName} WHERE {whereById "@id"}"""
|
|
||||||
|
|
||||||
/// Query to retrieve documents using a comparison on a JSON field
|
|
||||||
[<CompiledName "ByField">]
|
|
||||||
let byField tableName field =
|
|
||||||
$"""{Query.selectFromTable tableName} WHERE {whereByField field "@field"}"""
|
|
||||||
|
|
||||||
/// Document patching (partial update) queries
|
|
||||||
module Patch =
|
|
||||||
|
|
||||||
/// Create an UPDATE statement to patch documents
|
|
||||||
let internal update tableName whereClause =
|
|
||||||
$"UPDATE %s{tableName} SET data = json_patch(data, json(@data)) WHERE %s{whereClause}"
|
|
||||||
|
|
||||||
/// Query to patch (partially update) a document by its ID
|
|
||||||
[<CompiledName "ById">]
|
|
||||||
let byId tableName =
|
|
||||||
whereById "@id" |> update tableName
|
|
||||||
|
|
||||||
/// Query to patch (partially update) a document via a comparison on a JSON field
|
|
||||||
[<CompiledName "ByField">]
|
|
||||||
let byField tableName field =
|
|
||||||
whereByField field "@field" |> update tableName
|
|
||||||
|
|
||||||
/// Queries to remove fields from documents
|
|
||||||
module RemoveFields =
|
|
||||||
|
|
||||||
/// Create an UPDATE statement to remove parameters
|
|
||||||
let internal update tableName (parameters: SqliteParameter list) whereClause =
|
|
||||||
let paramNames = parameters |> List.map _.ParameterName |> String.concat ", "
|
|
||||||
$"UPDATE %s{tableName} SET data = json_remove(data, {paramNames}) WHERE {whereClause}"
|
|
||||||
|
|
||||||
/// Query to remove fields from a document by the document's ID
|
|
||||||
[<CompiledName "FSharpById">]
|
|
||||||
let byId tableName parameters =
|
|
||||||
whereById "@id" |> update tableName parameters
|
|
||||||
|
|
||||||
/// Query to remove fields from a document by the document's ID
|
|
||||||
let ById(tableName, parameters) =
|
|
||||||
byId tableName (List.ofSeq parameters)
|
|
||||||
|
|
||||||
/// Query to remove fields from documents via a comparison on a JSON field within the document
|
|
||||||
[<CompiledName "FSharpByField">]
|
|
||||||
let byField tableName field parameters =
|
|
||||||
whereByField field "@field" |> update tableName parameters
|
|
||||||
|
|
||||||
/// Query to remove fields from documents via a comparison on a JSON field within the document
|
|
||||||
let ByField(tableName, field, parameters) =
|
|
||||||
byField tableName field (List.ofSeq parameters)
|
|
||||||
|
|
||||||
/// Queries to delete documents
|
|
||||||
module Delete =
|
|
||||||
|
|
||||||
/// Query to delete a document by its ID
|
|
||||||
[<CompiledName "ById">]
|
|
||||||
let byId tableName =
|
|
||||||
$"""DELETE FROM %s{tableName} WHERE {whereById "@id"}"""
|
|
||||||
|
|
||||||
/// Query to delete documents using a comparison on a JSON field
|
|
||||||
[<CompiledName "ByField">]
|
|
||||||
let byField tableName field =
|
|
||||||
$"""DELETE FROM %s{tableName} WHERE {whereByField field "@field"}"""
|
|
||||||
|
|
||||||
|
|
||||||
/// Parameter handling helpers
|
/// Parameter handling helpers
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
|
@ -169,39 +96,39 @@ module Parameters =
|
||||||
let jsonParam name (it: 'TJson) =
|
let jsonParam name (it: 'TJson) =
|
||||||
SqliteParameter(name, Configuration.serializer().Serialize it)
|
SqliteParameter(name, Configuration.serializer().Serialize it)
|
||||||
|
|
||||||
/// Create a JSON field parameter (name "@field")
|
/// Create JSON field parameters
|
||||||
[<CompiledName "FSharpAddField">]
|
[<CompiledName "AddFields">]
|
||||||
let addFieldParam name field parameters =
|
let addFieldParams fields parameters =
|
||||||
match field.Op with
|
let name = ParameterName()
|
||||||
| EX | NEX -> parameters
|
fields
|
||||||
|
|> Seq.map (fun it ->
|
||||||
|
seq {
|
||||||
|
match it.Op with
|
||||||
|
| EX | NEX -> ()
|
||||||
| BT ->
|
| BT ->
|
||||||
let values = field.Value :?> obj list
|
let p = name.Derive it.ParameterName
|
||||||
SqliteParameter($"{name}min", values[0]) :: SqliteParameter($"{name}max", values[1]) :: parameters
|
let values = it.Value :?> obj list
|
||||||
| _ -> SqliteParameter(name, field.Value) :: parameters
|
yield SqliteParameter($"{p}min", List.head values)
|
||||||
|
yield SqliteParameter($"{p}max", List.last values)
|
||||||
/// Create a JSON field parameter (name "@field")
|
| _ -> yield SqliteParameter(name.Derive it.ParameterName, it.Value) })
|
||||||
let AddField(name, field, parameters) =
|
|> Seq.collect id
|
||||||
match field.Op with
|
|
||||||
| EX | NEX -> parameters
|
|
||||||
| BT ->
|
|
||||||
let values = field.Value :?> obj list
|
|
||||||
// let min = SqliteParameter($"{name}min", SqliteType.Integer)
|
|
||||||
// min.Value <- values[0]
|
|
||||||
// let max = SqliteParameter($"{name}max", SqliteType.Integer)
|
|
||||||
// max.Value <- values[1]
|
|
||||||
[ SqliteParameter($"{name}min", values[0]); SqliteParameter($"{name}max", values[1]) ]
|
|
||||||
// [min; max]
|
|
||||||
|> Seq.append parameters
|
|> Seq.append parameters
|
||||||
| _ -> SqliteParameter(name, field.Value) |> Seq.singleton |> Seq.append parameters
|
|> Seq.toList
|
||||||
|
|> Seq.ofList
|
||||||
|
|
||||||
|
/// Create a JSON field parameter (name "@field")
|
||||||
|
[<CompiledName "AddField">]
|
||||||
|
[<System.Obsolete "Use addFieldParams instead; will be removed in v4">]
|
||||||
|
let addFieldParam name field parameters =
|
||||||
|
addFieldParams [ { field with ParameterName = Some name } ] parameters
|
||||||
|
|
||||||
/// Append JSON field name parameters for the given field names to the given parameters
|
/// Append JSON field name parameters for the given field names to the given parameters
|
||||||
[<CompiledName "FSharpFieldNames">]
|
[<CompiledName "FieldNames">]
|
||||||
let fieldNameParams paramName (fieldNames: string list) =
|
let fieldNameParams paramName fieldNames =
|
||||||
fieldNames |> List.mapi (fun idx name -> SqliteParameter($"%s{paramName}{idx}", $"$.{name}"))
|
fieldNames
|
||||||
|
|> Seq.mapi (fun idx name -> SqliteParameter($"%s{paramName}{idx}", $"$.%s{name}"))
|
||||||
/// Append JSON field name parameters for the given field names to the given parameters
|
|> Seq.toList
|
||||||
let FieldNames(paramName, fieldNames: string seq) =
|
|> Seq.ofList
|
||||||
fieldNames |> Seq.mapi (fun idx name -> SqliteParameter($"%s{paramName}{idx}", $"$.{name}"))
|
|
||||||
|
|
||||||
/// An empty parameter sequence
|
/// An empty parameter sequence
|
||||||
[<CompiledName "None">]
|
[<CompiledName "None">]
|
||||||
|
@ -323,18 +250,37 @@ module WithConn =
|
||||||
[<CompiledName "EnsureTable">]
|
[<CompiledName "EnsureTable">]
|
||||||
let ensureTable name conn = backgroundTask {
|
let ensureTable name conn = backgroundTask {
|
||||||
do! Custom.nonQuery (Query.Definition.ensureTable name) [] conn
|
do! Custom.nonQuery (Query.Definition.ensureTable name) [] conn
|
||||||
do! Custom.nonQuery (Query.Definition.ensureKey name) [] conn
|
do! Custom.nonQuery (Query.Definition.ensureKey name SQLite) [] conn
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an index on a document table
|
/// Create an index on a document table
|
||||||
[<CompiledName "EnsureFieldIndex">]
|
[<CompiledName "EnsureFieldIndex">]
|
||||||
let ensureFieldIndex tableName indexName fields conn =
|
let ensureFieldIndex tableName indexName fields conn =
|
||||||
Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields) [] conn
|
Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields SQLite) [] conn
|
||||||
|
|
||||||
|
/// Commands to add documents
|
||||||
|
[<AutoOpen>]
|
||||||
|
module Document =
|
||||||
|
|
||||||
/// Insert a new document
|
/// Insert a new document
|
||||||
[<CompiledName "Insert">]
|
[<CompiledName "Insert">]
|
||||||
let insert<'TDoc> tableName (document: 'TDoc) conn =
|
let insert<'TDoc> tableName (document: 'TDoc) conn =
|
||||||
Custom.nonQuery (Query.insert tableName) [ jsonParam "@data" document ] conn
|
let query =
|
||||||
|
match Configuration.autoIdStrategy () with
|
||||||
|
| Disabled -> Query.insert tableName
|
||||||
|
| strategy ->
|
||||||
|
let idField = Configuration.idField ()
|
||||||
|
let dataParam =
|
||||||
|
if AutoId.NeedsAutoId strategy document idField then
|
||||||
|
match strategy with
|
||||||
|
| Number -> $"(SELECT coalesce(max(data->>'{idField}'), 0) + 1 FROM {tableName})"
|
||||||
|
| Guid -> $"'{AutoId.GenerateGuid()}'"
|
||||||
|
| RandomString -> $"'{AutoId.GenerateRandomString(Configuration.idStringLength ())}'"
|
||||||
|
| Disabled -> "@data"
|
||||||
|
|> function it -> $"json_set(@data, '$.{idField}', {it})"
|
||||||
|
else "@data"
|
||||||
|
(Query.insert tableName).Replace("@data", dataParam)
|
||||||
|
Custom.nonQuery query [ jsonParam "@data" document ] conn
|
||||||
|
|
||||||
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
|
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
|
||||||
[<CompiledName "Save">]
|
[<CompiledName "Save">]
|
||||||
|
@ -348,12 +294,13 @@ module WithConn =
|
||||||
/// Count all documents in a table
|
/// Count all documents in a table
|
||||||
[<CompiledName "All">]
|
[<CompiledName "All">]
|
||||||
let all tableName conn =
|
let all tableName conn =
|
||||||
Custom.scalar (Query.Count.all tableName) [] toCount conn
|
Custom.scalar (Query.count tableName) [] toCount conn
|
||||||
|
|
||||||
/// Count matching documents using a comparison on a JSON field
|
/// Count matching documents using a comparison on JSON fields
|
||||||
[<CompiledName "ByField">]
|
[<CompiledName "ByFields">]
|
||||||
let byField tableName field conn =
|
let byFields tableName howMatched fields conn =
|
||||||
Custom.scalar (Query.Count.byField tableName field) (addFieldParam "@field" field []) toCount conn
|
Custom.scalar
|
||||||
|
(Query.byFields (Query.count tableName) howMatched fields) (addFieldParams fields []) toCount conn
|
||||||
|
|
||||||
/// Commands to determine if documents exist
|
/// Commands to determine if documents exist
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
@ -362,12 +309,16 @@ module WithConn =
|
||||||
/// Determine if a document exists for the given ID
|
/// Determine if a document exists for the given ID
|
||||||
[<CompiledName "ById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName (docId: 'TKey) conn =
|
let byId tableName (docId: 'TKey) conn =
|
||||||
Custom.scalar (Query.Exists.byId tableName) [ idParam docId ] toExists conn
|
Custom.scalar (Query.exists tableName (Query.whereById "@id")) [ idParam docId ] toExists conn
|
||||||
|
|
||||||
/// Determine if a document exists using a comparison on a JSON field
|
/// Determine if a document exists using a comparison on JSON fields
|
||||||
[<CompiledName "ByField">]
|
[<CompiledName "ByFields">]
|
||||||
let byField tableName field conn =
|
let byFields tableName howMatched fields conn =
|
||||||
Custom.scalar (Query.Exists.byField tableName field) (addFieldParam "@field" field []) toExists conn
|
Custom.scalar
|
||||||
|
(Query.exists tableName (Query.whereByFields howMatched fields))
|
||||||
|
(addFieldParams fields [])
|
||||||
|
toExists
|
||||||
|
conn
|
||||||
|
|
||||||
/// Commands to retrieve documents
|
/// Commands to retrieve documents
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
@ -376,42 +327,99 @@ module WithConn =
|
||||||
/// Retrieve all documents in the given table
|
/// Retrieve all documents in the given table
|
||||||
[<CompiledName "FSharpAll">]
|
[<CompiledName "FSharpAll">]
|
||||||
let all<'TDoc> tableName conn =
|
let all<'TDoc> tableName conn =
|
||||||
Custom.list<'TDoc> (Query.selectFromTable tableName) [] fromData<'TDoc> conn
|
Custom.list<'TDoc> (Query.find tableName) [] fromData<'TDoc> conn
|
||||||
|
|
||||||
/// Retrieve all documents in the given table
|
/// Retrieve all documents in the given table
|
||||||
let All<'TDoc>(tableName, conn) =
|
let All<'TDoc>(tableName, conn) =
|
||||||
Custom.List(Query.selectFromTable tableName, [], fromData<'TDoc>, conn)
|
Custom.List(Query.find tableName, [], fromData<'TDoc>, conn)
|
||||||
|
|
||||||
|
/// Retrieve all documents in the given table ordered by the given fields in the document
|
||||||
|
[<CompiledName "FSharpAllOrdered">]
|
||||||
|
let allOrdered<'TDoc> tableName orderFields conn =
|
||||||
|
Custom.list<'TDoc> (Query.find tableName + Query.orderBy orderFields SQLite) [] fromData<'TDoc> conn
|
||||||
|
|
||||||
|
/// Retrieve all documents in the given table ordered by the given fields in the document
|
||||||
|
let AllOrdered<'TDoc>(tableName, orderFields, conn) =
|
||||||
|
Custom.List(Query.find tableName + Query.orderBy orderFields SQLite, [], fromData<'TDoc>, conn)
|
||||||
|
|
||||||
/// Retrieve a document by its ID (returns None if not found)
|
/// Retrieve a document by its ID (returns None if not found)
|
||||||
[<CompiledName "FSharpById">]
|
[<CompiledName "FSharpById">]
|
||||||
let byId<'TKey, 'TDoc> tableName (docId: 'TKey) conn =
|
let byId<'TKey, 'TDoc> tableName (docId: 'TKey) conn =
|
||||||
Custom.single<'TDoc> (Query.Find.byId tableName) [ idParam docId ] fromData<'TDoc> conn
|
Custom.single<'TDoc> (Query.byId (Query.find tableName) docId) [ idParam docId ] fromData<'TDoc> conn
|
||||||
|
|
||||||
/// Retrieve a document by its ID (returns null if not found)
|
/// Retrieve a document by its ID (returns null if not found)
|
||||||
let ById<'TKey, 'TDoc when 'TDoc: null>(tableName, docId: 'TKey, conn) =
|
let ById<'TKey, 'TDoc when 'TDoc: null>(tableName, docId: 'TKey, conn) =
|
||||||
Custom.Single<'TDoc>(Query.Find.byId tableName, [ idParam docId ], fromData<'TDoc>, conn)
|
Custom.Single<'TDoc>(Query.byId (Query.find tableName) docId, [ idParam docId ], fromData<'TDoc>, conn)
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field
|
/// Retrieve documents via a comparison on JSON fields
|
||||||
[<CompiledName "FSharpByField">]
|
[<CompiledName "FSharpByFields">]
|
||||||
let byField<'TDoc> tableName field conn =
|
let byFields<'TDoc> tableName howMatched fields conn =
|
||||||
Custom.list<'TDoc>
|
Custom.list<'TDoc>
|
||||||
(Query.Find.byField tableName field) (addFieldParam "@field" field []) fromData<'TDoc> conn
|
(Query.byFields (Query.find tableName) howMatched fields)
|
||||||
|
(addFieldParams fields [])
|
||||||
|
fromData<'TDoc>
|
||||||
|
conn
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field
|
/// Retrieve documents via a comparison on JSON fields
|
||||||
let ByField<'TDoc>(tableName, field, conn) =
|
let ByFields<'TDoc>(tableName, howMatched, fields, conn) =
|
||||||
Custom.List<'TDoc>(
|
Custom.List<'TDoc>(
|
||||||
Query.Find.byField tableName field, addFieldParam "@field" field [], fromData<'TDoc>, conn)
|
Query.byFields (Query.find tableName) howMatched fields,
|
||||||
|
addFieldParams fields [],
|
||||||
|
fromData<'TDoc>,
|
||||||
|
conn)
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field, returning only the first result
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
|
||||||
[<CompiledName "FSharpFirstByField">]
|
[<CompiledName "FSharpByFieldsOrdered">]
|
||||||
let firstByField<'TDoc> tableName field conn =
|
let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn =
|
||||||
|
Custom.list<'TDoc>
|
||||||
|
(Query.byFields (Query.find tableName) howMatched queryFields + Query.orderBy orderFields SQLite)
|
||||||
|
(addFieldParams queryFields [])
|
||||||
|
fromData<'TDoc>
|
||||||
|
conn
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
|
||||||
|
let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn) =
|
||||||
|
Custom.List<'TDoc>(
|
||||||
|
Query.byFields (Query.find tableName) howMatched queryFields + Query.orderBy orderFields SQLite,
|
||||||
|
addFieldParams queryFields [],
|
||||||
|
fromData<'TDoc>,
|
||||||
|
conn)
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields, returning only the first result
|
||||||
|
[<CompiledName "FSharpFirstByFields">]
|
||||||
|
let firstByFields<'TDoc> tableName howMatched fields conn =
|
||||||
Custom.single
|
Custom.single
|
||||||
$"{Query.Find.byField tableName field} LIMIT 1" (addFieldParam "@field" field []) fromData<'TDoc> conn
|
$"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1"
|
||||||
|
(addFieldParams fields [])
|
||||||
|
fromData<'TDoc>
|
||||||
|
conn
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field, returning only the first result
|
/// Retrieve documents via a comparison on JSON fields, returning only the first result
|
||||||
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) =
|
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields, conn) =
|
||||||
Custom.Single(
|
Custom.Single(
|
||||||
$"{Query.Find.byField tableName field} LIMIT 1", addFieldParam "@field" field [], fromData<'TDoc>, conn)
|
$"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1",
|
||||||
|
addFieldParams fields [],
|
||||||
|
fromData<'TDoc>,
|
||||||
|
conn)
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning
|
||||||
|
/// only the first result
|
||||||
|
[<CompiledName "FSharpFirstByFieldsOrdered">]
|
||||||
|
let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn =
|
||||||
|
Custom.single
|
||||||
|
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields SQLite} LIMIT 1"
|
||||||
|
(addFieldParams queryFields [])
|
||||||
|
fromData<'TDoc>
|
||||||
|
conn
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning
|
||||||
|
/// only the first result
|
||||||
|
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields, conn) =
|
||||||
|
Custom.Single(
|
||||||
|
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields SQLite} LIMIT 1",
|
||||||
|
addFieldParams queryFields [],
|
||||||
|
fromData<'TDoc>,
|
||||||
|
conn)
|
||||||
|
|
||||||
/// Commands to update documents
|
/// Commands to update documents
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
@ -420,7 +428,10 @@ module WithConn =
|
||||||
/// Update an entire document by its ID
|
/// Update an entire document by its ID
|
||||||
[<CompiledName "ById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName (docId: 'TKey) (document: 'TDoc) conn =
|
let byId tableName (docId: 'TKey) (document: 'TDoc) conn =
|
||||||
Custom.nonQuery (Query.update tableName) [ idParam docId; jsonParam "@data" document ] conn
|
Custom.nonQuery
|
||||||
|
(Query.statementWhere (Query.update tableName) (Query.whereById "@id"))
|
||||||
|
[ idParam docId; jsonParam "@data" document ]
|
||||||
|
conn
|
||||||
|
|
||||||
/// Update an entire document by its ID, using the provided function to obtain the ID from the document
|
/// Update an entire document by its ID, using the provided function to obtain the ID from the document
|
||||||
[<CompiledName "FSharpByFunc">]
|
[<CompiledName "FSharpByFunc">]
|
||||||
|
@ -438,38 +449,38 @@ module WithConn =
|
||||||
/// Patch a document by its ID
|
/// Patch a document by its ID
|
||||||
[<CompiledName "ById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName (docId: 'TKey) (patch: 'TPatch) conn =
|
let byId tableName (docId: 'TKey) (patch: 'TPatch) conn =
|
||||||
Custom.nonQuery (Query.Patch.byId tableName) [ idParam docId; jsonParam "@data" patch ] conn
|
|
||||||
|
|
||||||
/// Patch documents using a comparison on a JSON field
|
|
||||||
[<CompiledName "ByField">]
|
|
||||||
let byField tableName field (patch: 'TPatch) (conn: SqliteConnection) =
|
|
||||||
Custom.nonQuery
|
Custom.nonQuery
|
||||||
(Query.Patch.byField tableName field) (addFieldParam "@field" field [ jsonParam "@data" patch ]) conn
|
(Query.byId (Query.patch tableName) docId) [ idParam docId; jsonParam "@data" patch ] conn
|
||||||
|
|
||||||
|
/// Patch documents using a comparison on JSON fields
|
||||||
|
[<CompiledName "ByFields">]
|
||||||
|
let byFields tableName howMatched fields (patch: 'TPatch) conn =
|
||||||
|
Custom.nonQuery
|
||||||
|
(Query.byFields (Query.patch tableName) howMatched fields)
|
||||||
|
(addFieldParams fields [ jsonParam "@data" patch ])
|
||||||
|
conn
|
||||||
|
|
||||||
/// Commands to remove fields from documents
|
/// Commands to remove fields from documents
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module RemoveFields =
|
module RemoveFields =
|
||||||
|
|
||||||
/// Remove fields from a document by the document's ID
|
/// Remove fields from a document by the document's ID
|
||||||
[<CompiledName "FSharpById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName (docId: 'TKey) fieldNames conn =
|
let byId tableName (docId: 'TKey) fieldNames conn =
|
||||||
let nameParams = fieldNameParams "@name" fieldNames
|
let nameParams = fieldNameParams "@name" fieldNames
|
||||||
Custom.nonQuery (Query.RemoveFields.byId tableName nameParams) (idParam docId :: nameParams) conn
|
Custom.nonQuery
|
||||||
|
(Query.byId (Query.removeFields tableName nameParams) docId)
|
||||||
|
(idParam docId |> Seq.singleton |> Seq.append nameParams)
|
||||||
|
conn
|
||||||
|
|
||||||
/// Remove fields from a document by the document's ID
|
/// Remove fields from documents via a comparison on JSON fields in the document
|
||||||
let ById(tableName, docId: 'TKey, fieldNames, conn) =
|
[<CompiledName "ByFields">]
|
||||||
byId tableName docId (List.ofSeq fieldNames) conn
|
let byFields tableName howMatched fields fieldNames conn =
|
||||||
|
|
||||||
/// Remove fields from documents via a comparison on a JSON field in the document
|
|
||||||
[<CompiledName "FSharpByField">]
|
|
||||||
let byField tableName field fieldNames conn =
|
|
||||||
let nameParams = fieldNameParams "@name" fieldNames
|
let nameParams = fieldNameParams "@name" fieldNames
|
||||||
Custom.nonQuery
|
Custom.nonQuery
|
||||||
(Query.RemoveFields.byField tableName field nameParams) (addFieldParam "@field" field nameParams) conn
|
(Query.byFields (Query.removeFields tableName nameParams) howMatched fields)
|
||||||
|
(addFieldParams fields nameParams)
|
||||||
/// Remove fields from documents via a comparison on a JSON field in the document
|
conn
|
||||||
let ByField(tableName, field, fieldNames, conn) =
|
|
||||||
byField tableName field (List.ofSeq fieldNames) conn
|
|
||||||
|
|
||||||
/// Commands to delete documents
|
/// Commands to delete documents
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
@ -478,12 +489,12 @@ module WithConn =
|
||||||
/// Delete a document by its ID
|
/// Delete a document by its ID
|
||||||
[<CompiledName "ById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName (docId: 'TKey) conn =
|
let byId tableName (docId: 'TKey) conn =
|
||||||
Custom.nonQuery (Query.Delete.byId tableName) [ idParam docId ] conn
|
Custom.nonQuery (Query.byId (Query.delete tableName) docId) [ idParam docId ] conn
|
||||||
|
|
||||||
/// Delete documents by matching a comparison on a JSON field
|
/// Delete documents by matching a comparison on JSON fields
|
||||||
[<CompiledName "ByField">]
|
[<CompiledName "ByFields">]
|
||||||
let byField tableName field conn =
|
let byFields tableName howMatched fields conn =
|
||||||
Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam "@field" field []) conn
|
Custom.nonQuery (Query.byFields (Query.delete tableName) howMatched fields) (addFieldParams fields []) conn
|
||||||
|
|
||||||
|
|
||||||
/// Commands to execute custom SQL queries
|
/// Commands to execute custom SQL queries
|
||||||
|
@ -529,6 +540,7 @@ module Custom =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Custom.Scalar<'T>(query, parameters, mapFunc, conn)
|
WithConn.Custom.Scalar<'T>(query, parameters, mapFunc, conn)
|
||||||
|
|
||||||
|
|
||||||
/// Functions to create tables and indexes
|
/// Functions to create tables and indexes
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Definition =
|
module Definition =
|
||||||
|
@ -545,6 +557,7 @@ module Definition =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Definition.ensureFieldIndex tableName indexName fields conn
|
WithConn.Definition.ensureFieldIndex tableName indexName fields conn
|
||||||
|
|
||||||
|
|
||||||
/// Document insert/save functions
|
/// Document insert/save functions
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
module Document =
|
module Document =
|
||||||
|
@ -553,13 +566,14 @@ module Document =
|
||||||
[<CompiledName "Insert">]
|
[<CompiledName "Insert">]
|
||||||
let insert<'TDoc> tableName (document: 'TDoc) =
|
let insert<'TDoc> tableName (document: 'TDoc) =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.insert tableName document conn
|
WithConn.Document.insert tableName document conn
|
||||||
|
|
||||||
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
|
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
|
||||||
[<CompiledName "Save">]
|
[<CompiledName "Save">]
|
||||||
let save<'TDoc> tableName (document: 'TDoc) =
|
let save<'TDoc> tableName (document: 'TDoc) =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.save tableName document conn
|
WithConn.Document.save tableName document conn
|
||||||
|
|
||||||
|
|
||||||
/// Commands to count documents
|
/// Commands to count documents
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
@ -571,11 +585,12 @@ module Count =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Count.all tableName conn
|
WithConn.Count.all tableName conn
|
||||||
|
|
||||||
/// Count matching documents using a comparison on a JSON field
|
/// Count matching documents using a comparison on JSON fields
|
||||||
[<CompiledName "ByField">]
|
[<CompiledName "ByFields">]
|
||||||
let byField tableName field =
|
let byFields tableName howMatched fields =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Count.byField tableName field conn
|
WithConn.Count.byFields tableName howMatched fields conn
|
||||||
|
|
||||||
|
|
||||||
/// Commands to determine if documents exist
|
/// Commands to determine if documents exist
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
@ -587,11 +602,12 @@ module Exists =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Exists.byId tableName docId conn
|
WithConn.Exists.byId tableName docId conn
|
||||||
|
|
||||||
/// Determine if a document exists using a comparison on a JSON field
|
/// Determine if a document exists using a comparison on JSON fields
|
||||||
[<CompiledName "ByField">]
|
[<CompiledName "ByFields">]
|
||||||
let byField tableName field =
|
let byFields tableName howMatched fields =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Exists.byField tableName field conn
|
WithConn.Exists.byFields tableName howMatched fields conn
|
||||||
|
|
||||||
|
|
||||||
/// Commands to determine if documents exist
|
/// Commands to determine if documents exist
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
@ -608,6 +624,17 @@ module Find =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Find.All<'TDoc>(tableName, conn)
|
WithConn.Find.All<'TDoc>(tableName, conn)
|
||||||
|
|
||||||
|
/// Retrieve all documents in the given table ordered by the given fields in the document
|
||||||
|
[<CompiledName "FSharpAllOrdered">]
|
||||||
|
let allOrdered<'TDoc> tableName orderFields =
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
WithConn.Find.allOrdered<'TDoc> tableName orderFields conn
|
||||||
|
|
||||||
|
/// Retrieve all documents in the given table ordered by the given fields in the document
|
||||||
|
let AllOrdered<'TDoc> tableName orderFields =
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
WithConn.Find.AllOrdered<'TDoc>(tableName, orderFields, conn)
|
||||||
|
|
||||||
/// Retrieve a document by its ID (returns None if not found)
|
/// Retrieve a document by its ID (returns None if not found)
|
||||||
[<CompiledName "FSharpById">]
|
[<CompiledName "FSharpById">]
|
||||||
let byId<'TKey, 'TDoc> tableName docId =
|
let byId<'TKey, 'TDoc> tableName docId =
|
||||||
|
@ -619,27 +646,52 @@ module Find =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
|
WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field
|
/// Retrieve documents via a comparison on JSON fields
|
||||||
[<CompiledName "FSharpByField">]
|
[<CompiledName "FSharpByFields">]
|
||||||
let byField<'TDoc> tableName field =
|
let byFields<'TDoc> tableName howMatched fields =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Find.byField<'TDoc> tableName field conn
|
WithConn.Find.byFields<'TDoc> tableName howMatched fields conn
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field
|
/// Retrieve documents via a comparison on JSON fields
|
||||||
let ByField<'TDoc>(tableName, field) =
|
let ByFields<'TDoc>(tableName, howMatched, fields) =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Find.ByField<'TDoc>(tableName, field, conn)
|
WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn)
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field, returning only the first result
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
|
||||||
[<CompiledName "FSharpFirstByField">]
|
[<CompiledName "FSharpByFieldsOrdered">]
|
||||||
let firstByField<'TDoc> tableName field =
|
let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Find.firstByField<'TDoc> tableName field conn
|
WithConn.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
|
||||||
|
|
||||||
/// Retrieve documents via a comparison on a JSON field, returning only the first result
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
|
||||||
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
|
let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields) =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Find.FirstByField<'TDoc>(tableName, field, conn)
|
WithConn.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields, returning only the first result
|
||||||
|
[<CompiledName "FSharpFirstByFields">]
|
||||||
|
let firstByFields<'TDoc> tableName howMatched fields =
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields, returning only the first result
|
||||||
|
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields) =
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn)
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning only
|
||||||
|
/// the first result
|
||||||
|
[<CompiledName "FSharpFirstByFieldsOrdered">]
|
||||||
|
let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
WithConn.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
|
||||||
|
|
||||||
|
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning only
|
||||||
|
/// the first result
|
||||||
|
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields) =
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
|
||||||
|
|
||||||
|
|
||||||
/// Commands to update documents
|
/// Commands to update documents
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
@ -662,6 +714,7 @@ module Update =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Update.ByFunc(tableName, idFunc, document, conn)
|
WithConn.Update.ByFunc(tableName, idFunc, document, conn)
|
||||||
|
|
||||||
|
|
||||||
/// Commands to patch (partially update) documents
|
/// Commands to patch (partially update) documents
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Patch =
|
module Patch =
|
||||||
|
@ -672,37 +725,29 @@ module Patch =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Patch.byId tableName docId patch conn
|
WithConn.Patch.byId tableName docId patch conn
|
||||||
|
|
||||||
/// Patch documents using a comparison on a JSON field in the WHERE clause
|
/// Patch documents using a comparison on JSON fields in the WHERE clause
|
||||||
[<CompiledName "ByField">]
|
[<CompiledName "ByFields">]
|
||||||
let byField tableName field (patch: 'TPatch) =
|
let byFields tableName howMatched fields (patch: 'TPatch) =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Patch.byField tableName field patch conn
|
WithConn.Patch.byFields tableName howMatched fields patch conn
|
||||||
|
|
||||||
|
|
||||||
/// Commands to remove fields from documents
|
/// Commands to remove fields from documents
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module RemoveFields =
|
module RemoveFields =
|
||||||
|
|
||||||
/// Remove fields from a document by the document's ID
|
/// Remove fields from a document by the document's ID
|
||||||
[<CompiledName "FSharpById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName (docId: 'TKey) fieldNames =
|
let byId tableName (docId: 'TKey) fieldNames =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.RemoveFields.byId tableName docId fieldNames conn
|
WithConn.RemoveFields.byId tableName docId fieldNames conn
|
||||||
|
|
||||||
/// Remove fields from a document by the document's ID
|
/// Remove field from documents via a comparison on JSON fields in the document
|
||||||
let ById(tableName, docId: 'TKey, fieldNames) =
|
[<CompiledName "ByFields">]
|
||||||
|
let byFields tableName howMatched fields fieldNames =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.RemoveFields.ById(tableName, docId, fieldNames, conn)
|
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn
|
||||||
|
|
||||||
/// Remove field from documents via a comparison on a JSON field in the document
|
|
||||||
[<CompiledName "FSharpByField">]
|
|
||||||
let byField tableName field fieldNames =
|
|
||||||
use conn = Configuration.dbConn ()
|
|
||||||
WithConn.RemoveFields.byField tableName field fieldNames conn
|
|
||||||
|
|
||||||
/// Remove field from documents via a comparison on a JSON field in the document
|
|
||||||
let ByField(tableName, field, fieldNames) =
|
|
||||||
use conn = Configuration.dbConn ()
|
|
||||||
WithConn.RemoveFields.ByField(tableName, field, fieldNames, conn)
|
|
||||||
|
|
||||||
/// Commands to delete documents
|
/// Commands to delete documents
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
@ -714,8 +759,8 @@ module Delete =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Delete.byId tableName docId conn
|
WithConn.Delete.byId tableName docId conn
|
||||||
|
|
||||||
/// Delete documents by matching a comparison on a JSON field
|
/// Delete documents by matching a comparison on JSON fields
|
||||||
[<CompiledName "ByField">]
|
[<CompiledName "ByFields">]
|
||||||
let byField tableName field =
|
let byFields tableName howMatched fields =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
WithConn.Delete.byField tableName field conn
|
WithConn.Delete.byFields tableName howMatched fields conn
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Expecto.CSharp;
|
using Expecto.CSharp;
|
||||||
using Expecto;
|
using Expecto;
|
||||||
using Microsoft.FSharp.Collections;
|
using Microsoft.FSharp.Collections;
|
||||||
|
using Microsoft.FSharp.Core;
|
||||||
|
|
||||||
namespace BitBadger.Documents.Tests.CSharp;
|
namespace BitBadger.Documents.Tests.CSharp;
|
||||||
|
|
||||||
|
@ -21,58 +22,10 @@ internal class TestSerializer : IDocumentSerializer
|
||||||
public static class CommonCSharpTests
|
public static class CommonCSharpTests
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unit tests
|
/// Unit tests for the Op enum
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Tests]
|
private static readonly Test OpTests = TestList("Op",
|
||||||
public static readonly Test Unit = TestList("Common.C# Unit", new[]
|
[
|
||||||
{
|
|
||||||
TestSequenced(
|
|
||||||
TestList("Configuration", new[]
|
|
||||||
{
|
|
||||||
TestCase("UseSerializer succeeds", () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Configuration.UseSerializer(new TestSerializer());
|
|
||||||
|
|
||||||
var serialized = Configuration.Serializer().Serialize(new SubDocument
|
|
||||||
{
|
|
||||||
Foo = "howdy",
|
|
||||||
Bar = "bye"
|
|
||||||
});
|
|
||||||
Expect.equal(serialized, "{\"Overridden\":true}", "Specified serializer was not used");
|
|
||||||
|
|
||||||
var deserialized = Configuration.Serializer()
|
|
||||||
.Deserialize<object>("{\"Something\":\"here\"}");
|
|
||||||
Expect.isNull(deserialized, "Specified serializer should have returned null");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Configuration.UseSerializer(DocumentSerializer.Default);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
TestCase("Serializer returns configured serializer", () =>
|
|
||||||
{
|
|
||||||
Expect.isTrue(ReferenceEquals(DocumentSerializer.Default, Configuration.Serializer()),
|
|
||||||
"Serializer should have been the same");
|
|
||||||
}),
|
|
||||||
TestCase("UseIdField / IdField succeeds", () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Expect.equal(Configuration.IdField(), "Id",
|
|
||||||
"The default configured ID field was incorrect");
|
|
||||||
Configuration.UseIdField("id");
|
|
||||||
Expect.equal(Configuration.IdField(), "id", "UseIdField did not set the ID field");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Configuration.UseIdField("Id");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})),
|
|
||||||
TestList("Op", new[]
|
|
||||||
{
|
|
||||||
TestCase("EQ succeeds", () =>
|
TestCase("EQ succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Op.EQ.ToString(), "=", "The equals operator was not correct");
|
Expect.equal(Op.EQ.ToString(), "=", "The equals operator was not correct");
|
||||||
|
@ -109,9 +62,13 @@ public static class CommonCSharpTests
|
||||||
{
|
{
|
||||||
Expect.equal(Op.NEX.ToString(), "IS NULL", "The \"not exists\" operator was not correct");
|
Expect.equal(Op.NEX.ToString(), "IS NULL", "The \"not exists\" operator was not correct");
|
||||||
})
|
})
|
||||||
}),
|
]);
|
||||||
TestList("Field", new[]
|
|
||||||
{
|
/// <summary>
|
||||||
|
/// Unit tests for the Field class
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test FieldTests = TestList("Field",
|
||||||
|
[
|
||||||
TestCase("EQ succeeds", () =>
|
TestCase("EQ succeeds", () =>
|
||||||
{
|
{
|
||||||
var field = Field.EQ("Test", 14);
|
var field = Field.EQ("Test", 14);
|
||||||
|
@ -159,7 +116,7 @@ public static class CommonCSharpTests
|
||||||
var field = Field.BT("Age", 18, 49);
|
var field = Field.BT("Age", 18, 49);
|
||||||
Expect.equal(field.Name, "Age", "Field name incorrect");
|
Expect.equal(field.Name, "Age", "Field name incorrect");
|
||||||
Expect.equal(field.Op, Op.BT, "Operator incorrect");
|
Expect.equal(field.Op, Op.BT, "Operator incorrect");
|
||||||
Expect.equal(((FSharpList<object>)field.Value).ToArray(), new object[] { 18, 49 }, "Value incorrect");
|
Expect.equal(((FSharpList<object>)field.Value).ToArray(), [18, 49], "Value incorrect");
|
||||||
}),
|
}),
|
||||||
TestCase("EX succeeds", () =>
|
TestCase("EX succeeds", () =>
|
||||||
{
|
{
|
||||||
|
@ -172,48 +129,411 @@ public static class CommonCSharpTests
|
||||||
var field = Field.NEX("Rad");
|
var field = Field.NEX("Rad");
|
||||||
Expect.equal(field.Name, "Rad", "Field name incorrect");
|
Expect.equal(field.Name, "Rad", "Field name incorrect");
|
||||||
Expect.equal(field.Op, Op.NEX, "Operator incorrect");
|
Expect.equal(field.Op, Op.NEX, "Operator incorrect");
|
||||||
|
}),
|
||||||
|
TestList("NameToPath",
|
||||||
|
[
|
||||||
|
TestCase("succeeds for PostgreSQL and a simple name", () =>
|
||||||
|
{
|
||||||
|
Expect.equal("data->>'Simple'", Field.NameToPath("Simple", Dialect.PostgreSQL),
|
||||||
|
"Path not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for SQLite and a simple name", () =>
|
||||||
|
{
|
||||||
|
Expect.equal("data->>'Simple'", Field.NameToPath("Simple", Dialect.SQLite),
|
||||||
|
"Path not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for PostgreSQL and a nested name", () =>
|
||||||
|
{
|
||||||
|
Expect.equal("data#>>'{A,Long,Path,to,the,Property}'",
|
||||||
|
Field.NameToPath("A.Long.Path.to.the.Property", Dialect.PostgreSQL),
|
||||||
|
"Path not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for SQLite and a nested name", () =>
|
||||||
|
{
|
||||||
|
Expect.equal("data->>'A'->>'Long'->>'Path'->>'to'->>'the'->>'Property'",
|
||||||
|
Field.NameToPath("A.Long.Path.to.the.Property", Dialect.SQLite),
|
||||||
|
"Path not constructed correctly");
|
||||||
})
|
})
|
||||||
|
]),
|
||||||
|
TestCase("WithParameterName succeeds", () =>
|
||||||
|
{
|
||||||
|
var field = Field.EQ("Bob", "Tom").WithParameterName("@name");
|
||||||
|
Expect.isSome(field.ParameterName, "The parameter name should have been filled");
|
||||||
|
Expect.equal("@name", field.ParameterName.Value, "The parameter name is incorrect");
|
||||||
}),
|
}),
|
||||||
TestList("Query", new[]
|
TestCase("WithQualifier succeeds", () =>
|
||||||
{
|
{
|
||||||
TestCase("SelectFromTable succeeds", () =>
|
var field = Field.EQ("Bill", "Matt").WithQualifier("joe");
|
||||||
{
|
Expect.isSome(field.Qualifier, "The table qualifier should have been filled");
|
||||||
Expect.equal(Query.SelectFromTable("test.table"), "SELECT data FROM test.table",
|
Expect.equal("joe", field.Qualifier.Value, "The table qualifier is incorrect");
|
||||||
"SELECT statement not correct");
|
|
||||||
}),
|
}),
|
||||||
TestList("Definition", new[]
|
TestList("Path",
|
||||||
|
[
|
||||||
|
TestCase("succeeds for a PostgreSQL single field with no qualifier", () =>
|
||||||
{
|
{
|
||||||
|
var field = Field.GE("SomethingCool", 18);
|
||||||
|
Expect.equal("data->>'SomethingCool'", field.Path(Dialect.PostgreSQL),
|
||||||
|
"The PostgreSQL path is incorrect");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for a PostgreSQL single field with a qualifier", () =>
|
||||||
|
{
|
||||||
|
var field = Field.LT("SomethingElse", 9).WithQualifier("this");
|
||||||
|
Expect.equal("this.data->>'SomethingElse'", field.Path(Dialect.PostgreSQL),
|
||||||
|
"The PostgreSQL path is incorrect");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for a PostgreSQL nested field with no qualifier", () =>
|
||||||
|
{
|
||||||
|
var field = Field.EQ("My.Nested.Field", "howdy");
|
||||||
|
Expect.equal("data#>>'{My,Nested,Field}'", field.Path(Dialect.PostgreSQL),
|
||||||
|
"The PostgreSQL path is incorrect");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for a PostgreSQL nested field with a qualifier", () =>
|
||||||
|
{
|
||||||
|
var field = Field.EQ("Nest.Away", "doc").WithQualifier("bird");
|
||||||
|
Expect.equal("bird.data#>>'{Nest,Away}'", field.Path(Dialect.PostgreSQL),
|
||||||
|
"The PostgreSQL path is incorrect");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for a SQLite single field with no qualifier", () =>
|
||||||
|
{
|
||||||
|
var field = Field.GE("SomethingCool", 18);
|
||||||
|
Expect.equal("data->>'SomethingCool'", field.Path(Dialect.SQLite), "The SQLite path is incorrect");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for a SQLite single field with a qualifier", () =>
|
||||||
|
{
|
||||||
|
var field = Field.LT("SomethingElse", 9).WithQualifier("this");
|
||||||
|
Expect.equal("this.data->>'SomethingElse'", field.Path(Dialect.SQLite), "The SQLite path is incorrect");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for a SQLite nested field with no qualifier", () =>
|
||||||
|
{
|
||||||
|
var field = Field.EQ("My.Nested.Field", "howdy");
|
||||||
|
Expect.equal("data->>'My'->>'Nested'->>'Field'", field.Path(Dialect.SQLite),
|
||||||
|
"The SQLite path is incorrect");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for a SQLite nested field with a qualifier", () =>
|
||||||
|
{
|
||||||
|
var field = Field.EQ("Nest.Away", "doc").WithQualifier("bird");
|
||||||
|
Expect.equal("bird.data->>'Nest'->>'Away'", field.Path(Dialect.SQLite), "The SQLite path is incorrect");
|
||||||
|
})
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unit tests for the FieldMatch enum
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test FieldMatchTests = TestList("FieldMatch.ToString",
|
||||||
|
[
|
||||||
|
TestCase("succeeds for Any", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(FieldMatch.Any.ToString(), "OR", "SQL for Any is incorrect");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for All", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(FieldMatch.All.ToString(), "AND", "SQL for All is incorrect");
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unit tests for the ParameterName class
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test ParameterNameTests = TestList("ParameterName.Derive",
|
||||||
|
[
|
||||||
|
TestCase("succeeds with existing name", () =>
|
||||||
|
{
|
||||||
|
ParameterName name = new();
|
||||||
|
Expect.equal(name.Derive(FSharpOption<string>.Some("@taco")), "@taco", "Name should have been @taco");
|
||||||
|
Expect.equal(name.Derive(FSharpOption<string>.None), "@field0",
|
||||||
|
"Counter should not have advanced for named field");
|
||||||
|
}),
|
||||||
|
TestCase("Derive succeeds with non-existent name", () =>
|
||||||
|
{
|
||||||
|
ParameterName name = new();
|
||||||
|
Expect.equal(name.Derive(FSharpOption<string>.None), "@field0",
|
||||||
|
"Anonymous field name should have been returned");
|
||||||
|
Expect.equal(name.Derive(FSharpOption<string>.None), "@field1",
|
||||||
|
"Counter should have advanced from previous call");
|
||||||
|
Expect.equal(name.Derive(FSharpOption<string>.None), "@field2",
|
||||||
|
"Counter should have advanced from previous call");
|
||||||
|
Expect.equal(name.Derive(FSharpOption<string>.None), "@field3",
|
||||||
|
"Counter should have advanced from previous call");
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unit tests for the AutoId enum
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test AutoIdTests = TestList("AutoId",
|
||||||
|
[
|
||||||
|
TestCase("GenerateGuid succeeds", () =>
|
||||||
|
{
|
||||||
|
var autoId = AutoId.GenerateGuid();
|
||||||
|
Expect.isNotNull(autoId, "The GUID auto-ID should not have been null");
|
||||||
|
Expect.stringHasLength(autoId, 32, "The GUID auto-ID should have been 32 characters long");
|
||||||
|
Expect.equal(autoId, autoId.ToLowerInvariant(), "The GUID auto-ID should have been lowercase");
|
||||||
|
}),
|
||||||
|
TestCase("GenerateRandomString succeeds", () =>
|
||||||
|
{
|
||||||
|
foreach (var length in (int[]) [6, 8, 12, 20, 32, 57, 64])
|
||||||
|
{
|
||||||
|
var autoId = AutoId.GenerateRandomString(length);
|
||||||
|
Expect.isNotNull(autoId, $"Random string ({length}) should not have been null");
|
||||||
|
Expect.stringHasLength(autoId, length, $"Random string should have been {length} characters long");
|
||||||
|
Expect.equal(autoId, autoId.ToLowerInvariant(),
|
||||||
|
$"Random string ({length}) should have been lowercase");
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TestList("NeedsAutoId",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when no auto ID is configured", () =>
|
||||||
|
{
|
||||||
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Disabled, new object(), "id"),
|
||||||
|
"Disabled auto-ID never needs an automatic ID");
|
||||||
|
}),
|
||||||
|
TestCase("fails for any when the ID property is not found", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ = AutoId.NeedsAutoId(AutoId.Number, new { Key = "" }, "Id");
|
||||||
|
Expect.isTrue(false, "Non-existent ID property should have thrown an exception");
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for byte when the ID is zero", () =>
|
||||||
|
{
|
||||||
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.Number, new { Id = (sbyte)0 }, "Id"),
|
||||||
|
"Zero ID should have returned true");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for byte when the ID is non-zero", () =>
|
||||||
|
{
|
||||||
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Number, new { Id = (sbyte)4 }, "Id"),
|
||||||
|
"Non-zero ID should have returned false");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for short when the ID is zero", () =>
|
||||||
|
{
|
||||||
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.Number, new { Id = (short)0 }, "Id"),
|
||||||
|
"Zero ID should have returned true");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for short when the ID is non-zero", () =>
|
||||||
|
{
|
||||||
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Number, new { Id = (short)7 }, "Id"),
|
||||||
|
"Non-zero ID should have returned false");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for int when the ID is zero", () =>
|
||||||
|
{
|
||||||
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.Number, new { Id = 0 }, "Id"),
|
||||||
|
"Zero ID should have returned true");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for int when the ID is non-zero", () =>
|
||||||
|
{
|
||||||
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Number, new { Id = 32 }, "Id"),
|
||||||
|
"Non-zero ID should have returned false");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for long when the ID is zero", () =>
|
||||||
|
{
|
||||||
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.Number, new { Id = 0L }, "Id"),
|
||||||
|
"Zero ID should have returned true");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for long when the ID is non-zero", () =>
|
||||||
|
{
|
||||||
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Number, new { Id = 80L }, "Id"),
|
||||||
|
"Non-zero ID should have returned false");
|
||||||
|
}),
|
||||||
|
TestCase("fails for number when the ID is not a number", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ = AutoId.NeedsAutoId(AutoId.Number, new { Id = "" }, "Id");
|
||||||
|
Expect.isTrue(false, "Numeric ID against a string should have thrown an exception");
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for GUID when the ID is blank", () =>
|
||||||
|
{
|
||||||
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.Guid, new { Id = "" }, "Id"),
|
||||||
|
"Blank ID should have returned true");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for GUID when the ID is filled", () =>
|
||||||
|
{
|
||||||
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Guid, new { Id = "abc" }, "Id"),
|
||||||
|
"Filled ID should have returned false");
|
||||||
|
}),
|
||||||
|
TestCase("fails for GUID when the ID is not a string", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ = AutoId.NeedsAutoId(AutoId.Guid, new { Id = 8 }, "Id");
|
||||||
|
Expect.isTrue(false, "String ID against a number should have thrown an exception");
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for RandomString when the ID is blank", () =>
|
||||||
|
{
|
||||||
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.RandomString, new { Id = "" }, "Id"),
|
||||||
|
"Blank ID should have returned true");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for RandomString when the ID is filled", () =>
|
||||||
|
{
|
||||||
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.RandomString, new { Id = "x" }, "Id"),
|
||||||
|
"Filled ID should have returned false");
|
||||||
|
}),
|
||||||
|
TestCase("fails for RandomString when the ID is not a string", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ = AutoId.NeedsAutoId(AutoId.RandomString, new { Id = 33 }, "Id");
|
||||||
|
Expect.isTrue(false, "String ID against a number should have thrown an exception");
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
})
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unit tests for the Configuration static class
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test ConfigurationTests = TestList("Configuration",
|
||||||
|
[
|
||||||
|
TestCase("UseSerializer succeeds", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Configuration.UseSerializer(new TestSerializer());
|
||||||
|
|
||||||
|
var serialized = Configuration.Serializer().Serialize(new SubDocument
|
||||||
|
{
|
||||||
|
Foo = "howdy",
|
||||||
|
Bar = "bye"
|
||||||
|
});
|
||||||
|
Expect.equal(serialized, "{\"Overridden\":true}", "Specified serializer was not used");
|
||||||
|
|
||||||
|
var deserialized = Configuration.Serializer()
|
||||||
|
.Deserialize<object>("{\"Something\":\"here\"}");
|
||||||
|
Expect.isNull(deserialized, "Specified serializer should have returned null");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Configuration.UseSerializer(DocumentSerializer.Default);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TestCase("Serializer returns configured serializer", () =>
|
||||||
|
{
|
||||||
|
Expect.isTrue(ReferenceEquals(DocumentSerializer.Default, Configuration.Serializer()),
|
||||||
|
"Serializer should have been the same");
|
||||||
|
}),
|
||||||
|
TestCase("UseIdField / IdField succeeds", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Expect.equal(Configuration.IdField(), "Id",
|
||||||
|
"The default configured ID field was incorrect");
|
||||||
|
Configuration.UseIdField("id");
|
||||||
|
Expect.equal(Configuration.IdField(), "id", "UseIdField did not set the ID field");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Configuration.UseIdField("Id");
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TestCase("UseAutoIdStrategy / AutoIdStrategy succeeds", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Expect.equal(Configuration.AutoIdStrategy(), AutoId.Disabled,
|
||||||
|
"The default auto-ID strategy was incorrect");
|
||||||
|
Configuration.UseAutoIdStrategy(AutoId.Guid);
|
||||||
|
Expect.equal(Configuration.AutoIdStrategy(), AutoId.Guid,
|
||||||
|
"The auto-ID strategy was not set correctly");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Configuration.UseAutoIdStrategy(AutoId.Disabled);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TestCase("UseIdStringLength / IdStringLength succeeds", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Expect.equal(Configuration.IdStringLength(), 16, "The default ID string length was incorrect");
|
||||||
|
Configuration.UseIdStringLength(33);
|
||||||
|
Expect.equal(Configuration.IdStringLength(), 33, "The ID string length was not set correctly");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Configuration.UseIdStringLength(16);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unit tests for the Query static class
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test QueryTests = TestList("Query",
|
||||||
|
[
|
||||||
|
TestCase("StatementWhere succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.StatementWhere("q", "r"), "q WHERE r", "Statements not combined correctly");
|
||||||
|
}),
|
||||||
|
TestList("Definition",
|
||||||
|
[
|
||||||
TestCase("EnsureTableFor succeeds", () =>
|
TestCase("EnsureTableFor succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Query.Definition.EnsureTableFor("my.table", "JSONB"),
|
Expect.equal(Query.Definition.EnsureTableFor("my.table", "JSONB"),
|
||||||
"CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)",
|
"CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)",
|
||||||
"CREATE TABLE statement not constructed correctly");
|
"CREATE TABLE statement not constructed correctly");
|
||||||
}),
|
}),
|
||||||
TestList("EnsureKey", new[]
|
TestList("EnsureKey",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a schema is present", () =>
|
TestCase("succeeds when a schema is present", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Query.Definition.EnsureKey("test.table"),
|
Expect.equal(Query.Definition.EnsureKey("test.table", Dialect.SQLite),
|
||||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data ->> 'Id'))",
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'Id'))",
|
||||||
"CREATE INDEX for key statement with schema not constructed correctly");
|
"CREATE INDEX for key statement with schema not constructed correctly");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when a schema is not present", () =>
|
TestCase("succeeds when a schema is not present", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Query.Definition.EnsureKey("table"),
|
Expect.equal(Query.Definition.EnsureKey("table", Dialect.PostgreSQL),
|
||||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data ->> 'Id'))",
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data->>'Id'))",
|
||||||
"CREATE INDEX for key statement without schema not constructed correctly");
|
"CREATE INDEX for key statement without schema not constructed correctly");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestCase("EnsureIndexOn succeeds for multiple fields and directions", () =>
|
TestList("EnsureIndexOn",
|
||||||
|
[
|
||||||
|
TestCase("succeeds for multiple fields and directions", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(
|
Expect.equal(
|
||||||
Query.Definition.EnsureIndexOn("test.table", "gibberish",
|
Query.Definition.EnsureIndexOn("test.table", "gibberish",
|
||||||
new[] { "taco", "guac DESC", "salsa ASC" }),
|
["taco", "guac DESC", "salsa ASC"], Dialect.SQLite),
|
||||||
"CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table "
|
"CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table "
|
||||||
+ "((data ->> 'taco'), (data ->> 'guac') DESC, (data ->> 'salsa') ASC)",
|
+ "((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)",
|
||||||
"CREATE INDEX for multiple field statement incorrect");
|
"CREATE INDEX for multiple field statement incorrect");
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
|
TestCase("succeeds for nested PostgreSQL field", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(
|
||||||
|
Query.Definition.EnsureIndexOn("tbl", "nest", ["a.b.c"], Dialect.PostgreSQL),
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_tbl_nest ON tbl ((data#>>'{a,b,c}'))",
|
||||||
|
"CREATE INDEX for nested PostgreSQL field incorrect");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for nested SQLite field", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(
|
||||||
|
Query.Definition.EnsureIndexOn("tbl", "nest", ["a.b.c"], Dialect.SQLite),
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_tbl_nest ON tbl ((data->>'a'->>'b'->>'c'))",
|
||||||
|
"CREATE INDEX for nested SQLite field incorrect");
|
||||||
|
})
|
||||||
|
])
|
||||||
|
]),
|
||||||
TestCase("Insert succeeds", () =>
|
TestCase("Insert succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Query.Insert("tbl"), "INSERT INTO tbl VALUES (@data)", "INSERT statement not correct");
|
Expect.equal(Query.Insert("tbl"), "INSERT INTO tbl VALUES (@data)", "INSERT statement not correct");
|
||||||
|
@ -223,7 +543,92 @@ public static class CommonCSharpTests
|
||||||
Expect.equal(Query.Save("tbl"),
|
Expect.equal(Query.Save("tbl"),
|
||||||
"INSERT INTO tbl VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data",
|
"INSERT INTO tbl VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data",
|
||||||
"INSERT ON CONFLICT UPDATE statement not correct");
|
"INSERT ON CONFLICT UPDATE statement not correct");
|
||||||
|
}),
|
||||||
|
TestCase("Count succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.Count("tbl"), "SELECT COUNT(*) AS it FROM tbl", "Count query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("Exists succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.Exists("tbl", "chicken"), "SELECT EXISTS (SELECT 1 FROM tbl WHERE chicken) AS it",
|
||||||
|
"Exists query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("Find succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.Find("test.table"), "SELECT data FROM test.table", "Find query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("Update succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.Update("tbl"), "UPDATE tbl SET data = @data", "Update query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("Delete succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.Delete("tbl"), "DELETE FROM tbl", "Delete query not correct");
|
||||||
|
}),
|
||||||
|
TestList("OrderBy",
|
||||||
|
[
|
||||||
|
TestCase("succeeds for no fields", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.OrderBy([], Dialect.PostgreSQL), "", "Order By should have been blank (PostgreSQL)");
|
||||||
|
Expect.equal(Query.OrderBy([], Dialect.SQLite), "", "Order By should have been blank (SQLite)");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for PostgreSQL with one field and no direction", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.OrderBy([Field.Named("TestField")], Dialect.PostgreSQL),
|
||||||
|
" ORDER BY data->>'TestField'", "Order By not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for SQLite with one field and no direction", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.OrderBy([Field.Named("TestField")], Dialect.SQLite),
|
||||||
|
" ORDER BY data->>'TestField'", "Order By not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for PostgreSQL with multiple fields and direction", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(
|
||||||
|
Query.OrderBy(
|
||||||
|
[
|
||||||
|
Field.Named("Nested.Test.Field DESC"), Field.Named("AnotherField"),
|
||||||
|
Field.Named("It DESC")
|
||||||
|
], Dialect.PostgreSQL),
|
||||||
|
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
|
||||||
|
"Order By not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for SQLite with multiple fields and direction", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(
|
||||||
|
Query.OrderBy(
|
||||||
|
[
|
||||||
|
Field.Named("Nested.Test.Field DESC"), Field.Named("AnotherField"),
|
||||||
|
Field.Named("It DESC")
|
||||||
|
], Dialect.SQLite),
|
||||||
|
" ORDER BY data->>'Nested'->>'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
|
||||||
|
"Order By not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for PostgreSQL numeric fields", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.OrderBy([Field.Named("n:Test")], Dialect.PostgreSQL),
|
||||||
|
" ORDER BY (data->>'Test')::numeric", "Order By not constructed correctly for numeric field");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for SQLite numeric fields", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.OrderBy([Field.Named("n:Test")], Dialect.SQLite), " ORDER BY data->>'Test'",
|
||||||
|
"Order By not constructed correctly for numeric field");
|
||||||
})
|
})
|
||||||
})
|
])
|
||||||
});
|
]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unit tests
|
||||||
|
/// </summary>
|
||||||
|
[Tests]
|
||||||
|
public static readonly Test Unit = TestList("Common.C# Unit",
|
||||||
|
[
|
||||||
|
OpTests,
|
||||||
|
FieldTests,
|
||||||
|
FieldMatchTests,
|
||||||
|
ParameterNameTests,
|
||||||
|
AutoIdTests,
|
||||||
|
QueryTests,
|
||||||
|
TestSequenced(ConfigurationTests)
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,17 +31,17 @@ public class PostgresCSharpExtensionTests
|
||||||
/// Integration tests for the SQLite extension methods
|
/// Integration tests for the SQLite extension methods
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Tests]
|
[Tests]
|
||||||
public static readonly Test Integration = TestList("Postgres.C#.Extensions", new[]
|
public static readonly Test Integration = TestList("Postgres.C#.Extensions",
|
||||||
{
|
[
|
||||||
TestList("CustomList", new[]
|
TestList("CustomList",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when data is found", async () =>
|
TestCase("succeeds when data is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await conn.CustomList(Query.SelectFromTable(PostgresDb.TableName), Parameters.None,
|
var docs = await conn.CustomList(Query.Find(PostgresDb.TableName), Parameters.None,
|
||||||
Results.FromData<JsonDocument>);
|
Results.FromData<JsonDocument>);
|
||||||
Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
|
Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
|
||||||
}),
|
}),
|
||||||
|
@ -53,13 +53,13 @@ public class PostgresCSharpExtensionTests
|
||||||
|
|
||||||
var docs = await conn.CustomList(
|
var docs = await conn.CustomList(
|
||||||
$"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
|
$"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
|
||||||
new[] { Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)")) },
|
[Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)"))],
|
||||||
Results.FromData<JsonDocument>);
|
Results.FromData<JsonDocument>);
|
||||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("CustomSingle", new[]
|
TestList("CustomSingle",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a row is found", async () =>
|
TestCase("succeeds when a row is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -67,7 +67,7 @@ public class PostgresCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await conn.CustomSingle($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id",
|
var doc = await conn.CustomSingle($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id",
|
||||||
new[] { Tuple.Create("@id", Sql.@string("one")) }, Results.FromData<JsonDocument>);
|
[Tuple.Create("@id", Sql.@string("one"))], Results.FromData<JsonDocument>);
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.equal(doc.Id, "one", "The incorrect document was returned");
|
Expect.equal(doc.Id, "one", "The incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
|
@ -78,12 +78,12 @@ public class PostgresCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await conn.CustomSingle($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id",
|
var doc = await conn.CustomSingle($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id",
|
||||||
new[] { Tuple.Create("@id", Sql.@string("eighty")) }, Results.FromData<JsonDocument>);
|
[Tuple.Create("@id", Sql.@string("eighty"))], Results.FromData<JsonDocument>);
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("CustomNonQuery", new[]
|
TestList("CustomNonQuery",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when operating on data", async () =>
|
TestCase("succeeds when operating on data", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -102,12 +102,12 @@ public class PostgresCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.CustomNonQuery($"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
|
await conn.CustomNonQuery($"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
|
||||||
new[] { Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)")) });
|
[Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)"))]);
|
||||||
|
|
||||||
var remaining = await conn.CountAll(PostgresDb.TableName);
|
var remaining = await conn.CountAll(PostgresDb.TableName);
|
||||||
Expect.equal(remaining, 5, "There should be 5 documents remaining in the table");
|
Expect.equal(remaining, 5, "There should be 5 documents remaining in the table");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestCase("Scalar succeeds", async () =>
|
TestCase("Scalar succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -119,58 +119,64 @@ public class PostgresCSharpExtensionTests
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
var tableExists = () => conn.CustomScalar(
|
|
||||||
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it", Parameters.None,
|
|
||||||
Results.ToExists);
|
|
||||||
var keyExists = () => conn.CustomScalar(
|
|
||||||
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it", Parameters.None,
|
|
||||||
Results.ToExists);
|
|
||||||
|
|
||||||
var exists = await tableExists();
|
var exists = await TableExists();
|
||||||
var alsoExists = await keyExists();
|
var alsoExists = await KeyExists();
|
||||||
Expect.isFalse(exists, "The table should not exist already");
|
Expect.isFalse(exists, "The table should not exist already");
|
||||||
Expect.isFalse(alsoExists, "The key index should not exist already");
|
Expect.isFalse(alsoExists, "The key index should not exist already");
|
||||||
|
|
||||||
await conn.EnsureTable("ensured");
|
await conn.EnsureTable("ensured");
|
||||||
exists = await tableExists();
|
exists = await TableExists();
|
||||||
alsoExists = await keyExists();
|
alsoExists = await KeyExists();
|
||||||
Expect.isTrue(exists, "The table should now exist");
|
Expect.isTrue(exists, "The table should now exist");
|
||||||
Expect.isTrue(alsoExists, "The key index should now exist");
|
Expect.isTrue(alsoExists, "The key index should now exist");
|
||||||
|
return;
|
||||||
|
|
||||||
|
Task<bool> KeyExists() =>
|
||||||
|
conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it",
|
||||||
|
Parameters.None, Results.ToExists);
|
||||||
|
Task<bool> TableExists() =>
|
||||||
|
conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it",
|
||||||
|
Parameters.None, Results.ToExists);
|
||||||
}),
|
}),
|
||||||
TestCase("EnsureDocumentIndex succeeds", async () =>
|
TestCase("EnsureDocumentIndex succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
var indexExists = () => conn.CustomScalar(
|
|
||||||
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it", Parameters.None,
|
|
||||||
Results.ToExists);
|
|
||||||
|
|
||||||
var exists = await indexExists();
|
var exists = await IndexExists();
|
||||||
Expect.isFalse(exists, "The index should not exist already");
|
Expect.isFalse(exists, "The index should not exist already");
|
||||||
|
|
||||||
await conn.EnsureTable("ensured");
|
await conn.EnsureTable("ensured");
|
||||||
await conn.EnsureDocumentIndex("ensured", DocumentIndex.Optimized);
|
await conn.EnsureDocumentIndex("ensured", DocumentIndex.Optimized);
|
||||||
exists = await indexExists();
|
exists = await IndexExists();
|
||||||
Expect.isTrue(exists, "The index should now exist");
|
Expect.isTrue(exists, "The index should now exist");
|
||||||
|
return;
|
||||||
|
|
||||||
|
Task<bool> IndexExists() =>
|
||||||
|
conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it",
|
||||||
|
Parameters.None, Results.ToExists);
|
||||||
}),
|
}),
|
||||||
TestCase("EnsureFieldIndex succeeds", async () =>
|
TestCase("EnsureFieldIndex succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
var indexExists = () => conn.CustomScalar(
|
|
||||||
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it", Parameters.None,
|
|
||||||
Results.ToExists);
|
|
||||||
|
|
||||||
var exists = await indexExists();
|
var exists = await IndexExists();
|
||||||
Expect.isFalse(exists, "The index should not exist already");
|
Expect.isFalse(exists, "The index should not exist already");
|
||||||
|
|
||||||
await conn.EnsureTable("ensured");
|
await conn.EnsureTable("ensured");
|
||||||
await conn.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
|
await conn.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
|
||||||
exists = await indexExists();
|
exists = await IndexExists();
|
||||||
Expect.isTrue(exists, "The index should now exist");
|
Expect.isTrue(exists, "The index should now exist");
|
||||||
|
return;
|
||||||
|
|
||||||
|
Task<bool> IndexExists() =>
|
||||||
|
conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it",
|
||||||
|
Parameters.None, Results.ToExists);
|
||||||
}),
|
}),
|
||||||
TestList("Insert", new[]
|
TestList("Insert",
|
||||||
{
|
[
|
||||||
TestCase("succeeds", async () =>
|
TestCase("succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -198,9 +204,9 @@ public class PostgresCSharpExtensionTests
|
||||||
// This is what should have happened
|
// This is what should have happened
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("save", new[]
|
TestList("save",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is inserted", async () =>
|
TestCase("succeeds when a document is inserted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -230,7 +236,7 @@ public class PostgresCSharpExtensionTests
|
||||||
Expect.isNotNull(after, "There should have been a document returned post-update");
|
Expect.isNotNull(after, "There should have been a document returned post-update");
|
||||||
Expect.equal(after.Sub!.Foo, "c", "The updated document is not correct");
|
Expect.equal(after.Sub!.Foo, "c", "The updated document is not correct");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestCase("CountAll succeeds", async () =>
|
TestCase("CountAll succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -240,13 +246,14 @@ public class PostgresCSharpExtensionTests
|
||||||
var theCount = await conn.CountAll(PostgresDb.TableName);
|
var theCount = await conn.CountAll(PostgresDb.TableName);
|
||||||
Expect.equal(theCount, 5, "There should have been 5 matching documents");
|
Expect.equal(theCount, 5, "There should have been 5 matching documents");
|
||||||
}),
|
}),
|
||||||
TestCase("CountByField succeeds", async () =>
|
TestCase("CountByFields succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var theCount = await conn.CountByField(PostgresDb.TableName, Field.EQ("Value", "purple"));
|
var theCount = await conn.CountByFields(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "purple")]);
|
||||||
Expect.equal(theCount, 2, "There should have been 2 matching documents");
|
Expect.equal(theCount, 2, "There should have been 2 matching documents");
|
||||||
}),
|
}),
|
||||||
TestCase("CountByContains succeeds", async () =>
|
TestCase("CountByContains succeeds", async () =>
|
||||||
|
@ -267,8 +274,8 @@ public class PostgresCSharpExtensionTests
|
||||||
var theCount = await conn.CountByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 5)");
|
var theCount = await conn.CountByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 5)");
|
||||||
Expect.equal(theCount, 3, "There should have been 3 matching documents");
|
Expect.equal(theCount, 3, "There should have been 3 matching documents");
|
||||||
}),
|
}),
|
||||||
TestList("ExistsById", new[]
|
TestList("ExistsById",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document exists", async () =>
|
TestCase("succeeds when a document exists", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -287,16 +294,16 @@ public class PostgresCSharpExtensionTests
|
||||||
var exists = await conn.ExistsById(PostgresDb.TableName, "seven");
|
var exists = await conn.ExistsById(PostgresDb.TableName, "seven");
|
||||||
Expect.isFalse(exists, "There should not have been an existing document");
|
Expect.isFalse(exists, "There should not have been an existing document");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ExistsByField", new[]
|
TestList("ExistsByField",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents exist", async () =>
|
TestCase("succeeds when documents exist", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var exists = await conn.ExistsByField(PostgresDb.TableName, Field.EX("Sub"));
|
var exists = await conn.ExistsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EX("Sub")]);
|
||||||
Expect.isTrue(exists, "There should have been existing documents");
|
Expect.isTrue(exists, "There should have been existing documents");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when documents do not exist", async () =>
|
TestCase("succeeds when documents do not exist", async () =>
|
||||||
|
@ -305,12 +312,13 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var exists = await conn.ExistsByField(PostgresDb.TableName, Field.EQ("NumValue", "six"));
|
var exists =
|
||||||
|
await conn.ExistsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "six")]);
|
||||||
Expect.isFalse(exists, "There should not have been existing documents");
|
Expect.isFalse(exists, "There should not have been existing documents");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ExistsByContains", new[]
|
TestList("ExistsByContains",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents exist", async () =>
|
TestCase("succeeds when documents exist", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -329,9 +337,9 @@ public class PostgresCSharpExtensionTests
|
||||||
var exists = await conn.ExistsByContains(PostgresDb.TableName, new { Nothing = "none" });
|
var exists = await conn.ExistsByContains(PostgresDb.TableName, new { Nothing = "none" });
|
||||||
Expect.isFalse(exists, "There should not have been any existing documents");
|
Expect.isFalse(exists, "There should not have been any existing documents");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ExistsByJsonPath", new[]
|
TestList("ExistsByJsonPath",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents exist", async () =>
|
TestCase("succeeds when documents exist", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -350,9 +358,9 @@ public class PostgresCSharpExtensionTests
|
||||||
var exists = await conn.ExistsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 1000)");
|
var exists = await conn.ExistsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 1000)");
|
||||||
Expect.isFalse(exists, "There should not have been any existing documents");
|
Expect.isFalse(exists, "There should not have been any existing documents");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindAll", new[]
|
TestList("FindAll",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when there is data", async () =>
|
TestCase("succeeds when there is data", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -372,9 +380,47 @@ public class PostgresCSharpExtensionTests
|
||||||
var results = await conn.FindAll<JsonDocument>(PostgresDb.TableName);
|
var results = await conn.FindAll<JsonDocument>(PostgresDb.TableName);
|
||||||
Expect.isEmpty(results, "There should have been no documents returned");
|
Expect.isEmpty(results, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindById", new[]
|
TestList("FindAllOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when ordering numerically", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var results =
|
||||||
|
await conn.FindAllOrdered<JsonDocument>(PostgresDb.TableName, [Field.Named("n:NumValue")]);
|
||||||
|
Expect.hasLength(results, 5, "There should have been 5 documents returned");
|
||||||
|
Expect.equal(string.Join('|', results.Select(x => x.Id)), "one|three|two|four|five",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when ordering numerically descending", async () =>
|
||||||
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var results =
|
||||||
|
await conn.FindAllOrdered<JsonDocument>(PostgresDb.TableName, [Field.Named("n:NumValue DESC")]);
|
||||||
|
Expect.hasLength(results, 5, "There should have been 5 documents returned");
|
||||||
|
Expect.equal(string.Join('|', results.Select(x => x.Id)), "five|four|two|three|one",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when ordering alphabetically", async () =>
|
||||||
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var results = await conn.FindAllOrdered<JsonDocument>(PostgresDb.TableName, [Field.Named("Id DESC")]);
|
||||||
|
Expect.hasLength(results, 5, "There should have been 5 documents returned");
|
||||||
|
Expect.equal(string.Join('|', results.Select(x => x.Id)), "two|three|one|four|five",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("FindById",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is found", async () =>
|
TestCase("succeeds when a document is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -394,16 +440,17 @@ public class PostgresCSharpExtensionTests
|
||||||
var doc = await conn.FindById<string, JsonDocument>(PostgresDb.TableName, "three hundred eighty-seven");
|
var doc = await conn.FindById<string, JsonDocument>(PostgresDb.TableName, "three hundred eighty-seven");
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindByField", new[]
|
TestList("FindByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents are found", async () =>
|
TestCase("succeeds when documents are found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await conn.FindByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another"));
|
var docs = await conn.FindByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "another")]);
|
||||||
Expect.equal(docs.Count, 1, "There should have been one document returned");
|
Expect.equal(docs.Count, 1, "There should have been one document returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when documents are not found", async () =>
|
TestCase("succeeds when documents are not found", async () =>
|
||||||
|
@ -412,12 +459,40 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await conn.FindByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "mauve"));
|
var docs = await conn.FindByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "mauve")]);
|
||||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindByContains", new[]
|
TestList("FindByFieldsOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when documents are found", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var docs = await conn.FindByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "purple")], [Field.Named("Id")]);
|
||||||
|
Expect.hasLength(docs, 2, "There should have been two document returned");
|
||||||
|
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "five|four",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when documents are not found", async () =>
|
||||||
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var docs = await conn.FindByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "purple")], [Field.Named("Id DESC")]);
|
||||||
|
Expect.hasLength(docs, 2, "There should have been two document returned");
|
||||||
|
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|five",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("FindByContains",
|
||||||
|
[
|
||||||
TestCase("succeeds when documents are found", async () =>
|
TestCase("succeeds when documents are found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -437,9 +512,37 @@ public class PostgresCSharpExtensionTests
|
||||||
var docs = await conn.FindByContains<JsonDocument>(PostgresDb.TableName, new { Value = "mauve" });
|
var docs = await conn.FindByContains<JsonDocument>(PostgresDb.TableName, new { Value = "mauve" });
|
||||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindByJsonPath", new[]
|
TestList("FindByContainsOrdered",
|
||||||
|
[
|
||||||
|
// Id = two, Sub.Bar = blue; Id = four, Sub.Bar = red
|
||||||
|
TestCase("succeeds when sorting ascending", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var docs = await conn.FindByContainsOrdered<JsonDocument>(PostgresDb.TableName,
|
||||||
|
new { Sub = new { Foo = "green" } }, [Field.Named("Sub.Bar")]);
|
||||||
|
Expect.hasLength(docs, 2, "There should have been two documents returned");
|
||||||
|
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "two|four",
|
||||||
|
"Documents not ordered correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when sorting descending", async () =>
|
||||||
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var docs = await conn.FindByContainsOrdered<JsonDocument>(PostgresDb.TableName,
|
||||||
|
new { Sub = new { Foo = "green" } }, [Field.Named("Sub.Bar DESC")]);
|
||||||
|
Expect.hasLength(docs, 2, "There should have been two documents returned");
|
||||||
|
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|two",
|
||||||
|
"Documents not ordered correctly");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("FindByJsonPath",
|
||||||
|
[
|
||||||
TestCase("succeeds when documents are found", async () =>
|
TestCase("succeeds when documents are found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -458,16 +561,45 @@ public class PostgresCSharpExtensionTests
|
||||||
var docs = await conn.FindByJsonPath<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 0)");
|
var docs = await conn.FindByJsonPath<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 0)");
|
||||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindFirstByField", new[]
|
TestList("FindByJsonPathOrdered",
|
||||||
|
[
|
||||||
|
// Id = one, NumValue = 0; Id = two, NumValue = 10; Id = three, NumValue = 4
|
||||||
|
TestCase("succeeds when sorting ascending", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var docs = await conn.FindByJsonPathOrdered<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 15)",
|
||||||
|
[Field.Named("n:NumValue")]);
|
||||||
|
Expect.hasLength(docs, 3, "There should have been 3 documents returned");
|
||||||
|
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "one|three|two",
|
||||||
|
"Documents not ordered correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when sorting descending", async () =>
|
||||||
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var docs = await conn.FindByJsonPathOrdered<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 15)",
|
||||||
|
[Field.Named("n:NumValue DESC")]);
|
||||||
|
Expect.hasLength(docs, 3, "There should have been 3 documents returned");
|
||||||
|
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "two|three|one",
|
||||||
|
"Documents not ordered correctly");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("FindFirstByFields",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is found", async () =>
|
TestCase("succeeds when a document is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another"));
|
var doc = await conn.FindFirstByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "another")]);
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.equal(doc.Id, "two", "The incorrect document was returned");
|
Expect.equal(doc.Id, "two", "The incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
|
@ -477,9 +609,10 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "purple"));
|
var doc = await conn.FindFirstByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "purple")]);
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.contains(new[] { "five", "four" }, doc.Id, "An incorrect document was returned");
|
Expect.contains(["five", "four"], doc.Id, "An incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when a document is not found", async () =>
|
TestCase("succeeds when a document is not found", async () =>
|
||||||
{
|
{
|
||||||
|
@ -487,12 +620,38 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "absent"));
|
var doc = await conn.FindFirstByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "absent")]);
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindFirstByContains", new[]
|
TestList("FindFirstByFieldsOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when sorting ascending", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var doc = await conn.FindFirstByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "purple")], [Field.Named("Id")]);
|
||||||
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
|
Expect.equal("five", doc.Id, "An incorrect document was returned");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when a document is not found", async () =>
|
||||||
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var doc = await conn.FindFirstByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "purple")], [Field.Named("Id DESC")]);
|
||||||
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
|
Expect.equal("four", doc.Id, "An incorrect document was returned");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("FindFirstByContains",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is found", async () =>
|
TestCase("succeeds when a document is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -512,7 +671,7 @@ public class PostgresCSharpExtensionTests
|
||||||
var doc = await conn.FindFirstByContains<JsonDocument>(PostgresDb.TableName,
|
var doc = await conn.FindFirstByContains<JsonDocument>(PostgresDb.TableName,
|
||||||
new { Sub = new { Foo = "green" } });
|
new { Sub = new { Foo = "green" } });
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.contains(new[] { "two", "four" }, doc.Id, "An incorrect document was returned");
|
Expect.contains(["two", "four"], doc.Id, "An incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when a document is not found", async () =>
|
TestCase("succeeds when a document is not found", async () =>
|
||||||
{
|
{
|
||||||
|
@ -523,9 +682,34 @@ public class PostgresCSharpExtensionTests
|
||||||
var doc = await conn.FindFirstByContains<JsonDocument>(PostgresDb.TableName, new { Value = "absent" });
|
var doc = await conn.FindFirstByContains<JsonDocument>(PostgresDb.TableName, new { Value = "absent" });
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindFirstByJsonPath", new[]
|
TestList("FindFirstByContainsOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when sorting ascending", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var doc = await conn.FindFirstByContainsOrdered<JsonDocument>(PostgresDb.TableName,
|
||||||
|
new { Sub = new { Foo = "green" } }, [Field.Named("Value")]);
|
||||||
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
|
Expect.equal("two", doc.Id, "An incorrect document was returned");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when sorting descending", async () =>
|
||||||
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var doc = await conn.FindFirstByContainsOrdered<JsonDocument>(PostgresDb.TableName,
|
||||||
|
new { Sub = new { Foo = "green" } }, [Field.Named("Value DESC")]);
|
||||||
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
|
Expect.equal("four", doc.Id, "An incorrect document was returned");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("FindFirstByJsonPath",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is found", async () =>
|
TestCase("succeeds when a document is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -546,7 +730,7 @@ public class PostgresCSharpExtensionTests
|
||||||
var doc = await conn.FindFirstByJsonPath<JsonDocument>(PostgresDb.TableName,
|
var doc = await conn.FindFirstByJsonPath<JsonDocument>(PostgresDb.TableName,
|
||||||
"$.Sub.Foo ? (@ == \"green\")");
|
"$.Sub.Foo ? (@ == \"green\")");
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.contains(new[] { "two", "four" }, doc.Id, "An incorrect document was returned");
|
Expect.contains(["two", "four"], doc.Id, "An incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when a document is not found", async () =>
|
TestCase("succeeds when a document is not found", async () =>
|
||||||
{
|
{
|
||||||
|
@ -557,9 +741,34 @@ public class PostgresCSharpExtensionTests
|
||||||
var doc = await conn.FindFirstByJsonPath<JsonDocument>(PostgresDb.TableName, "$.Id ? (@ == \"nope\")");
|
var doc = await conn.FindFirstByJsonPath<JsonDocument>(PostgresDb.TableName, "$.Id ? (@ == \"nope\")");
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("UpdateById", new[]
|
TestList("FindFirstByJsonPathOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when sorting ascending", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var doc = await conn.FindFirstByJsonPathOrdered<JsonDocument>(PostgresDb.TableName,
|
||||||
|
"$.Sub.Foo ? (@ == \"green\")", [Field.Named("Sub.Bar")]);
|
||||||
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
|
Expect.equal("two", doc.Id, "An incorrect document was returned");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when sorting descending", async () =>
|
||||||
|
{
|
||||||
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
await using var conn = MkConn(db);
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var doc = await conn.FindFirstByJsonPathOrdered<JsonDocument>(PostgresDb.TableName,
|
||||||
|
"$.Sub.Foo ? (@ == \"green\")", [Field.Named("Sub.Bar DESC")]);
|
||||||
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
|
Expect.equal("four", doc.Id, "An incorrect document was returned");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("UpdateById",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -588,9 +797,9 @@ public class PostgresCSharpExtensionTests
|
||||||
await conn.UpdateById(PostgresDb.TableName, "test",
|
await conn.UpdateById(PostgresDb.TableName, "test",
|
||||||
new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } });
|
new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("UpdateByFunc", new[]
|
TestList("UpdateByFunc",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -617,9 +826,9 @@ public class PostgresCSharpExtensionTests
|
||||||
await conn.UpdateByFunc(PostgresDb.TableName, doc => doc.Id,
|
await conn.UpdateByFunc(PostgresDb.TableName, doc => doc.Id,
|
||||||
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
|
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("PatchById", new[]
|
TestList("PatchById",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -641,17 +850,19 @@ public class PostgresCSharpExtensionTests
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.PatchById(PostgresDb.TableName, "test", new { Foo = "green" });
|
await conn.PatchById(PostgresDb.TableName, "test", new { Foo = "green" });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("PatchByField", new[]
|
TestList("PatchByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.PatchByField(PostgresDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
|
await conn.PatchByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
|
||||||
var after = await conn.CountByField(PostgresDb.TableName, Field.EQ("NumValue", "77"));
|
new { NumValue = 77 });
|
||||||
|
var after = await conn.CountByFields(PostgresDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("NumValue", "77")]);
|
||||||
Expect.equal(after, 2, "There should have been 2 documents returned");
|
Expect.equal(after, 2, "There should have been 2 documents returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is updated", async () =>
|
TestCase("succeeds when no document is updated", async () =>
|
||||||
|
@ -662,11 +873,12 @@ public class PostgresCSharpExtensionTests
|
||||||
Expect.equal(before, 0, "There should have been no documents returned");
|
Expect.equal(before, 0, "There should have been no documents returned");
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.PatchByField(PostgresDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
|
await conn.PatchByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "burgundy")],
|
||||||
|
new { Foo = "green" });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("PatchByContains", new[]
|
TestList("PatchByContains",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -687,9 +899,9 @@ public class PostgresCSharpExtensionTests
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.PatchByContains(PostgresDb.TableName, new { Value = "burgundy" }, new { Foo = "green" });
|
await conn.PatchByContains(PostgresDb.TableName, new { Value = "burgundy" }, new { Foo = "green" });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("PatchByJsonPath", new[]
|
TestList("PatchByJsonPath",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -710,16 +922,16 @@ public class PostgresCSharpExtensionTests
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.PatchByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ < 0)", new { Foo = "green" });
|
await conn.PatchByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ < 0)", new { Foo = "green" });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("RemoveFieldsById", new[]
|
TestList("RemoveFieldsById",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when multiple fields are removed", async () =>
|
TestCase("succeeds when multiple fields are removed", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Sub", "Value" });
|
await conn.RemoveFieldsById(PostgresDb.TableName, "two", ["Sub", "Value"]);
|
||||||
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
|
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.equal(updated.Value, "", "The string value should have been removed");
|
Expect.equal(updated.Value, "", "The string value should have been removed");
|
||||||
|
@ -731,7 +943,7 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Sub" });
|
await conn.RemoveFieldsById(PostgresDb.TableName, "two", ["Sub"]);
|
||||||
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
|
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
|
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
|
||||||
|
@ -744,7 +956,7 @@ public class PostgresCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
|
await conn.RemoveFieldsById(PostgresDb.TableName, "two", ["AFieldThatIsNotThere"]);
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is matched", async () =>
|
TestCase("succeeds when no document is matched", async () =>
|
||||||
{
|
{
|
||||||
|
@ -752,19 +964,19 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Value" });
|
await conn.RemoveFieldsById(PostgresDb.TableName, "two", ["Value"]);
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("RemoveFieldsByField", new[]
|
TestList("RemoveFieldsByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when multiple fields are removed", async () =>
|
TestCase("succeeds when multiple fields are removed", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"),
|
await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
|
||||||
new[] { "Sub", "Value" });
|
["Sub", "Value"]);
|
||||||
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.equal(updated.Value, "", "The string value should have been removed");
|
Expect.equal(updated.Value, "", "The string value should have been removed");
|
||||||
|
@ -776,7 +988,8 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Sub" });
|
await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
|
||||||
|
["Sub"]);
|
||||||
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
|
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
|
||||||
|
@ -789,7 +1002,8 @@ public class PostgresCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Nothing" });
|
await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
|
||||||
|
["Nothing"]);
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is matched", async () =>
|
TestCase("succeeds when no document is matched", async () =>
|
||||||
{
|
{
|
||||||
|
@ -797,20 +1011,19 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"),
|
await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any,
|
||||||
new[] { "Value" });
|
[Field.NE("Abracadabra", "apple")], ["Value"]);
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("RemoveFieldsByContains", new[]
|
TestList("RemoveFieldsByContains",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when multiple fields are removed", async () =>
|
TestCase("succeeds when multiple fields are removed", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 },
|
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Sub", "Value"]);
|
||||||
new[] { "Sub", "Value" });
|
|
||||||
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.equal(updated.Value, "", "The string value should have been removed");
|
Expect.equal(updated.Value, "", "The string value should have been removed");
|
||||||
|
@ -822,7 +1035,7 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Sub" });
|
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Sub"]);
|
||||||
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
|
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
|
||||||
|
@ -835,7 +1048,7 @@ public class PostgresCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Nothing" });
|
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Nothing"]);
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is matched", async () =>
|
TestCase("succeeds when no document is matched", async () =>
|
||||||
{
|
{
|
||||||
|
@ -843,20 +1056,18 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" },
|
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, ["Value"]);
|
||||||
new[] { "Value" });
|
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("RemoveFieldsByJsonPath", new[]
|
TestList("RemoveFieldsByJsonPath",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when multiple fields are removed", async () =>
|
TestCase("succeeds when multiple fields are removed", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)",
|
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Sub", "Value"]);
|
||||||
new[] { "Sub", "Value" });
|
|
||||||
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.equal(updated.Value, "", "The string value should have been removed");
|
Expect.equal(updated.Value, "", "The string value should have been removed");
|
||||||
|
@ -868,7 +1079,7 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Sub" });
|
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Sub"]);
|
||||||
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
|
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
|
||||||
|
@ -881,7 +1092,7 @@ public class PostgresCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Nothing" });
|
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Nothing"]);
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is matched", async () =>
|
TestCase("succeeds when no document is matched", async () =>
|
||||||
{
|
{
|
||||||
|
@ -889,12 +1100,11 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")",
|
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", ["Value"]);
|
||||||
new[] { "Value" });
|
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("DeleteById", new[]
|
TestList("DeleteById",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is deleted", async () =>
|
TestCase("succeeds when a document is deleted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -915,16 +1125,16 @@ public class PostgresCSharpExtensionTests
|
||||||
var remaining = await conn.CountAll(PostgresDb.TableName);
|
var remaining = await conn.CountAll(PostgresDb.TableName);
|
||||||
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("DeleteByField", new[]
|
TestList("DeleteByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents are deleted", async () =>
|
TestCase("succeeds when documents are deleted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.DeleteByField(PostgresDb.TableName, Field.NE("Value", "purple"));
|
await conn.DeleteByFields(PostgresDb.TableName, FieldMatch.Any, [Field.NE("Value", "purple")]);
|
||||||
var remaining = await conn.CountAll(PostgresDb.TableName);
|
var remaining = await conn.CountAll(PostgresDb.TableName);
|
||||||
Expect.equal(remaining, 2, "There should have been 2 documents remaining");
|
Expect.equal(remaining, 2, "There should have been 2 documents remaining");
|
||||||
}),
|
}),
|
||||||
|
@ -934,13 +1144,13 @@ public class PostgresCSharpExtensionTests
|
||||||
await using var conn = MkConn(db);
|
await using var conn = MkConn(db);
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.DeleteByField(PostgresDb.TableName, Field.EQ("Value", "crimson"));
|
await conn.DeleteByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "crimson")]);
|
||||||
var remaining = await conn.CountAll(PostgresDb.TableName);
|
var remaining = await conn.CountAll(PostgresDb.TableName);
|
||||||
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("DeleteByContains", new[]
|
TestList("DeleteByContains",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents are deleted", async () =>
|
TestCase("succeeds when documents are deleted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -961,9 +1171,9 @@ public class PostgresCSharpExtensionTests
|
||||||
var remaining = await conn.CountAll(PostgresDb.TableName);
|
var remaining = await conn.CountAll(PostgresDb.TableName);
|
||||||
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("DeleteByJsonPath", new[]
|
TestList("DeleteByJsonPath",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents are deleted", async () =>
|
TestCase("succeeds when documents are deleted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
|
@ -984,6 +1194,6 @@ public class PostgresCSharpExtensionTests
|
||||||
var remaining = await conn.CountAll(PostgresDb.TableName);
|
var remaining = await conn.CountAll(PostgresDb.TableName);
|
||||||
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
});
|
]);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,4 @@
|
||||||
|
using BitBadger.Documents.Postgres;
|
||||||
using Npgsql;
|
using Npgsql;
|
||||||
using Npgsql.FSharp;
|
using Npgsql.FSharp;
|
||||||
using ThrowawayDb.Postgres;
|
using ThrowawayDb.Postgres;
|
||||||
|
@ -131,7 +132,7 @@ public static class PostgresDb
|
||||||
var sqlProps = Sql.connect(database.ConnectionString);
|
var sqlProps = Sql.connect(database.ConnectionString);
|
||||||
|
|
||||||
Sql.executeNonQuery(Sql.query(Postgres.Query.Definition.EnsureTable(TableName), sqlProps));
|
Sql.executeNonQuery(Sql.query(Postgres.Query.Definition.EnsureTable(TableName), sqlProps));
|
||||||
Sql.executeNonQuery(Sql.query(Query.Definition.EnsureKey(TableName), sqlProps));
|
Sql.executeNonQuery(Sql.query(Query.Definition.EnsureKey(TableName, Dialect.PostgreSQL), sqlProps));
|
||||||
|
|
||||||
Postgres.Configuration.UseDataSource(MkDataSource(database.ConnectionString));
|
Postgres.Configuration.UseDataSource(MkDataSource(database.ConnectionString));
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using Expecto.CSharp;
|
using Expecto.CSharp;
|
||||||
using Expecto;
|
using Expecto;
|
||||||
using Microsoft.Data.Sqlite;
|
|
||||||
using BitBadger.Documents.Sqlite;
|
using BitBadger.Documents.Sqlite;
|
||||||
|
|
||||||
namespace BitBadger.Documents.Tests.CSharp;
|
namespace BitBadger.Documents.Tests.CSharp;
|
||||||
|
@ -18,10 +17,10 @@ public static class SqliteCSharpExtensionTests
|
||||||
/// Integration tests for the SQLite extension methods
|
/// Integration tests for the SQLite extension methods
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Tests]
|
[Tests]
|
||||||
public static readonly Test Integration = TestList("Sqlite.C#.Extensions", new[]
|
public static readonly Test Integration = TestList("Sqlite.C#.Extensions",
|
||||||
{
|
[
|
||||||
TestList("CustomSingle", new[]
|
TestList("CustomSingle",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a row is found", async () =>
|
TestCase("succeeds when a row is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -29,7 +28,7 @@ public static class SqliteCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await conn.CustomSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
|
var doc = await conn.CustomSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
|
||||||
new[] { Parameters.Id("one") }, Results.FromData<JsonDocument>);
|
[Parameters.Id("one")], Results.FromData<JsonDocument>);
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.equal(doc!.Id, "one", "The incorrect document was returned");
|
Expect.equal(doc!.Id, "one", "The incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
|
@ -40,19 +39,19 @@ public static class SqliteCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await conn.CustomSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
|
var doc = await conn.CustomSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
|
||||||
new[] { Parameters.Id("eighty") }, Results.FromData<JsonDocument>);
|
[Parameters.Id("eighty")], Results.FromData<JsonDocument>);
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("CustomList", new[]
|
TestList("CustomList",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when data is found", async () =>
|
TestCase("succeeds when data is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await conn.CustomList(Query.SelectFromTable(SqliteDb.TableName), Parameters.None,
|
var docs = await conn.CustomList(Query.Find(SqliteDb.TableName), Parameters.None,
|
||||||
Results.FromData<JsonDocument>);
|
Results.FromData<JsonDocument>);
|
||||||
Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
|
Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
|
||||||
}),
|
}),
|
||||||
|
@ -63,13 +62,13 @@ public static class SqliteCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await conn.CustomList(
|
var docs = await conn.CustomList(
|
||||||
$"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value",
|
$"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", [new("@value", 100)],
|
||||||
new[] { new SqliteParameter("@value", 100) }, Results.FromData<JsonDocument>);
|
Results.FromData<JsonDocument>);
|
||||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("CustomNonQuery", new[]
|
TestList("CustomNonQuery",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when operating on data", async () =>
|
TestCase("succeeds when operating on data", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -88,12 +87,12 @@ public static class SqliteCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.CustomNonQuery($"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value",
|
await conn.CustomNonQuery($"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value",
|
||||||
new[] { new SqliteParameter("@value", 100) });
|
[new("@value", 100)]);
|
||||||
|
|
||||||
var remaining = await conn.CountAll(SqliteDb.TableName);
|
var remaining = await conn.CountAll(SqliteDb.TableName);
|
||||||
Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table");
|
Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestCase("CustomScalar succeeds", async () =>
|
TestCase("CustomScalar succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -107,41 +106,44 @@ public static class SqliteCSharpExtensionTests
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
|
|
||||||
Func<string, ValueTask<bool>> itExists = async name =>
|
var exists = await ItExists("ensured");
|
||||||
await conn.CustomScalar(
|
var alsoExists = await ItExists("idx_ensured_key");
|
||||||
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
|
|
||||||
new SqliteParameter[] { new("@name", name) }, Results.ToExists);
|
|
||||||
|
|
||||||
var exists = await itExists("ensured");
|
|
||||||
var alsoExists = await itExists("idx_ensured_key");
|
|
||||||
Expect.isFalse(exists, "The table should not exist already");
|
Expect.isFalse(exists, "The table should not exist already");
|
||||||
Expect.isFalse(alsoExists, "The key index should not exist already");
|
Expect.isFalse(alsoExists, "The key index should not exist already");
|
||||||
|
|
||||||
await conn.EnsureTable("ensured");
|
await conn.EnsureTable("ensured");
|
||||||
|
|
||||||
exists = await itExists("ensured");
|
exists = await ItExists("ensured");
|
||||||
alsoExists = await itExists("idx_ensured_key");
|
alsoExists = await ItExists("idx_ensured_key");
|
||||||
Expect.isTrue(exists, "The table should now exist");
|
Expect.isTrue(exists, "The table should now exist");
|
||||||
Expect.isTrue(alsoExists, "The key index should now exist");
|
Expect.isTrue(alsoExists, "The key index should now exist");
|
||||||
|
return;
|
||||||
|
|
||||||
|
Task<bool> ItExists(string name) =>
|
||||||
|
conn.CustomScalar($"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
|
||||||
|
[new("@name", name)], Results.ToExists);
|
||||||
}),
|
}),
|
||||||
TestCase("EnsureFieldIndex succeeds", async () =>
|
TestCase("EnsureFieldIndex succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
var indexExists = () => conn.CustomScalar(
|
|
||||||
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it",
|
|
||||||
Parameters.None, Results.ToExists);
|
|
||||||
|
|
||||||
var exists = await indexExists();
|
var exists = await IndexExists();
|
||||||
Expect.isFalse(exists, "The index should not exist already");
|
Expect.isFalse(exists, "The index should not exist already");
|
||||||
|
|
||||||
await conn.EnsureTable("ensured");
|
await conn.EnsureTable("ensured");
|
||||||
await conn.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
|
await conn.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
|
||||||
exists = await indexExists();
|
exists = await IndexExists();
|
||||||
Expect.isTrue(exists, "The index should now exist");
|
Expect.isTrue(exists, "The index should now exist");
|
||||||
|
return;
|
||||||
|
|
||||||
|
Task<bool> IndexExists() =>
|
||||||
|
conn.CustomScalar(
|
||||||
|
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it",
|
||||||
|
Parameters.None, Results.ToExists);
|
||||||
}),
|
}),
|
||||||
TestList("Insert", new[]
|
TestList("Insert",
|
||||||
{
|
[
|
||||||
TestCase("succeeds", async () =>
|
TestCase("succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -168,9 +170,9 @@ public static class SqliteCSharpExtensionTests
|
||||||
// This is what is supposed to happen
|
// This is what is supposed to happen
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("Save", new[]
|
TestList("Save",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is inserted", async () =>
|
TestCase("succeeds when a document is inserted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -203,7 +205,7 @@ public static class SqliteCSharpExtensionTests
|
||||||
Expect.equal(after!.Id, "test", "The updated document is not correct");
|
Expect.equal(after!.Id, "test", "The updated document is not correct");
|
||||||
Expect.isNull(after.Sub, "There should not have been a sub-document in the updated document");
|
Expect.isNull(after.Sub, "There should not have been a sub-document in the updated document");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestCase("CountAll succeeds", async () =>
|
TestCase("CountAll succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -219,11 +221,11 @@ public static class SqliteCSharpExtensionTests
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var theCount = await conn.CountByField(SqliteDb.TableName, Field.EQ("Value", "purple"));
|
var theCount = await conn.CountByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")]);
|
||||||
Expect.equal(theCount, 2L, "There should have been 2 matching documents");
|
Expect.equal(theCount, 2L, "There should have been 2 matching documents");
|
||||||
}),
|
}),
|
||||||
TestList("ExistsById", new[]
|
TestList("ExistsById",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document exists", async () =>
|
TestCase("succeeds when a document exists", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -242,16 +244,16 @@ public static class SqliteCSharpExtensionTests
|
||||||
var exists = await conn.ExistsById(SqliteDb.TableName, "seven");
|
var exists = await conn.ExistsById(SqliteDb.TableName, "seven");
|
||||||
Expect.isFalse(exists, "There should not have been an existing document");
|
Expect.isFalse(exists, "There should not have been an existing document");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ExistsByField", new[]
|
TestList("ExistsByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents exist", async () =>
|
TestCase("succeeds when documents exist", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var exists = await conn.ExistsByField(SqliteDb.TableName, Field.GE("NumValue", 10));
|
var exists = await conn.ExistsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.GE("NumValue", 10)]);
|
||||||
Expect.isTrue(exists, "There should have been existing documents");
|
Expect.isTrue(exists, "There should have been existing documents");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no matching documents exist", async () =>
|
TestCase("succeeds when no matching documents exist", async () =>
|
||||||
|
@ -260,12 +262,13 @@ public static class SqliteCSharpExtensionTests
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var exists = await conn.ExistsByField(SqliteDb.TableName, Field.EQ("Nothing", "none"));
|
var exists =
|
||||||
|
await conn.ExistsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Nothing", "none")]);
|
||||||
Expect.isFalse(exists, "There should not have been any existing documents");
|
Expect.isFalse(exists, "There should not have been any existing documents");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindAll", new[]
|
TestList("FindAll",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when there is data", async () =>
|
TestCase("succeeds when there is data", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -285,9 +288,46 @@ public static class SqliteCSharpExtensionTests
|
||||||
var results = await conn.FindAll<JsonDocument>(SqliteDb.TableName);
|
var results = await conn.FindAll<JsonDocument>(SqliteDb.TableName);
|
||||||
Expect.isEmpty(results, "There should have been no documents returned");
|
Expect.isEmpty(results, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindById", new[]
|
TestList("FindAllOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when ordering numerically", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var results = await conn.FindAllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("n:NumValue")]);
|
||||||
|
Expect.hasLength(results, 5, "There should have been 5 documents returned");
|
||||||
|
Expect.equal(string.Join('|', results.Select(x => x.Id)), "one|three|two|four|five",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when ordering numerically descending", async () =>
|
||||||
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var results =
|
||||||
|
await conn.FindAllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("n:NumValue DESC")]);
|
||||||
|
Expect.hasLength(results, 5, "There should have been 5 documents returned");
|
||||||
|
Expect.equal(string.Join('|', results.Select(x => x.Id)), "five|four|two|three|one",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when ordering alphabetically", async () =>
|
||||||
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var results = await conn.FindAllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("Id DESC")]);
|
||||||
|
Expect.hasLength(results, 5, "There should have been 5 documents returned");
|
||||||
|
Expect.equal(string.Join('|', results.Select(x => x.Id)), "two|three|one|four|five",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("FindById",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is found", async () =>
|
TestCase("succeeds when a document is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -307,16 +347,17 @@ public static class SqliteCSharpExtensionTests
|
||||||
var doc = await conn.FindById<string, JsonDocument>(SqliteDb.TableName, "eighty-seven");
|
var doc = await conn.FindById<string, JsonDocument>(SqliteDb.TableName, "eighty-seven");
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindByField", new[]
|
TestList("FindByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents are found", async () =>
|
TestCase("succeeds when documents are found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await conn.FindByField<JsonDocument>(SqliteDb.TableName, Field.GT("NumValue", 15));
|
var docs = await conn.FindByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.GT("NumValue", 15)]);
|
||||||
Expect.equal(docs.Count, 2, "There should have been two documents returned");
|
Expect.equal(docs.Count, 2, "There should have been two documents returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when documents are not found", async () =>
|
TestCase("succeeds when documents are not found", async () =>
|
||||||
|
@ -325,19 +366,46 @@ public static class SqliteCSharpExtensionTests
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await conn.FindByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "mauve"));
|
var docs = await conn.FindByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "mauve")]);
|
||||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FindFirstByField", new[]
|
TestList("ByFieldsOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when documents are found", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var docs = await conn.FindByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.GT("NumValue", 15)], [Field.Named("Id")]);
|
||||||
|
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "five|four",
|
||||||
|
"There should have been two documents returned");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when documents are not found", async () =>
|
||||||
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var docs = await conn.FindByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.GT("NumValue", 15)], [Field.Named("Id DESC")]);
|
||||||
|
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|five",
|
||||||
|
"There should have been two documents returned");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("FindFirstByFields",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is found", async () =>
|
TestCase("succeeds when a document is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "another"));
|
var doc = await conn.FindFirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "another")]);
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.equal(doc!.Id, "two", "The incorrect document was returned");
|
Expect.equal(doc!.Id, "two", "The incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
|
@ -347,9 +415,10 @@ public static class SqliteCSharpExtensionTests
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Sub.Foo", "green"));
|
var doc = await conn.FindFirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Sub.Foo", "green")]);
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.contains(new[] { "two", "four" }, doc!.Id, "An incorrect document was returned");
|
Expect.contains(["two", "four"], doc!.Id, "An incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when a document is not found", async () =>
|
TestCase("succeeds when a document is not found", async () =>
|
||||||
{
|
{
|
||||||
|
@ -357,12 +426,38 @@ public static class SqliteCSharpExtensionTests
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "absent"));
|
var doc = await conn.FindFirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "absent")]);
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("UpdateById", new[]
|
TestList("FindFirstByFieldsOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when sorting ascending", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var doc = await conn.FindFirstByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Sub.Foo", "green")], [Field.Named("Sub.Bar")]);
|
||||||
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
|
Expect.equal("two", doc!.Id, "An incorrect document was returned");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when sorting descending", async () =>
|
||||||
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var doc = await conn.FindFirstByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]);
|
||||||
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
|
Expect.equal("four", doc!.Id, "An incorrect document was returned");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("UpdateById",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -389,9 +484,9 @@ public static class SqliteCSharpExtensionTests
|
||||||
await conn.UpdateById(SqliteDb.TableName, "test",
|
await conn.UpdateById(SqliteDb.TableName, "test",
|
||||||
new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } });
|
new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("UpdateByFunc", new[]
|
TestList("UpdateByFunc",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -418,9 +513,9 @@ public static class SqliteCSharpExtensionTests
|
||||||
await conn.UpdateByFunc(SqliteDb.TableName, doc => doc.Id,
|
await conn.UpdateByFunc(SqliteDb.TableName, doc => doc.Id,
|
||||||
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
|
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("PatchById", new[]
|
TestList("PatchById",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -443,17 +538,18 @@ public static class SqliteCSharpExtensionTests
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.PatchById(SqliteDb.TableName, "test", new { Foo = "green" });
|
await conn.PatchById(SqliteDb.TableName, "test", new { Foo = "green" });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("PatchByField", new[]
|
TestList("PatchByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
|
await conn.PatchByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
|
||||||
var after = await conn.CountByField(SqliteDb.TableName, Field.EQ("NumValue", 77));
|
new { NumValue = 77 });
|
||||||
|
var after = await conn.CountByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 77)]);
|
||||||
Expect.equal(after, 2L, "There should have been 2 documents returned");
|
Expect.equal(after, 2L, "There should have been 2 documents returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is updated", async () =>
|
TestCase("succeeds when no document is updated", async () =>
|
||||||
|
@ -464,18 +560,19 @@ public static class SqliteCSharpExtensionTests
|
||||||
Expect.isEmpty(before, "There should have been no documents returned");
|
Expect.isEmpty(before, "There should have been no documents returned");
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
|
await conn.PatchByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "burgundy")],
|
||||||
|
new { Foo = "green" });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("RemoveFieldsById", new[]
|
TestList("RemoveFieldsById",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when fields are removed", async () =>
|
TestCase("succeeds when fields are removed", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "Sub", "Value" });
|
await conn.RemoveFieldsById(SqliteDb.TableName, "two", ["Sub", "Value"]);
|
||||||
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
|
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.equal(updated.Value, "", "The string value should have been removed");
|
Expect.equal(updated.Value, "", "The string value should have been removed");
|
||||||
|
@ -488,7 +585,7 @@ public static class SqliteCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
|
await conn.RemoveFieldsById(SqliteDb.TableName, "two", ["AFieldThatIsNotThere"]);
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is matched", async () =>
|
TestCase("succeeds when no document is matched", async () =>
|
||||||
{
|
{
|
||||||
|
@ -496,18 +593,19 @@ public static class SqliteCSharpExtensionTests
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "Value" });
|
await conn.RemoveFieldsById(SqliteDb.TableName, "two", ["Value"]);
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("RemoveFieldsByField", new[]
|
TestList("RemoveFieldsByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a field is removed", async () =>
|
TestCase("succeeds when a field is removed", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Sub" });
|
await conn.RemoveFieldsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 17)],
|
||||||
|
["Sub"]);
|
||||||
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
|
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.isNull(updated.Sub, "The sub-document should have been removed");
|
Expect.isNull(updated.Sub, "The sub-document should have been removed");
|
||||||
|
@ -519,7 +617,8 @@ public static class SqliteCSharpExtensionTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Nothing" });
|
await conn.RemoveFieldsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 17)],
|
||||||
|
["Nothing"]);
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is matched", async () =>
|
TestCase("succeeds when no document is matched", async () =>
|
||||||
{
|
{
|
||||||
|
@ -527,11 +626,12 @@ public static class SqliteCSharpExtensionTests
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await conn.RemoveFieldsByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" });
|
await conn.RemoveFieldsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NE("Abracadabra", "apple")],
|
||||||
|
["Value"]);
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("DeleteById", new[]
|
TestList("DeleteById",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is deleted", async () =>
|
TestCase("succeeds when a document is deleted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -552,16 +652,16 @@ public static class SqliteCSharpExtensionTests
|
||||||
var remaining = await conn.CountAll(SqliteDb.TableName);
|
var remaining = await conn.CountAll(SqliteDb.TableName);
|
||||||
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
|
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("DeleteByField", new[]
|
TestList("DeleteByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents are deleted", async () =>
|
TestCase("succeeds when documents are deleted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.DeleteByField(SqliteDb.TableName, Field.NE("Value", "purple"));
|
await conn.DeleteByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NE("Value", "purple")]);
|
||||||
var remaining = await conn.CountAll(SqliteDb.TableName);
|
var remaining = await conn.CountAll(SqliteDb.TableName);
|
||||||
Expect.equal(remaining, 2L, "There should have been 2 documents remaining");
|
Expect.equal(remaining, 2L, "There should have been 2 documents remaining");
|
||||||
}),
|
}),
|
||||||
|
@ -571,11 +671,11 @@ public static class SqliteCSharpExtensionTests
|
||||||
await using var conn = Sqlite.Configuration.DbConn();
|
await using var conn = Sqlite.Configuration.DbConn();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await conn.DeleteByField(SqliteDb.TableName, Field.EQ("Value", "crimson"));
|
await conn.DeleteByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "crimson")]);
|
||||||
var remaining = await conn.CountAll(SqliteDb.TableName);
|
var remaining = await conn.CountAll(SqliteDb.TableName);
|
||||||
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
|
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
|
TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
|
||||||
});
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
using System.Text.Json;
|
using Expecto.CSharp;
|
||||||
using Expecto.CSharp;
|
|
||||||
using Expecto;
|
using Expecto;
|
||||||
using Microsoft.Data.Sqlite;
|
|
||||||
using Microsoft.FSharp.Core;
|
using Microsoft.FSharp.Core;
|
||||||
using BitBadger.Documents.Sqlite;
|
using BitBadger.Documents.Sqlite;
|
||||||
|
|
||||||
|
@ -15,134 +13,87 @@ using static Runner;
|
||||||
public static class SqliteCSharpTests
|
public static class SqliteCSharpTests
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unit tests for the SQLite library
|
/// Unit tests for the Query module of the SQLite library
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly Test Unit = TestList("Unit", new[]
|
private static readonly Test QueryTests = TestList("Query",
|
||||||
|
[
|
||||||
|
TestList("WhereByFields",
|
||||||
|
[
|
||||||
|
TestCase("succeeds for a single field when a logical operator is passed", () =>
|
||||||
{
|
{
|
||||||
TestList("Query", new[]
|
Expect.equal(
|
||||||
{
|
Sqlite.Query.WhereByFields(FieldMatch.Any, [Field.GT("theField", 0).WithParameterName("@test")]),
|
||||||
TestList("WhereByField", new[]
|
|
||||||
{
|
|
||||||
TestCase("succeeds when a logical operator is passed", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.WhereByField(Field.GT("theField", 0), "@test"),
|
|
||||||
"data->>'theField' > @test", "WHERE clause not correct");
|
"data->>'theField' > @test", "WHERE clause not correct");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when an existence operator is passed", () =>
|
TestCase("succeeds for a single field when an existence operator is passed", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Sqlite.Query.WhereByField(Field.NEX("thatField"), ""), "data->>'thatField' IS NULL",
|
Expect.equal(Sqlite.Query.WhereByFields(FieldMatch.Any, [Field.NEX("thatField")]),
|
||||||
"WHERE clause not correct");
|
"data->>'thatField' IS NULL", "WHERE clause not correct");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when the between operator is passed", () =>
|
TestCase("succeeds for a single field when a between operator is passed", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Sqlite.Query.WhereByField(Field.BT("aField", 50, 99), "@range"),
|
Expect.equal(
|
||||||
|
Sqlite.Query.WhereByFields(FieldMatch.All,
|
||||||
|
[Field.BT("aField", 50, 99).WithParameterName("@range")]),
|
||||||
"data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct");
|
"data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct");
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
|
TestCase("succeeds for all multiple fields with logical operators", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(
|
||||||
|
Sqlite.Query.WhereByFields(FieldMatch.All, [Field.EQ("theFirst", "1"), Field.EQ("numberTwo", "2")]),
|
||||||
|
"data->>'theFirst' = @field0 AND data->>'numberTwo' = @field1", "WHERE clause not correct");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for any multiple fields with an existence operator", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(
|
||||||
|
Sqlite.Query.WhereByFields(FieldMatch.Any, [Field.NEX("thatField"), Field.GE("thisField", 18)]),
|
||||||
|
"data->>'thatField' IS NULL OR data->>'thisField' >= @field0", "WHERE clause not correct");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds for all multiple fields with between operators", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(
|
||||||
|
Sqlite.Query.WhereByFields(FieldMatch.All,
|
||||||
|
[Field.BT("aField", 50, 99), Field.BT("anotherField", "a", "b")]),
|
||||||
|
"data->>'aField' BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max",
|
||||||
|
"WHERE clause not correct");
|
||||||
|
})
|
||||||
|
]),
|
||||||
TestCase("WhereById succeeds", () =>
|
TestCase("WhereById succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Sqlite.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct");
|
Expect.equal(Sqlite.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct");
|
||||||
}),
|
}),
|
||||||
|
TestCase("Patch succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Sqlite.Query.Patch(SqliteDb.TableName),
|
||||||
|
$"UPDATE {SqliteDb.TableName} SET data = json_patch(data, json(@data))", "Patch query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("RemoveFields succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Sqlite.Query.RemoveFields(SqliteDb.TableName, [new("@a", "a"), new("@b", "b")]),
|
||||||
|
$"UPDATE {SqliteDb.TableName} SET data = json_remove(data, @a, @b)",
|
||||||
|
"Field removal query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("ById succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Sqlite.Query.ById("test", "14"), "test WHERE data->>'Id' = @id", "By-ID query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("ByFields succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Sqlite.Query.ByFields("unit", FieldMatch.Any, [Field.GT("That", 14)]),
|
||||||
|
"unit WHERE data->>'That' > @field0", "By-Field query not correct");
|
||||||
|
}),
|
||||||
TestCase("Definition.EnsureTable succeeds", () =>
|
TestCase("Definition.EnsureTable succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Sqlite.Query.Definition.EnsureTable("tbl"),
|
Expect.equal(Sqlite.Query.Definition.EnsureTable("tbl"),
|
||||||
"CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)", "CREATE TABLE statement not correct");
|
"CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)", "CREATE TABLE statement not correct");
|
||||||
}),
|
|
||||||
TestCase("Update succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Update("tbl"), "UPDATE tbl SET data = @data WHERE data->>'Id' = @id",
|
|
||||||
"UPDATE full statement not correct");
|
|
||||||
}),
|
|
||||||
TestList("Count", new[]
|
|
||||||
{
|
|
||||||
TestCase("All succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Count.All("tbl"), "SELECT COUNT(*) AS it FROM tbl",
|
|
||||||
"Count query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Count.ByField("tbl", Field.EQ("thatField", 0)),
|
|
||||||
"SELECT COUNT(*) AS it FROM tbl WHERE data->>'thatField' = @field",
|
|
||||||
"JSON field text comparison count query not correct");
|
|
||||||
})
|
})
|
||||||
}),
|
]);
|
||||||
TestList("Exists", new[]
|
|
||||||
{
|
/// <summary>
|
||||||
TestCase("ById succeeds", () =>
|
/// Unit tests for the Parameters module of the SQLite library
|
||||||
{
|
/// </summary>
|
||||||
Expect.equal(Sqlite.Query.Exists.ById("tbl"),
|
private static readonly Test ParametersTests = TestList("Parameters",
|
||||||
"SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Id' = @id) AS it",
|
[
|
||||||
"ID existence query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Exists.ByField("tbl", Field.LT("Test", 0)),
|
|
||||||
"SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Test' < @field) AS it",
|
|
||||||
"JSON field text comparison exists query not correct");
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
TestList("Find", new[]
|
|
||||||
{
|
|
||||||
TestCase("ById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Find.ById("tbl"), "SELECT data FROM tbl WHERE data->>'Id' = @id",
|
|
||||||
"SELECT by ID query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Find.ByField("tbl", Field.GE("Golf", 0)),
|
|
||||||
"SELECT data FROM tbl WHERE data->>'Golf' >= @field",
|
|
||||||
"SELECT by JSON comparison query not correct");
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
TestList("Patch", new[]
|
|
||||||
{
|
|
||||||
TestCase("ById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Patch.ById("tbl"),
|
|
||||||
"UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Id' = @id",
|
|
||||||
"UPDATE partial by ID statement not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Patch.ByField("tbl", Field.NE("Part", 0)),
|
|
||||||
"UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Part' <> @field",
|
|
||||||
"UPDATE partial by JSON comparison query not correct");
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
TestList("RemoveFields", new[]
|
|
||||||
{
|
|
||||||
TestCase("ById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.RemoveFields.ById("tbl", new[] { new SqliteParameter("@name", "one") }),
|
|
||||||
"UPDATE tbl SET data = json_remove(data, @name) WHERE data->>'Id' = @id",
|
|
||||||
"Remove field by ID query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.RemoveFields.ByField("tbl", Field.LT("Fly", 0),
|
|
||||||
new[] { new SqliteParameter("@name0", "one"), new SqliteParameter("@name1", "two") }),
|
|
||||||
"UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data->>'Fly' < @field",
|
|
||||||
"Remove field by field query not correct");
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
TestList("Delete", new[]
|
|
||||||
{
|
|
||||||
TestCase("ById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Delete.ById("tbl"), "DELETE FROM tbl WHERE data->>'Id' = @id",
|
|
||||||
"DELETE by ID query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Delete.ByField("tbl", Field.NEX("gone")),
|
|
||||||
"DELETE FROM tbl WHERE data->>'gone' IS NULL", "DELETE by JSON comparison query not correct");
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
TestList("Parameters", new[]
|
|
||||||
{
|
|
||||||
TestCase("Id succeeds", () =>
|
TestCase("Id succeeds", () =>
|
||||||
{
|
{
|
||||||
var theParam = Parameters.Id(7);
|
var theParam = Parameters.Id(7);
|
||||||
|
@ -155,10 +106,10 @@ public static class SqliteCSharpTests
|
||||||
Expect.equal(theParam.ParameterName, "@test", "The parameter name is incorrect");
|
Expect.equal(theParam.ParameterName, "@test", "The parameter name is incorrect");
|
||||||
Expect.equal(theParam.Value, "{\"Nice\":\"job\"}", "The parameter value is incorrect");
|
Expect.equal(theParam.Value, "{\"Nice\":\"job\"}", "The parameter value is incorrect");
|
||||||
}),
|
}),
|
||||||
|
#pragma warning disable CS0618
|
||||||
TestCase("AddField succeeds when adding a parameter", () =>
|
TestCase("AddField succeeds when adding a parameter", () =>
|
||||||
{
|
{
|
||||||
var paramList = Parameters.AddField("@field", Field.EQ("it", 99), Enumerable.Empty<SqliteParameter>())
|
var paramList = Parameters.AddField("@field", Field.EQ("it", 99), []).ToList();
|
||||||
.ToList();
|
|
||||||
Expect.hasLength(paramList, 1, "There should have been a parameter added");
|
Expect.hasLength(paramList, 1, "There should have been a parameter added");
|
||||||
var theParam = paramList[0];
|
var theParam = paramList[0];
|
||||||
Expect.equal(theParam.ParameterName, "@field", "The parameter name is incorrect");
|
Expect.equal(theParam.ParameterName, "@field", "The parameter name is incorrect");
|
||||||
|
@ -166,25 +117,29 @@ public static class SqliteCSharpTests
|
||||||
}),
|
}),
|
||||||
TestCase("AddField succeeds when not adding a parameter", () =>
|
TestCase("AddField succeeds when not adding a parameter", () =>
|
||||||
{
|
{
|
||||||
var paramSeq = Parameters.AddField("@it", Field.EX("Coffee"), Enumerable.Empty<SqliteParameter>());
|
var paramSeq = Parameters.AddField("@it", Field.EX("Coffee"), []);
|
||||||
Expect.isEmpty(paramSeq, "There should not have been any parameters added");
|
Expect.isEmpty(paramSeq, "There should not have been any parameters added");
|
||||||
}),
|
}),
|
||||||
|
#pragma warning restore CS0618
|
||||||
TestCase("None succeeds", () =>
|
TestCase("None succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.isEmpty(Parameters.None, "The parameter list should have been empty");
|
Expect.isEmpty(Parameters.None, "The parameter list should have been empty");
|
||||||
})
|
})
|
||||||
})
|
]);
|
||||||
// Results are exhaustively executed in the context of other tests
|
|
||||||
});
|
|
||||||
|
|
||||||
private static readonly List<JsonDocument> TestDocuments = new()
|
// Results are exhaustively executed in the context of other tests
|
||||||
{
|
|
||||||
|
/// <summary>
|
||||||
|
/// Documents used for integration tests
|
||||||
|
/// </summary>
|
||||||
|
private static readonly List<JsonDocument> TestDocuments =
|
||||||
|
[
|
||||||
new() { Id = "one", Value = "FIRST!", NumValue = 0 },
|
new() { Id = "one", Value = "FIRST!", NumValue = 0 },
|
||||||
new() { Id = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } },
|
new() { Id = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } },
|
||||||
new() { Id = "three", Value = "", NumValue = 4 },
|
new() { Id = "three", Value = "", NumValue = 4 },
|
||||||
new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } },
|
new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } },
|
||||||
new() { Id = "five", Value = "purple", NumValue = 18 }
|
new() { Id = "five", Value = "purple", NumValue = 18 }
|
||||||
};
|
];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add the test documents to the database
|
/// Add the test documents to the database
|
||||||
|
@ -194,9 +149,10 @@ public static class SqliteCSharpTests
|
||||||
foreach (var doc in TestDocuments) await Document.Insert(SqliteDb.TableName, doc);
|
foreach (var doc in TestDocuments) await Document.Insert(SqliteDb.TableName, doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Test Integration = TestList("Integration", new[]
|
/// <summary>
|
||||||
{
|
/// Integration tests for the Configuration module of the SQLite library
|
||||||
TestCase("Configuration.UseConnectionString succeeds", () =>
|
/// </summary>
|
||||||
|
private static readonly Test ConfigurationTests = TestCase("Configuration.UseConnectionString succeeds", () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -208,18 +164,22 @@ public static class SqliteCSharpTests
|
||||||
{
|
{
|
||||||
Sqlite.Configuration.UseConnectionString("Data Source=:memory:");
|
Sqlite.Configuration.UseConnectionString("Data Source=:memory:");
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
TestList("Custom", new[]
|
|
||||||
{
|
/// <summary>
|
||||||
TestList("Single", new[]
|
/// Integration tests for the Custom module of the SQLite library
|
||||||
{
|
/// </summary>
|
||||||
|
private static readonly Test CustomTests = TestList("Custom",
|
||||||
|
[
|
||||||
|
TestList("Single",
|
||||||
|
[
|
||||||
TestCase("succeeds when a row is found", async () =>
|
TestCase("succeeds when a row is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await Custom.Single($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
|
var doc = await Custom.Single($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
|
||||||
new[] { Parameters.Id("one") }, Results.FromData<JsonDocument>);
|
[Parameters.Id("one")], Results.FromData<JsonDocument>);
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.equal(doc!.Id, "one", "The incorrect document was returned");
|
Expect.equal(doc!.Id, "one", "The incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
|
@ -229,18 +189,18 @@ public static class SqliteCSharpTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await Custom.Single($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
|
var doc = await Custom.Single($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
|
||||||
new[] { Parameters.Id("eighty") }, Results.FromData<JsonDocument>);
|
[Parameters.Id("eighty")], Results.FromData<JsonDocument>);
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("List", new[]
|
TestList("List",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when data is found", async () =>
|
TestCase("succeeds when data is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await Custom.List(Query.SelectFromTable(SqliteDb.TableName), Parameters.None,
|
var docs = await Custom.List(Query.Find(SqliteDb.TableName), Parameters.None,
|
||||||
Results.FromData<JsonDocument>);
|
Results.FromData<JsonDocument>);
|
||||||
Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
|
Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
|
||||||
}),
|
}),
|
||||||
|
@ -250,13 +210,13 @@ public static class SqliteCSharpTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await Custom.List(
|
var docs = await Custom.List(
|
||||||
$"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value",
|
$"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", [new("@value", 100)],
|
||||||
new[] { new SqliteParameter("@value", 100) }, Results.FromData<JsonDocument>);
|
Results.FromData<JsonDocument>);
|
||||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("NonQuery", new[]
|
TestList("NonQuery",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when operating on data", async () =>
|
TestCase("succeeds when operating on data", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -273,12 +233,12 @@ public static class SqliteCSharpTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await Custom.NonQuery($"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value",
|
await Custom.NonQuery($"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value",
|
||||||
new[] { new SqliteParameter("@value", 100) });
|
[new("@value", 100)]);
|
||||||
|
|
||||||
var remaining = await Count.All(SqliteDb.TableName);
|
var remaining = await Count.All(SqliteDb.TableName);
|
||||||
Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table");
|
Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestCase("Scalar succeeds", async () =>
|
TestCase("Scalar succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -286,9 +246,13 @@ public static class SqliteCSharpTests
|
||||||
var nbr = await Custom.Scalar("SELECT 5 AS test_value", Parameters.None, rdr => rdr.GetInt32(0));
|
var nbr = await Custom.Scalar("SELECT 5 AS test_value", Parameters.None, rdr => rdr.GetInt32(0));
|
||||||
Expect.equal(nbr, 5, "The query should have returned the number 5");
|
Expect.equal(nbr, 5, "The query should have returned the number 5");
|
||||||
})
|
})
|
||||||
}),
|
]);
|
||||||
TestList("Definition", new[]
|
|
||||||
{
|
/// <summary>
|
||||||
|
/// Integration tests for the Definition module of the SQLite library
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test DefinitionTests = TestList("Definition",
|
||||||
|
[
|
||||||
TestCase("EnsureTable succeeds", async () =>
|
TestCase("EnsureTable succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -308,29 +272,36 @@ public static class SqliteCSharpTests
|
||||||
|
|
||||||
async ValueTask<bool> ItExists(string name)
|
async ValueTask<bool> ItExists(string name)
|
||||||
{
|
{
|
||||||
return await Custom.Scalar(
|
return await Custom.Scalar($"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
|
||||||
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
|
[new("@name", name)], Results.ToExists);
|
||||||
new SqliteParameter[] { new("@name", name) }, Results.ToExists);
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
TestCase("EnsureFieldIndex succeeds", async () =>
|
TestCase("EnsureFieldIndex succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
var indexExists = () => Custom.Scalar(
|
|
||||||
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it",
|
|
||||||
Parameters.None, Results.ToExists);
|
|
||||||
|
|
||||||
var exists = await indexExists();
|
var exists = await IndexExists();
|
||||||
Expect.isFalse(exists, "The index should not exist already");
|
Expect.isFalse(exists, "The index should not exist already");
|
||||||
|
|
||||||
await Definition.EnsureTable("ensured");
|
await Definition.EnsureTable("ensured");
|
||||||
await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
|
await Definition.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
|
||||||
exists = await indexExists();
|
exists = await IndexExists();
|
||||||
Expect.isTrue(exists, "The index should now exist");
|
Expect.isTrue(exists, "The index should now exist");
|
||||||
|
return;
|
||||||
|
|
||||||
|
Task<bool> IndexExists() => Custom.Scalar(
|
||||||
|
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it",
|
||||||
|
Parameters.None, Results.ToExists);
|
||||||
})
|
})
|
||||||
}),
|
]);
|
||||||
TestList("Document.Insert", new[]
|
|
||||||
{
|
/// <summary>
|
||||||
|
/// Integration tests for the Document module of the SQLite library
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test DocumentTests = TestList("Document",
|
||||||
|
[
|
||||||
|
TestList("Insert",
|
||||||
|
[
|
||||||
TestCase("succeeds", async () =>
|
TestCase("succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -354,10 +325,87 @@ public static class SqliteCSharpTests
|
||||||
{
|
{
|
||||||
// This is what is supposed to happen
|
// This is what is supposed to happen
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
TestList("Document.Save", new[]
|
TestCase("succeeds when adding a numeric auto ID", async () =>
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Configuration.UseAutoIdStrategy(AutoId.Number);
|
||||||
|
Configuration.UseIdField("Key");
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
var before = await Count.All(SqliteDb.TableName);
|
||||||
|
Expect.equal(before, 0L, "There should be no documents in the table");
|
||||||
|
|
||||||
|
await Document.Insert(SqliteDb.TableName, new NumIdDocument { Text = "one" });
|
||||||
|
await Document.Insert(SqliteDb.TableName, new NumIdDocument { Text = "two" });
|
||||||
|
await Document.Insert(SqliteDb.TableName, new NumIdDocument { Key = 77, Text = "three" });
|
||||||
|
await Document.Insert(SqliteDb.TableName, new NumIdDocument { Text = "four" });
|
||||||
|
|
||||||
|
var after = await Find.AllOrdered<NumIdDocument>(SqliteDb.TableName, [Field.Named("Key")]);
|
||||||
|
Expect.hasLength(after, 4, "There should have been 4 documents returned");
|
||||||
|
Expect.sequenceEqual(after.Select(x => x.Key), [1, 2, 77, 78],
|
||||||
|
"The IDs were not generated correctly");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Configuration.UseAutoIdStrategy(AutoId.Disabled);
|
||||||
|
Configuration.UseIdField("Id");
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when adding a GUID auto ID", async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Configuration.UseAutoIdStrategy(AutoId.Guid);
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
var before = await Count.All(SqliteDb.TableName);
|
||||||
|
Expect.equal(before, 0L, "There should be no documents in the table");
|
||||||
|
|
||||||
|
await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "one" });
|
||||||
|
await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "two" });
|
||||||
|
await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "abc123", Value = "three" });
|
||||||
|
await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "four" });
|
||||||
|
|
||||||
|
var after = await Find.All<JsonDocument>(SqliteDb.TableName);
|
||||||
|
Expect.hasLength(after, 4, "There should have been 4 documents returned");
|
||||||
|
Expect.equal(after.Count(x => x.Id.Length == 32), 3, "Three of the IDs should have been GUIDs");
|
||||||
|
Expect.equal(after.Count(x => x.Id == "abc123"), 1, "The provided ID should have been used as-is");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Configuration.UseAutoIdStrategy(AutoId.Disabled);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when adding a RandomString auto ID", async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Configuration.UseAutoIdStrategy(AutoId.RandomString);
|
||||||
|
Configuration.UseIdStringLength(44);
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
var before = await Count.All(SqliteDb.TableName);
|
||||||
|
Expect.equal(before, 0L, "There should be no documents in the table");
|
||||||
|
|
||||||
|
await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "one" });
|
||||||
|
await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "two" });
|
||||||
|
await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "abc123", Value = "three" });
|
||||||
|
await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "four" });
|
||||||
|
|
||||||
|
var after = await Find.All<JsonDocument>(SqliteDb.TableName);
|
||||||
|
Expect.hasLength(after, 4, "There should have been 4 documents returned");
|
||||||
|
Expect.equal(after.Count(x => x.Id.Length == 44), 3,
|
||||||
|
"Three of the IDs should have been 44-character random strings");
|
||||||
|
Expect.equal(after.Count(x => x.Id == "abc123"), 1, "The provided ID should have been used as-is");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Configuration.UseAutoIdStrategy(AutoId.Disabled);
|
||||||
|
Configuration.UseIdStringLength(16);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("Save",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is inserted", async () =>
|
TestCase("succeeds when a document is inserted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -388,9 +436,14 @@ public static class SqliteCSharpTests
|
||||||
Expect.equal(after!.Id, "test", "The updated document is not correct");
|
Expect.equal(after!.Id, "test", "The updated document is not correct");
|
||||||
Expect.isNull(after.Sub, "There should not have been a sub-document in the updated document");
|
Expect.isNull(after.Sub, "There should not have been a sub-document in the updated document");
|
||||||
})
|
})
|
||||||
}),
|
])
|
||||||
TestList("Count", new[]
|
]);
|
||||||
{
|
|
||||||
|
/// <summary>
|
||||||
|
/// Integration tests for the Count module of the SQLite library
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test CountTests = TestList("Count",
|
||||||
|
[
|
||||||
TestCase("All succeeds", async () =>
|
TestCase("All succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -399,27 +452,35 @@ public static class SqliteCSharpTests
|
||||||
var theCount = await Count.All(SqliteDb.TableName);
|
var theCount = await Count.All(SqliteDb.TableName);
|
||||||
Expect.equal(theCount, 5L, "There should have been 5 matching documents");
|
Expect.equal(theCount, 5L, "There should have been 5 matching documents");
|
||||||
}),
|
}),
|
||||||
TestCase("ByField succeeds for numeric range", async () =>
|
TestList("ByFields",
|
||||||
|
[
|
||||||
|
TestCase("succeeds for numeric range", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("NumValue", 10, 20));
|
var theCount = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.BT("NumValue", 10, 20)]);
|
||||||
Expect.equal(theCount, 3L, "There should have been 3 matching documents");
|
Expect.equal(theCount, 3L, "There should have been 3 matching documents");
|
||||||
}),
|
}),
|
||||||
TestCase("ByField succeeds for non-numeric range", async () =>
|
TestCase("succeeds for non-numeric range", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("Value", "aardvark", "apple"));
|
var theCount = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.BT("Value", "aardvark", "apple")]);
|
||||||
Expect.equal(theCount, 1L, "There should have been 1 matching document");
|
Expect.equal(theCount, 1L, "There should have been 1 matching document");
|
||||||
})
|
})
|
||||||
}),
|
])
|
||||||
TestList("Exists", new[]
|
]);
|
||||||
{
|
|
||||||
TestList("ById", new[]
|
/// <summary>
|
||||||
{
|
/// Integration tests for the Exists module of the SQLite library
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test ExistsTests = TestList("Exists",
|
||||||
|
[
|
||||||
|
TestList("ById",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document exists", async () =>
|
TestCase("succeeds when a document exists", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -436,15 +497,15 @@ public static class SqliteCSharpTests
|
||||||
var exists = await Exists.ById(SqliteDb.TableName, "seven");
|
var exists = await Exists.ById(SqliteDb.TableName, "seven");
|
||||||
Expect.isFalse(exists, "There should not have been an existing document");
|
Expect.isFalse(exists, "There should not have been an existing document");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ByField", new[]
|
TestList("ByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents exist", async () =>
|
TestCase("succeeds when documents exist", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var exists = await Exists.ByField(SqliteDb.TableName, Field.GE("NumValue", 10));
|
var exists = await Exists.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.GE("NumValue", 10)]);
|
||||||
Expect.isTrue(exists, "There should have been existing documents");
|
Expect.isTrue(exists, "There should have been existing documents");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no matching documents exist", async () =>
|
TestCase("succeeds when no matching documents exist", async () =>
|
||||||
|
@ -452,15 +513,19 @@ public static class SqliteCSharpTests
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var exists = await Exists.ByField(SqliteDb.TableName, Field.EQ("Nothing", "none"));
|
var exists = await Exists.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Nothing", "none")]);
|
||||||
Expect.isFalse(exists, "There should not have been any existing documents");
|
Expect.isFalse(exists, "There should not have been any existing documents");
|
||||||
})
|
})
|
||||||
})
|
])
|
||||||
}),
|
]);
|
||||||
TestList("Find", new[]
|
|
||||||
{
|
/// <summary>
|
||||||
TestList("All", new[]
|
/// Integration tests for the Find module of the SQLite library
|
||||||
{
|
/// </summary>
|
||||||
|
private static readonly Test FindTests = TestList("Find",
|
||||||
|
[
|
||||||
|
TestList("All",
|
||||||
|
[
|
||||||
TestCase("succeeds when there is data", async () =>
|
TestCase("succeeds when there is data", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -478,9 +543,42 @@ public static class SqliteCSharpTests
|
||||||
var results = await Find.All<SubDocument>(SqliteDb.TableName);
|
var results = await Find.All<SubDocument>(SqliteDb.TableName);
|
||||||
Expect.isEmpty(results, "There should have been no documents returned");
|
Expect.isEmpty(results, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ById", new[]
|
TestList("AllOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when ordering numerically", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var results = await Find.AllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("n:NumValue")]);
|
||||||
|
Expect.hasLength(results, 5, "There should have been 5 documents returned");
|
||||||
|
Expect.equal(string.Join('|', results.Select(x => x.Id)), "one|three|two|four|five",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when ordering numerically descending", async () =>
|
||||||
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var results = await Find.AllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("n:NumValue DESC")]);
|
||||||
|
Expect.hasLength(results, 5, "There should have been 5 documents returned");
|
||||||
|
Expect.equal(string.Join('|', results.Select(x => x.Id)), "five|four|two|three|one",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when ordering alphabetically", async () =>
|
||||||
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var results = await Find.AllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("Id DESC")]);
|
||||||
|
Expect.hasLength(results, 5, "There should have been 5 documents returned");
|
||||||
|
Expect.equal(string.Join('|', results.Select(x => x.Id)), "two|three|one|four|five",
|
||||||
|
"The documents were not ordered correctly");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("ById",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is found", async () =>
|
TestCase("succeeds when a document is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -498,15 +596,16 @@ public static class SqliteCSharpTests
|
||||||
var doc = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "twenty two");
|
var doc = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "twenty two");
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ByField", new[]
|
TestList("ByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents are found", async () =>
|
TestCase("succeeds when documents are found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await Find.ByField<JsonDocument>(SqliteDb.TableName, Field.GT("NumValue", 15));
|
var docs = await Find.ByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.GT("NumValue", 15)]);
|
||||||
Expect.equal(docs.Count, 2, "There should have been two documents returned");
|
Expect.equal(docs.Count, 2, "There should have been two documents returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when documents are not found", async () =>
|
TestCase("succeeds when documents are not found", async () =>
|
||||||
|
@ -514,18 +613,43 @@ public static class SqliteCSharpTests
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var docs = await Find.ByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "mauve"));
|
var docs = await Find.ByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "mauve")]);
|
||||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("FirstByField", new[]
|
TestList("ByFieldsOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when documents are found", async () =>
|
||||||
{
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var docs = await Find.ByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.GT("NumValue", 15)], [Field.Named("Id")]);
|
||||||
|
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "five|four",
|
||||||
|
"There should have been two documents returned");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when documents are not found", async () =>
|
||||||
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var docs = await Find.ByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.GT("NumValue", 15)], [Field.Named("Id DESC")]);
|
||||||
|
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|five",
|
||||||
|
"There should have been two documents returned");
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
TestList("FirstByFields",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is found", async () =>
|
TestCase("succeeds when a document is found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "another"));
|
var doc = await Find.FirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "another")]);
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.equal(doc!.Id, "two", "The incorrect document was returned");
|
Expect.equal(doc!.Id, "two", "The incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
|
@ -534,24 +658,53 @@ public static class SqliteCSharpTests
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Sub.Foo", "green"));
|
var doc = await Find.FirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Sub.Foo", "green")]);
|
||||||
Expect.isNotNull(doc, "There should have been a document returned");
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
Expect.contains(new[] { "two", "four" }, doc!.Id, "An incorrect document was returned");
|
Expect.contains(["two", "four"], doc!.Id, "An incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when a document is not found", async () =>
|
TestCase("succeeds when a document is not found", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "absent"));
|
var doc = await Find.FirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Value", "absent")]);
|
||||||
Expect.isNull(doc, "There should not have been a document returned");
|
Expect.isNull(doc, "There should not have been a document returned");
|
||||||
})
|
})
|
||||||
})
|
]),
|
||||||
|
TestList("FirstByFieldsOrdered",
|
||||||
|
[
|
||||||
|
TestCase("succeeds when sorting ascending", async () =>
|
||||||
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var doc = await Find.FirstByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Sub.Foo", "green")], [Field.Named("Sub.Bar")]);
|
||||||
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
|
Expect.equal("two", doc!.Id, "An incorrect document was returned");
|
||||||
}),
|
}),
|
||||||
TestList("Update", new[]
|
TestCase("succeeds when sorting descending", async () =>
|
||||||
{
|
|
||||||
TestList("ById", new[]
|
|
||||||
{
|
{
|
||||||
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
await LoadDocs();
|
||||||
|
|
||||||
|
var doc = await Find.FirstByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
|
||||||
|
[Field.EQ("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]);
|
||||||
|
Expect.isNotNull(doc, "There should have been a document returned");
|
||||||
|
Expect.equal("four", doc!.Id, "An incorrect document was returned");
|
||||||
|
})
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Integration tests for the Update module of the SQLite library
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Test UpdateTests = TestList("Update",
|
||||||
|
[
|
||||||
|
TestList("ById",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -577,9 +730,9 @@ public static class SqliteCSharpTests
|
||||||
await Update.ById(SqliteDb.TableName, "test",
|
await Update.ById(SqliteDb.TableName, "test",
|
||||||
new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } });
|
new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ByFunc", new[]
|
TestList("ByFunc",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -605,12 +758,16 @@ public static class SqliteCSharpTests
|
||||||
await Update.ByFunc(SqliteDb.TableName, doc => doc.Id,
|
await Update.ByFunc(SqliteDb.TableName, doc => doc.Id,
|
||||||
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
|
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
}),
|
]);
|
||||||
TestList("Patch", new[]
|
|
||||||
{
|
/// <summary>
|
||||||
TestList("ById", new[]
|
/// Integration tests for the Patch module of the SQLite library
|
||||||
{
|
/// </summary>
|
||||||
|
private static readonly Test PatchTests = TestList("Patch",
|
||||||
|
[
|
||||||
|
TestList("ById",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -632,16 +789,17 @@ public static class SqliteCSharpTests
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await Patch.ById(SqliteDb.TableName, "test", new { Foo = "green" });
|
await Patch.ById(SqliteDb.TableName, "test", new { Foo = "green" });
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ByField", new[]
|
TestList("ByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a document is updated", async () =>
|
TestCase("succeeds when a document is updated", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
|
await Patch.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
|
||||||
var after = await Count.ByField(SqliteDb.TableName, Field.EQ("NumValue", 77));
|
new { NumValue = 77 });
|
||||||
|
var after = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 77)]);
|
||||||
Expect.equal(after, 2L, "There should have been 2 documents returned");
|
Expect.equal(after, 2L, "There should have been 2 documents returned");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is updated", async () =>
|
TestCase("succeeds when no document is updated", async () =>
|
||||||
|
@ -652,20 +810,25 @@ public static class SqliteCSharpTests
|
||||||
Expect.isEmpty(before, "There should have been no documents returned");
|
Expect.isEmpty(before, "There should have been no documents returned");
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
|
await Patch.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "burgundy")],
|
||||||
|
new { Foo = "green" });
|
||||||
})
|
})
|
||||||
})
|
])
|
||||||
}),
|
]);
|
||||||
TestList("RemoveFields", new[]
|
|
||||||
{
|
/// <summary>
|
||||||
TestList("ById", new[]
|
/// Integration tests for the RemoveFields module of the SQLite library
|
||||||
{
|
/// </summary>
|
||||||
|
private static readonly Test RemoveFieldsTests = TestList("RemoveFields",
|
||||||
|
[
|
||||||
|
TestList("ById",
|
||||||
|
[
|
||||||
TestCase("succeeds when fields are removed", async () =>
|
TestCase("succeeds when fields are removed", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Sub", "Value" });
|
await RemoveFields.ById(SqliteDb.TableName, "two", ["Sub", "Value"]);
|
||||||
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
|
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.equal(updated.Value, "", "The string value should have been removed");
|
Expect.equal(updated.Value, "", "The string value should have been removed");
|
||||||
|
@ -677,24 +840,24 @@ public static class SqliteCSharpTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
|
await RemoveFields.ById(SqliteDb.TableName, "two", ["AFieldThatIsNotThere"]);
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is matched", async () =>
|
TestCase("succeeds when no document is matched", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Value" });
|
await RemoveFields.ById(SqliteDb.TableName, "two", ["Value"]);
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ByField", new[]
|
TestList("ByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when a field is removed", async () =>
|
TestCase("succeeds when a field is removed", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Sub" });
|
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 17)], ["Sub"]);
|
||||||
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
|
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
|
||||||
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
Expect.isNotNull(updated, "The updated document should have been retrieved");
|
||||||
Expect.isNull(updated.Sub, "The sub-document should have been removed");
|
Expect.isNull(updated.Sub, "The sub-document should have been removed");
|
||||||
|
@ -705,21 +868,27 @@ public static class SqliteCSharpTests
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Nothing" });
|
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 17)],
|
||||||
|
["Nothing"]);
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when no document is matched", async () =>
|
TestCase("succeeds when no document is matched", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" });
|
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NE("Abracadabra", "apple")],
|
||||||
|
["Value"]);
|
||||||
})
|
})
|
||||||
})
|
])
|
||||||
}),
|
]);
|
||||||
TestList("Delete", new[]
|
|
||||||
{
|
/// <summary>
|
||||||
TestList("ById", new[]
|
/// Integration tests for the Delete module of the SQLite library
|
||||||
{
|
/// </summary>
|
||||||
|
private static readonly Test DeleteTests = TestList("Delete",
|
||||||
|
[
|
||||||
|
TestList("ById",
|
||||||
|
[
|
||||||
TestCase("succeeds when a document is deleted", async () =>
|
TestCase("succeeds when a document is deleted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
|
@ -738,15 +907,15 @@ public static class SqliteCSharpTests
|
||||||
var remaining = await Count.All(SqliteDb.TableName);
|
var remaining = await Count.All(SqliteDb.TableName);
|
||||||
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
|
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
|
||||||
})
|
})
|
||||||
}),
|
]),
|
||||||
TestList("ByField", new[]
|
TestList("ByFields",
|
||||||
{
|
[
|
||||||
TestCase("succeeds when documents are deleted", async () =>
|
TestCase("succeeds when documents are deleted", async () =>
|
||||||
{
|
{
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await Delete.ByField(SqliteDb.TableName, Field.NE("Value", "purple"));
|
await Delete.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NE("Value", "purple")]);
|
||||||
var remaining = await Count.All(SqliteDb.TableName);
|
var remaining = await Count.All(SqliteDb.TableName);
|
||||||
Expect.equal(remaining, 2L, "There should have been 2 documents remaining");
|
Expect.equal(remaining, 2L, "There should have been 2 documents remaining");
|
||||||
}),
|
}),
|
||||||
|
@ -755,18 +924,34 @@ public static class SqliteCSharpTests
|
||||||
await using var db = await SqliteDb.BuildDb();
|
await using var db = await SqliteDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
await Delete.ByField(SqliteDb.TableName, Field.EQ("Value", "crimson"));
|
await Delete.ByFields(SqliteDb.TableName, FieldMatch.All, [Field.EQ("Value", "crimson")]);
|
||||||
var remaining = await Count.All(SqliteDb.TableName);
|
var remaining = await Count.All(SqliteDb.TableName);
|
||||||
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
|
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
|
||||||
})
|
})
|
||||||
})
|
])
|
||||||
}),
|
]);
|
||||||
TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
|
|
||||||
});
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All tests for SQLite C# functions and methods
|
/// All tests for SQLite C# functions and methods
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Tests]
|
[Tests]
|
||||||
public static readonly Test All = TestList("Sqlite.C#", new[] { Unit, TestSequenced(Integration) });
|
public static readonly Test All = TestList("Sqlite.C#",
|
||||||
|
[
|
||||||
|
TestList("Unit", [QueryTests, ParametersTests]),
|
||||||
|
TestSequenced(TestList("Integration",
|
||||||
|
[
|
||||||
|
ConfigurationTests,
|
||||||
|
CustomTests,
|
||||||
|
DefinitionTests,
|
||||||
|
DocumentTests,
|
||||||
|
CountTests,
|
||||||
|
ExistsTests,
|
||||||
|
FindTests,
|
||||||
|
UpdateTests,
|
||||||
|
PatchTests,
|
||||||
|
RemoveFieldsTests,
|
||||||
|
DeleteTests,
|
||||||
|
TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
|
||||||
|
]))
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
namespace BitBadger.Documents.Tests.CSharp;
|
namespace BitBadger.Documents.Tests.CSharp;
|
||||||
|
|
||||||
|
public class NumIdDocument
|
||||||
|
{
|
||||||
|
public int Key { get; set; } = 0;
|
||||||
|
public string Text { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
public class SubDocument
|
public class SubDocument
|
||||||
{
|
{
|
||||||
public string Foo { get; set; } = "";
|
public string Foo { get; set; } = "";
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
|
<NoWarn>1182</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="CommonTests.fs" />
|
|
||||||
<Compile Include="Types.fs" />
|
<Compile Include="Types.fs" />
|
||||||
|
<Compile Include="CommonTests.fs" />
|
||||||
<Compile Include="PostgresTests.fs" />
|
<Compile Include="PostgresTests.fs" />
|
||||||
<Compile Include="PostgresExtensionTests.fs" />
|
<Compile Include="PostgresExtensionTests.fs" />
|
||||||
<Compile Include="SqliteTests.fs" />
|
<Compile Include="SqliteTests.fs" />
|
||||||
|
|
|
@ -6,10 +6,8 @@ open Expecto
|
||||||
/// Test table name
|
/// Test table name
|
||||||
let tbl = "test_table"
|
let tbl = "test_table"
|
||||||
|
|
||||||
/// Tests which do not hit the database
|
/// Unit tests for the Op DU
|
||||||
let all =
|
let opTests = testList "Op" [
|
||||||
testList "Common" [
|
|
||||||
testList "Op" [
|
|
||||||
test "EQ succeeds" {
|
test "EQ succeeds" {
|
||||||
Expect.equal (string EQ) "=" "The equals operator was not correct"
|
Expect.equal (string EQ) "=" "The equals operator was not correct"
|
||||||
}
|
}
|
||||||
|
@ -37,64 +35,303 @@ let all =
|
||||||
test "NEX succeeds" {
|
test "NEX succeeds" {
|
||||||
Expect.equal (string NEX) "IS NULL" """The "not exists" operator was not correct"""
|
Expect.equal (string NEX) "IS NULL" """The "not exists" operator was not correct"""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "Field" [
|
|
||||||
|
/// Unit tests for the Field class
|
||||||
|
let fieldTests = testList "Field" [
|
||||||
test "EQ succeeds" {
|
test "EQ succeeds" {
|
||||||
let field = Field.EQ "Test" 14
|
let field = Field.EQ "Test" 14
|
||||||
Expect.equal field.Name "Test" "Field name incorrect"
|
Expect.equal field.Name "Test" "Field name incorrect"
|
||||||
Expect.equal field.Op EQ "Operator incorrect"
|
Expect.equal field.Op EQ "Operator incorrect"
|
||||||
Expect.equal field.Value 14 "Value incorrect"
|
Expect.equal field.Value 14 "Value incorrect"
|
||||||
|
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||||
|
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||||
}
|
}
|
||||||
test "GT succeeds" {
|
test "GT succeeds" {
|
||||||
let field = Field.GT "Great" "night"
|
let field = Field.GT "Great" "night"
|
||||||
Expect.equal field.Name "Great" "Field name incorrect"
|
Expect.equal field.Name "Great" "Field name incorrect"
|
||||||
Expect.equal field.Op GT "Operator incorrect"
|
Expect.equal field.Op GT "Operator incorrect"
|
||||||
Expect.equal field.Value "night" "Value incorrect"
|
Expect.equal field.Value "night" "Value incorrect"
|
||||||
|
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||||
|
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||||
}
|
}
|
||||||
test "GE succeeds" {
|
test "GE succeeds" {
|
||||||
let field = Field.GE "Nice" 88L
|
let field = Field.GE "Nice" 88L
|
||||||
Expect.equal field.Name "Nice" "Field name incorrect"
|
Expect.equal field.Name "Nice" "Field name incorrect"
|
||||||
Expect.equal field.Op GE "Operator incorrect"
|
Expect.equal field.Op GE "Operator incorrect"
|
||||||
Expect.equal field.Value 88L "Value incorrect"
|
Expect.equal field.Value 88L "Value incorrect"
|
||||||
|
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||||
|
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||||
}
|
}
|
||||||
test "LT succeeds" {
|
test "LT succeeds" {
|
||||||
let field = Field.LT "Lesser" "seven"
|
let field = Field.LT "Lesser" "seven"
|
||||||
Expect.equal field.Name "Lesser" "Field name incorrect"
|
Expect.equal field.Name "Lesser" "Field name incorrect"
|
||||||
Expect.equal field.Op LT "Operator incorrect"
|
Expect.equal field.Op LT "Operator incorrect"
|
||||||
Expect.equal field.Value "seven" "Value incorrect"
|
Expect.equal field.Value "seven" "Value incorrect"
|
||||||
|
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||||
|
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||||
}
|
}
|
||||||
test "LE succeeds" {
|
test "LE succeeds" {
|
||||||
let field = Field.LE "Nobody" "KNOWS";
|
let field = Field.LE "Nobody" "KNOWS";
|
||||||
Expect.equal field.Name "Nobody" "Field name incorrect"
|
Expect.equal field.Name "Nobody" "Field name incorrect"
|
||||||
Expect.equal field.Op LE "Operator incorrect"
|
Expect.equal field.Op LE "Operator incorrect"
|
||||||
Expect.equal field.Value "KNOWS" "Value incorrect"
|
Expect.equal field.Value "KNOWS" "Value incorrect"
|
||||||
|
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||||
|
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||||
}
|
}
|
||||||
test "NE succeeds" {
|
test "NE succeeds" {
|
||||||
let field = Field.NE "Park" "here"
|
let field = Field.NE "Park" "here"
|
||||||
Expect.equal field.Name "Park" "Field name incorrect"
|
Expect.equal field.Name "Park" "Field name incorrect"
|
||||||
Expect.equal field.Op NE "Operator incorrect"
|
Expect.equal field.Op NE "Operator incorrect"
|
||||||
Expect.equal field.Value "here" "Value incorrect"
|
Expect.equal field.Value "here" "Value incorrect"
|
||||||
|
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||||
|
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||||
}
|
}
|
||||||
test "BT succeeds" {
|
test "BT succeeds" {
|
||||||
let field = Field.BT "Age" 18 49
|
let field = Field.BT "Age" 18 49
|
||||||
Expect.equal field.Name "Age" "Field name incorrect"
|
Expect.equal field.Name "Age" "Field name incorrect"
|
||||||
Expect.equal field.Op BT "Operator incorrect"
|
Expect.equal field.Op BT "Operator incorrect"
|
||||||
Expect.sequenceEqual (field.Value :?> obj list) [ 18; 49 ] "Value incorrect"
|
Expect.sequenceEqual (field.Value :?> obj list) [ 18; 49 ] "Value incorrect"
|
||||||
|
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||||
|
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||||
}
|
}
|
||||||
test "EX succeeds" {
|
test "EX succeeds" {
|
||||||
let field = Field.EX "Groovy"
|
let field = Field.EX "Groovy"
|
||||||
Expect.equal field.Name "Groovy" "Field name incorrect"
|
Expect.equal field.Name "Groovy" "Field name incorrect"
|
||||||
Expect.equal field.Op EX "Operator incorrect"
|
Expect.equal field.Op EX "Operator incorrect"
|
||||||
|
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||||
|
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||||
}
|
}
|
||||||
test "NEX succeeds" {
|
test "NEX succeeds" {
|
||||||
let field = Field.NEX "Rad"
|
let field = Field.NEX "Rad"
|
||||||
Expect.equal field.Name "Rad" "Field name incorrect"
|
Expect.equal field.Name "Rad" "Field name incorrect"
|
||||||
Expect.equal field.Op NEX "Operator incorrect"
|
Expect.equal field.Op NEX "Operator incorrect"
|
||||||
|
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||||
|
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||||
|
}
|
||||||
|
testList "NameToPath" [
|
||||||
|
test "succeeds for PostgreSQL and a simple name" {
|
||||||
|
Expect.equal "data->>'Simple'" (Field.NameToPath "Simple" PostgreSQL) "Path not constructed correctly"
|
||||||
|
}
|
||||||
|
test "succeeds for SQLite and a simple name" {
|
||||||
|
Expect.equal "data->>'Simple'" (Field.NameToPath "Simple" SQLite) "Path not constructed correctly"
|
||||||
|
}
|
||||||
|
test "succeeds for PostgreSQL and a nested name" {
|
||||||
|
Expect.equal
|
||||||
|
"data#>>'{A,Long,Path,to,the,Property}'"
|
||||||
|
(Field.NameToPath "A.Long.Path.to.the.Property" PostgreSQL)
|
||||||
|
"Path not constructed correctly"
|
||||||
|
}
|
||||||
|
test "succeeds for SQLite and a nested name" {
|
||||||
|
Expect.equal
|
||||||
|
"data->>'A'->>'Long'->>'Path'->>'to'->>'the'->>'Property'"
|
||||||
|
(Field.NameToPath "A.Long.Path.to.the.Property" SQLite)
|
||||||
|
"Path not constructed correctly"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "Query" [
|
test "WithParameterName succeeds" {
|
||||||
test "selectFromTable succeeds" {
|
let field = (Field.EQ "Bob" "Tom").WithParameterName "@name"
|
||||||
Expect.equal (Query.selectFromTable tbl) $"SELECT data FROM {tbl}" "SELECT statement not correct"
|
Expect.isSome field.ParameterName "The parameter name should have been filled"
|
||||||
|
Expect.equal "@name" field.ParameterName.Value "The parameter name is incorrect"
|
||||||
|
}
|
||||||
|
test "WithQualifier succeeds" {
|
||||||
|
let field = (Field.EQ "Bill" "Matt").WithQualifier "joe"
|
||||||
|
Expect.isSome field.Qualifier "The table qualifier should have been filled"
|
||||||
|
Expect.equal "joe" field.Qualifier.Value "The table qualifier is incorrect"
|
||||||
|
}
|
||||||
|
testList "Path" [
|
||||||
|
test "succeeds for a PostgreSQL single field with no qualifier" {
|
||||||
|
let field = Field.GE "SomethingCool" 18
|
||||||
|
Expect.equal "data->>'SomethingCool'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for a PostgreSQL single field with a qualifier" {
|
||||||
|
let field = { Field.LT "SomethingElse" 9 with Qualifier = Some "this" }
|
||||||
|
Expect.equal "this.data->>'SomethingElse'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for a PostgreSQL nested field with no qualifier" {
|
||||||
|
let field = Field.EQ "My.Nested.Field" "howdy"
|
||||||
|
Expect.equal "data#>>'{My,Nested,Field}'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for a PostgreSQL nested field with a qualifier" {
|
||||||
|
let field = { Field.EQ "Nest.Away" "doc" with Qualifier = Some "bird" }
|
||||||
|
Expect.equal "bird.data#>>'{Nest,Away}'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for a SQLite single field with no qualifier" {
|
||||||
|
let field = Field.GE "SomethingCool" 18
|
||||||
|
Expect.equal "data->>'SomethingCool'" (field.Path SQLite) "The SQLite path is incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for a SQLite single field with a qualifier" {
|
||||||
|
let field = { Field.LT "SomethingElse" 9 with Qualifier = Some "this" }
|
||||||
|
Expect.equal "this.data->>'SomethingElse'" (field.Path SQLite) "The SQLite path is incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for a SQLite nested field with no qualifier" {
|
||||||
|
let field = Field.EQ "My.Nested.Field" "howdy"
|
||||||
|
Expect.equal "data->>'My'->>'Nested'->>'Field'" (field.Path SQLite) "The SQLite path is incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for a SQLite nested field with a qualifier" {
|
||||||
|
let field = { Field.EQ "Nest.Away" "doc" with Qualifier = Some "bird" }
|
||||||
|
Expect.equal "bird.data->>'Nest'->>'Away'" (field.Path SQLite) "The SQLite path is incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Unit tests for the FieldMatch DU
|
||||||
|
let fieldMatchTests = testList "FieldMatch.ToString" [
|
||||||
|
test "succeeds for Any" {
|
||||||
|
Expect.equal (string Any) "OR" "SQL for Any is incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for All" {
|
||||||
|
Expect.equal (string All) "AND" "SQL for All is incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Unit tests for the ParameterName class
|
||||||
|
let parameterNameTests = testList "ParameterName.Derive" [
|
||||||
|
test "succeeds with existing name" {
|
||||||
|
let name = ParameterName()
|
||||||
|
Expect.equal (name.Derive(Some "@taco")) "@taco" "Name should have been @taco"
|
||||||
|
Expect.equal (name.Derive None) "@field0" "Counter should not have advanced for named field"
|
||||||
|
}
|
||||||
|
test "succeeds with non-existent name" {
|
||||||
|
let name = ParameterName()
|
||||||
|
Expect.equal (name.Derive None) "@field0" "Anonymous field name should have been returned"
|
||||||
|
Expect.equal (name.Derive None) "@field1" "Counter should have advanced from previous call"
|
||||||
|
Expect.equal (name.Derive None) "@field2" "Counter should have advanced from previous call"
|
||||||
|
Expect.equal (name.Derive None) "@field3" "Counter should have advanced from previous call"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Unit tests for the AutoId DU
|
||||||
|
let autoIdTests = testList "AutoId" [
|
||||||
|
test "GenerateGuid succeeds" {
|
||||||
|
let autoId = AutoId.GenerateGuid()
|
||||||
|
Expect.isNotNull autoId "The GUID auto-ID should not have been null"
|
||||||
|
Expect.stringHasLength autoId 32 "The GUID auto-ID should have been 32 characters long"
|
||||||
|
Expect.equal autoId (autoId.ToLowerInvariant ()) "The GUID auto-ID should have been lowercase"
|
||||||
|
}
|
||||||
|
test "GenerateRandomString succeeds" {
|
||||||
|
[ 6; 8; 12; 20; 32; 57; 64 ]
|
||||||
|
|> List.iter (fun length ->
|
||||||
|
let autoId = AutoId.GenerateRandomString length
|
||||||
|
Expect.isNotNull autoId $"Random string ({length}) should not have been null"
|
||||||
|
Expect.stringHasLength autoId length $"Random string should have been {length} characters long"
|
||||||
|
Expect.equal autoId (autoId.ToLowerInvariant ()) $"Random string ({length}) should have been lowercase")
|
||||||
|
}
|
||||||
|
testList "NeedsAutoId" [
|
||||||
|
test "succeeds when no auto ID is configured" {
|
||||||
|
Expect.isFalse (AutoId.NeedsAutoId Disabled (obj ()) "id") "Disabled auto-ID never needs an automatic ID"
|
||||||
|
}
|
||||||
|
test "fails for any when the ID property is not found" {
|
||||||
|
Expect.throwsT<System.InvalidOperationException>
|
||||||
|
(fun () -> AutoId.NeedsAutoId Number {| Key = "" |} "Id" |> ignore)
|
||||||
|
"Non-existent ID property should have thrown an exception"
|
||||||
|
}
|
||||||
|
test "succeeds for byte when the ID is zero" {
|
||||||
|
Expect.isTrue (AutoId.NeedsAutoId Number {| Id = int8 0 |} "Id") "Zero ID should have returned true"
|
||||||
|
}
|
||||||
|
test "succeeds for byte when the ID is non-zero" {
|
||||||
|
Expect.isFalse (AutoId.NeedsAutoId Number {| Id = int8 4 |} "Id") "Non-zero ID should have returned false"
|
||||||
|
}
|
||||||
|
test "succeeds for short when the ID is zero" {
|
||||||
|
Expect.isTrue (AutoId.NeedsAutoId Number {| Id = int16 0 |} "Id") "Zero ID should have returned true"
|
||||||
|
}
|
||||||
|
test "succeeds for short when the ID is non-zero" {
|
||||||
|
Expect.isFalse (AutoId.NeedsAutoId Number {| Id = int16 7 |} "Id") "Non-zero ID should have returned false"
|
||||||
|
}
|
||||||
|
test "succeeds for int when the ID is zero" {
|
||||||
|
Expect.isTrue (AutoId.NeedsAutoId Number {| Id = 0 |} "Id") "Zero ID should have returned true"
|
||||||
|
}
|
||||||
|
test "succeeds for int when the ID is non-zero" {
|
||||||
|
Expect.isFalse (AutoId.NeedsAutoId Number {| Id = 32 |} "Id") "Non-zero ID should have returned false"
|
||||||
|
}
|
||||||
|
test "succeeds for long when the ID is zero" {
|
||||||
|
Expect.isTrue (AutoId.NeedsAutoId Number {| Id = 0L |} "Id") "Zero ID should have returned true"
|
||||||
|
}
|
||||||
|
test "succeeds for long when the ID is non-zero" {
|
||||||
|
Expect.isFalse (AutoId.NeedsAutoId Number {| Id = 80L |} "Id") "Non-zero ID should have returned false"
|
||||||
|
}
|
||||||
|
test "fails for number when the ID is not a number" {
|
||||||
|
Expect.throwsT<System.InvalidOperationException>
|
||||||
|
(fun () -> AutoId.NeedsAutoId Number {| Id = "" |} "Id" |> ignore)
|
||||||
|
"Numeric ID against a string should have thrown an exception"
|
||||||
|
}
|
||||||
|
test "succeeds for GUID when the ID is blank" {
|
||||||
|
Expect.isTrue (AutoId.NeedsAutoId Guid {| Id = "" |} "Id") "Blank ID should have returned true"
|
||||||
|
}
|
||||||
|
test "succeeds for GUID when the ID is filled" {
|
||||||
|
Expect.isFalse (AutoId.NeedsAutoId Guid {| Id = "abc" |} "Id") "Filled ID should have returned false"
|
||||||
|
}
|
||||||
|
test "fails for GUID when the ID is not a string" {
|
||||||
|
Expect.throwsT<System.InvalidOperationException>
|
||||||
|
(fun () -> AutoId.NeedsAutoId Guid {| Id = 8 |} "Id" |> ignore)
|
||||||
|
"String ID against a number should have thrown an exception"
|
||||||
|
}
|
||||||
|
test "succeeds for RandomString when the ID is blank" {
|
||||||
|
Expect.isTrue (AutoId.NeedsAutoId RandomString {| Id = "" |} "Id") "Blank ID should have returned true"
|
||||||
|
}
|
||||||
|
test "succeeds for RandomString when the ID is filled" {
|
||||||
|
Expect.isFalse (AutoId.NeedsAutoId RandomString {| Id = "x" |} "Id") "Filled ID should have returned false"
|
||||||
|
}
|
||||||
|
test "fails for RandomString when the ID is not a string" {
|
||||||
|
Expect.throwsT<System.InvalidOperationException>
|
||||||
|
(fun () -> AutoId.NeedsAutoId RandomString {| Id = 33 |} "Id" |> ignore)
|
||||||
|
"String ID against a number should have thrown an exception"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Unit tests for the Configuration module
|
||||||
|
let configurationTests = testList "Configuration" [
|
||||||
|
test "useSerializer succeeds" {
|
||||||
|
try
|
||||||
|
Configuration.useSerializer
|
||||||
|
{ new IDocumentSerializer with
|
||||||
|
member _.Serialize<'T>(it: 'T) : string = """{"Overridden":true}"""
|
||||||
|
member _.Deserialize<'T>(it: string) : 'T = Unchecked.defaultof<'T>
|
||||||
|
}
|
||||||
|
|
||||||
|
let serialized = Configuration.serializer().Serialize {| Foo = "howdy"; Bar = "bye" |}
|
||||||
|
Expect.equal serialized """{"Overridden":true}""" "Specified serializer was not used"
|
||||||
|
|
||||||
|
let deserialized = Configuration.serializer().Deserialize<obj> """{"Something":"here"}"""
|
||||||
|
Expect.isNull deserialized "Specified serializer should have returned null"
|
||||||
|
finally
|
||||||
|
Configuration.useSerializer DocumentSerializer.``default``
|
||||||
|
}
|
||||||
|
test "serializer returns configured serializer" {
|
||||||
|
Expect.isTrue (obj.ReferenceEquals(DocumentSerializer.``default``, Configuration.serializer ()))
|
||||||
|
"Serializer should have been the same"
|
||||||
|
}
|
||||||
|
test "useIdField / idField succeeds" {
|
||||||
|
try
|
||||||
|
Expect.equal (Configuration.idField ()) "Id" "The default configured ID field was incorrect"
|
||||||
|
Configuration.useIdField "id"
|
||||||
|
Expect.equal (Configuration.idField ()) "id" "useIdField did not set the ID field"
|
||||||
|
finally
|
||||||
|
Configuration.useIdField "Id"
|
||||||
|
}
|
||||||
|
test "useAutoIdStrategy / autoIdStrategy succeeds" {
|
||||||
|
try
|
||||||
|
Expect.equal (Configuration.autoIdStrategy ()) Disabled "The default auto-ID strategy was incorrect"
|
||||||
|
Configuration.useAutoIdStrategy Guid
|
||||||
|
Expect.equal (Configuration.autoIdStrategy ()) Guid "The auto-ID strategy was not set correctly"
|
||||||
|
finally
|
||||||
|
Configuration.useAutoIdStrategy Disabled
|
||||||
|
}
|
||||||
|
test "useIdStringLength / idStringLength succeeds" {
|
||||||
|
try
|
||||||
|
Expect.equal (Configuration.idStringLength ()) 16 "The default ID string length was incorrect"
|
||||||
|
Configuration.useIdStringLength 33
|
||||||
|
Expect.equal (Configuration.idStringLength ()) 33 "The ID string length was not set correctly"
|
||||||
|
finally
|
||||||
|
Configuration.useIdStringLength 16
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Unit tests for the Query module
|
||||||
|
let queryTests = testList "Query" [
|
||||||
|
test "statementWhere succeeds" {
|
||||||
|
Expect.equal (Query.statementWhere "x" "y") "x WHERE y" "Statements not combined correctly"
|
||||||
}
|
}
|
||||||
testList "Definition" [
|
testList "Definition" [
|
||||||
test "ensureTableFor succeeds" {
|
test "ensureTableFor succeeds" {
|
||||||
|
@ -106,25 +343,40 @@ let all =
|
||||||
testList "ensureKey" [
|
testList "ensureKey" [
|
||||||
test "succeeds when a schema is present" {
|
test "succeeds when a schema is present" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.Definition.ensureKey "test.table")
|
(Query.Definition.ensureKey "test.table" PostgreSQL)
|
||||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data ->> 'Id'))"
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'Id'))"
|
||||||
"CREATE INDEX for key statement with schema not constructed correctly"
|
"CREATE INDEX for key statement with schema not constructed correctly"
|
||||||
}
|
}
|
||||||
test "succeeds when a schema is not present" {
|
test "succeeds when a schema is not present" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.Definition.ensureKey "table")
|
(Query.Definition.ensureKey "table" SQLite)
|
||||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data ->> 'Id'))"
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data->>'Id'))"
|
||||||
"CREATE INDEX for key statement without schema not constructed correctly"
|
"CREATE INDEX for key statement without schema not constructed correctly"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
test "ensureIndexOn succeeds for multiple fields and directions" {
|
testList "ensureIndexOn" [
|
||||||
|
test "succeeds for multiple fields and directions" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.Definition.ensureIndexOn "test.table" "gibberish" [ "taco"; "guac DESC"; "salsa ASC" ])
|
(Query.Definition.ensureIndexOn
|
||||||
|
"test.table" "gibberish" [ "taco"; "guac DESC"; "salsa ASC" ] PostgreSQL)
|
||||||
([ "CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table "
|
([ "CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table "
|
||||||
"((data ->> 'taco'), (data ->> 'guac') DESC, (data ->> 'salsa') ASC)" ]
|
"((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)" ]
|
||||||
|> String.concat "")
|
|> String.concat "")
|
||||||
"CREATE INDEX for multiple field statement incorrect"
|
"CREATE INDEX for multiple field statement incorrect"
|
||||||
}
|
}
|
||||||
|
test "succeeds for nested PostgreSQL field" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.Definition.ensureIndexOn tbl "nest" [ "a.b.c" ] PostgreSQL)
|
||||||
|
$"CREATE INDEX IF NOT EXISTS idx_{tbl}_nest ON {tbl} ((data#>>'{{a,b,c}}'))"
|
||||||
|
"CREATE INDEX for nested PostgreSQL field incorrect"
|
||||||
|
}
|
||||||
|
test "succeeds for nested SQLite field" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.Definition.ensureIndexOn tbl "nest" [ "a.b.c" ] SQLite)
|
||||||
|
$"CREATE INDEX IF NOT EXISTS idx_{tbl}_nest ON {tbl} ((data->>'a'->>'b'->>'c'))"
|
||||||
|
"CREATE INDEX for nested SQLite field incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
]
|
]
|
||||||
test "insert succeeds" {
|
test "insert succeeds" {
|
||||||
Expect.equal (Query.insert tbl) $"INSERT INTO {tbl} VALUES (@data)" "INSERT statement not correct"
|
Expect.equal (Query.insert tbl) $"INSERT INTO {tbl} VALUES (@data)" "INSERT statement not correct"
|
||||||
|
@ -135,6 +387,79 @@ let all =
|
||||||
$"INSERT INTO {tbl} VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data"
|
$"INSERT INTO {tbl} VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data"
|
||||||
"INSERT ON CONFLICT UPDATE statement not correct"
|
"INSERT ON CONFLICT UPDATE statement not correct"
|
||||||
}
|
}
|
||||||
|
test "count succeeds" {
|
||||||
|
Expect.equal (Query.count tbl) $"SELECT COUNT(*) AS it FROM {tbl}" "Count query not correct"
|
||||||
|
}
|
||||||
|
test "exists succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.exists tbl "turkey")
|
||||||
|
$"SELECT EXISTS (SELECT 1 FROM {tbl} WHERE turkey) AS it"
|
||||||
|
"Exists query not correct"
|
||||||
|
}
|
||||||
|
test "find succeeds" {
|
||||||
|
Expect.equal (Query.find tbl) $"SELECT data FROM {tbl}" "Find query not correct"
|
||||||
|
}
|
||||||
|
test "update succeeds" {
|
||||||
|
Expect.equal (Query.update tbl) $"UPDATE {tbl} SET data = @data" "Update query not correct"
|
||||||
|
}
|
||||||
|
test "delete succeeds" {
|
||||||
|
Expect.equal (Query.delete tbl) $"DELETE FROM {tbl}" "Delete query not correct"
|
||||||
|
}
|
||||||
|
testList "orderBy" [
|
||||||
|
test "succeeds for no fields" {
|
||||||
|
Expect.equal (Query.orderBy [] PostgreSQL) "" "Order By should have been blank (PostgreSQL)"
|
||||||
|
Expect.equal (Query.orderBy [] SQLite) "" "Order By should have been blank (SQLite)"
|
||||||
|
}
|
||||||
|
test "succeeds for PostgreSQL with one field and no direction" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.orderBy [ Field.Named "TestField" ] PostgreSQL)
|
||||||
|
" ORDER BY data->>'TestField'"
|
||||||
|
"Order By not constructed correctly"
|
||||||
|
}
|
||||||
|
test "succeeds for SQLite with one field and no direction" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.orderBy [ Field.Named "TestField" ] SQLite)
|
||||||
|
" ORDER BY data->>'TestField'"
|
||||||
|
"Order By not constructed correctly"
|
||||||
|
}
|
||||||
|
test "succeeds for PostgreSQL with multiple fields and direction" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.orderBy
|
||||||
|
[ Field.Named "Nested.Test.Field DESC"; Field.Named "AnotherField"; Field.Named "It DESC" ]
|
||||||
|
PostgreSQL)
|
||||||
|
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC"
|
||||||
|
"Order By not constructed correctly"
|
||||||
|
}
|
||||||
|
test "succeeds for SQLite with multiple fields and direction" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.orderBy
|
||||||
|
[ Field.Named "Nested.Test.Field DESC"; Field.Named "AnotherField"; Field.Named "It DESC" ]
|
||||||
|
SQLite)
|
||||||
|
" ORDER BY data->>'Nested'->>'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC"
|
||||||
|
"Order By not constructed correctly"
|
||||||
|
}
|
||||||
|
test "succeeds for PostgreSQL numeric fields" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.orderBy [ Field.Named "n:Test" ] PostgreSQL)
|
||||||
|
" ORDER BY (data->>'Test')::numeric"
|
||||||
|
"Order By not constructed correctly for numeric field"
|
||||||
|
}
|
||||||
|
test "succeeds for SQLite numeric fields" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.orderBy [ Field.Named "n:Test" ] SQLite)
|
||||||
|
" ORDER BY data->>'Test'"
|
||||||
|
"Order By not constructed correctly for numeric field"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/// Tests which do not hit the database
|
||||||
|
let all = testList "Common" [
|
||||||
|
opTests
|
||||||
|
fieldTests
|
||||||
|
fieldMatchTests
|
||||||
|
parameterNameTests
|
||||||
|
autoIdTests
|
||||||
|
queryTests
|
||||||
|
testSequenced configurationTests
|
||||||
|
]
|
||||||
|
|
|
@ -25,7 +25,7 @@ let integrationTests =
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
let! docs = conn.customList (Query.selectFromTable PostgresDb.TableName) [] fromData<JsonDocument>
|
let! docs = conn.customList (Query.find PostgresDb.TableName) [] fromData<JsonDocument>
|
||||||
Expect.equal (List.length docs) 5 "There should have been 5 documents returned"
|
Expect.equal (List.length docs) 5 "There should have been 5 documents returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when data is not found" {
|
testTask "succeeds when data is not found" {
|
||||||
|
@ -209,12 +209,12 @@ let integrationTests =
|
||||||
let! theCount = conn.countAll PostgresDb.TableName
|
let! theCount = conn.countAll PostgresDb.TableName
|
||||||
Expect.equal theCount 5 "There should have been 5 matching documents"
|
Expect.equal theCount 5 "There should have been 5 matching documents"
|
||||||
}
|
}
|
||||||
testTask "countByField succeeds" {
|
testTask "countByFields succeeds" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
let! theCount = conn.countByField PostgresDb.TableName (Field.EQ "Value" "purple")
|
let! theCount = conn.countByFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ]
|
||||||
Expect.equal theCount 2 "There should have been 2 matching documents"
|
Expect.equal theCount 2 "There should have been 2 matching documents"
|
||||||
}
|
}
|
||||||
testTask "countByContains succeeds" {
|
testTask "countByContains succeeds" {
|
||||||
|
@ -251,13 +251,13 @@ let integrationTests =
|
||||||
Expect.isFalse exists "There should not have been an existing document"
|
Expect.isFalse exists "There should not have been an existing document"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "existsByField" [
|
testList "existsByFields" [
|
||||||
testTask "succeeds when documents exist" {
|
testTask "succeeds when documents exist" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
let! exists = conn.existsByField PostgresDb.TableName (Field.EX "Sub")
|
let! exists = conn.existsByFields PostgresDb.TableName Any [ Field.EX "Sub" ]
|
||||||
Expect.isTrue exists "There should have been existing documents"
|
Expect.isTrue exists "There should have been existing documents"
|
||||||
}
|
}
|
||||||
testTask "succeeds when documents do not exist" {
|
testTask "succeeds when documents do not exist" {
|
||||||
|
@ -265,7 +265,7 @@ let integrationTests =
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
let! exists = conn.existsByField PostgresDb.TableName (Field.EQ "NumValue" "six")
|
let! exists = conn.existsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "six" ]
|
||||||
Expect.isFalse exists "There should not have been existing documents"
|
Expect.isFalse exists "There should not have been existing documents"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -315,12 +315,10 @@ let integrationTests =
|
||||||
do! conn.insert PostgresDb.TableName { Foo = "five"; Bar = "six" }
|
do! conn.insert PostgresDb.TableName { Foo = "five"; Bar = "six" }
|
||||||
|
|
||||||
let! results = conn.findAll<SubDocument> PostgresDb.TableName
|
let! results = conn.findAll<SubDocument> PostgresDb.TableName
|
||||||
let expected = [
|
Expect.equal
|
||||||
{ Foo = "one"; Bar = "two" }
|
results
|
||||||
{ Foo = "three"; Bar = "four" }
|
[ { Foo = "one"; Bar = "two" }; { Foo = "three"; Bar = "four" }; { Foo = "five"; Bar = "six" } ]
|
||||||
{ Foo = "five"; Bar = "six" }
|
"There should have been 3 documents returned"
|
||||||
]
|
|
||||||
Expect.equal results expected "There should have been 3 documents returned"
|
|
||||||
}
|
}
|
||||||
testTask "succeeds when there is no data" {
|
testTask "succeeds when there is no data" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
|
@ -329,6 +327,44 @@ let integrationTests =
|
||||||
Expect.equal results [] "There should have been no documents returned"
|
Expect.equal results [] "There should have been no documents returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
testList "findAllOrdered" [
|
||||||
|
testTask "succeeds when ordering numerically" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! results = conn.findAllOrdered<JsonDocument> PostgresDb.TableName [ Field.Named "n:NumValue" ]
|
||||||
|
Expect.hasLength results 5 "There should have been 5 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(results |> List.map _.Id |> String.concat "|")
|
||||||
|
"one|three|two|four|five"
|
||||||
|
"The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when ordering numerically descending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! results = conn.findAllOrdered<JsonDocument> PostgresDb.TableName [ Field.Named "n:NumValue DESC" ]
|
||||||
|
Expect.hasLength results 5 "There should have been 5 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(results |> List.map _.Id |> String.concat "|")
|
||||||
|
"five|four|two|three|one"
|
||||||
|
"The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when ordering alphabetically" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! results = conn.findAllOrdered<JsonDocument> PostgresDb.TableName [ Field.Named "Id DESC" ]
|
||||||
|
Expect.hasLength results 5 "There should have been 5 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(results |> List.map _.Id |> String.concat "|")
|
||||||
|
"two|three|one|four|five"
|
||||||
|
"The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
]
|
||||||
testList "findById" [
|
testList "findById" [
|
||||||
testTask "succeeds when a document is found" {
|
testTask "succeeds when a document is found" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
|
@ -348,13 +384,13 @@ let integrationTests =
|
||||||
Expect.isNone doc "There should not have been a document returned"
|
Expect.isNone doc "There should not have been a document returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "findByField" [
|
testList "findByFields" [
|
||||||
testTask "succeeds when documents are found" {
|
testTask "succeeds when documents are found" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
let! docs = conn.findByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another")
|
let! docs = conn.findByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "another" ]
|
||||||
Expect.equal (List.length docs) 1 "There should have been one document returned"
|
Expect.equal (List.length docs) 1 "There should have been one document returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when documents are not found" {
|
testTask "succeeds when documents are not found" {
|
||||||
|
@ -362,10 +398,36 @@ let integrationTests =
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
let! docs = conn.findByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "mauve")
|
let! docs = conn.findByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "mauve" ]
|
||||||
Expect.isEmpty docs "There should have been no documents returned"
|
Expect.isEmpty docs "There should have been no documents returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
testList "findByFieldsOrdered" [
|
||||||
|
testTask "succeeds when sorting ascending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! docs =
|
||||||
|
conn.findByFieldsOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName All [ Field.EQ "Value" "purple" ] [ Field.Named "Id" ]
|
||||||
|
Expect.hasLength docs 2 "There should have been two documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(docs |> List.map _.Id |> String.concat "|") "five|four" "Documents not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when sorting descending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! docs =
|
||||||
|
conn.findByFieldsOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName All [ Field.EQ "Value" "purple" ] [ Field.Named "Id DESC" ]
|
||||||
|
Expect.hasLength docs 2 "There should have been two documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(docs |> List.map _.Id |> String.concat "|") "four|five" "Documents not ordered correctly"
|
||||||
|
}
|
||||||
|
]
|
||||||
testList "findByContains" [
|
testList "findByContains" [
|
||||||
testTask "succeeds when documents are found" {
|
testTask "succeeds when documents are found" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
|
@ -384,6 +446,33 @@ let integrationTests =
|
||||||
Expect.isEmpty docs "There should have been no documents returned"
|
Expect.isEmpty docs "There should have been no documents returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
testList "findByContainsOrdered" [
|
||||||
|
// Id = two, Sub.Bar = blue; Id = four, Sub.Bar = red
|
||||||
|
testTask "succeeds when sorting ascending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! docs =
|
||||||
|
conn.findByContainsOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Sub.Bar" ]
|
||||||
|
Expect.hasLength docs 2 "There should have been two documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(docs |> List.map _.Id |> String.concat "|") "two|four" "Documents not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when sorting descending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! docs =
|
||||||
|
conn.findByContainsOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Sub.Bar DESC" ]
|
||||||
|
Expect.hasLength docs 2 "There should have been two documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(docs |> List.map _.Id |> String.concat "|") "four|two" "Documents not ordered correctly"
|
||||||
|
}
|
||||||
|
]
|
||||||
testList "findByJsonPath" [
|
testList "findByJsonPath" [
|
||||||
testTask "succeeds when documents are found" {
|
testTask "succeeds when documents are found" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
|
@ -402,13 +491,40 @@ let integrationTests =
|
||||||
Expect.isEmpty docs "There should have been no documents returned"
|
Expect.isEmpty docs "There should have been no documents returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "findFirstByField" [
|
testList "findByJsonPathOrdered" [
|
||||||
|
// Id = one, NumValue = 0; Id = two, NumValue = 10; Id = three, NumValue = 4
|
||||||
|
testTask "succeeds when sorting ascending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! docs =
|
||||||
|
conn.findByJsonPathOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName "$.NumValue ? (@ < 15)" [ Field.Named "n:NumValue" ]
|
||||||
|
Expect.hasLength docs 3 "There should have been 3 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(docs |> List.map _.Id |> String.concat "|") "one|three|two" "Documents not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when sorting descending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! docs =
|
||||||
|
conn.findByJsonPathOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName "$.NumValue ? (@ < 15)" [ Field.Named "n:NumValue DESC" ]
|
||||||
|
Expect.hasLength docs 3 "There should have been 3 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(docs |> List.map _.Id |> String.concat "|") "two|three|one" "Documents not ordered correctly"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "findFirstByFields" [
|
||||||
testTask "succeeds when a document is found" {
|
testTask "succeeds when a document is found" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another")
|
let! doc = conn.findFirstByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "another" ]
|
||||||
Expect.isSome doc "There should have been a document returned"
|
Expect.isSome doc "There should have been a document returned"
|
||||||
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
|
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
|
||||||
}
|
}
|
||||||
|
@ -417,7 +533,7 @@ let integrationTests =
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "purple")
|
let! doc = conn.findFirstByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "purple" ]
|
||||||
Expect.isSome doc "There should have been a document returned"
|
Expect.isSome doc "There should have been a document returned"
|
||||||
Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned"
|
Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned"
|
||||||
}
|
}
|
||||||
|
@ -426,10 +542,34 @@ let integrationTests =
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "absent")
|
let! doc = conn.findFirstByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "absent" ]
|
||||||
Expect.isNone doc "There should not have been a document returned"
|
Expect.isNone doc "There should not have been a document returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
testList "findFirstByFieldsOrdered" [
|
||||||
|
testTask "succeeds when sorting ascending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! doc =
|
||||||
|
conn.findFirstByFieldsOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] [ Field.Named "Id" ]
|
||||||
|
Expect.isSome doc "There should have been a document returned"
|
||||||
|
Expect.equal "five" doc.Value.Id "An incorrect document was returned"
|
||||||
|
}
|
||||||
|
testTask "succeeds when sorting descending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! doc =
|
||||||
|
conn.findFirstByFieldsOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] [ Field.Named "Id DESC" ]
|
||||||
|
Expect.isSome doc "There should have been a document returned"
|
||||||
|
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
|
||||||
|
}
|
||||||
|
]
|
||||||
testList "findFirstByContains" [
|
testList "findFirstByContains" [
|
||||||
testTask "succeeds when a document is found" {
|
testTask "succeeds when a document is found" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
|
@ -458,6 +598,30 @@ let integrationTests =
|
||||||
Expect.isNone doc "There should not have been a document returned"
|
Expect.isNone doc "There should not have been a document returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
testList "findFirstByContainsOrdered" [
|
||||||
|
testTask "succeeds when sorting ascending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! doc =
|
||||||
|
conn.findFirstByContainsOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Value" ]
|
||||||
|
Expect.isSome doc "There should have been a document returned"
|
||||||
|
Expect.equal "two" doc.Value.Id "An incorrect document was returned"
|
||||||
|
}
|
||||||
|
testTask "succeeds when sorting descending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! doc =
|
||||||
|
conn.findFirstByContainsOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Value DESC" ]
|
||||||
|
Expect.isSome doc "There should have been a document returned"
|
||||||
|
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
|
||||||
|
}
|
||||||
|
]
|
||||||
testList "findFirstByJsonPath" [
|
testList "findFirstByJsonPath" [
|
||||||
testTask "succeeds when a document is found" {
|
testTask "succeeds when a document is found" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
|
@ -486,6 +650,30 @@ let integrationTests =
|
||||||
Expect.isNone doc "There should not have been a document returned"
|
Expect.isNone doc "There should not have been a document returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
testList "findFirstByJsonPathOrdered" [
|
||||||
|
testTask "succeeds when sorting ascending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! doc =
|
||||||
|
conn.findFirstByJsonPathOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName """$.Sub.Foo ? (@ == "green")""" [ Field.Named "Sub.Bar" ]
|
||||||
|
Expect.isSome doc "There should have been a document returned"
|
||||||
|
Expect.equal "two" doc.Value.Id "An incorrect document was returned"
|
||||||
|
}
|
||||||
|
testTask "succeeds when sorting descending" {
|
||||||
|
use db = PostgresDb.BuildDb()
|
||||||
|
use conn = mkConn db
|
||||||
|
do! loadDocs conn
|
||||||
|
|
||||||
|
let! doc =
|
||||||
|
conn.findFirstByJsonPathOrdered<JsonDocument>
|
||||||
|
PostgresDb.TableName """$.Sub.Foo ? (@ == "green")""" [ Field.Named "Sub.Bar DESC" ]
|
||||||
|
Expect.isSome doc "There should have been a document returned"
|
||||||
|
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
|
||||||
|
}
|
||||||
|
]
|
||||||
testList "updateById" [
|
testList "updateById" [
|
||||||
testTask "succeeds when a document is updated" {
|
testTask "succeeds when a document is updated" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
|
@ -556,14 +744,14 @@ let integrationTests =
|
||||||
do! conn.patchById PostgresDb.TableName "test" {| Foo = "green" |}
|
do! conn.patchById PostgresDb.TableName "test" {| Foo = "green" |}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "patchByField" [
|
testList "patchByFields" [
|
||||||
testTask "succeeds when a document is updated" {
|
testTask "succeeds when a document is updated" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.patchByField PostgresDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
|
do! conn.patchByFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
|
||||||
let! after = conn.countByField PostgresDb.TableName (Field.EQ "NumValue" "77")
|
let! after = conn.countByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "77" ]
|
||||||
Expect.equal after 2 "There should have been 2 documents returned"
|
Expect.equal after 2 "There should have been 2 documents returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when no document is updated" {
|
testTask "succeeds when no document is updated" {
|
||||||
|
@ -573,7 +761,7 @@ let integrationTests =
|
||||||
Expect.equal before 0 "There should have been no documents returned"
|
Expect.equal before 0 "There should have been no documents returned"
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! conn.patchByField PostgresDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
|
do! conn.patchByFields PostgresDb.TableName Any [ Field.EQ "Value" "burgundy" ] {| Foo = "green" |}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "patchByContains" [
|
testList "patchByContains" [
|
||||||
|
@ -623,9 +811,9 @@ let integrationTests =
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub"; "Value" ]
|
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub"; "Value" ]
|
||||||
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
|
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
|
||||||
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
||||||
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
|
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
|
||||||
Expect.equal noValue 1 "There should be 1 document without Value fields"
|
Expect.equal noValue 1 "There should be 1 document without Value fields"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a single field is removed" {
|
testTask "succeeds when a single field is removed" {
|
||||||
|
@ -634,9 +822,9 @@ let integrationTests =
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub" ]
|
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub" ]
|
||||||
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
|
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
|
||||||
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
||||||
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
|
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
|
||||||
Expect.equal noValue 0 "There should be no documents without Value fields"
|
Expect.equal noValue 0 "There should be no documents without Value fields"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a field is not removed" {
|
testTask "succeeds when a field is not removed" {
|
||||||
|
@ -655,16 +843,16 @@ let integrationTests =
|
||||||
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Value" ]
|
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Value" ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "removeFieldsByField" [
|
testList "removeFieldsByFields" [
|
||||||
testTask "succeeds when multiple fields are removed" {
|
testTask "succeeds when multiple fields are removed" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub"; "Value" ]
|
do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Sub"; "Value" ]
|
||||||
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
|
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
|
||||||
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
||||||
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
|
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
|
||||||
Expect.equal noValue 1 "There should be 1 document without Value fields"
|
Expect.equal noValue 1 "There should be 1 document without Value fields"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a single field is removed" {
|
testTask "succeeds when a single field is removed" {
|
||||||
|
@ -672,10 +860,10 @@ let integrationTests =
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub" ]
|
do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Sub" ]
|
||||||
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
|
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
|
||||||
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
||||||
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
|
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
|
||||||
Expect.equal noValue 0 "There should be no documents without Value fields"
|
Expect.equal noValue 0 "There should be no documents without Value fields"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a field is not removed" {
|
testTask "succeeds when a field is not removed" {
|
||||||
|
@ -684,14 +872,14 @@ let integrationTests =
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Nothing" ]
|
do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Nothing" ]
|
||||||
}
|
}
|
||||||
testTask "succeeds when no document is matched" {
|
testTask "succeeds when no document is matched" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! conn.removeFieldsByField PostgresDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
|
do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.NE "Abracadabra" "apple" ] [ "Value" ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "removeFieldsByContains" [
|
testList "removeFieldsByContains" [
|
||||||
|
@ -701,9 +889,9 @@ let integrationTests =
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ]
|
do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ]
|
||||||
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
|
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
|
||||||
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
||||||
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
|
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
|
||||||
Expect.equal noValue 1 "There should be 1 document without Value fields"
|
Expect.equal noValue 1 "There should be 1 document without Value fields"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a single field is removed" {
|
testTask "succeeds when a single field is removed" {
|
||||||
|
@ -712,9 +900,9 @@ let integrationTests =
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ]
|
do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ]
|
||||||
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
|
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
|
||||||
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
||||||
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
|
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
|
||||||
Expect.equal noValue 0 "There should be no documents without Value fields"
|
Expect.equal noValue 0 "There should be no documents without Value fields"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a field is not removed" {
|
testTask "succeeds when a field is not removed" {
|
||||||
|
@ -740,9 +928,9 @@ let integrationTests =
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ]
|
do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ]
|
||||||
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
|
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
|
||||||
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
||||||
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
|
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
|
||||||
Expect.equal noValue 1 "There should be 1 document without Value fields"
|
Expect.equal noValue 1 "There should be 1 document without Value fields"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a single field is removed" {
|
testTask "succeeds when a single field is removed" {
|
||||||
|
@ -751,9 +939,9 @@ let integrationTests =
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ]
|
do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ]
|
||||||
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
|
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
|
||||||
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
|
||||||
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
|
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
|
||||||
Expect.equal noValue 0 "There should be no documents without Value fields"
|
Expect.equal noValue 0 "There should be no documents without Value fields"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a field is not removed" {
|
testTask "succeeds when a field is not removed" {
|
||||||
|
@ -792,13 +980,13 @@ let integrationTests =
|
||||||
Expect.equal remaining 5 "There should have been 5 documents remaining"
|
Expect.equal remaining 5 "There should have been 5 documents remaining"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "deleteByField" [
|
testList "deleteByFields" [
|
||||||
testTask "succeeds when documents are deleted" {
|
testTask "succeeds when documents are deleted" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.deleteByField PostgresDb.TableName (Field.EQ "Value" "purple")
|
do! conn.deleteByFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ]
|
||||||
let! remaining = conn.countAll PostgresDb.TableName
|
let! remaining = conn.countAll PostgresDb.TableName
|
||||||
Expect.equal remaining 3 "There should have been 3 documents remaining"
|
Expect.equal remaining 3 "There should have been 3 documents remaining"
|
||||||
}
|
}
|
||||||
|
@ -807,7 +995,7 @@ let integrationTests =
|
||||||
use conn = mkConn db
|
use conn = mkConn db
|
||||||
do! loadDocs conn
|
do! loadDocs conn
|
||||||
|
|
||||||
do! conn.deleteByField PostgresDb.TableName (Field.EQ "Value" "crimson")
|
do! conn.deleteByFields PostgresDb.TableName Any [ Field.EQ "Value" "crimson" ]
|
||||||
let! remaining = conn.countAll PostgresDb.TableName
|
let! remaining = conn.countAll PostgresDb.TableName
|
||||||
Expect.equal remaining 5 "There should have been 5 documents remaining"
|
Expect.equal remaining 5 "There should have been 5 documents remaining"
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -113,12 +113,12 @@ let integrationTests =
|
||||||
let! theCount = conn.countAll SqliteDb.TableName
|
let! theCount = conn.countAll SqliteDb.TableName
|
||||||
Expect.equal theCount 5L "There should have been 5 matching documents"
|
Expect.equal theCount 5L "There should have been 5 matching documents"
|
||||||
}
|
}
|
||||||
testTask "countByField succeeds" {
|
testTask "countByFields succeeds" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! theCount = conn.countByField SqliteDb.TableName (Field.EQ "Value" "purple")
|
let! theCount = conn.countByFields SqliteDb.TableName Any [ Field.EQ "Value" "purple" ]
|
||||||
Expect.equal theCount 2L "There should have been 2 matching documents"
|
Expect.equal theCount 2L "There should have been 2 matching documents"
|
||||||
}
|
}
|
||||||
testList "existsById" [
|
testList "existsById" [
|
||||||
|
@ -139,13 +139,13 @@ let integrationTests =
|
||||||
Expect.isFalse exists "There should not have been an existing document"
|
Expect.isFalse exists "There should not have been an existing document"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "existsByField" [
|
testList "existsByFields" [
|
||||||
testTask "succeeds when documents exist" {
|
testTask "succeeds when documents exist" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! exists = conn.existsByField SqliteDb.TableName (Field.EQ "NumValue" 10)
|
let! exists = conn.existsByFields SqliteDb.TableName Any [ Field.EQ "NumValue" 10 ]
|
||||||
Expect.isTrue exists "There should have been existing documents"
|
Expect.isTrue exists "There should have been existing documents"
|
||||||
}
|
}
|
||||||
testTask "succeeds when no matching documents exist" {
|
testTask "succeeds when no matching documents exist" {
|
||||||
|
@ -153,7 +153,7 @@ let integrationTests =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! exists = conn.existsByField SqliteDb.TableName (Field.EQ "Nothing" "none")
|
let! exists = conn.existsByFields SqliteDb.TableName Any [ Field.EQ "Nothing" "none" ]
|
||||||
Expect.isFalse exists "There should not have been any existing documents"
|
Expect.isFalse exists "There should not have been any existing documents"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -181,6 +181,44 @@ let integrationTests =
|
||||||
Expect.equal results [] "There should have been no documents returned"
|
Expect.equal results [] "There should have been no documents returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
testList "findAllOrdered" [
|
||||||
|
testTask "succeeds when ordering numerically" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! results = conn.findAllOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "n:NumValue" ]
|
||||||
|
Expect.hasLength results 5 "There should have been 5 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(results |> List.map _.Id |> String.concat "|")
|
||||||
|
"one|three|two|four|five"
|
||||||
|
"The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when ordering numerically descending" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! results = conn.findAllOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "n:NumValue DESC" ]
|
||||||
|
Expect.hasLength results 5 "There should have been 5 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(results |> List.map _.Id |> String.concat "|")
|
||||||
|
"five|four|two|three|one"
|
||||||
|
"The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when ordering alphabetically" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! results = conn.findAllOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "Id DESC" ]
|
||||||
|
Expect.hasLength results 5 "There should have been 5 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(results |> List.map _.Id |> String.concat "|")
|
||||||
|
"two|three|one|four|five"
|
||||||
|
"The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
]
|
||||||
testList "findById" [
|
testList "findById" [
|
||||||
testTask "succeeds when a document is found" {
|
testTask "succeeds when a document is found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
@ -188,7 +226,7 @@ let integrationTests =
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! doc = conn.findById<string, JsonDocument> SqliteDb.TableName "two"
|
let! doc = conn.findById<string, JsonDocument> SqliteDb.TableName "two"
|
||||||
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
|
Expect.isSome doc "There should have been a document returned"
|
||||||
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
|
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a document is not found" {
|
testTask "succeeds when a document is not found" {
|
||||||
|
@ -197,35 +235,59 @@ let integrationTests =
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! doc = conn.findById<string, JsonDocument> SqliteDb.TableName "three hundred eighty-seven"
|
let! doc = conn.findById<string, JsonDocument> SqliteDb.TableName "three hundred eighty-seven"
|
||||||
Expect.isFalse (Option.isSome doc) "There should not have been a document returned"
|
Expect.isNone doc "There should not have been a document returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "findByField" [
|
testList "findByFields" [
|
||||||
testTask "succeeds when documents are found" {
|
testTask "succeeds when documents are found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! docs = conn.findByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green")
|
let! docs = conn.findByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ]
|
||||||
Expect.equal (List.length docs) 2 "There should have been two documents returned"
|
Expect.hasLength docs 2 "There should have been two documents returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when documents are not found" {
|
testTask "succeeds when documents are not found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! docs = conn.findByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "mauve")
|
let! docs = conn.findByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "mauve" ]
|
||||||
Expect.isTrue (List.isEmpty docs) "There should have been no documents returned"
|
Expect.isEmpty docs "There should have been no documents returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "findFirstByField" [
|
testList "findByFieldsOrdered" [
|
||||||
|
testTask "succeeds when sorting ascending" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! docs =
|
||||||
|
conn.findByFieldsOrdered<JsonDocument>
|
||||||
|
SqliteDb.TableName Any [ Field.GT "NumValue" 15 ] [ Field.Named "Id" ]
|
||||||
|
Expect.equal
|
||||||
|
(docs |> List.map _.Id |> String.concat "|") "five|four" "The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when sorting descending" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! docs =
|
||||||
|
conn.findByFieldsOrdered<JsonDocument>
|
||||||
|
SqliteDb.TableName Any [ Field.GT "NumValue" 15 ] [ Field.Named "Id DESC" ]
|
||||||
|
Expect.equal
|
||||||
|
(docs |> List.map _.Id |> String.concat "|") "four|five" "The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "findFirstByFields" [
|
||||||
testTask "succeeds when a document is found" {
|
testTask "succeeds when a document is found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "another")
|
let! doc = conn.findFirstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "another" ]
|
||||||
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
|
Expect.isSome doc "There should have been a document returned"
|
||||||
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
|
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when multiple documents are found" {
|
testTask "succeeds when multiple documents are found" {
|
||||||
|
@ -233,8 +295,8 @@ let integrationTests =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green")
|
let! doc = conn.findFirstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ]
|
||||||
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
|
Expect.isSome doc "There should have been a document returned"
|
||||||
Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned"
|
Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a document is not found" {
|
testTask "succeeds when a document is not found" {
|
||||||
|
@ -242,8 +304,32 @@ let integrationTests =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "absent")
|
let! doc = conn.findFirstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "absent" ]
|
||||||
Expect.isFalse (Option.isSome doc) "There should not have been a document returned"
|
Expect.isNone doc "There should not have been a document returned"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "findFirstByFieldsOrdered" [
|
||||||
|
testTask "succeeds when sorting ascending" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! doc =
|
||||||
|
conn.findFirstByFieldsOrdered<JsonDocument>
|
||||||
|
SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ] [ Field.Named "Sub.Bar" ]
|
||||||
|
Expect.isSome doc "There should have been a document returned"
|
||||||
|
Expect.equal "two" doc.Value.Id "An incorrect document was returned"
|
||||||
|
}
|
||||||
|
testTask "succeeds when sorting descending" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
use conn = Configuration.dbConn ()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! doc =
|
||||||
|
conn.findFirstByFieldsOrdered<JsonDocument>
|
||||||
|
SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ] [ Field.Named "Sub.Bar DESC" ]
|
||||||
|
Expect.isSome doc "There should have been a document returned"
|
||||||
|
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "updateById" [
|
testList "updateById" [
|
||||||
|
@ -324,14 +410,14 @@ let integrationTests =
|
||||||
do! conn.patchById SqliteDb.TableName "test" {| Foo = "green" |}
|
do! conn.patchById SqliteDb.TableName "test" {| Foo = "green" |}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "patchByField" [
|
testList "patchByFields" [
|
||||||
testTask "succeeds when a document is updated" {
|
testTask "succeeds when a document is updated" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
do! conn.patchByField SqliteDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
|
do! conn.patchByFields SqliteDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
|
||||||
let! after = conn.countByField SqliteDb.TableName (Field.EQ "NumValue" 77)
|
let! after = conn.countByFields SqliteDb.TableName Any [ Field.EQ "NumValue" 77 ]
|
||||||
Expect.equal after 2L "There should have been 2 documents returned"
|
Expect.equal after 2L "There should have been 2 documents returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when no document is updated" {
|
testTask "succeeds when no document is updated" {
|
||||||
|
@ -342,7 +428,7 @@ let integrationTests =
|
||||||
Expect.isEmpty before "There should have been no documents returned"
|
Expect.isEmpty before "There should have been no documents returned"
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! conn.patchByField SqliteDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
|
do! conn.patchByFields SqliteDb.TableName Any [ Field.EQ "Value" "burgundy" ] {| Foo = "green" |}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "removeFieldsById" [
|
testList "removeFieldsById" [
|
||||||
|
@ -375,13 +461,13 @@ let integrationTests =
|
||||||
do! conn.removeFieldsById SqliteDb.TableName "two" [ "Value" ]
|
do! conn.removeFieldsById SqliteDb.TableName "two" [ "Value" ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "removeFieldByField" [
|
testList "removeFieldByFields" [
|
||||||
testTask "succeeds when a field is removed" {
|
testTask "succeeds when a field is removed" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
do! conn.removeFieldsByField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Sub" ]
|
do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.EQ "NumValue" 17 ] [ "Sub" ]
|
||||||
try
|
try
|
||||||
let! _ = conn.findById<string, JsonDocument> SqliteDb.TableName "four"
|
let! _ = conn.findById<string, JsonDocument> SqliteDb.TableName "four"
|
||||||
Expect.isTrue false "The updated document should have failed to parse"
|
Expect.isTrue false "The updated document should have failed to parse"
|
||||||
|
@ -395,14 +481,14 @@ let integrationTests =
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! conn.removeFieldsByField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Nothing" ]
|
do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.EQ "NumValue" 17 ] [ "Nothing" ]
|
||||||
}
|
}
|
||||||
testTask "succeeds when no document is matched" {
|
testTask "succeeds when no document is matched" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! conn.removeFieldsByField SqliteDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
|
do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.NE "Abracadabra" "apple" ] [ "Value" ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "deleteById" [
|
testList "deleteById" [
|
||||||
|
@ -425,13 +511,13 @@ let integrationTests =
|
||||||
Expect.equal remaining 5L "There should have been 5 documents remaining"
|
Expect.equal remaining 5L "There should have been 5 documents remaining"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "deleteByField" [
|
testList "deleteByFields" [
|
||||||
testTask "succeeds when documents are deleted" {
|
testTask "succeeds when documents are deleted" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
do! conn.deleteByField SqliteDb.TableName (Field.NE "Value" "purple")
|
do! conn.deleteByFields SqliteDb.TableName Any [ Field.NE "Value" "purple" ]
|
||||||
let! remaining = conn.countAll SqliteDb.TableName
|
let! remaining = conn.countAll SqliteDb.TableName
|
||||||
Expect.equal remaining 2L "There should have been 2 documents remaining"
|
Expect.equal remaining 2L "There should have been 2 documents remaining"
|
||||||
}
|
}
|
||||||
|
@ -440,7 +526,7 @@ let integrationTests =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
do! conn.deleteByField SqliteDb.TableName (Field.EQ "Value" "crimson")
|
do! conn.deleteByFields SqliteDb.TableName Any [ Field.EQ "Value" "crimson" ]
|
||||||
let! remaining = conn.countAll SqliteDb.TableName
|
let! remaining = conn.countAll SqliteDb.TableName
|
||||||
Expect.equal remaining 5L "There should have been 5 documents remaining"
|
Expect.equal remaining 5L "There should have been 5 documents remaining"
|
||||||
}
|
}
|
||||||
|
@ -478,8 +564,8 @@ let integrationTests =
|
||||||
use conn = Configuration.dbConn ()
|
use conn = Configuration.dbConn ()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! docs = conn.customList (Query.selectFromTable SqliteDb.TableName) [] fromData<JsonDocument>
|
let! docs = conn.customList (Query.find SqliteDb.TableName) [] fromData<JsonDocument>
|
||||||
Expect.hasCountOf docs 5u (fun _ -> true) "There should have been 5 documents returned"
|
Expect.hasLength docs 5 "There should have been 5 documents returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when data is not found" {
|
testTask "succeeds when data is not found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
|
|
@ -8,131 +8,84 @@ open Expecto
|
||||||
open Microsoft.Data.Sqlite
|
open Microsoft.Data.Sqlite
|
||||||
open Types
|
open Types
|
||||||
|
|
||||||
/// Unit tests for the SQLite library
|
#nowarn "0044"
|
||||||
let unitTests =
|
|
||||||
testList "Unit" [
|
(** UNIT TESTS **)
|
||||||
testList "Query" [
|
|
||||||
testList "whereByField" [
|
/// Unit tests for the Query module of the SQLite library
|
||||||
test "succeeds when a logical operator is passed" {
|
let queryTests = testList "Query" [
|
||||||
|
testList "whereByFields" [
|
||||||
|
test "succeeds for a single field when a logical operator is passed" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.whereByField (Field.GT "theField" 0) "@test")
|
(Query.whereByFields Any [ { Field.GT "theField" 0 with ParameterName = Some "@test" } ])
|
||||||
"data->>'theField' > @test"
|
"data->>'theField' > @test"
|
||||||
"WHERE clause not correct"
|
"WHERE clause not correct"
|
||||||
}
|
}
|
||||||
test "succeeds when an existence operator is passed" {
|
test "succeeds for a single field when an existence operator is passed" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.whereByField (Field.NEX "thatField") "")
|
(Query.whereByFields Any [ Field.NEX "thatField" ])
|
||||||
"data->>'thatField' IS NULL"
|
"data->>'thatField' IS NULL"
|
||||||
"WHERE clause not correct"
|
"WHERE clause not correct"
|
||||||
}
|
}
|
||||||
test "succeeds when the between operator is passed" {
|
test "succeeds for a single field when a between operator is passed" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.whereByField (Field.BT "aField" 50 99) "@range")
|
(Query.whereByFields All [ { Field.BT "aField" 50 99 with ParameterName = Some "@range" } ])
|
||||||
"data->>'aField' BETWEEN @rangemin AND @rangemax"
|
"data->>'aField' BETWEEN @rangemin AND @rangemax"
|
||||||
"WHERE clause not correct"
|
"WHERE clause not correct"
|
||||||
}
|
}
|
||||||
|
test "succeeds for all multiple fields with logical operators" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.whereByFields All [ Field.EQ "theFirst" "1"; Field.EQ "numberTwo" "2" ])
|
||||||
|
"data->>'theFirst' = @field0 AND data->>'numberTwo' = @field1"
|
||||||
|
"WHERE clause not correct"
|
||||||
|
}
|
||||||
|
test "succeeds for any multiple fields with an existence operator" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.whereByFields Any [ Field.NEX "thatField"; Field.GE "thisField" 18 ])
|
||||||
|
"data->>'thatField' IS NULL OR data->>'thisField' >= @field0"
|
||||||
|
"WHERE clause not correct"
|
||||||
|
}
|
||||||
|
test "succeeds for all multiple fields with between operators" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.whereByFields All [ Field.BT "aField" 50 99; Field.BT "anotherField" "a" "b" ])
|
||||||
|
"data->>'aField' BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max"
|
||||||
|
"WHERE clause not correct"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
test "whereById succeeds" {
|
test "whereById succeeds" {
|
||||||
Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause not correct"
|
Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause not correct"
|
||||||
}
|
}
|
||||||
|
test "patch succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.patch SqliteDb.TableName)
|
||||||
|
$"UPDATE {SqliteDb.TableName} SET data = json_patch(data, json(@data))"
|
||||||
|
"Patch query not correct"
|
||||||
|
}
|
||||||
|
test "removeFields succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.removeFields SqliteDb.TableName [ SqliteParameter("@a", "a"); SqliteParameter("@b", "b") ])
|
||||||
|
$"UPDATE {SqliteDb.TableName} SET data = json_remove(data, @a, @b)"
|
||||||
|
"Field removal query not correct"
|
||||||
|
}
|
||||||
|
test "byId succeeds" {
|
||||||
|
Expect.equal (Query.byId "test" "14") "test WHERE data->>'Id' = @id" "By-ID query not correct"
|
||||||
|
}
|
||||||
|
test "byFields succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.byFields "unit" Any [ Field.GT "That" 14 ])
|
||||||
|
"unit WHERE data->>'That' > @field0"
|
||||||
|
"By-Field query not correct"
|
||||||
|
}
|
||||||
test "Definition.ensureTable succeeds" {
|
test "Definition.ensureTable succeeds" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.Definition.ensureTable "tbl")
|
(Query.Definition.ensureTable "tbl")
|
||||||
"CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)"
|
"CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)"
|
||||||
"CREATE TABLE statement not correct"
|
"CREATE TABLE statement not correct"
|
||||||
}
|
}
|
||||||
test "update succeeds" {
|
]
|
||||||
Expect.equal
|
|
||||||
(Query.update "tbl")
|
/// Unit tests for the Parameters module of the SQLite library
|
||||||
"UPDATE tbl SET data = @data WHERE data->>'Id' = @id"
|
let parametersTests = testList "Parameters" [
|
||||||
"UPDATE full statement not correct"
|
|
||||||
}
|
|
||||||
testList "Count" [
|
|
||||||
test "all succeeds" {
|
|
||||||
Expect.equal (Query.Count.all "tbl") $"SELECT COUNT(*) AS it FROM tbl" "Count query not correct"
|
|
||||||
}
|
|
||||||
test "byField succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Count.byField "tbl" (Field.EQ "thatField" 0))
|
|
||||||
"SELECT COUNT(*) AS it FROM tbl WHERE data->>'thatField' = @field"
|
|
||||||
"JSON field text comparison count query not correct"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
testList "Exists" [
|
|
||||||
test "byId succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Exists.byId "tbl")
|
|
||||||
"SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Id' = @id) AS it"
|
|
||||||
"ID existence query not correct"
|
|
||||||
}
|
|
||||||
test "byField succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Exists.byField "tbl" (Field.LT "Test" 0))
|
|
||||||
"SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Test' < @field) AS it"
|
|
||||||
"JSON field text comparison exists query not correct"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
testList "Find" [
|
|
||||||
test "byId succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Find.byId "tbl")
|
|
||||||
"SELECT data FROM tbl WHERE data->>'Id' = @id"
|
|
||||||
"SELECT by ID query not correct"
|
|
||||||
}
|
|
||||||
test "byField succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Find.byField "tbl" (Field.GE "Golf" 0))
|
|
||||||
"SELECT data FROM tbl WHERE data->>'Golf' >= @field"
|
|
||||||
"SELECT by JSON comparison query not correct"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
testList "Patch" [
|
|
||||||
test "byId succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Patch.byId "tbl")
|
|
||||||
"UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Id' = @id"
|
|
||||||
"UPDATE partial by ID statement not correct"
|
|
||||||
}
|
|
||||||
test "byField succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Patch.byField "tbl" (Field.NE "Part" 0))
|
|
||||||
"UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Part' <> @field"
|
|
||||||
"UPDATE partial by JSON comparison query not correct"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
testList "RemoveFields" [
|
|
||||||
test "byId succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.RemoveFields.byId "tbl" [ SqliteParameter("@name", "one") ])
|
|
||||||
"UPDATE tbl SET data = json_remove(data, @name) WHERE data->>'Id' = @id"
|
|
||||||
"Remove field by ID query not correct"
|
|
||||||
}
|
|
||||||
test "byField succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.RemoveFields.byField
|
|
||||||
"tbl"
|
|
||||||
(Field.GT "Fly" 0)
|
|
||||||
[ SqliteParameter("@name0", "one"); SqliteParameter("@name1", "two") ])
|
|
||||||
"UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data->>'Fly' > @field"
|
|
||||||
"Remove field by field query not correct"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
testList "Delete" [
|
|
||||||
test "byId succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Delete.byId "tbl")
|
|
||||||
"DELETE FROM tbl WHERE data->>'Id' = @id"
|
|
||||||
"DELETE by ID query not correct"
|
|
||||||
}
|
|
||||||
test "byField succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Delete.byField "tbl" (Field.NEX "gone"))
|
|
||||||
"DELETE FROM tbl WHERE data->>'gone' IS NULL"
|
|
||||||
"DELETE by JSON comparison query not correct"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
testList "Parameters" [
|
|
||||||
test "idParam succeeds" {
|
test "idParam succeeds" {
|
||||||
let theParam = idParam 7
|
let theParam = idParam 7
|
||||||
Expect.equal theParam.ParameterName "@id" "The parameter name is incorrect"
|
Expect.equal theParam.ParameterName "@id" "The parameter name is incorrect"
|
||||||
|
@ -147,7 +100,7 @@ let unitTests =
|
||||||
test "succeeds when adding a parameter" {
|
test "succeeds when adding a parameter" {
|
||||||
let paramList = addFieldParam "@field" (Field.EQ "it" 99) []
|
let paramList = addFieldParam "@field" (Field.EQ "it" 99) []
|
||||||
Expect.hasLength paramList 1 "There should have been a parameter added"
|
Expect.hasLength paramList 1 "There should have been a parameter added"
|
||||||
let theParam = paramList[0]
|
let theParam = Seq.head paramList
|
||||||
Expect.equal theParam.ParameterName "@field" "The parameter name is incorrect"
|
Expect.equal theParam.ParameterName "@field" "The parameter name is incorrect"
|
||||||
Expect.equal theParam.Value 99 "The parameter value is incorrect"
|
Expect.equal theParam.Value 99 "The parameter value is incorrect"
|
||||||
}
|
}
|
||||||
|
@ -159,24 +112,19 @@ let unitTests =
|
||||||
test "noParams succeeds" {
|
test "noParams succeeds" {
|
||||||
Expect.isEmpty noParams "The parameter list should have been empty"
|
Expect.isEmpty noParams "The parameter list should have been empty"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
// Results are exhaustively executed in the context of other tests
|
// Results are exhaustively executed in the context of other tests
|
||||||
]
|
|
||||||
|
|
||||||
/// These tests each use a fresh copy of a SQLite database
|
|
||||||
let integrationTests =
|
(** INTEGRATION TESTS **)
|
||||||
let documents = [
|
|
||||||
{ Id = "one"; Value = "FIRST!"; NumValue = 0; Sub = None }
|
/// Load a table with the test documents
|
||||||
{ Id = "two"; Value = "another"; NumValue = 10; Sub = Some { Foo = "green"; Bar = "blue" } }
|
let loadDocs () = backgroundTask {
|
||||||
{ Id = "three"; Value = ""; NumValue = 4; Sub = None }
|
for doc in testDocuments do do! insert SqliteDb.TableName doc
|
||||||
{ Id = "four"; Value = "purple"; NumValue = 17; Sub = Some { Foo = "green"; Bar = "red" } }
|
}
|
||||||
{ Id = "five"; Value = "purple"; NumValue = 18; Sub = None }
|
|
||||||
]
|
/// Integration tests for the Configuration module of the SQLite library
|
||||||
let loadDocs () = backgroundTask {
|
let configurationTests = testList "Configuration" [
|
||||||
for doc in documents do do! insert SqliteDb.TableName doc
|
|
||||||
}
|
|
||||||
testList "Integration" [
|
|
||||||
testList "Configuration" [
|
|
||||||
test "useConnectionString / connectionString succeed" {
|
test "useConnectionString / connectionString succeed" {
|
||||||
try
|
try
|
||||||
Configuration.useConnectionString "Data Source=test.db"
|
Configuration.useConnectionString "Data Source=test.db"
|
||||||
|
@ -187,34 +135,10 @@ let integrationTests =
|
||||||
finally
|
finally
|
||||||
Configuration.useConnectionString "Data Source=:memory:"
|
Configuration.useConnectionString "Data Source=:memory:"
|
||||||
}
|
}
|
||||||
test "useSerializer succeeds" {
|
]
|
||||||
try
|
|
||||||
Configuration.useSerializer
|
|
||||||
{ new IDocumentSerializer with
|
|
||||||
member _.Serialize<'T>(it: 'T) : string = """{"Overridden":true}"""
|
|
||||||
member _.Deserialize<'T>(it: string) : 'T = Unchecked.defaultof<'T>
|
|
||||||
}
|
|
||||||
|
|
||||||
let serialized = Configuration.serializer().Serialize { Foo = "howdy"; Bar = "bye"}
|
/// Integration tests for the Custom module of the SQLite library
|
||||||
Expect.equal serialized """{"Overridden":true}""" "Specified serializer was not used"
|
let customTests = testList "Custom" [
|
||||||
|
|
||||||
let deserialized = Configuration.serializer().Deserialize<obj> """{"Something":"here"}"""
|
|
||||||
Expect.isNull deserialized "Specified serializer should have returned null"
|
|
||||||
finally
|
|
||||||
Configuration.useSerializer DocumentSerializer.``default``
|
|
||||||
}
|
|
||||||
test "serializer returns configured serializer" {
|
|
||||||
Expect.isTrue (obj.ReferenceEquals(DocumentSerializer.``default``, Configuration.serializer ()))
|
|
||||||
"Serializer should have been the same"
|
|
||||||
}
|
|
||||||
test "useIdField / idField succeeds" {
|
|
||||||
Expect.equal (Configuration.idField ()) "Id" "The default configured ID field was incorrect"
|
|
||||||
Configuration.useIdField "id"
|
|
||||||
Expect.equal (Configuration.idField ()) "id" "useIdField did not set the ID field"
|
|
||||||
Configuration.useIdField "Id"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
testList "Custom" [
|
|
||||||
testList "single" [
|
testList "single" [
|
||||||
testTask "succeeds when a row is found" {
|
testTask "succeeds when a row is found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
@ -245,7 +169,7 @@ let integrationTests =
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! docs = Custom.list (Query.selectFromTable SqliteDb.TableName) [] fromData<JsonDocument>
|
let! docs = Custom.list (Query.find SqliteDb.TableName) [] fromData<JsonDocument>
|
||||||
Expect.hasCountOf docs 5u (fun _ -> true) "There should have been 5 documents returned"
|
Expect.hasCountOf docs 5u (fun _ -> true) "There should have been 5 documents returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when data is not found" {
|
testTask "succeeds when data is not found" {
|
||||||
|
@ -288,8 +212,10 @@ let integrationTests =
|
||||||
let! nbr = Custom.scalar "SELECT 5 AS test_value" [] _.GetInt32(0)
|
let! nbr = Custom.scalar "SELECT 5 AS test_value" [] _.GetInt32(0)
|
||||||
Expect.equal nbr 5 "The query should have returned the number 5"
|
Expect.equal nbr 5 "The query should have returned the number 5"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "Definition" [
|
|
||||||
|
/// Integration tests for the Definition module of the SQLite library
|
||||||
|
let definitionTests = testList "Definition" [
|
||||||
testTask "ensureTable succeeds" {
|
testTask "ensureTable succeeds" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
let itExists (name: string) =
|
let itExists (name: string) =
|
||||||
|
@ -325,7 +251,10 @@ let integrationTests =
|
||||||
let! exists' = indexExists ()
|
let! exists' = indexExists ()
|
||||||
Expect.isTrue exists' "The index should now exist"
|
Expect.isTrue exists' "The index should now exist"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/// Integration tests for the (auto-opened) Document module of the SQLite library
|
||||||
|
let documentTests = testList "Document" [
|
||||||
testList "insert" [
|
testList "insert" [
|
||||||
testTask "succeeds" {
|
testTask "succeeds" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
@ -345,6 +274,68 @@ let integrationTests =
|
||||||
insert SqliteDb.TableName {emptyDoc with Id = "test" } |> Async.AwaitTask |> Async.RunSynchronously)
|
insert SqliteDb.TableName {emptyDoc with Id = "test" } |> Async.AwaitTask |> Async.RunSynchronously)
|
||||||
"An exception should have been raised for duplicate document ID insert"
|
"An exception should have been raised for duplicate document ID insert"
|
||||||
}
|
}
|
||||||
|
testTask "succeeds when adding a numeric auto ID" {
|
||||||
|
try
|
||||||
|
Configuration.useAutoIdStrategy Number
|
||||||
|
Configuration.useIdField "Key"
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
let! before = Count.all SqliteDb.TableName
|
||||||
|
Expect.equal before 0L "There should be no documents in the table"
|
||||||
|
|
||||||
|
do! insert SqliteDb.TableName { Key = 0; Text = "one" }
|
||||||
|
do! insert SqliteDb.TableName { Key = 0; Text = "two" }
|
||||||
|
do! insert SqliteDb.TableName { Key = 77; Text = "three" }
|
||||||
|
do! insert SqliteDb.TableName { Key = 0; Text = "four" }
|
||||||
|
|
||||||
|
let! after = Find.allOrdered<NumIdDocument> SqliteDb.TableName [ Field.Named "Key" ]
|
||||||
|
Expect.hasLength after 4 "There should have been 4 documents returned"
|
||||||
|
Expect.equal (after |> List.map _.Key) [ 1; 2; 77; 78 ] "The IDs were not generated correctly"
|
||||||
|
finally
|
||||||
|
Configuration.useAutoIdStrategy Disabled
|
||||||
|
Configuration.useIdField "Id"
|
||||||
|
}
|
||||||
|
testTask "succeeds when adding a GUID auto ID" {
|
||||||
|
try
|
||||||
|
Configuration.useAutoIdStrategy Guid
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
let! before = Count.all SqliteDb.TableName
|
||||||
|
Expect.equal before 0L "There should be no documents in the table"
|
||||||
|
|
||||||
|
do! insert SqliteDb.TableName { emptyDoc with Value = "one" }
|
||||||
|
do! insert SqliteDb.TableName { emptyDoc with Value = "two" }
|
||||||
|
do! insert SqliteDb.TableName { emptyDoc with Id = "abc123"; Value = "three" }
|
||||||
|
do! insert SqliteDb.TableName { emptyDoc with Value = "four" }
|
||||||
|
|
||||||
|
let! after = Find.all<JsonDocument> SqliteDb.TableName
|
||||||
|
Expect.hasLength after 4 "There should have been 4 documents returned"
|
||||||
|
Expect.hasCountOf after 3u (fun doc -> doc.Id.Length = 32) "Three of the IDs should have been GUIDs"
|
||||||
|
Expect.hasCountOf after 1u (fun doc -> doc.Id = "abc123") "The provided ID should have been used as-is"
|
||||||
|
finally
|
||||||
|
Configuration.useAutoIdStrategy Disabled
|
||||||
|
}
|
||||||
|
testTask "succeeds when adding a RandomString auto ID" {
|
||||||
|
try
|
||||||
|
Configuration.useAutoIdStrategy RandomString
|
||||||
|
Configuration.useIdStringLength 44
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
let! before = Count.all SqliteDb.TableName
|
||||||
|
Expect.equal before 0L "There should be no documents in the table"
|
||||||
|
|
||||||
|
do! insert SqliteDb.TableName { emptyDoc with Value = "one" }
|
||||||
|
do! insert SqliteDb.TableName { emptyDoc with Value = "two" }
|
||||||
|
do! insert SqliteDb.TableName { emptyDoc with Id = "abc123"; Value = "three" }
|
||||||
|
do! insert SqliteDb.TableName { emptyDoc with Value = "four" }
|
||||||
|
|
||||||
|
let! after = Find.all<JsonDocument> SqliteDb.TableName
|
||||||
|
Expect.hasLength after 4 "There should have been 4 documents returned"
|
||||||
|
Expect.hasCountOf
|
||||||
|
after 3u (fun doc -> doc.Id.Length = 44)
|
||||||
|
"Three of the IDs should have been 44-character random strings"
|
||||||
|
Expect.hasCountOf after 1u (fun doc -> doc.Id = "abc123") "The provided ID should have been used as-is"
|
||||||
|
finally
|
||||||
|
Configuration.useAutoIdStrategy Disabled
|
||||||
|
Configuration.useIdStringLength 16
|
||||||
|
}
|
||||||
]
|
]
|
||||||
testList "save" [
|
testList "save" [
|
||||||
testTask "succeeds when a document is inserted" {
|
testTask "succeeds when a document is inserted" {
|
||||||
|
@ -373,7 +364,10 @@ let integrationTests =
|
||||||
Expect.equal after.Value upd8Doc "The updated document is not correct"
|
Expect.equal after.Value upd8Doc "The updated document is not correct"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "Count" [
|
]
|
||||||
|
|
||||||
|
/// Integration tests for the Count module of the SQLite library
|
||||||
|
let countTests = testList "Count" [
|
||||||
testTask "all succeeds" {
|
testTask "all succeeds" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
@ -381,22 +375,26 @@ let integrationTests =
|
||||||
let! theCount = Count.all SqliteDb.TableName
|
let! theCount = Count.all SqliteDb.TableName
|
||||||
Expect.equal theCount 5L "There should have been 5 matching documents"
|
Expect.equal theCount 5L "There should have been 5 matching documents"
|
||||||
}
|
}
|
||||||
testTask "byField succeeds for a numeric range" {
|
testList "byFields" [
|
||||||
|
testTask "succeeds for a numeric range" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! theCount = Count.byField SqliteDb.TableName (Field.BT "NumValue" 10 20)
|
let! theCount = Count.byFields SqliteDb.TableName Any [ Field.BT "NumValue" 10 20 ]
|
||||||
Expect.equal theCount 3L "There should have been 3 matching documents"
|
Expect.equal theCount 3L "There should have been 3 matching documents"
|
||||||
}
|
}
|
||||||
testTask "byField succeeds for a non-numeric range" {
|
testTask "succeeds for a non-numeric range" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! theCount = Count.byField SqliteDb.TableName (Field.BT "Value" "aardvark" "apple")
|
let! theCount = Count.byFields SqliteDb.TableName Any [ Field.BT "Value" "aardvark" "apple" ]
|
||||||
Expect.equal theCount 1L "There should have been 1 matching document"
|
Expect.equal theCount 1L "There should have been 1 matching document"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "Exists" [
|
]
|
||||||
|
|
||||||
|
/// Integration tests for the Exists module of the SQLite library
|
||||||
|
let existsTests = testList "Exists" [
|
||||||
testList "byId" [
|
testList "byId" [
|
||||||
testTask "succeeds when a document exists" {
|
testTask "succeeds when a document exists" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
@ -413,24 +411,26 @@ let integrationTests =
|
||||||
Expect.isFalse exists "There should not have been an existing document"
|
Expect.isFalse exists "There should not have been an existing document"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "byField" [
|
testList "byFields" [
|
||||||
testTask "succeeds when documents exist" {
|
testTask "succeeds when documents exist" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! exists = Exists.byField SqliteDb.TableName (Field.EQ "NumValue" 10)
|
let! exists = Exists.byFields SqliteDb.TableName Any [ Field.EQ "NumValue" 10 ]
|
||||||
Expect.isTrue exists "There should have been existing documents"
|
Expect.isTrue exists "There should have been existing documents"
|
||||||
}
|
}
|
||||||
testTask "succeeds when no matching documents exist" {
|
testTask "succeeds when no matching documents exist" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! exists = Exists.byField SqliteDb.TableName (Field.LT "Nothing" "none")
|
let! exists = Exists.byFields SqliteDb.TableName Any [ Field.LT "Nothing" "none" ]
|
||||||
Expect.isFalse exists "There should not have been any existing documents"
|
Expect.isFalse exists "There should not have been any existing documents"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
testList "Find" [
|
|
||||||
|
/// Integration tests for the Find module of the SQLite library
|
||||||
|
let findTests = testList "Find" [
|
||||||
testList "all" [
|
testList "all" [
|
||||||
testTask "succeeds when there is data" {
|
testTask "succeeds when there is data" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
@ -440,12 +440,10 @@ let integrationTests =
|
||||||
do! insert SqliteDb.TableName { Foo = "five"; Bar = "six" }
|
do! insert SqliteDb.TableName { Foo = "five"; Bar = "six" }
|
||||||
|
|
||||||
let! results = Find.all<SubDocument> SqliteDb.TableName
|
let! results = Find.all<SubDocument> SqliteDb.TableName
|
||||||
let expected = [
|
Expect.equal
|
||||||
{ Foo = "one"; Bar = "two" }
|
results
|
||||||
{ Foo = "three"; Bar = "four" }
|
[ { Foo = "one"; Bar = "two" }; { Foo = "three"; Bar = "four" }; { Foo = "five"; Bar = "six" } ]
|
||||||
{ Foo = "five"; Bar = "six" }
|
"There should have been 3 documents returned"
|
||||||
]
|
|
||||||
Expect.equal results expected "There should have been 3 documents returned"
|
|
||||||
}
|
}
|
||||||
testTask "succeeds when there is no data" {
|
testTask "succeeds when there is no data" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
@ -453,13 +451,48 @@ let integrationTests =
|
||||||
Expect.equal results [] "There should have been no documents returned"
|
Expect.equal results [] "There should have been no documents returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
testList "allOrdered" [
|
||||||
|
testTask "succeeds when ordering numerically" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! results = Find.allOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "n:NumValue" ]
|
||||||
|
Expect.hasLength results 5 "There should have been 5 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(results |> List.map _.Id |> String.concat "|")
|
||||||
|
"one|three|two|four|five"
|
||||||
|
"The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when ordering numerically descending" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! results = Find.allOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "n:NumValue DESC" ]
|
||||||
|
Expect.hasLength results 5 "There should have been 5 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(results |> List.map _.Id |> String.concat "|")
|
||||||
|
"five|four|two|three|one"
|
||||||
|
"The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when ordering alphabetically" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! results = Find.allOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "Id DESC" ]
|
||||||
|
Expect.hasLength results 5 "There should have been 5 documents returned"
|
||||||
|
Expect.equal
|
||||||
|
(results |> List.map _.Id |> String.concat "|")
|
||||||
|
"two|three|one|four|five"
|
||||||
|
"The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
]
|
||||||
testList "byId" [
|
testList "byId" [
|
||||||
testTask "succeeds when a document is found" {
|
testTask "succeeds when a document is found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! doc = Find.byId<string, JsonDocument> SqliteDb.TableName "two"
|
let! doc = Find.byId<string, JsonDocument> SqliteDb.TableName "two"
|
||||||
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
|
Expect.isSome doc "There should have been a document returned"
|
||||||
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
|
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a document is not found" {
|
testTask "succeeds when a document is not found" {
|
||||||
|
@ -467,52 +500,98 @@ let integrationTests =
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! doc = Find.byId<string, JsonDocument> SqliteDb.TableName "three hundred eighty-seven"
|
let! doc = Find.byId<string, JsonDocument> SqliteDb.TableName "three hundred eighty-seven"
|
||||||
Expect.isFalse (Option.isSome doc) "There should not have been a document returned"
|
Expect.isNone doc "There should not have been a document returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "byField" [
|
testList "byFields" [
|
||||||
testTask "succeeds when documents are found" {
|
testTask "succeeds when documents are found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! docs = Find.byField<JsonDocument> SqliteDb.TableName (Field.GT "NumValue" 15)
|
let! docs = Find.byFields<JsonDocument> SqliteDb.TableName Any [ Field.GT "NumValue" 15 ]
|
||||||
Expect.equal (List.length docs) 2 "There should have been two documents returned"
|
Expect.equal (List.length docs) 2 "There should have been two documents returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when documents are not found" {
|
testTask "succeeds when documents are not found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! docs = Find.byField<JsonDocument> SqliteDb.TableName (Field.GT "NumValue" 100)
|
let! docs = Find.byFields<JsonDocument> SqliteDb.TableName Any [ Field.GT "NumValue" 100 ]
|
||||||
Expect.isTrue (List.isEmpty docs) "There should have been no documents returned"
|
Expect.isTrue (List.isEmpty docs) "There should have been no documents returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "firstByField" [
|
testList "byFieldsOrdered" [
|
||||||
|
testTask "succeeds when sorting ascending" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! docs =
|
||||||
|
Find.byFieldsOrdered<JsonDocument>
|
||||||
|
SqliteDb.TableName Any [ Field.GT "NumValue" 15 ] [ Field.Named "Id" ]
|
||||||
|
Expect.equal
|
||||||
|
(docs |> List.map _.Id |> String.concat "|") "five|four" "The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
testTask "succeeds when sorting descending" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! docs =
|
||||||
|
Find.byFieldsOrdered<JsonDocument>
|
||||||
|
SqliteDb.TableName Any [ Field.GT "NumValue" 15 ] [ Field.Named "Id DESC" ]
|
||||||
|
Expect.equal
|
||||||
|
(docs |> List.map _.Id |> String.concat "|") "four|five" "The documents were not ordered correctly"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "firstByFields" [
|
||||||
testTask "succeeds when a document is found" {
|
testTask "succeeds when a document is found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "another")
|
let! doc = Find.firstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "another" ]
|
||||||
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
|
Expect.isSome doc "There should have been a document returned"
|
||||||
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
|
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when multiple documents are found" {
|
testTask "succeeds when multiple documents are found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green")
|
let! doc = Find.firstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ]
|
||||||
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
|
Expect.isSome doc "There should have been a document returned"
|
||||||
Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned"
|
Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when a document is not found" {
|
testTask "succeeds when a document is not found" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "absent")
|
let! doc = Find.firstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "absent" ]
|
||||||
Expect.isFalse (Option.isSome doc) "There should not have been a document returned"
|
Expect.isNone doc "There should not have been a document returned"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
testList "firstByFieldsOrdered" [
|
||||||
|
testTask "succeeds when sorting ascending" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! doc =
|
||||||
|
Find.firstByFieldsOrdered<JsonDocument>
|
||||||
|
SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ] [ Field.Named "Sub.Bar" ]
|
||||||
|
Expect.isSome doc "There should have been a document returned"
|
||||||
|
Expect.equal "two" doc.Value.Id "An incorrect document was returned"
|
||||||
|
}
|
||||||
|
testTask "succeeds when sorting descending" {
|
||||||
|
use! db = SqliteDb.BuildDb()
|
||||||
|
do! loadDocs ()
|
||||||
|
|
||||||
|
let! doc =
|
||||||
|
Find.firstByFieldsOrdered<JsonDocument>
|
||||||
|
SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ] [ Field.Named "Sub.Bar DESC" ]
|
||||||
|
Expect.isSome doc "There should have been a document returned"
|
||||||
|
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
testList "Update" [
|
]
|
||||||
|
|
||||||
|
/// Integration tests for the Update module of the SQLite library
|
||||||
|
let updateTests = testList "Update" [
|
||||||
testList "byId" [
|
testList "byId" [
|
||||||
testTask "succeeds when a document is updated" {
|
testTask "succeeds when a document is updated" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
@ -532,9 +611,7 @@ let integrationTests =
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! Update.byId
|
do! Update.byId
|
||||||
SqliteDb.TableName
|
SqliteDb.TableName "test" { emptyDoc with Id = "x"; Sub = Some { Foo = "blue"; Bar = "red" } }
|
||||||
"test"
|
|
||||||
{ emptyDoc with Id = "x"; Sub = Some { Foo = "blue"; Bar = "red" } }
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "byFunc" [
|
testList "byFunc" [
|
||||||
|
@ -542,8 +619,7 @@ let integrationTests =
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
do! Update.byFunc
|
do! Update.byFunc SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
|
||||||
SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
|
|
||||||
let! after = Find.byId<string, JsonDocument> SqliteDb.TableName "one"
|
let! after = Find.byId<string, JsonDocument> SqliteDb.TableName "one"
|
||||||
Expect.isSome after "There should have been a document returned post-update"
|
Expect.isSome after "There should have been a document returned post-update"
|
||||||
Expect.equal
|
Expect.equal
|
||||||
|
@ -558,12 +634,13 @@ let integrationTests =
|
||||||
Expect.isEmpty before "There should have been no documents returned"
|
Expect.isEmpty before "There should have been no documents returned"
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! Update.byFunc
|
do! Update.byFunc SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
|
||||||
SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
testList "Patch" [
|
|
||||||
|
/// Integration tests for the Patch module of the SQLite library
|
||||||
|
let patchTests = testList "Patch" [
|
||||||
testList "byId" [
|
testList "byId" [
|
||||||
testTask "succeeds when a document is updated" {
|
testTask "succeeds when a document is updated" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
@ -584,13 +661,13 @@ let integrationTests =
|
||||||
do! Patch.byId SqliteDb.TableName "test" {| Foo = "green" |}
|
do! Patch.byId SqliteDb.TableName "test" {| Foo = "green" |}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "byField" [
|
testList "byFields" [
|
||||||
testTask "succeeds when a document is updated" {
|
testTask "succeeds when a document is updated" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
do! Patch.byField SqliteDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
|
do! Patch.byFields SqliteDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
|
||||||
let! after = Count.byField SqliteDb.TableName (Field.EQ "NumValue" 77)
|
let! after = Count.byFields SqliteDb.TableName Any [ Field.EQ "NumValue" 77 ]
|
||||||
Expect.equal after 2L "There should have been 2 documents returned"
|
Expect.equal after 2L "There should have been 2 documents returned"
|
||||||
}
|
}
|
||||||
testTask "succeeds when no document is updated" {
|
testTask "succeeds when no document is updated" {
|
||||||
|
@ -600,11 +677,13 @@ let integrationTests =
|
||||||
Expect.isEmpty before "There should have been no documents returned"
|
Expect.isEmpty before "There should have been no documents returned"
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! Patch.byField SqliteDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
|
do! Patch.byFields SqliteDb.TableName Any [ Field.EQ "Value" "burgundy" ] {| Foo = "green" |}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
testList "RemoveFields" [
|
|
||||||
|
/// Integration tests for the RemoveFields module of the SQLite library
|
||||||
|
let removeFieldsTests = testList "RemoveFields" [
|
||||||
testList "byId" [
|
testList "byId" [
|
||||||
testTask "succeeds when fields is removed" {
|
testTask "succeeds when fields is removed" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
@ -632,12 +711,12 @@ let integrationTests =
|
||||||
do! RemoveFields.byId SqliteDb.TableName "two" [ "Value" ]
|
do! RemoveFields.byId SqliteDb.TableName "two" [ "Value" ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "byField" [
|
testList "byFields" [
|
||||||
testTask "succeeds when a field is removed" {
|
testTask "succeeds when a field is removed" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
do! RemoveFields.byField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Sub" ]
|
do! RemoveFields.byFields SqliteDb.TableName Any [ Field.EQ "NumValue" 17 ] [ "Sub" ]
|
||||||
try
|
try
|
||||||
let! _ = Find.byId<string, JsonDocument> SqliteDb.TableName "four"
|
let! _ = Find.byId<string, JsonDocument> SqliteDb.TableName "four"
|
||||||
Expect.isTrue false "The updated document should have failed to parse"
|
Expect.isTrue false "The updated document should have failed to parse"
|
||||||
|
@ -650,17 +729,19 @@ let integrationTests =
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! RemoveFields.byField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Nothing" ]
|
do! RemoveFields.byFields SqliteDb.TableName Any [ Field.EQ "NumValue" 17 ] [ "Nothing" ]
|
||||||
}
|
}
|
||||||
testTask "succeeds when no document is matched" {
|
testTask "succeeds when no document is matched" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
|
||||||
// This not raising an exception is the test
|
// This not raising an exception is the test
|
||||||
do! RemoveFields.byField SqliteDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
|
do! RemoveFields.byFields SqliteDb.TableName Any [ Field.NE "Abracadabra" "apple" ] [ "Value" ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
testList "Delete" [
|
|
||||||
|
/// Integration tests for the Delete module of the SQLite library
|
||||||
|
let deleteTests = testList "Delete" [
|
||||||
testList "byId" [
|
testList "byId" [
|
||||||
testTask "succeeds when a document is deleted" {
|
testTask "succeeds when a document is deleted" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
|
@ -679,12 +760,12 @@ let integrationTests =
|
||||||
Expect.equal remaining 5L "There should have been 5 documents remaining"
|
Expect.equal remaining 5L "There should have been 5 documents remaining"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "byField" [
|
testList "byFields" [
|
||||||
testTask "succeeds when documents are deleted" {
|
testTask "succeeds when documents are deleted" {
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
do! Delete.byField SqliteDb.TableName (Field.NE "Value" "purple")
|
do! Delete.byFields SqliteDb.TableName Any [ Field.NE "Value" "purple" ]
|
||||||
let! remaining = Count.all SqliteDb.TableName
|
let! remaining = Count.all SqliteDb.TableName
|
||||||
Expect.equal remaining 2L "There should have been 2 documents remaining"
|
Expect.equal remaining 2L "There should have been 2 documents remaining"
|
||||||
}
|
}
|
||||||
|
@ -692,16 +773,28 @@ let integrationTests =
|
||||||
use! db = SqliteDb.BuildDb()
|
use! db = SqliteDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
do! Delete.byField SqliteDb.TableName (Field.EQ "Value" "crimson")
|
do! Delete.byFields SqliteDb.TableName Any [ Field.EQ "Value" "crimson" ]
|
||||||
let! remaining = Count.all SqliteDb.TableName
|
let! remaining = Count.all SqliteDb.TableName
|
||||||
Expect.equal remaining 5L "There should have been 5 documents remaining"
|
Expect.equal remaining 5L "There should have been 5 documents remaining"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
test "clean up database" {
|
|
||||||
Configuration.useConnectionString "data source=:memory:"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|> testSequenced
|
|
||||||
|
|
||||||
let all = testList "Sqlite" [ unitTests; integrationTests ]
|
/// All tests for the SQLite library
|
||||||
|
let all = testList "Sqlite" [
|
||||||
|
testList "Unit" [ queryTests; parametersTests ]
|
||||||
|
testSequenced <| testList "Integration" [
|
||||||
|
configurationTests
|
||||||
|
customTests
|
||||||
|
definitionTests
|
||||||
|
documentTests
|
||||||
|
countTests
|
||||||
|
existsTests
|
||||||
|
findTests
|
||||||
|
updateTests
|
||||||
|
patchTests
|
||||||
|
removeFieldsTests
|
||||||
|
deleteTests
|
||||||
|
test "clean up database" { Configuration.useConnectionString "data source=:memory:" }
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
module Types
|
module Types
|
||||||
|
|
||||||
|
type NumIdDocument =
|
||||||
|
{ Key: int
|
||||||
|
Text: string }
|
||||||
|
|
||||||
type SubDocument =
|
type SubDocument =
|
||||||
{ Foo: string
|
{ Foo: string
|
||||||
Bar: string }
|
Bar: string }
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
echo --- Package Common library
|
echo --- Package Common library
|
||||||
|
rm Common/bin/Release/BitBadger.Documents.Common.*.nupkg || true
|
||||||
dotnet pack Common/BitBadger.Documents.Common.fsproj -c Release
|
dotnet pack Common/BitBadger.Documents.Common.fsproj -c Release
|
||||||
cp Common/bin/Release/BitBadger.Documents.Common.*.nupkg .
|
cp Common/bin/Release/BitBadger.Documents.Common.*.nupkg .
|
||||||
|
|
||||||
echo --- Package PostgreSQL library
|
echo --- Package PostgreSQL library
|
||||||
|
rm Postgres/bin/Release/BitBadger.Documents.Postgres*.nupkg || true
|
||||||
dotnet pack Postgres/BitBadger.Documents.Postgres.fsproj -c Release
|
dotnet pack Postgres/BitBadger.Documents.Postgres.fsproj -c Release
|
||||||
cp Postgres/bin/Release/BitBadger.Documents.Postgres.*.nupkg .
|
cp Postgres/bin/Release/BitBadger.Documents.Postgres.*.nupkg .
|
||||||
|
|
||||||
echo --- Package SQLite library
|
echo --- Package SQLite library
|
||||||
|
rm Sqlite/bin/Release/BitBadger.Documents.Sqlite*.nupkg || true
|
||||||
dotnet pack Sqlite/BitBadger.Documents.Sqlite.fsproj -c Release
|
dotnet pack Sqlite/BitBadger.Documents.Sqlite.fsproj -c Release
|
||||||
cp Sqlite/bin/Release/BitBadger.Documents.Sqlite.*.nupkg .
|
cp Sqlite/bin/Release/BitBadger.Documents.Sqlite.*.nupkg .
|
||||||
|
|
Loading…
Reference in New Issue
Block a user