Compare commits
No commits in common. "a0da5f83b1e061c792a9c637fcc99dbbe64ec605" and "fc045d021cd427d799fe73ccb80d294b02b22962" have entirely different histories.
a0da5f83b1
...
fc045d021c
@ -13,8 +13,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FSharp.SystemTextJson" Version="1.3.13" />
|
<PackageReference Include="FSharp.SystemTextJson" Version="1.2.42" />
|
||||||
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -15,8 +15,6 @@ type Op =
|
|||||||
| LE
|
| LE
|
||||||
/// Not Equal to (<>)
|
/// Not Equal to (<>)
|
||||||
| NE
|
| NE
|
||||||
/// Between (BETWEEN)
|
|
||||||
| BT
|
|
||||||
/// Exists (IS NOT NULL)
|
/// Exists (IS NOT NULL)
|
||||||
| EX
|
| EX
|
||||||
/// Does Not Exist (IS NULL)
|
/// Does Not Exist (IS NULL)
|
||||||
@ -30,7 +28,6 @@ type Op =
|
|||||||
| LT -> "<"
|
| LT -> "<"
|
||||||
| LE -> "<="
|
| LE -> "<="
|
||||||
| NE -> "<>"
|
| NE -> "<>"
|
||||||
| BT -> "BETWEEN"
|
|
||||||
| EX -> "IS NOT NULL"
|
| EX -> "IS NOT NULL"
|
||||||
| NEX -> "IS NULL"
|
| NEX -> "IS NULL"
|
||||||
|
|
||||||
@ -71,15 +68,11 @@ type Field = {
|
|||||||
static member NE name (value: obj) =
|
static member NE name (value: obj) =
|
||||||
{ Name = name; Op = NE; Value = value }
|
{ Name = name; Op = NE; Value = value }
|
||||||
|
|
||||||
/// Create a BETWEEN field criterion
|
|
||||||
static member BT name (min: obj) (max: obj) =
|
|
||||||
{ Name = name; Op = BT; Value = [ min; max ] }
|
|
||||||
|
|
||||||
/// 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 () }
|
||||||
|
|
||||||
/// Create a not exists (IS NULL) field criterion
|
/// Create an 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 () }
|
||||||
|
|
||||||
@ -157,6 +150,17 @@ module Query =
|
|||||||
let selectFromTable tableName =
|
let selectFromTable tableName =
|
||||||
$"SELECT data FROM %s{tableName}"
|
$"SELECT data FROM %s{tableName}"
|
||||||
|
|
||||||
|
/// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
|
||||||
|
[<CompiledName "WhereByField">]
|
||||||
|
let whereByField field paramName =
|
||||||
|
let theRest = match field.Op with EX | NEX -> string field.Op | _ -> $"{field.Op} %s{paramName}"
|
||||||
|
$"data ->> '%s{field.Name}' {theRest}"
|
||||||
|
|
||||||
|
/// Create a WHERE clause fragment to implement an ID-based query
|
||||||
|
[<CompiledName "WhereById">]
|
||||||
|
let whereById paramName =
|
||||||
|
whereByField (Field.EQ (Configuration.idField ()) 0) paramName
|
||||||
|
|
||||||
/// Queries to define tables and indexes
|
/// Queries to define tables and indexes
|
||||||
module Definition =
|
module Definition =
|
||||||
|
|
||||||
@ -201,3 +205,59 @@ 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 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 =
|
||||||
|
$"""{selectFromTable tableName} WHERE {whereById "@id"}"""
|
||||||
|
|
||||||
|
/// Query to retrieve documents using a comparison on a JSON field
|
||||||
|
[<CompiledName "ByField">]
|
||||||
|
let byField tableName field =
|
||||||
|
$"""{selectFromTable tableName} WHERE {whereByField field "@field"}"""
|
||||||
|
|
||||||
|
/// 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"}"""
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||||
<AssemblyVersion>3.1.0.0</AssemblyVersion>
|
<AssemblyVersion>3.0.0.0</AssemblyVersion>
|
||||||
<FileVersion>3.1.0.0</FileVersion>
|
<FileVersion>3.0.0.0</FileVersion>
|
||||||
<VersionPrefix>3.1.0</VersionPrefix>
|
<VersionPrefix>3.0.0</VersionPrefix>
|
||||||
<PackageReleaseNotes>Add BT (between) operator; drop .NET 7 support</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>
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Npgsql.FSharp" Version="5.7.0" />
|
<PackageReference Include="Npgsql.FSharp" Version="5.7.0" />
|
||||||
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -63,29 +63,17 @@ module Parameters =
|
|||||||
let jsonParam (name: string) (it: 'TJson) =
|
let jsonParam (name: string) (it: 'TJson) =
|
||||||
name, Sql.jsonb (Configuration.serializer().Serialize it)
|
name, Sql.jsonb (Configuration.serializer().Serialize it)
|
||||||
|
|
||||||
/// Create a JSON field parameter
|
/// Create a JSON field parameter (name "@field")
|
||||||
[<CompiledName "FSharpAddField">]
|
[<CompiledName "FSharpAddField">]
|
||||||
let addFieldParam name field parameters =
|
let addFieldParam name field parameters =
|
||||||
match field.Op with
|
match field.Op with
|
||||||
| EX | NEX -> parameters
|
| EX | NEX -> parameters
|
||||||
| BT ->
|
|
||||||
let values = field.Value :?> obj list
|
|
||||||
List.concat
|
|
||||||
[ parameters
|
|
||||||
[ ($"{name}min", Sql.parameter (NpgsqlParameter($"{name}min", List.head values)))
|
|
||||||
($"{name}max", Sql.parameter (NpgsqlParameter($"{name}max", List.last values))) ] ]
|
|
||||||
| _ -> (name, Sql.parameter (NpgsqlParameter(name, field.Value))) :: parameters
|
| _ -> (name, Sql.parameter (NpgsqlParameter(name, field.Value))) :: parameters
|
||||||
|
|
||||||
/// Create a JSON field parameter
|
/// Create a JSON field parameter (name "@field")
|
||||||
let AddField name field parameters =
|
let AddField name field parameters =
|
||||||
match field.Op with
|
match field.Op with
|
||||||
| EX | NEX -> parameters
|
| EX | NEX -> parameters
|
||||||
| BT ->
|
|
||||||
let values = field.Value :?> obj list
|
|
||||||
ResizeArray
|
|
||||||
[ ($"{name}min", Sql.parameter (NpgsqlParameter($"{name}min", List.head values)))
|
|
||||||
($"{name}max", Sql.parameter (NpgsqlParameter($"{name}max", List.last values))) ]
|
|
||||||
|> Seq.append parameters
|
|
||||||
| _ -> (name, Sql.parameter (NpgsqlParameter(name, field.Value))) |> Seq.singleton |> Seq.append parameters
|
| _ -> (name, Sql.parameter (NpgsqlParameter(name, field.Value))) |> Seq.singleton |> Seq.append 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
|
||||||
@ -109,25 +97,6 @@ module Parameters =
|
|||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Query =
|
module Query =
|
||||||
|
|
||||||
/// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
|
|
||||||
[<CompiledName "WhereByField">]
|
|
||||||
let whereByField field paramName =
|
|
||||||
match field.Op with
|
|
||||||
| EX | NEX -> $"data->>'{field.Name}' {field.Op}"
|
|
||||||
| BT ->
|
|
||||||
let names = $"{paramName}min AND {paramName}max"
|
|
||||||
let values = field.Value :?> obj list
|
|
||||||
match values[0] with
|
|
||||||
| :? int8 | :? uint8 | :? int16 | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64
|
|
||||||
| :? decimal | :? single | :? double -> $"(data->>'{field.Name}')::numeric {field.Op} {names}"
|
|
||||||
| _ -> $"data->>'{field.Name}' {field.Op} {names}"
|
|
||||||
| _ -> $"data->>'{field.Name}' {field.Op} %s{paramName}"
|
|
||||||
|
|
||||||
/// Create a WHERE clause fragment to implement an ID-based query
|
|
||||||
[<CompiledName "WhereById">]
|
|
||||||
let whereById paramName =
|
|
||||||
whereByField (Field.EQ (Configuration.idField ()) 0) paramName
|
|
||||||
|
|
||||||
/// Table and index definition queries
|
/// Table and index definition queries
|
||||||
module Definition =
|
module Definition =
|
||||||
|
|
||||||
@ -143,11 +112,6 @@ module Query =
|
|||||||
let tableName = name.Split '.' |> Array.last
|
let tableName = name.Split '.' |> Array.last
|
||||||
$"CREATE INDEX IF NOT EXISTS idx_{tableName}_document ON {name} USING GIN (data{extraOps})"
|
$"CREATE INDEX IF NOT EXISTS idx_{tableName}_document ON {name} USING GIN (data{extraOps})"
|
||||||
|
|
||||||
/// Query to update a document
|
|
||||||
[<CompiledName "Update">]
|
|
||||||
let update tableName =
|
|
||||||
$"""UPDATE %s{tableName} SET data = @data WHERE {whereById "@id"}"""
|
|
||||||
|
|
||||||
/// Create a WHERE clause fragment to implement a @> (JSON contains) condition
|
/// Create a WHERE clause fragment to implement a @> (JSON contains) condition
|
||||||
[<CompiledName "WhereDataContains">]
|
[<CompiledName "WhereDataContains">]
|
||||||
let whereDataContains paramName =
|
let whereDataContains paramName =
|
||||||
@ -161,16 +125,6 @@ module Query =
|
|||||||
/// Queries for counting documents
|
/// Queries for counting documents
|
||||||
module Count =
|
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"}"""
|
|
||||||
|
|
||||||
/// Query to count matching documents using a JSON containment query (@>)
|
/// Query to count matching documents using a JSON containment query (@>)
|
||||||
[<CompiledName "ByContains">]
|
[<CompiledName "ByContains">]
|
||||||
let byContains tableName =
|
let byContains tableName =
|
||||||
@ -184,16 +138,6 @@ module Query =
|
|||||||
/// Queries for determining document existence
|
/// Queries for determining document existence
|
||||||
module Exists =
|
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"""
|
|
||||||
|
|
||||||
/// Query to determine if documents exist using a JSON containment query (@>)
|
/// Query to determine if documents exist using a JSON containment query (@>)
|
||||||
[<CompiledName "ByContains">]
|
[<CompiledName "ByContains">]
|
||||||
let byContains tableName =
|
let byContains tableName =
|
||||||
@ -207,16 +151,6 @@ module Query =
|
|||||||
/// Queries for retrieving documents
|
/// Queries for retrieving documents
|
||||||
module Find =
|
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"}"""
|
|
||||||
|
|
||||||
/// Query to retrieve documents using a JSON containment query (@>)
|
/// Query to retrieve documents using a JSON containment query (@>)
|
||||||
[<CompiledName "ByContains">]
|
[<CompiledName "ByContains">]
|
||||||
let byContains tableName =
|
let byContains tableName =
|
||||||
@ -237,12 +171,12 @@ module Query =
|
|||||||
/// Query to patch a document by its ID
|
/// Query to patch a document by its ID
|
||||||
[<CompiledName "ById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName =
|
let byId tableName =
|
||||||
whereById "@id" |> update tableName
|
Query.whereById "@id" |> update tableName
|
||||||
|
|
||||||
/// Query to patch documents match a JSON field comparison (->> =)
|
/// Query to patch documents match a JSON field comparison (->> =)
|
||||||
[<CompiledName "ByField">]
|
[<CompiledName "ByField">]
|
||||||
let byField tableName field =
|
let byField tableName field =
|
||||||
whereByField field "@field" |> update tableName
|
Query.whereByField field "@field" |> update tableName
|
||||||
|
|
||||||
/// Query to patch documents matching a JSON containment query (@>)
|
/// Query to patch documents matching a JSON containment query (@>)
|
||||||
[<CompiledName "ByContains">]
|
[<CompiledName "ByContains">]
|
||||||
@ -264,12 +198,12 @@ module Query =
|
|||||||
/// Query to remove fields from a document by the document's ID
|
/// Query to remove fields from a document by the document's ID
|
||||||
[<CompiledName "ById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName =
|
let byId tableName =
|
||||||
whereById "@id" |> update tableName
|
Query.whereById "@id" |> update tableName
|
||||||
|
|
||||||
/// Query to remove fields from documents via a comparison on a JSON field within the document
|
/// Query to remove fields from documents via a comparison on a JSON field within the document
|
||||||
[<CompiledName "ByField">]
|
[<CompiledName "ByField">]
|
||||||
let byField tableName field =
|
let byField tableName field =
|
||||||
whereByField field "@field" |> update tableName
|
Query.whereByField field "@field" |> update tableName
|
||||||
|
|
||||||
/// Query to patch documents matching a JSON containment query (@>)
|
/// Query to patch documents matching a JSON containment query (@>)
|
||||||
[<CompiledName "ByContains">]
|
[<CompiledName "ByContains">]
|
||||||
@ -284,16 +218,6 @@ module Query =
|
|||||||
/// Queries to delete documents
|
/// Queries to delete documents
|
||||||
module Delete =
|
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"}"""
|
|
||||||
|
|
||||||
/// Query to delete documents using a JSON containment query (@>)
|
/// Query to delete documents using a JSON containment query (@>)
|
||||||
[<CompiledName "ByContains">]
|
[<CompiledName "ByContains">]
|
||||||
let byContains tableName =
|
let byContains tableName =
|
||||||
|
@ -14,8 +14,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.6" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.1" />
|
||||||
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -31,21 +31,6 @@ module Configuration =
|
|||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Query =
|
module Query =
|
||||||
|
|
||||||
/// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
|
|
||||||
[<CompiledName "WhereByField">]
|
|
||||||
let whereByField field paramName =
|
|
||||||
let theRest =
|
|
||||||
match field.Op with
|
|
||||||
| EX | NEX -> ""
|
|
||||||
| BT -> $" {paramName}min AND {paramName}max"
|
|
||||||
| _ -> $" %s{paramName}"
|
|
||||||
$"data->>'{field.Name}' {field.Op}{theRest}"
|
|
||||||
|
|
||||||
/// Create a WHERE clause fragment to implement an ID-based query
|
|
||||||
[<CompiledName "WhereById">]
|
|
||||||
let whereById paramName =
|
|
||||||
whereByField (Field.EQ (Configuration.idField ()) 0) paramName
|
|
||||||
|
|
||||||
/// Data definition
|
/// Data definition
|
||||||
module Definition =
|
module Definition =
|
||||||
|
|
||||||
@ -54,50 +39,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
|
/// Document patching (partial update) queries
|
||||||
module Patch =
|
module Patch =
|
||||||
|
|
||||||
@ -108,12 +49,12 @@ module Query =
|
|||||||
/// Query to patch (partially update) a document by its ID
|
/// Query to patch (partially update) a document by its ID
|
||||||
[<CompiledName "ById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName =
|
let byId tableName =
|
||||||
whereById "@id" |> update tableName
|
Query.whereById "@id" |> update tableName
|
||||||
|
|
||||||
/// Query to patch (partially update) a document via a comparison on a JSON field
|
/// Query to patch (partially update) a document via a comparison on a JSON field
|
||||||
[<CompiledName "ByField">]
|
[<CompiledName "ByField">]
|
||||||
let byField tableName field =
|
let byField tableName field =
|
||||||
whereByField field "@field" |> update tableName
|
Query.whereByField field "@field" |> update tableName
|
||||||
|
|
||||||
/// Queries to remove fields from documents
|
/// Queries to remove fields from documents
|
||||||
module RemoveFields =
|
module RemoveFields =
|
||||||
@ -126,7 +67,7 @@ module Query =
|
|||||||
/// Query to remove fields from a document by the document's ID
|
/// Query to remove fields from a document by the document's ID
|
||||||
[<CompiledName "FSharpById">]
|
[<CompiledName "FSharpById">]
|
||||||
let byId tableName parameters =
|
let byId tableName parameters =
|
||||||
whereById "@id" |> update tableName parameters
|
Query.whereById "@id" |> update tableName parameters
|
||||||
|
|
||||||
/// Query to remove fields from a document by the document's ID
|
/// Query to remove fields from a document by the document's ID
|
||||||
let ById(tableName, parameters) =
|
let ById(tableName, parameters) =
|
||||||
@ -135,25 +76,12 @@ module Query =
|
|||||||
/// Query to remove fields from documents via a comparison on a JSON field within the document
|
/// Query to remove fields from documents via a comparison on a JSON field within the document
|
||||||
[<CompiledName "FSharpByField">]
|
[<CompiledName "FSharpByField">]
|
||||||
let byField tableName field parameters =
|
let byField tableName field parameters =
|
||||||
whereByField field "@field" |> update tableName parameters
|
Query.whereByField field "@field" |> update tableName parameters
|
||||||
|
|
||||||
/// Query to remove fields from documents via a comparison on a JSON field within the document
|
/// Query to remove fields from documents via a comparison on a JSON field within the document
|
||||||
let ByField(tableName, field, parameters) =
|
let ByField(tableName, field, parameters) =
|
||||||
byField tableName field (List.ofSeq 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>]
|
||||||
@ -172,26 +100,12 @@ module Parameters =
|
|||||||
/// Create a JSON field parameter (name "@field")
|
/// Create a JSON field parameter (name "@field")
|
||||||
[<CompiledName "FSharpAddField">]
|
[<CompiledName "FSharpAddField">]
|
||||||
let addFieldParam name field parameters =
|
let addFieldParam name field parameters =
|
||||||
match field.Op with
|
match field.Op with EX | NEX -> parameters | _ -> SqliteParameter(name, field.Value) :: parameters
|
||||||
| EX | NEX -> parameters
|
|
||||||
| BT ->
|
|
||||||
let values = field.Value :?> obj list
|
|
||||||
SqliteParameter($"{name}min", values[0]) :: SqliteParameter($"{name}max", values[1]) :: parameters
|
|
||||||
| _ -> SqliteParameter(name, field.Value) :: parameters
|
|
||||||
|
|
||||||
/// Create a JSON field parameter (name "@field")
|
/// Create a JSON field parameter (name "@field")
|
||||||
let AddField(name, field, parameters) =
|
let AddField(name, field, parameters) =
|
||||||
match field.Op with
|
match field.Op with
|
||||||
| EX | NEX -> parameters
|
| 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
|
|
||||||
| _ -> SqliteParameter(name, field.Value) |> Seq.singleton |> Seq.append parameters
|
| _ -> SqliteParameter(name, field.Value) |> Seq.singleton |> Seq.append 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
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Expecto" Version="10.2.1" />
|
<PackageReference Include="Expecto" Version="10.1.0" />
|
||||||
<PackageReference Include="ThrowawayDb.Postgres" Version="1.4.0" />
|
<PackageReference Include="ThrowawayDb.Postgres" Version="1.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Expecto.CSharp;
|
using Expecto.CSharp;
|
||||||
using Expecto;
|
using Expecto;
|
||||||
using Microsoft.FSharp.Collections;
|
|
||||||
|
|
||||||
namespace BitBadger.Documents.Tests.CSharp;
|
namespace BitBadger.Documents.Tests.CSharp;
|
||||||
|
|
||||||
@ -97,10 +96,6 @@ public static class CommonCSharpTests
|
|||||||
{
|
{
|
||||||
Expect.equal(Op.NE.ToString(), "<>", "The not equal to operator was not correct");
|
Expect.equal(Op.NE.ToString(), "<>", "The not equal to operator was not correct");
|
||||||
}),
|
}),
|
||||||
TestCase("BT succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Op.BT.ToString(), "BETWEEN", "The \"between\" operator was not correct");
|
|
||||||
}),
|
|
||||||
TestCase("EX succeeds", () =>
|
TestCase("EX succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Op.EX.ToString(), "IS NOT NULL", "The \"exists\" operator was not correct");
|
Expect.equal(Op.EX.ToString(), "IS NOT NULL", "The \"exists\" operator was not correct");
|
||||||
@ -154,13 +149,6 @@ public static class CommonCSharpTests
|
|||||||
Expect.equal(field.Op, Op.NE, "Operator incorrect");
|
Expect.equal(field.Op, Op.NE, "Operator incorrect");
|
||||||
Expect.equal(field.Value, "here", "Value incorrect");
|
Expect.equal(field.Value, "here", "Value incorrect");
|
||||||
}),
|
}),
|
||||||
TestCase("BT succeeds", () =>
|
|
||||||
{
|
|
||||||
var field = Field.BT("Age", 18, 49);
|
|
||||||
Expect.equal(field.Name, "Age", "Field name incorrect");
|
|
||||||
Expect.equal(field.Op, Op.BT, "Operator incorrect");
|
|
||||||
Expect.equal(((FSharpList<object>)field.Value).ToArray(), new object[] { 18, 49 }, "Value incorrect");
|
|
||||||
}),
|
|
||||||
TestCase("EX succeeds", () =>
|
TestCase("EX succeeds", () =>
|
||||||
{
|
{
|
||||||
var field = Field.EX("Groovy");
|
var field = Field.EX("Groovy");
|
||||||
@ -181,6 +169,23 @@ public static class CommonCSharpTests
|
|||||||
Expect.equal(Query.SelectFromTable("test.table"), "SELECT data FROM test.table",
|
Expect.equal(Query.SelectFromTable("test.table"), "SELECT data FROM test.table",
|
||||||
"SELECT statement not correct");
|
"SELECT statement not correct");
|
||||||
}),
|
}),
|
||||||
|
TestCase("WhereById succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.WhereById("@id"), "data ->> 'Id' = @id", "WHERE clause not correct");
|
||||||
|
}),
|
||||||
|
TestList("WhereByField", new[]
|
||||||
|
{
|
||||||
|
TestCase("succeeds when a logical operator is passed", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.WhereByField(Field.GT("theField", 0), "@test"), "data ->> 'theField' > @test",
|
||||||
|
"WHERE clause not correct");
|
||||||
|
}),
|
||||||
|
TestCase("succeeds when an existence operator is passed", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.WhereByField(Field.NEX("thatField"), ""), "data ->> 'thatField' IS NULL",
|
||||||
|
"WHERE clause not correct");
|
||||||
|
})
|
||||||
|
}),
|
||||||
TestList("Definition", new[]
|
TestList("Definition", new[]
|
||||||
{
|
{
|
||||||
TestCase("EnsureTableFor succeeds", () =>
|
TestCase("EnsureTableFor succeeds", () =>
|
||||||
@ -221,8 +226,69 @@ public static class CommonCSharpTests
|
|||||||
TestCase("Save succeeds", () =>
|
TestCase("Save succeeds", () =>
|
||||||
{
|
{
|
||||||
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("Update succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(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(Query.Count.All("tbl"), "SELECT COUNT(*) AS it FROM tbl", "Count query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("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", new[]
|
||||||
|
{
|
||||||
|
TestCase("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");
|
||||||
|
}),
|
||||||
|
TestCase("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", new[]
|
||||||
|
{
|
||||||
|
TestCase("ById succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.Find.ById("tbl"), "SELECT data FROM tbl WHERE data ->> 'Id' = @id",
|
||||||
|
"SELECT by ID query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("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("Delete", new[]
|
||||||
|
{
|
||||||
|
TestCase("ById succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Query.Delete.ById("tbl"), "DELETE FROM tbl WHERE data ->> 'Id' = @id",
|
||||||
|
"DELETE by ID query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("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");
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -47,48 +47,42 @@ public static class PostgresCSharpTests
|
|||||||
{
|
{
|
||||||
var it = Parameters.AddField("@it", Field.EX("It"), Enumerable.Empty<Tuple<string, SqlValue>>());
|
var it = Parameters.AddField("@it", Field.EX("It"), Enumerable.Empty<Tuple<string, SqlValue>>());
|
||||||
Expect.isEmpty(it, "There should not have been any parameters added");
|
Expect.isEmpty(it, "There should not have been any parameters added");
|
||||||
}),
|
|
||||||
TestCase("succeeds when two parameters are added", () =>
|
|
||||||
{
|
|
||||||
var it = Parameters.AddField("@field", Field.BT("that", "eh", "zed"),
|
|
||||||
Enumerable.Empty<Tuple<string, SqlValue>>()).ToList();
|
|
||||||
Expect.hasLength(it, 2, "There should have been 2 parameters added");
|
|
||||||
Expect.equal(it[0].Item1, "@fieldmin", "Minimum field name not correct");
|
|
||||||
Expect.isTrue(it[0].Item2.IsParameter, "Minimum field parameter value incorrect");
|
|
||||||
Expect.equal(it[1].Item1, "@fieldmax", "Maximum field name not correct");
|
|
||||||
Expect.isTrue(it[1].Item2.IsParameter, "Maximum field parameter value incorrect");
|
|
||||||
})
|
})
|
||||||
|
}),
|
||||||
|
TestList("RemoveFields", new[]
|
||||||
|
{
|
||||||
|
TestCase("ById succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.RemoveFields.ById("tbl"),
|
||||||
|
"UPDATE tbl SET data = data - @name WHERE data ->> 'Id' = @id",
|
||||||
|
"Remove field by ID query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("ByField succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.RemoveFields.ByField("tbl", Field.LT("Fly", 0)),
|
||||||
|
"UPDATE tbl SET data = data - @name WHERE data ->> 'Fly' < @field",
|
||||||
|
"Remove field by field query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("ByContains succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.RemoveFields.ByContains("tbl"),
|
||||||
|
"UPDATE tbl SET data = data - @name WHERE data @> @criteria",
|
||||||
|
"Remove field by contains query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("ByJsonPath succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.RemoveFields.ByJsonPath("tbl"),
|
||||||
|
"UPDATE tbl SET data = data - @name WHERE data @? @path::jsonpath",
|
||||||
|
"Remove field by JSON path query not correct");
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
TestCase("None succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.isEmpty(Parameters.None, "The no-params sequence should be empty");
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
TestList("Query", new[]
|
TestList("Query", new[]
|
||||||
{
|
{
|
||||||
TestList("WhereByField", new[]
|
|
||||||
{
|
|
||||||
TestCase("succeeds when a logical operator is passed", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.WhereByField(Field.GT("theField", 0), "@test"),
|
|
||||||
"data->>'theField' > @test", "WHERE clause not correct");
|
|
||||||
}),
|
|
||||||
TestCase("succeeds when an existence operator is passed", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.WhereByField(Field.NEX("thatField"), ""), "data->>'thatField' IS NULL",
|
|
||||||
"WHERE clause not correct");
|
|
||||||
}),
|
|
||||||
TestCase("succeeds when a between operator is passed with numeric values", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.WhereByField(Field.BT("aField", 50, 99), "@range"),
|
|
||||||
"(data->>'aField')::numeric BETWEEN @rangemin AND @rangemax", "WHERE clause not correct");
|
|
||||||
}),
|
|
||||||
TestCase("succeeds when a between operator is passed with non-numeric values", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.WhereByField(Field.BT("field0", "a", "b"), "@alpha"),
|
|
||||||
"data->>'field0' BETWEEN @alphamin AND @alphamax", "WHERE clause not correct");
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
TestCase("WhereById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct");
|
|
||||||
}),
|
|
||||||
TestList("Definition", new[]
|
TestList("Definition", new[]
|
||||||
{
|
{
|
||||||
TestCase("EnsureTable succeeds", () =>
|
TestCase("EnsureTable succeeds", () =>
|
||||||
@ -113,11 +107,6 @@ public static class PostgresCSharpTests
|
|||||||
"CREATE INDEX statement not constructed correctly");
|
"CREATE INDEX statement not constructed correctly");
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
TestCase("Update succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.Update("tbl"), "UPDATE tbl SET data = @data WHERE data->>'Id' = @id",
|
|
||||||
"UPDATE full statement not correct");
|
|
||||||
}),
|
|
||||||
TestCase("WhereDataContains succeeds", () =>
|
TestCase("WhereDataContains succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Postgres.Query.WhereDataContains("@test"), "data @> @test",
|
Expect.equal(Postgres.Query.WhereDataContains("@test"), "data @> @test",
|
||||||
@ -130,17 +119,6 @@ public static class PostgresCSharpTests
|
|||||||
}),
|
}),
|
||||||
TestList("Count", new[]
|
TestList("Count", new[]
|
||||||
{
|
{
|
||||||
TestCase("All succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.Count.All(PostgresDb.TableName),
|
|
||||||
$"SELECT COUNT(*) AS it FROM {PostgresDb.TableName}", "Count query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.Count.ByField(PostgresDb.TableName, Field.EQ("thatField", 0)),
|
|
||||||
$"SELECT COUNT(*) AS it FROM {PostgresDb.TableName} WHERE data->>'thatField' = @field",
|
|
||||||
"JSON field text comparison count query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByContains succeeds", () =>
|
TestCase("ByContains succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Postgres.Query.Count.ByContains(PostgresDb.TableName),
|
Expect.equal(Postgres.Query.Count.ByContains(PostgresDb.TableName),
|
||||||
@ -156,18 +134,6 @@ public static class PostgresCSharpTests
|
|||||||
}),
|
}),
|
||||||
TestList("Exists", new[]
|
TestList("Exists", new[]
|
||||||
{
|
{
|
||||||
TestCase("ById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.Exists.ById(PostgresDb.TableName),
|
|
||||||
$"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data->>'Id' = @id) AS it",
|
|
||||||
"ID existence query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.Exists.ByField(PostgresDb.TableName, Field.LT("Test", 0)),
|
|
||||||
$"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data->>'Test' < @field) AS it",
|
|
||||||
"JSON field text comparison exists query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByContains succeeds", () =>
|
TestCase("ByContains succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Postgres.Query.Exists.ByContains(PostgresDb.TableName),
|
Expect.equal(Postgres.Query.Exists.ByContains(PostgresDb.TableName),
|
||||||
@ -183,18 +149,6 @@ public static class PostgresCSharpTests
|
|||||||
}),
|
}),
|
||||||
TestList("Find", new[]
|
TestList("Find", new[]
|
||||||
{
|
{
|
||||||
TestCase("ById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.Find.ById(PostgresDb.TableName),
|
|
||||||
$"SELECT data FROM {PostgresDb.TableName} WHERE data->>'Id' = @id",
|
|
||||||
"SELECT by ID query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.Find.ByField(PostgresDb.TableName, Field.GE("Golf", 0)),
|
|
||||||
$"SELECT data FROM {PostgresDb.TableName} WHERE data->>'Golf' >= @field",
|
|
||||||
"SELECT by JSON comparison query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("byContains succeeds", () =>
|
TestCase("byContains succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Postgres.Query.Find.ByContains(PostgresDb.TableName),
|
Expect.equal(Postgres.Query.Find.ByContains(PostgresDb.TableName),
|
||||||
@ -235,47 +189,8 @@ public static class PostgresCSharpTests
|
|||||||
"UPDATE partial by JSON Path statement not correct");
|
"UPDATE partial by JSON Path statement not correct");
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
TestList("RemoveFields", new[]
|
|
||||||
{
|
|
||||||
TestCase("ById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.RemoveFields.ById(PostgresDb.TableName),
|
|
||||||
$"UPDATE {PostgresDb.TableName} SET data = data - @name WHERE data->>'Id' = @id",
|
|
||||||
"Remove field by ID query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.RemoveFields.ByField(PostgresDb.TableName, Field.LT("Fly", 0)),
|
|
||||||
$"UPDATE {PostgresDb.TableName} SET data = data - @name WHERE data->>'Fly' < @field",
|
|
||||||
"Remove field by field query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByContains succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.RemoveFields.ByContains(PostgresDb.TableName),
|
|
||||||
$"UPDATE {PostgresDb.TableName} SET data = data - @name WHERE data @> @criteria",
|
|
||||||
"Remove field by contains query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByJsonPath succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.RemoveFields.ByJsonPath(PostgresDb.TableName),
|
|
||||||
$"UPDATE {PostgresDb.TableName} SET data = data - @name WHERE data @? @path::jsonpath",
|
|
||||||
"Remove field by JSON path query not correct");
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
TestList("Delete", new[]
|
TestList("Delete", new[]
|
||||||
{
|
{
|
||||||
TestCase("ById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.Delete.ById(PostgresDb.TableName),
|
|
||||||
$"DELETE FROM {PostgresDb.TableName} WHERE data->>'Id' = @id",
|
|
||||||
"DELETE by ID query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Postgres.Query.Delete.ByField(PostgresDb.TableName, Field.NEX("gone")),
|
|
||||||
$"DELETE FROM {PostgresDb.TableName} WHERE data->>'gone' IS NULL",
|
|
||||||
"DELETE by JSON comparison query not correct");
|
|
||||||
}),
|
|
||||||
TestCase("byContains succeeds", () =>
|
TestCase("byContains succeeds", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Postgres.Query.Delete.ByContains(PostgresDb.TableName),
|
Expect.equal(Postgres.Query.Delete.ByContains(PostgresDb.TableName),
|
||||||
@ -549,21 +464,13 @@ public static class PostgresCSharpTests
|
|||||||
var theCount = await Count.All(PostgresDb.TableName);
|
var theCount = await Count.All(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("ByField succeeds for numeric range", async () =>
|
TestCase("ByField succeeds", async () =>
|
||||||
{
|
{
|
||||||
await using var db = PostgresDb.BuildDb();
|
await using var db = PostgresDb.BuildDb();
|
||||||
await LoadDocs();
|
await LoadDocs();
|
||||||
|
|
||||||
var theCount = await Count.ByField(PostgresDb.TableName, Field.BT("NumValue", 10, 20));
|
var theCount = await Count.ByField(PostgresDb.TableName, Field.EQ("Value", "purple"));
|
||||||
Expect.equal(theCount, 3, "There should have been 3 matching documents");
|
Expect.equal(theCount, 2, "There should have been 2 matching documents");
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds for non-numeric range", async () =>
|
|
||||||
{
|
|
||||||
await using var db = PostgresDb.BuildDb();
|
|
||||||
await LoadDocs();
|
|
||||||
|
|
||||||
var theCount = await Count.ByField(PostgresDb.TableName, Field.BT("Value", "aardvark", "apple"));
|
|
||||||
Expect.equal(theCount, 1, "There should have been 1 matching document");
|
|
||||||
}),
|
}),
|
||||||
TestCase("ByContains succeeds", async () =>
|
TestCase("ByContains succeeds", async () =>
|
||||||
{
|
{
|
||||||
|
@ -21,81 +21,11 @@ public static class SqliteCSharpTests
|
|||||||
{
|
{
|
||||||
TestList("Query", new[]
|
TestList("Query", new[]
|
||||||
{
|
{
|
||||||
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");
|
|
||||||
}),
|
|
||||||
TestCase("succeeds when an existence operator is passed", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.WhereByField(Field.NEX("thatField"), ""), "data->>'thatField' IS NULL",
|
|
||||||
"WHERE clause not correct");
|
|
||||||
}),
|
|
||||||
TestCase("succeeds when the between operator is passed", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.WhereByField(Field.BT("aField", 50, 99), "@range"),
|
|
||||||
"data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct");
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
TestCase("WhereById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause 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[]
|
|
||||||
{
|
|
||||||
TestCase("ById succeeds", () =>
|
|
||||||
{
|
|
||||||
Expect.equal(Sqlite.Query.Exists.ById("tbl"),
|
|
||||||
"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[]
|
TestList("Patch", new[]
|
||||||
{
|
{
|
||||||
TestCase("ById succeeds", () =>
|
TestCase("ById succeeds", () =>
|
||||||
@ -126,19 +56,6 @@ public static class SqliteCSharpTests
|
|||||||
"UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data ->> 'Fly' < @field",
|
"UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data ->> 'Fly' < @field",
|
||||||
"Remove field by field query not correct");
|
"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[]
|
TestList("Parameters", new[]
|
||||||
@ -399,21 +316,13 @@ 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 () =>
|
TestCase("ByField succeeds", 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.ByField(SqliteDb.TableName, Field.EQ("Value", "purple"));
|
||||||
Expect.equal(theCount, 3L, "There should have been 3 matching documents");
|
Expect.equal(theCount, 2L, "There should have been 2 matching documents");
|
||||||
}),
|
|
||||||
TestCase("ByField succeeds for non-numeric range", async () =>
|
|
||||||
{
|
|
||||||
await using var db = await SqliteDb.BuildDb();
|
|
||||||
await LoadDocs();
|
|
||||||
|
|
||||||
var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("Value", "aardvark", "apple"));
|
|
||||||
Expect.equal(theCount, 1L, "There should have been 1 matching document");
|
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
TestList("Exists", new[]
|
TestList("Exists", new[]
|
||||||
|
@ -15,8 +15,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Expecto" Version="10.2.1" />
|
<PackageReference Include="Expecto" Version="10.1.0" />
|
||||||
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -28,9 +28,6 @@ let all =
|
|||||||
test "NE succeeds" {
|
test "NE succeeds" {
|
||||||
Expect.equal (string NE) "<>" "The not equal to operator was not correct"
|
Expect.equal (string NE) "<>" "The not equal to operator was not correct"
|
||||||
}
|
}
|
||||||
test "BT succeeds" {
|
|
||||||
Expect.equal (string BT) "BETWEEN" """The "between" operator was not correct"""
|
|
||||||
}
|
|
||||||
test "EX succeeds" {
|
test "EX succeeds" {
|
||||||
Expect.equal (string EX) "IS NOT NULL" """The "exists" operator was not correct"""
|
Expect.equal (string EX) "IS NOT NULL" """The "exists" operator was not correct"""
|
||||||
}
|
}
|
||||||
@ -38,64 +35,27 @@ let all =
|
|||||||
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" [
|
|
||||||
test "EQ succeeds" {
|
|
||||||
let field = Field.EQ "Test" 14
|
|
||||||
Expect.equal field.Name "Test" "Field name incorrect"
|
|
||||||
Expect.equal field.Op EQ "Operator incorrect"
|
|
||||||
Expect.equal field.Value 14 "Value incorrect"
|
|
||||||
}
|
|
||||||
test "GT succeeds" {
|
|
||||||
let field = Field.GT "Great" "night"
|
|
||||||
Expect.equal field.Name "Great" "Field name incorrect"
|
|
||||||
Expect.equal field.Op GT "Operator incorrect"
|
|
||||||
Expect.equal field.Value "night" "Value incorrect"
|
|
||||||
}
|
|
||||||
test "GE succeeds" {
|
|
||||||
let field = Field.GE "Nice" 88L
|
|
||||||
Expect.equal field.Name "Nice" "Field name incorrect"
|
|
||||||
Expect.equal field.Op GE "Operator incorrect"
|
|
||||||
Expect.equal field.Value 88L "Value incorrect"
|
|
||||||
}
|
|
||||||
test "LT succeeds" {
|
|
||||||
let field = Field.LT "Lesser" "seven"
|
|
||||||
Expect.equal field.Name "Lesser" "Field name incorrect"
|
|
||||||
Expect.equal field.Op LT "Operator incorrect"
|
|
||||||
Expect.equal field.Value "seven" "Value incorrect"
|
|
||||||
}
|
|
||||||
test "LE succeeds" {
|
|
||||||
let field = Field.LE "Nobody" "KNOWS";
|
|
||||||
Expect.equal field.Name "Nobody" "Field name incorrect"
|
|
||||||
Expect.equal field.Op LE "Operator incorrect"
|
|
||||||
Expect.equal field.Value "KNOWS" "Value incorrect"
|
|
||||||
}
|
|
||||||
test "NE succeeds" {
|
|
||||||
let field = Field.NE "Park" "here"
|
|
||||||
Expect.equal field.Name "Park" "Field name incorrect"
|
|
||||||
Expect.equal field.Op NE "Operator incorrect"
|
|
||||||
Expect.equal field.Value "here" "Value incorrect"
|
|
||||||
}
|
|
||||||
test "BT succeeds" {
|
|
||||||
let field = Field.BT "Age" 18 49
|
|
||||||
Expect.equal field.Name "Age" "Field name incorrect"
|
|
||||||
Expect.equal field.Op BT "Operator incorrect"
|
|
||||||
Expect.sequenceEqual (field.Value :?> obj list) [ 18; 49 ] "Value incorrect"
|
|
||||||
}
|
|
||||||
test "EX succeeds" {
|
|
||||||
let field = Field.EX "Groovy"
|
|
||||||
Expect.equal field.Name "Groovy" "Field name incorrect"
|
|
||||||
Expect.equal field.Op EX "Operator incorrect"
|
|
||||||
}
|
|
||||||
test "NEX succeeds" {
|
|
||||||
let field = Field.NEX "Rad"
|
|
||||||
Expect.equal field.Name "Rad" "Field name incorrect"
|
|
||||||
Expect.equal field.Op NEX "Operator incorrect"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
testList "Query" [
|
testList "Query" [
|
||||||
test "selectFromTable succeeds" {
|
test "selectFromTable succeeds" {
|
||||||
Expect.equal (Query.selectFromTable tbl) $"SELECT data FROM {tbl}" "SELECT statement not correct"
|
Expect.equal (Query.selectFromTable tbl) $"SELECT data FROM {tbl}" "SELECT statement not correct"
|
||||||
}
|
}
|
||||||
|
test "whereById succeeds" {
|
||||||
|
Expect.equal (Query.whereById "@id") "data ->> 'Id' = @id" "WHERE clause not correct"
|
||||||
|
}
|
||||||
|
testList "whereByField" [
|
||||||
|
test "succeeds when a logical operator is passed" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.whereByField (Field.GT "theField" 0) "@test")
|
||||||
|
"data ->> 'theField' > @test"
|
||||||
|
"WHERE clause not correct"
|
||||||
|
}
|
||||||
|
test "succeeds when an existence operator is passed" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.whereByField (Field.NEX "thatField") "")
|
||||||
|
"data ->> 'thatField' IS NULL"
|
||||||
|
"WHERE clause not correct"
|
||||||
|
}
|
||||||
|
]
|
||||||
testList "Definition" [
|
testList "Definition" [
|
||||||
test "ensureTableFor succeeds" {
|
test "ensureTableFor succeeds" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
@ -135,6 +95,65 @@ 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 "update succeeds" {
|
||||||
|
Expect.equal
|
||||||
|
(Query.update tbl)
|
||||||
|
$"UPDATE {tbl} SET data = @data WHERE data ->> 'Id' = @id"
|
||||||
|
"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 "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"
|
||||||
|
}
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -34,59 +34,12 @@ let unitTests =
|
|||||||
let paramList = addFieldParam "@field" (Field.EX "tacos") []
|
let paramList = addFieldParam "@field" (Field.EX "tacos") []
|
||||||
Expect.isEmpty paramList "There should not have been any parameters added"
|
Expect.isEmpty paramList "There should not have been any parameters added"
|
||||||
}
|
}
|
||||||
test "succeeds when two parameters are added" {
|
|
||||||
let paramList = addFieldParam "@field" (Field.BT "that" "eh" "zed") []
|
|
||||||
Expect.hasLength paramList 2 "There should have been 2 parameters added"
|
|
||||||
let min = paramList[0]
|
|
||||||
Expect.equal (fst min) "@fieldmin" "Minimum field name not correct"
|
|
||||||
match snd min with
|
|
||||||
| SqlValue.Parameter value ->
|
|
||||||
Expect.equal value.ParameterName "@fieldmin" "Minimum parameter name not correct"
|
|
||||||
Expect.equal value.Value "eh" "Minimum parameter value not correct"
|
|
||||||
| _ -> Expect.isTrue false "Minimum parameter was not a Parameter type"
|
|
||||||
let max = paramList[1]
|
|
||||||
Expect.equal (fst max) "@fieldmax" "Maximum field name not correct"
|
|
||||||
match snd max with
|
|
||||||
| SqlValue.Parameter value ->
|
|
||||||
Expect.equal value.ParameterName "@fieldmax" "Maximum parameter name not correct"
|
|
||||||
Expect.equal value.Value "zed" "Maximum parameter value not correct"
|
|
||||||
| _ -> Expect.isTrue false "Maximum parameter was not a Parameter type"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
test "noParams succeeds" {
|
test "noParams succeeds" {
|
||||||
Expect.isEmpty noParams "The no-params sequence should be empty"
|
Expect.isEmpty noParams "The no-params sequence should be empty"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "Query" [
|
testList "Query" [
|
||||||
testList "whereByField" [
|
|
||||||
test "succeeds when a logical operator is passed" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.whereByField (Field.GT "theField" 0) "@test")
|
|
||||||
"data->>'theField' > @test"
|
|
||||||
"WHERE clause not correct"
|
|
||||||
}
|
|
||||||
test "succeeds when an existence operator is passed" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.whereByField (Field.NEX "thatField") "")
|
|
||||||
"data->>'thatField' IS NULL"
|
|
||||||
"WHERE clause not correct"
|
|
||||||
}
|
|
||||||
test "succeeds when a between operator is passed with numeric values" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.whereByField (Field.BT "aField" 50 99) "@range")
|
|
||||||
"(data->>'aField')::numeric BETWEEN @rangemin AND @rangemax"
|
|
||||||
"WHERE clause not correct"
|
|
||||||
}
|
|
||||||
test "succeeds when a between operator is passed with non-numeric values" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.whereByField (Field.BT "field0" "a" "b") "@alpha")
|
|
||||||
"data->>'field0' BETWEEN @alphamin AND @alphamax"
|
|
||||||
"WHERE clause not correct"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
test "whereById succeeds" {
|
|
||||||
Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause not correct"
|
|
||||||
}
|
|
||||||
testList "Definition" [
|
testList "Definition" [
|
||||||
test "ensureTable succeeds" {
|
test "ensureTable succeeds" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
@ -108,12 +61,6 @@ let unitTests =
|
|||||||
"CREATE INDEX statement not constructed correctly"
|
"CREATE INDEX statement not constructed correctly"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
test "update succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.update PostgresDb.TableName)
|
|
||||||
$"UPDATE {PostgresDb.TableName} SET data = @data WHERE data->>'Id' = @id"
|
|
||||||
"UPDATE full statement not correct"
|
|
||||||
}
|
|
||||||
test "whereDataContains succeeds" {
|
test "whereDataContains succeeds" {
|
||||||
Expect.equal (Query.whereDataContains "@test") "data @> @test" "WHERE clause not correct"
|
Expect.equal (Query.whereDataContains "@test") "data @> @test" "WHERE clause not correct"
|
||||||
}
|
}
|
||||||
@ -121,18 +68,6 @@ let unitTests =
|
|||||||
Expect.equal (Query.whereJsonPathMatches "@path") "data @? @path::jsonpath" "WHERE clause not correct"
|
Expect.equal (Query.whereJsonPathMatches "@path") "data @? @path::jsonpath" "WHERE clause not correct"
|
||||||
}
|
}
|
||||||
testList "Count" [
|
testList "Count" [
|
||||||
test "all succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Count.all PostgresDb.TableName)
|
|
||||||
$"SELECT COUNT(*) AS it FROM {PostgresDb.TableName}"
|
|
||||||
"Count query not correct"
|
|
||||||
}
|
|
||||||
test "byField succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Count.byField PostgresDb.TableName (Field.EQ "thatField" 0))
|
|
||||||
$"SELECT COUNT(*) AS it FROM {PostgresDb.TableName} WHERE data->>'thatField' = @field"
|
|
||||||
"JSON field text comparison count query not correct"
|
|
||||||
}
|
|
||||||
test "byContains succeeds" {
|
test "byContains succeeds" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.Count.byContains PostgresDb.TableName)
|
(Query.Count.byContains PostgresDb.TableName)
|
||||||
@ -147,18 +82,6 @@ let unitTests =
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "Exists" [
|
testList "Exists" [
|
||||||
test "byId succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Exists.byId PostgresDb.TableName)
|
|
||||||
$"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data->>'Id' = @id) AS it"
|
|
||||||
"ID existence query not correct"
|
|
||||||
}
|
|
||||||
test "byField succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Exists.byField PostgresDb.TableName (Field.LT "Test" 0))
|
|
||||||
$"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data->>'Test' < @field) AS it"
|
|
||||||
"JSON field text comparison exists query not correct"
|
|
||||||
}
|
|
||||||
test "byContains succeeds" {
|
test "byContains succeeds" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.Exists.byContains PostgresDb.TableName)
|
(Query.Exists.byContains PostgresDb.TableName)
|
||||||
@ -173,18 +96,6 @@ let unitTests =
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "Find" [
|
testList "Find" [
|
||||||
test "byId succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Find.byId PostgresDb.TableName)
|
|
||||||
$"SELECT data FROM {PostgresDb.TableName} WHERE data->>'Id' = @id"
|
|
||||||
"SELECT by ID query not correct"
|
|
||||||
}
|
|
||||||
test "byField succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Find.byField PostgresDb.TableName (Field.GE "Golf" 0))
|
|
||||||
$"SELECT data FROM {PostgresDb.TableName} WHERE data->>'Golf' >= @field"
|
|
||||||
"SELECT by JSON comparison query not correct"
|
|
||||||
}
|
|
||||||
test "byContains succeeds" {
|
test "byContains succeeds" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.Find.byContains PostgresDb.TableName)
|
(Query.Find.byContains PostgresDb.TableName)
|
||||||
@ -251,18 +162,6 @@ let unitTests =
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "Delete" [
|
testList "Delete" [
|
||||||
test "byId succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Delete.byId PostgresDb.TableName)
|
|
||||||
$"DELETE FROM {PostgresDb.TableName} WHERE data->>'Id' = @id"
|
|
||||||
"DELETE by ID query not correct"
|
|
||||||
}
|
|
||||||
test "byField succeeds" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.Delete.byField PostgresDb.TableName (Field.NEX "gone"))
|
|
||||||
$"DELETE FROM {PostgresDb.TableName} WHERE data->>'gone' IS NULL"
|
|
||||||
"DELETE by JSON comparison query not correct"
|
|
||||||
}
|
|
||||||
test "byContains succeeds" {
|
test "byContains succeeds" {
|
||||||
Expect.equal (Query.Delete.byContains PostgresDb.TableName)
|
Expect.equal (Query.Delete.byContains PostgresDb.TableName)
|
||||||
$"DELETE FROM {PostgresDb.TableName} WHERE data @> @criteria"
|
$"DELETE FROM {PostgresDb.TableName} WHERE data @> @criteria"
|
||||||
@ -488,19 +387,12 @@ let integrationTests =
|
|||||||
let! theCount = Count.all PostgresDb.TableName
|
let! theCount = Count.all 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 "byField succeeds for numeric range" {
|
testTask "byField succeeds" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
let! theCount = Count.byField PostgresDb.TableName (Field.BT "NumValue" 10 20)
|
let! theCount = Count.byField PostgresDb.TableName (Field.EQ "Value" "purple")
|
||||||
Expect.equal theCount 3 "There should have been 3 matching documents"
|
Expect.equal theCount 2 "There should have been 2 matching documents"
|
||||||
}
|
|
||||||
testTask "byField succeeds for non-numeric range" {
|
|
||||||
use db = PostgresDb.BuildDb()
|
|
||||||
do! loadDocs ()
|
|
||||||
|
|
||||||
let! theCount = Count.byField PostgresDb.TableName (Field.BT "Value" "aardvark" "apple")
|
|
||||||
Expect.equal theCount 1 "There should have been 1 matching document"
|
|
||||||
}
|
}
|
||||||
testTask "byContains succeeds" {
|
testTask "byContains succeeds" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
|
@ -12,80 +12,12 @@ open Types
|
|||||||
let unitTests =
|
let unitTests =
|
||||||
testList "Unit" [
|
testList "Unit" [
|
||||||
testList "Query" [
|
testList "Query" [
|
||||||
testList "whereByField" [
|
|
||||||
test "succeeds when a logical operator is passed" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.whereByField (Field.GT "theField" 0) "@test")
|
|
||||||
"data->>'theField' > @test"
|
|
||||||
"WHERE clause not correct"
|
|
||||||
}
|
|
||||||
test "succeeds when an existence operator is passed" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.whereByField (Field.NEX "thatField") "")
|
|
||||||
"data->>'thatField' IS NULL"
|
|
||||||
"WHERE clause not correct"
|
|
||||||
}
|
|
||||||
test "succeeds when the between operator is passed" {
|
|
||||||
Expect.equal
|
|
||||||
(Query.whereByField (Field.BT "aField" 50 99) "@range")
|
|
||||||
"data->>'aField' BETWEEN @rangemin AND @rangemax"
|
|
||||||
"WHERE clause not correct"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
test "whereById succeeds" {
|
|
||||||
Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause 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")
|
|
||||||
"UPDATE tbl SET data = @data WHERE data->>'Id' = @id"
|
|
||||||
"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" [
|
testList "Patch" [
|
||||||
test "byId succeeds" {
|
test "byId succeeds" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
@ -117,20 +49,6 @@ let unitTests =
|
|||||||
"Remove field by field query not correct"
|
"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" [
|
testList "Parameters" [
|
||||||
test "idParam succeeds" {
|
test "idParam succeeds" {
|
||||||
@ -381,19 +299,12 @@ 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" {
|
testTask "byField succeeds" {
|
||||||
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.byField SqliteDb.TableName (Field.EQ "Value" "purple")
|
||||||
Expect.equal theCount 3L "There should have been 3 matching documents"
|
Expect.equal theCount 2L "There should have been 2 matching documents"
|
||||||
}
|
|
||||||
testTask "byField succeeds for a non-numeric range" {
|
|
||||||
use! db = SqliteDb.BuildDb()
|
|
||||||
do! loadDocs ()
|
|
||||||
|
|
||||||
let! theCount = Count.byField SqliteDb.TableName (Field.BT "Value" "aardvark" "apple")
|
|
||||||
Expect.equal theCount 1L "There should have been 1 matching document"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "Exists" [
|
testList "Exists" [
|
||||||
|
@ -8,7 +8,7 @@ cd ./Tests || exit
|
|||||||
|
|
||||||
export BBDOX_PG_PORT=8301
|
export BBDOX_PG_PORT=8301
|
||||||
PG_VERSIONS=('12' '13' '14' '15' 'latest')
|
PG_VERSIONS=('12' '13' '14' '15' 'latest')
|
||||||
NET_VERSIONS=('6.0' '8.0')
|
NET_VERSIONS=('6.0' '7.0' '8.0')
|
||||||
|
|
||||||
for PG_VERSION in "${PG_VERSIONS[@]}"
|
for PG_VERSION in "${PG_VERSIONS[@]}"
|
||||||
do
|
do
|
||||||
|
Loading…
Reference in New Issue
Block a user