Add byFields to SQLite implementation

- Add fieldNameParams/FieldNames to Postgres
(syncs names between both implementations)
This commit is contained in:
Daniel J. Summers 2024-08-08 19:45:25 -04:00
parent 202fca272e
commit d8f64417e5
8 changed files with 461 additions and 258 deletions

View File

@ -1,5 +1,6 @@
namespace BitBadger.Documents.Postgres
open BitBadger.Documents
open Npgsql
open Npgsql.FSharp
@ -56,7 +57,7 @@ module Extensions =
/// Count matching documents using a JSON field comparison query (->> =)
[<System.Obsolete "Use countByFields instead; will be removed in v4">]
member conn.countByField tableName field =
WithProps.Count.byField tableName field (Sql.existingConnection conn)
conn.countByFields tableName Any [ field ]
/// Count matching documents using a JSON containment query (@>)
member conn.countByContains tableName criteria =
@ -77,7 +78,7 @@ module Extensions =
/// Determine if documents exist using a JSON field comparison query (->> =)
[<System.Obsolete "Use existsByFields instead; will be removed in v4">]
member conn.existsByField tableName field =
WithProps.Exists.byField tableName field (Sql.existingConnection conn)
conn.existsByFields tableName Any [ field ]
/// Determine if documents exist using a JSON containment query (@>)
member conn.existsByContains tableName criteria =
@ -102,7 +103,7 @@ module Extensions =
/// Retrieve documents matching a JSON field comparison query (->> =)
[<System.Obsolete "Use findByFields instead; will be removed in v4">]
member conn.findByField<'TDoc> tableName field =
WithProps.Find.byField<'TDoc> tableName field (Sql.existingConnection conn)
conn.findByFields<'TDoc> tableName Any [ field ]
/// Retrieve documents matching a JSON containment query (@>)
member conn.findByContains<'TDoc> tableName (criteria: obj) =
@ -119,7 +120,7 @@ module Extensions =
/// 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">]
member conn.findFirstByField<'TDoc> tableName field =
WithProps.Find.firstByField<'TDoc> tableName field (Sql.existingConnection conn)
conn.findFirstByFields<'TDoc> tableName Any [ field ]
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
member conn.findFirstByContains<'TDoc> tableName (criteria: obj) =
@ -148,7 +149,7 @@ module Extensions =
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<System.Obsolete "Use patchByFields instead; will be removed in v4">]
member conn.patchByField tableName field (patch: 'TPatch) =
WithProps.Patch.byField tableName field patch (Sql.existingConnection conn)
conn.patchByFields tableName Any [ field ] patch
/// Patch documents using a JSON containment query in the WHERE clause (@>)
member conn.patchByContains tableName (criteria: 'TCriteria) (patch: 'TPatch) =
@ -169,7 +170,7 @@ module Extensions =
/// Remove fields from documents via a comparison on a JSON field in the document
[<System.Obsolete "Use removeFieldsByFields instead; will be removed in v4">]
member conn.removeFieldsByField tableName field fieldNames =
WithProps.RemoveFields.byField tableName field fieldNames (Sql.existingConnection conn)
conn.removeFieldsByFields tableName Any [ field ] fieldNames
/// Remove fields from documents via a JSON containment query (@>)
member conn.removeFieldsByContains tableName (criteria: 'TContains) fieldNames =
@ -189,7 +190,7 @@ module Extensions =
/// Delete documents by matching a JSON field comparison query (->> =)
[<System.Obsolete "Use deleteByFields instead; will be removed in v4">]
member conn.deleteByField tableName field =
WithProps.Delete.byField tableName field (Sql.existingConnection conn)
conn.deleteByFields tableName Any [ field ]
/// Delete documents by matching a JSON containment query (@>)
member conn.deleteByContains tableName (criteria: 'TContains) =
@ -266,7 +267,7 @@ type NpgsqlConnectionCSharpExtensions =
[<Extension>]
[<System.Obsolete "Use CountByFields instead; will be removed in v4">]
static member inline CountByField(conn, tableName, field) =
WithProps.Count.byField tableName field (Sql.existingConnection conn)
conn.CountByFields(tableName, Any, [ field ])
/// Count matching documents using a JSON containment query (@>)
[<Extension>]
@ -292,7 +293,7 @@ type NpgsqlConnectionCSharpExtensions =
[<Extension>]
[<System.Obsolete "Use ExistsByFields instead; will be removed in v4">]
static member inline ExistsByField(conn, tableName, field) =
WithProps.Exists.byField tableName field (Sql.existingConnection conn)
conn.ExistsByFields(tableName, Any, [ field ])
/// Determine if documents exist using a JSON containment query (@>)
[<Extension>]
@ -323,7 +324,7 @@ type NpgsqlConnectionCSharpExtensions =
[<Extension>]
[<System.Obsolete "Use FindByFields instead; will be removed in v4">]
static member inline FindByField<'TDoc>(conn, tableName, field) =
WithProps.Find.ByField<'TDoc>(tableName, field, Sql.existingConnection conn)
conn.FindByFields<'TDoc>(tableName, Any, [ field ])
/// Retrieve documents matching a JSON containment query (@>)
[<Extension>]
@ -344,7 +345,7 @@ type NpgsqlConnectionCSharpExtensions =
[<Extension>]
[<System.Obsolete "Use FindFirstByFields instead; will be removed in v4">]
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
WithProps.Find.FirstByField<'TDoc>(tableName, field, Sql.existingConnection conn)
conn.FindFirstByFields<'TDoc>(tableName, Any, [ field ])
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
[<Extension>]
@ -380,7 +381,7 @@ type NpgsqlConnectionCSharpExtensions =
[<Extension>]
[<System.Obsolete "Use PatchByFields instead; will be removed in v4">]
static member inline PatchByField(conn, tableName, field, patch: 'TPatch) =
WithProps.Patch.byField tableName field patch (Sql.existingConnection conn)
conn.PatchByFields(tableName, Any, [ field ], patch)
/// Patch documents using a JSON containment query in the WHERE clause (@>)
[<Extension>]
@ -406,7 +407,7 @@ type NpgsqlConnectionCSharpExtensions =
[<Extension>]
[<System.Obsolete "Use RemoveFieldsByFields instead; will be removed in v4">]
static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
WithProps.RemoveFields.byField tableName field fieldNames (Sql.existingConnection conn)
conn.RemoveFieldsByFields(tableName, Any, [ field ], fieldNames)
/// Remove fields from documents via a JSON containment query (@>)
[<Extension>]
@ -432,7 +433,7 @@ type NpgsqlConnectionCSharpExtensions =
[<Extension>]
[<System.Obsolete "Use DeleteByFields instead; will be removed in v4">]
static member inline DeleteByField(conn, tableName, field) =
WithProps.Delete.byField tableName field (Sql.existingConnection conn)
conn.DeleteByFields(tableName, Any, [ field ])
/// Delete documents by matching a JSON containment query (@>)
[<Extension>]

View File

@ -90,12 +90,18 @@ module Parameters =
[<System.Obsolete "Use addFieldParams (F#) / AddFields (C#) 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
[<CompiledName "FieldName">]
let fieldNameParam (fieldNames: string seq) =
[<CompiledName "FieldNames">]
let fieldNameParams (fieldNames: string seq) =
if Seq.length fieldNames = 1 then "@name", Sql.string (Seq.head fieldNames)
else "@name", Sql.stringArray (Array.ofSeq fieldNames)
/// 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">]
let fieldNameParam fieldNames =
fieldNameParams fieldNames
/// An empty parameter sequence
[<CompiledName "None">]
@ -707,7 +713,7 @@ module WithProps =
/// Remove fields from a document by the document's ID
[<CompiledName "FSharpById">]
let byId tableName (docId: 'TKey) fieldNames sqlProps =
Custom.nonQuery (Query.RemoveFields.byId tableName) [ idParam docId; fieldNameParam fieldNames ] sqlProps
Custom.nonQuery (Query.RemoveFields.byId tableName) [ idParam docId; fieldNameParams fieldNames ] sqlProps
/// Remove fields from a document by the document's ID
let ById(tableName, docId: 'TKey, fieldNames, sqlProps) =
@ -718,7 +724,7 @@ module WithProps =
let byFields tableName howMatched fields fieldNames sqlProps =
Custom.nonQuery
(Query.RemoveFields.byFields tableName howMatched fields)
(addFieldParams fields [ fieldNameParam fieldNames ])
(addFieldParams fields [ fieldNameParams fieldNames ])
sqlProps
/// Remove fields from documents via a comparison on a JSON field in the document
@ -732,7 +738,7 @@ module WithProps =
let byContains tableName (criteria: 'TContains) fieldNames sqlProps =
Custom.nonQuery
(Query.RemoveFields.byContains tableName)
[ jsonParam "@criteria" criteria; fieldNameParam fieldNames ]
[ jsonParam "@criteria" criteria; fieldNameParams fieldNames ]
sqlProps
/// Remove fields from documents via a JSON containment query (@>)
@ -744,7 +750,7 @@ module WithProps =
let byJsonPath tableName jsonPath fieldNames sqlProps =
Custom.nonQuery
(Query.RemoveFields.byJsonPath tableName)
[ "@path", Sql.string jsonPath; fieldNameParam fieldNames ]
[ "@path", Sql.string jsonPath; fieldNameParams fieldNames ]
sqlProps
/// Remove fields from documents via a JSON Path match query (@?)

View File

@ -1,5 +1,6 @@
namespace BitBadger.Documents.Sqlite
open BitBadger.Documents
open Microsoft.Data.Sqlite
/// F# extensions for the SqliteConnection type
@ -44,17 +45,27 @@ module Extensions =
member conn.countAll tableName =
WithConn.Count.all tableName conn
/// Count matching documents using a comparison on JSON fields
member conn.countByFields tableName howMatched fields =
WithConn.Count.byFields tableName howMatched fields conn
/// Count matching documents using a comparison on a JSON field
[<System.Obsolete "Use countByFields instead; will be removed in v4">]
member conn.countByField tableName field =
WithConn.Count.byField tableName field conn
conn.countByFields tableName Any [ field ]
/// Determine if a document exists for the given ID
member conn.existsById tableName (docId: 'TKey) =
WithConn.Exists.byId tableName docId conn
/// Determine if a document exists using a comparison on JSON fields
member conn.existsByFields tableName howMatched fields =
WithConn.Exists.byFields tableName howMatched fields conn
/// Determine if a document exists using a comparison on a JSON field
[<System.Obsolete "Use existsByFields instead; will be removed in v4">]
member conn.existsByField tableName field =
WithConn.Exists.byField tableName field conn
conn.existsByFields tableName Any [ field ]
/// Retrieve all documents in the given table
member conn.findAll<'TDoc> tableName =
@ -63,14 +74,24 @@ module Extensions =
/// Retrieve a document by its ID
member conn.findById<'TKey, 'TDoc> tableName (docId: 'TKey) =
WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn
/// Retrieve documents via a comparison on JSON fields
member conn.findByFields<'TDoc> tableName howMatched fields =
WithConn.Find.byFields<'TDoc> tableName howMatched fields conn
/// Retrieve documents via a comparison on a JSON field
[<System.Obsolete "Use findByFields instead; will be removed in v4">]
member conn.findByField<'TDoc> tableName field =
WithConn.Find.byField<'TDoc> tableName field conn
conn.findByFields<'TDoc> tableName Any [ field ]
/// 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 a JSON field, returning only the first result
[<System.Obsolete "Use findFirstByFields instead; will be removed in v4">]
member conn.findFirstByField<'TDoc> tableName field =
WithConn.Find.firstByField<'TDoc> tableName field conn
conn.findFirstByFields<'TDoc> tableName Any [ field ]
/// Update an entire document by its ID
member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) =
@ -84,25 +105,40 @@ module Extensions =
member conn.patchById tableName (docId: 'TKey) (patch: 'TPatch) =
WithConn.Patch.byId tableName docId patch conn
/// Patch documents using a comparison on JSON fields
member conn.patchByFields tableName howMatched fields (patch: 'TPatch) =
WithConn.Patch.byFields tableName howMatched fields patch conn
/// Patch documents using a comparison on a JSON field
[<System.Obsolete "Use patchByFields instead; will be removed in v4">]
member conn.patchByField tableName field (patch: 'TPatch) =
WithConn.Patch.byField tableName field patch conn
conn.patchByFields tableName Any [ field ] patch
/// Remove fields from a document by the document's ID
member conn.removeFieldsById tableName (docId: 'TKey) fieldNames =
WithConn.RemoveFields.byId tableName docId fieldNames conn
/// Remove a field from a document via a comparison on JSON fields in the document
member conn.removeFieldsByFields tableName howMatched fields fieldNames =
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn
/// Remove a field from a document via a comparison on a JSON field in the document
[<System.Obsolete "Use removeFieldsByFields instead; will be removed in v4">]
member conn.removeFieldsByField tableName field fieldNames =
WithConn.RemoveFields.byField tableName field fieldNames conn
conn.removeFieldsByFields tableName Any [ field ] fieldNames
/// Delete a document by its ID
member conn.deleteById tableName (docId: 'TKey) =
WithConn.Delete.byId tableName docId conn
/// Delete documents by matching a comparison on JSON fields
member conn.deleteByFields tableName howMatched fields =
WithConn.Delete.byFields tableName howMatched fields conn
/// Delete documents by matching a comparison on a JSON field
[<System.Obsolete "Use deleteByFields instead; will be removed in v4">]
member conn.deleteByField tableName field =
WithConn.Delete.byField tableName field conn
conn.deleteByFields tableName Any [ field ]
open System.Runtime.CompilerServices
@ -157,20 +193,32 @@ type SqliteConnectionCSharpExtensions =
static member inline CountAll(conn, tableName) =
WithConn.Count.all tableName conn
/// Count matching documents using a comparison on JSON fields
[<Extension>]
static member inline CountByFields(conn, tableName, howMatched, fields) =
WithConn.Count.byFields tableName howMatched fields conn
/// Count matching documents using a comparison on a JSON field
[<Extension>]
[<System.Obsolete "Use CountByFields instead; will be removed in v4">]
static member inline CountByField(conn, tableName, field) =
WithConn.Count.byField tableName field conn
conn.CountByFields(tableName, Any, [ field ])
/// Determine if a document exists for the given ID
[<Extension>]
static member inline ExistsById<'TKey>(conn, tableName, docId: 'TKey) =
WithConn.Exists.byId tableName docId conn
/// Determine if a document exists using a comparison on JSON fields
[<Extension>]
static member inline ExistsByFields(conn, tableName, howMatched, fields) =
WithConn.Exists.byFields tableName howMatched fields conn
/// Determine if a document exists using a comparison on a JSON field
[<Extension>]
[<System.Obsolete "Use ExistsByFields instead; will be removed in v4">]
static member inline ExistsByField(conn, tableName, field) =
WithConn.Exists.byField tableName field conn
conn.ExistsByFields(tableName, Any, [ field ])
/// Retrieve all documents in the given table
[<Extension>]
@ -182,15 +230,27 @@ type SqliteConnectionCSharpExtensions =
static member inline FindById<'TKey, 'TDoc when 'TDoc: null>(conn, tableName, docId: 'TKey) =
WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
/// Retrieve documents via a comparison on JSON fields
[<Extension>]
static member inline FindByFields<'TDoc>(conn, tableName, howMatched, fields) =
WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn)
/// Retrieve documents via a comparison on a JSON field
[<Extension>]
[<System.Obsolete "Use FindByFields instead; will be removed in v4">]
static member inline FindByField<'TDoc>(conn, tableName, field) =
WithConn.Find.ByField<'TDoc>(tableName, field, conn)
conn.FindByFields<'TDoc>(tableName, Any, [ field ])
/// 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 a JSON field, returning only the first result
[<Extension>]
[<System.Obsolete "Use FindFirstByFields instead; will be removed in v4">]
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
WithConn.Find.FirstByField<'TDoc>(tableName, field, conn)
conn.FindFirstByFields<'TDoc>(tableName, Any, [ field ])
/// Update an entire document by its ID
[<Extension>]
@ -207,27 +267,45 @@ type SqliteConnectionCSharpExtensions =
static member inline PatchById<'TKey, 'TPatch>(conn, tableName, docId: 'TKey, patch: 'TPatch) =
WithConn.Patch.byId tableName docId patch conn
/// Patch documents using a comparison on JSON fields
[<Extension>]
static member inline PatchByFields<'TPatch>(conn, tableName, howMatched, fields, patch: 'TPatch) =
WithConn.Patch.byFields tableName howMatched fields patch conn
/// Patch documents using a comparison on a JSON field
[<Extension>]
[<System.Obsolete "Use PatchByFields instead; will be removed in v4">]
static member inline PatchByField<'TPatch>(conn, tableName, field, patch: 'TPatch) =
WithConn.Patch.byField tableName field patch conn
conn.PatchByFields(tableName, Any, [ field ], patch)
/// Remove fields from a document by the document's ID
[<Extension>]
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 JSON fields in the document
[<Extension>]
static member inline RemoveFieldsByFields(conn, tableName, howMatched, fields, fieldNames) =
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames 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">]
static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
WithConn.RemoveFields.ByField(tableName, field, fieldNames, conn)
conn.RemoveFieldsByFields(tableName, Any, [ field ], fieldNames)
/// Delete a document by its ID
[<Extension>]
static member inline DeleteById<'TKey>(conn, tableName, docId: 'TKey) =
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
[<Extension>]
[<System.Obsolete "Use DeleteByFields instead; will be removed in v4">]
static member inline DeleteByField(conn, tableName, field) =
WithConn.Delete.byField tableName field conn
conn.DeleteByFields(tableName, Any, [ field ])

View File

@ -32,11 +32,11 @@ module Configuration =
module Query =
/// Create a WHERE clause fragment to implement a comparison on fields in a JSON document
[<CompiledName "FSharpWhereByFields">]
[<CompiledName "WhereByFields">]
let whereByFields howMatched fields =
let name = ParameterName()
fields
|> List.map (fun it ->
|> Seq.map (fun it ->
match it.Op with
| EX | NEX -> $"{it.SqlitePath} {it.Op}"
| BT ->
@ -45,10 +45,6 @@ module Query =
| _ -> $"{it.SqlitePath} {it.Op} {name.Derive it.ParameterName}")
|> String.concat (match howMatched with Any -> " OR " | All -> " AND ")
/// Create a WHERE clause fragment to implement a comparison on fields in a JSON document
let WhereByFields(howMatched, fields: Field seq) =
whereByFields howMatched (List.ofSeq fields)
/// 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">]
@ -81,11 +77,16 @@ module Query =
let all tableName =
$"SELECT COUNT(*) AS it FROM %s{tableName}"
/// Query to count matching documents using a text comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
$"SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereByFields howMatched fields}"
/// Query to count matching documents using a text comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
whereByFields Any [ { field with ParameterName = Some "@field" } ]
|> sprintf "SELECT COUNT(*) AS it FROM %s WHERE %s" tableName
byFields tableName Any [ field ]
/// Queries for determining document existence
module Exists =
@ -94,12 +95,17 @@ module Query =
[<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 JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
$"SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereByFields howMatched fields}) AS it"
/// Query to determine if documents exist using a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
whereByFields Any [ { field with ParameterName = Some "@field" } ]
|> sprintf "SELECT EXISTS (SELECT 1 FROM %s WHERE %s) AS it" tableName
byFields tableName Any [ field ]
/// Queries for retrieving documents
module Find =
@ -109,12 +115,17 @@ module Query =
let byId tableName =
$"""{Query.selectFromTable tableName} WHERE {whereById "@id"}"""
/// Query to retrieve documents using a comparison on JSON fields
[<CompiledName "ByField">]
let byFields tableName howMatched fields =
$"{Query.selectFromTable tableName} WHERE {whereByFields howMatched fields}"
/// Query to retrieve documents using a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
whereByFields Any [ { field with ParameterName = Some "@field" } ]
|> sprintf "%s WHERE %s" (Query.selectFromTable tableName)
byFields tableName Any [ field ]
/// Document patching (partial update) queries
module Patch =
@ -126,37 +137,41 @@ module Query =
[<CompiledName "ById">]
let byId tableName =
whereById "@id" |> update tableName
/// Query to patch (partially update) a document via a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
whereByFields howMatched fields |> update tableName
/// Query to patch (partially update) a document via a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
whereByFields Any [ { field with ParameterName = Some "@field" } ] |> update tableName
byFields tableName Any [ field ]
/// 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}"
let internal update tableName (parameters: SqliteParameter seq) whereClause =
let paramNames = parameters |> Seq.map _.ParameterName |> String.concat ", "
$"UPDATE %s{tableName} SET data = json_remove(data, {paramNames}) WHERE %s{whereClause}"
/// Query to remove fields from a document by the document's ID
[<CompiledName "FSharpById">]
[<CompiledName "ById">]
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 JSON fields within the document
[<CompiledName "ByFields">]
let byFields tableName howMatched fields parameters =
whereByFields howMatched fields |> update tableName parameters
/// Query to remove fields from documents via a comparison on a JSON field within the document
[<CompiledName "FSharpByField">]
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field parameters =
whereByFields Any [ { field with ParameterName = Some "@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)
byFields tableName Any [ field ] parameters
/// Queries to delete documents
module Delete =
@ -165,12 +180,17 @@ module Query =
[<CompiledName "ById">]
let byId tableName =
$"""DELETE FROM %s{tableName} WHERE {whereById "@id"}"""
/// Query to delete documents using a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
$"DELETE FROM %s{tableName} WHERE {whereByFields howMatched fields}"
/// Query to delete documents using a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
whereByFields Any [ { field with ParameterName = Some "@field" } ]
|> sprintf "DELETE FROM %s WHERE %s" tableName
byFields tableName Any [ field ]
/// Parameter handling helpers
@ -187,8 +207,9 @@ module Parameters =
let jsonParam name (it: 'TJson) =
SqliteParameter(name, Configuration.serializer().Serialize it)
/// Convert the fields to their parameters
let private convertFieldsToParameters fields =
/// Create JSON field parameters
[<CompiledName "AddFields">]
let addFieldParams fields parameters =
let name = ParameterName()
fields
|> Seq.map (fun it ->
@ -202,38 +223,23 @@ module Parameters =
yield SqliteParameter($"{p}max", List.last values)
| _ -> yield SqliteParameter(name.Derive it.ParameterName, it.Value) })
|> Seq.collect id
/// Create JSON field parameters
[<CompiledName "FSharpAddFields">]
let addFieldParams (fields: Field list) parameters =
convertFieldsToParameters fields
|> Seq.toList
|> List.append parameters
/// Create JSON field parameters
let AddFields fields parameters =
convertFieldsToParameters fields
|> Seq.append parameters
|> Seq.toList
|> Seq.ofList
/// Create a JSON field parameter (name "@field")
[<CompiledName "FSharpAddField">]
[<CompiledName "AddField">]
[<System.Obsolete "Use addFieldParams instead; will be removed in v4">]
let addFieldParam name field parameters =
addFieldParams [ { field with ParameterName = Some name } ] parameters
/// Create a JSON field parameter (name "@field")
[<System.Obsolete "Use AddFields instead; will be removed in v4">]
let AddField(name, field, parameters) =
AddFields [ { field with ParameterName = Some name } ] parameters
/// Append JSON field name parameters for the given field names to the given parameters
[<CompiledName "FSharpFieldNames">]
let fieldNameParams paramName (fieldNames: string list) =
fieldNames |> List.mapi (fun idx name -> SqliteParameter($"%s{paramName}{idx}", $"$.{name}"))
/// Append JSON field name parameters for the given field names to the given parameters
let FieldNames(paramName, fieldNames: string seq) =
fieldNames |> Seq.mapi (fun idx name -> SqliteParameter($"%s{paramName}{idx}", $"$.{name}"))
[<CompiledName "FieldNames">]
let fieldNameParams paramName fieldNames =
fieldNames
|> Seq.mapi (fun idx name -> SqliteParameter($"%s{paramName}{idx}", $"$.%s{name}"))
|> Seq.toList
|> Seq.ofList
/// An empty parameter sequence
[<CompiledName "None">]
@ -382,14 +388,16 @@ module WithConn =
let all tableName conn =
Custom.scalar (Query.Count.all tableName) [] toCount conn
/// Count matching documents using a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields conn =
Custom.scalar (Query.Count.byFields tableName howMatched fields) (addFieldParams fields []) toCount conn
/// Count matching documents using a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field conn =
Custom.scalar
(Query.Count.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
toCount
conn
byFields tableName Any [ field ] conn
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
@ -399,15 +407,17 @@ module WithConn =
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) conn =
Custom.scalar (Query.Exists.byId tableName) [ idParam docId ] toExists conn
/// Determine if a document exists using a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields conn =
Custom.scalar (Query.Exists.byFields tableName howMatched fields) (addFieldParams fields []) toExists conn
/// Determine if a document exists using a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field conn =
Custom.scalar
(Query.Exists.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
toExists
conn
byFields tableName Any [ field ] conn
/// Commands to retrieve documents
[<RequireQualifiedAccess>]
@ -430,40 +440,56 @@ module WithConn =
/// Retrieve a document by its ID (returns null if not found)
let ById<'TKey, 'TDoc when 'TDoc: null>(tableName, docId: 'TKey, conn) =
Custom.Single<'TDoc>(Query.Find.byId tableName, [ idParam docId ], fromData<'TDoc>, conn)
/// Retrieve documents via a comparison on JSON fields
[<CompiledName "FSharpByFields">]
let byFields<'TDoc> tableName howMatched fields conn =
Custom.list<'TDoc>
(Query.Find.byFields tableName howMatched fields) (addFieldParams fields []) fromData<'TDoc> conn
/// Retrieve documents via a comparison on a JSON field
[<CompiledName "FSharpByField">]
[<System.Obsolete "Use byFields instead; will be removed in v4">]
let byField<'TDoc> tableName field conn =
Custom.list<'TDoc>
(Query.Find.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
fromData<'TDoc>
conn
byFields<'TDoc> tableName Any [ field ] conn
/// Retrieve documents via a comparison on a JSON field
let ByField<'TDoc>(tableName, field, conn) =
/// Retrieve documents via a comparison on JSON fields
let ByFields<'TDoc>(tableName, howMatched, fields, conn) =
Custom.List<'TDoc>(
Query.Find.byField tableName field,
addFieldParams [ { field with ParameterName = Some "@field" } ] [],
fromData<'TDoc>,
conn)
Query.Find.byFields tableName howMatched fields, addFieldParams fields [], fromData<'TDoc>, conn)
/// Retrieve documents via a comparison on a JSON field
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let ByField<'TDoc>(tableName, field, conn) =
ByFields<'TDoc>(tableName, Any, [ field ], conn)
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<CompiledName "FSharpFirstByField">]
let firstByField<'TDoc> tableName field 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
$"{Query.Find.byField tableName field} LIMIT 1"
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
$"{Query.Find.byFields tableName howMatched fields} LIMIT 1"
(addFieldParams fields [])
fromData<'TDoc>
conn
/// Retrieve documents via a comparison on a JSON field, returning only the first result
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) =
[<CompiledName "FSharpFirstByField">]
[<System.Obsolete "Use firstByFields instead; will be removed in v4">]
let firstByField<'TDoc> tableName field conn =
firstByFields<'TDoc> tableName Any [ field ] conn
/// Retrieve documents via a comparison on JSON fields, returning only the first result
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields, conn) =
Custom.Single(
$"{Query.Find.byField tableName field} LIMIT 1",
addFieldParams [ { field with ParameterName = Some "@field" } ] [],
$"{Query.Find.byFields tableName howMatched fields} LIMIT 1",
addFieldParams fields [],
fromData<'TDoc>,
conn)
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<System.Obsolete "Use FirstByFields instead; will be removed in v4">]
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) =
FirstByFields(tableName, Any, [ field ], conn)
/// Commands to update documents
[<RequireQualifiedAccess>]
@ -492,40 +518,47 @@ module WithConn =
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 JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields (patch: 'TPatch) conn =
Custom.nonQuery
(Query.Patch.byFields tableName howMatched fields)
(addFieldParams fields [ 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
(Query.Patch.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [ jsonParam "@data" patch ])
conn
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field (patch: 'TPatch) conn =
byFields tableName Any [ field ] patch conn
/// Commands to remove fields from documents
[<RequireQualifiedAccess>]
module RemoveFields =
/// Remove fields from a document by the document's ID
[<CompiledName "FSharpById">]
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) fieldNames conn =
let nameParams = fieldNameParams "@name" fieldNames
Custom.nonQuery (Query.RemoveFields.byId tableName nameParams) (idParam docId :: nameParams) conn
/// Remove fields from a document by the document's ID
let ById(tableName, docId: 'TKey, fieldNames, conn) =
byId tableName docId (List.ofSeq 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
Custom.nonQuery
(Query.RemoveFields.byField tableName field nameParams)
(addFieldParams [ { field with ParameterName = Some "@field" } ] nameParams)
(Query.RemoveFields.byId tableName nameParams)
(idParam docId |> Seq.singleton |> Seq.append nameParams)
conn
/// Remove fields from documents via a comparison on JSON fields in the document
[<CompiledName "ByFields">]
let byFields tableName howMatched fields fieldNames conn =
let nameParams = fieldNameParams "@name" fieldNames
Custom.nonQuery
(Query.RemoveFields.byFields tableName howMatched fields nameParams)
(addFieldParams fields nameParams)
conn
/// Remove fields from documents via a comparison on a JSON field in the document
let ByField(tableName, field, fieldNames, conn) =
byField tableName field (List.ofSeq fieldNames) conn
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field fieldNames conn =
byFields tableName Any [ field ] fieldNames conn
/// Commands to delete documents
[<RequireQualifiedAccess>]
@ -535,14 +568,17 @@ module WithConn =
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) conn =
Custom.nonQuery (Query.Delete.byId tableName) [ idParam docId ] conn
/// Delete documents by matching a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields conn =
Custom.nonQuery (Query.Delete.byFields tableName howMatched fields) (addFieldParams fields []) conn
/// Delete documents by matching a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field conn =
Custom.nonQuery
(Query.Delete.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
conn
byFields tableName Any [ field ] conn
/// Commands to execute custom SQL queries
@ -630,11 +666,17 @@ module Count =
use conn = Configuration.dbConn ()
WithConn.Count.all tableName conn
/// Count matching documents using a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
use conn = Configuration.dbConn ()
WithConn.Count.byFields tableName howMatched fields conn
/// Count matching documents using a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
use conn = Configuration.dbConn ()
WithConn.Count.byField tableName field conn
byFields tableName Any [ field ]
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
@ -645,12 +687,18 @@ module Exists =
let byId tableName (docId: 'TKey) =
use conn = Configuration.dbConn ()
WithConn.Exists.byId tableName docId conn
/// Determine if a document exists using a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
use conn = Configuration.dbConn ()
WithConn.Exists.byFields tableName howMatched fields conn
/// Determine if a document exists using a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
use conn = Configuration.dbConn ()
WithConn.Exists.byField tableName field conn
byFields tableName Any [ field ]
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
@ -678,27 +726,49 @@ module Find =
use conn = Configuration.dbConn ()
WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
/// Retrieve documents via a comparison on JSON fields
[<CompiledName "FSharpByFields">]
let byFields<'TDoc> tableName howMatched fields =
use conn = Configuration.dbConn ()
WithConn.Find.byFields<'TDoc> tableName howMatched fields conn
/// Retrieve documents via a comparison on a JSON field
[<CompiledName "FSharpByField">]
[<System.Obsolete "Use byFields instead; will be removed in v4">]
let byField<'TDoc> tableName field =
byFields tableName Any [ field ]
/// Retrieve documents via a comparison on JSON fields
let ByFields<'TDoc>(tableName, howMatched, fields) =
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
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let ByField<'TDoc>(tableName, field) =
use conn = Configuration.dbConn ()
WithConn.Find.ByField<'TDoc>(tableName, field, conn)
ByFields<'TDoc>(tableName, Any, [ field ])
/// 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 a JSON field, returning only the first result
[<CompiledName "FSharpFirstByField">]
[<System.Obsolete "Use firstByFields instead; will be removed in v4">]
let firstByField<'TDoc> tableName field =
firstByFields<'TDoc> tableName Any [ field ]
/// 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.firstByField<'TDoc> tableName field conn
WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn)
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<System.Obsolete "Use FirstByFields instead; will be removed in v4">]
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
use conn = Configuration.dbConn ()
WithConn.Find.FirstByField<'TDoc>(tableName, field, conn)
FirstByFields<'TDoc>(tableName, Any, [ field ])
/// Commands to update documents
[<RequireQualifiedAccess>]
@ -731,37 +801,39 @@ module Patch =
use conn = Configuration.dbConn ()
WithConn.Patch.byId tableName docId patch conn
/// Patch documents using a comparison on JSON fields in the WHERE clause
[<CompiledName "ByFields">]
let byFields tableName howMatched fields (patch: 'TPatch) =
use conn = Configuration.dbConn ()
WithConn.Patch.byFields tableName howMatched fields patch conn
/// Patch documents using a comparison on a JSON field in the WHERE clause
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field (patch: 'TPatch) =
use conn = Configuration.dbConn ()
WithConn.Patch.byField tableName field patch conn
byFields tableName Any [ field ] patch
/// Commands to remove fields from documents
[<RequireQualifiedAccess>]
module RemoveFields =
/// Remove fields from a document by the document's ID
[<CompiledName "FSharpById">]
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) fieldNames =
use conn = Configuration.dbConn ()
WithConn.RemoveFields.byId tableName docId fieldNames conn
/// Remove fields from a document by the document's ID
let ById(tableName, docId: 'TKey, fieldNames) =
/// Remove field from documents via a comparison on JSON fields in the document
[<CompiledName "ByFields">]
let byFields tableName howMatched fields fieldNames =
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">]
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
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)
byFields tableName Any [ field ] fieldNames
/// Commands to delete documents
[<RequireQualifiedAccess>]
@ -772,9 +844,15 @@ module Delete =
let byId tableName (docId: 'TKey) =
use conn = Configuration.dbConn ()
WithConn.Delete.byId tableName docId conn
/// Delete documents by matching a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
use conn = Configuration.dbConn ()
WithConn.Delete.byFields tableName howMatched fields conn
/// Delete documents by matching a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
use conn = Configuration.dbConn ()
WithConn.Delete.byField tableName field conn
byFields tableName Any [ field ]

View File

@ -118,7 +118,6 @@ public static class PostgresCSharpTests
Expect.isTrue(it[1].Item2.IsParameter, "Maximum field parameter value incorrect");
})
]),
#pragma warning restore CS0618
TestList("FieldName",
[
TestCase("succeeds for one name", () =>
@ -140,6 +139,7 @@ public static class PostgresCSharpTests
}
})
])
#pragma warning restore CS0618
]),
TestList("Query",
[

View File

@ -18,10 +18,10 @@ public static class SqliteCSharpExtensionTests
/// Integration tests for the SQLite extension methods
/// </summary>
[Tests]
public static readonly Test Integration = TestList("Sqlite.C#.Extensions", new[]
{
TestList("CustomSingle", new[]
{
public static readonly Test Integration = TestList("Sqlite.C#.Extensions",
[
TestList("CustomSingle",
[
TestCase("succeeds when a row is found", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -43,9 +43,9 @@ public static class SqliteCSharpExtensionTests
new[] { Parameters.Id("eighty") }, Results.FromData<JsonDocument>);
Expect.isNull(doc, "There should not have been a document returned");
})
}),
TestList("CustomList", new[]
{
]),
TestList("CustomList",
[
TestCase("succeeds when data is found", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -67,9 +67,9 @@ public static class SqliteCSharpExtensionTests
new[] { new SqliteParameter("@value", 100) }, Results.FromData<JsonDocument>);
Expect.isEmpty(docs, "There should have been no documents returned");
})
}),
TestList("CustomNonQuery", new[]
{
]),
TestList("CustomNonQuery",
[
TestCase("succeeds when operating on data", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -93,7 +93,7 @@ public static class SqliteCSharpExtensionTests
var remaining = await conn.CountAll(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table");
})
}),
]),
TestCase("CustomScalar succeeds", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -140,8 +140,8 @@ public static class SqliteCSharpExtensionTests
exists = await indexExists();
Expect.isTrue(exists, "The index should now exist");
}),
TestList("Insert", new[]
{
TestList("Insert",
[
TestCase("succeeds", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -168,9 +168,9 @@ public static class SqliteCSharpExtensionTests
// This is what is supposed to happen
}
})
}),
TestList("Save", new[]
{
]),
TestList("Save",
[
TestCase("succeeds when a document is inserted", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -203,7 +203,7 @@ public static class SqliteCSharpExtensionTests
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");
})
}),
]),
TestCase("CountAll succeeds", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -213,6 +213,7 @@ public static class SqliteCSharpExtensionTests
var theCount = await conn.CountAll(SqliteDb.TableName);
Expect.equal(theCount, 5L, "There should have been 5 matching documents");
}),
#pragma warning disable CS0618
TestCase("CountByField succeeds", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -222,8 +223,9 @@ public static class SqliteCSharpExtensionTests
var theCount = await conn.CountByField(SqliteDb.TableName, Field.EQ("Value", "purple"));
Expect.equal(theCount, 2L, "There should have been 2 matching documents");
}),
TestList("ExistsById", new[]
{
#pragma warning restore CS0618
TestList("ExistsById",
[
TestCase("succeeds when a document exists", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -242,9 +244,10 @@ public static class SqliteCSharpExtensionTests
var exists = await conn.ExistsById(SqliteDb.TableName, "seven");
Expect.isFalse(exists, "There should not have been an existing document");
})
}),
TestList("ExistsByField", new[]
{
]),
#pragma warning disable CS0618
TestList("ExistsByField",
[
TestCase("succeeds when documents exist", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -263,9 +266,10 @@ public static class SqliteCSharpExtensionTests
var exists = await conn.ExistsByField(SqliteDb.TableName, Field.EQ("Nothing", "none"));
Expect.isFalse(exists, "There should not have been any existing documents");
})
}),
TestList("FindAll", new[]
{
]),
#pragma warning restore CS0618
TestList("FindAll",
[
TestCase("succeeds when there is data", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -285,9 +289,9 @@ public static class SqliteCSharpExtensionTests
var results = await conn.FindAll<JsonDocument>(SqliteDb.TableName);
Expect.isEmpty(results, "There should have been no documents returned");
})
}),
TestList("FindById", new[]
{
]),
TestList("FindById",
[
TestCase("succeeds when a document is found", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -307,9 +311,10 @@ public static class SqliteCSharpExtensionTests
var doc = await conn.FindById<string, JsonDocument>(SqliteDb.TableName, "eighty-seven");
Expect.isNull(doc, "There should not have been a document returned");
})
}),
TestList("FindByField", new[]
{
]),
#pragma warning disable CS0618
TestList("FindByField",
[
TestCase("succeeds when documents are found", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -328,9 +333,9 @@ public static class SqliteCSharpExtensionTests
var docs = await conn.FindByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "mauve"));
Expect.isEmpty(docs, "There should have been no documents returned");
})
}),
TestList("FindFirstByField", new[]
{
]),
TestList("FindFirstByField",
[
TestCase("succeeds when a document is found", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -360,9 +365,10 @@ public static class SqliteCSharpExtensionTests
var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "absent"));
Expect.isNull(doc, "There should not have been a document returned");
})
}),
TestList("UpdateById", new[]
{
]),
#pragma warning restore CS0618
TestList("UpdateById",
[
TestCase("succeeds when a document is updated", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -389,9 +395,9 @@ public static class SqliteCSharpExtensionTests
await conn.UpdateById(SqliteDb.TableName, "test",
new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } });
})
}),
TestList("UpdateByFunc", new[]
{
]),
TestList("UpdateByFunc",
[
TestCase("succeeds when a document is updated", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -418,9 +424,9 @@ public static class SqliteCSharpExtensionTests
await conn.UpdateByFunc(SqliteDb.TableName, doc => doc.Id,
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
})
}),
TestList("PatchById", new[]
{
]),
TestList("PatchById",
[
TestCase("succeeds when a document is updated", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -443,9 +449,10 @@ public static class SqliteCSharpExtensionTests
// This not raising an exception is the test
await conn.PatchById(SqliteDb.TableName, "test", new { Foo = "green" });
})
}),
TestList("PatchByField", new[]
{
]),
#pragma warning disable CS0618
TestList("PatchByField",
[
TestCase("succeeds when a document is updated", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -466,9 +473,10 @@ public static class SqliteCSharpExtensionTests
// This not raising an exception is the test
await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
})
}),
TestList("RemoveFieldsById", new[]
{
]),
#pragma warning restore CS0618
TestList("RemoveFieldsById",
[
TestCase("succeeds when fields are removed", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -498,9 +506,10 @@ public static class SqliteCSharpExtensionTests
// This not raising an exception is the test
await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "Value" });
})
}),
TestList("RemoveFieldsByField", new[]
{
]),
#pragma warning disable CS0618
TestList("RemoveFieldsByField",
[
TestCase("succeeds when a field is removed", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -529,9 +538,10 @@ public static class SqliteCSharpExtensionTests
// This not raising an exception is the test
await conn.RemoveFieldsByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" });
})
}),
TestList("DeleteById", new[]
{
]),
#pragma warning restore CS0618
TestList("DeleteById",
[
TestCase("succeeds when a document is deleted", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -552,9 +562,10 @@ public static class SqliteCSharpExtensionTests
var remaining = await conn.CountAll(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
})
}),
TestList("DeleteByField", new[]
{
]),
#pragma warning disable CS0618
TestList("DeleteByField",
[
TestCase("succeeds when documents are deleted", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -575,7 +586,8 @@ public static class SqliteCSharpExtensionTests
var remaining = await conn.CountAll(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
})
}),
]),
#pragma warning restore CS0618
TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
});
]);
}

View File

@ -63,6 +63,7 @@ public static class SqliteCSharpTests
"WHERE clause not correct");
})
]),
#pragma warning disable CS0618
TestList("WhereByField",
[
TestCase("succeeds when a logical operator is passed", () =>
@ -81,6 +82,7 @@ public static class SqliteCSharpTests
"data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct");
})
]),
#pragma warning restore CS0618
TestCase("WhereById succeeds", () =>
{
Expect.equal(Sqlite.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct");
@ -102,12 +104,14 @@ public static class SqliteCSharpTests
Expect.equal(Sqlite.Query.Count.All("tbl"), "SELECT COUNT(*) AS it FROM tbl",
"Count query not correct");
}),
#pragma warning disable CS0618
TestCase("ByField succeeds", () =>
{
Expect.equal(Sqlite.Query.Count.ByField("tbl", Field.EQ("thatField", 0)),
"SELECT COUNT(*) AS it FROM tbl WHERE data->>'thatField' = @field",
"SELECT COUNT(*) AS it FROM tbl WHERE data->>'thatField' = @field0",
"JSON field text comparison count query not correct");
})
#pragma warning restore CS0618
]),
TestList("Exists",
[
@ -117,12 +121,14 @@ public static class SqliteCSharpTests
"SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Id' = @id) AS it",
"ID existence query not correct");
}),
#pragma warning disable CS0618
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",
"SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Test' < @field0) AS it",
"JSON field text comparison exists query not correct");
})
#pragma warning restore CS0618
]),
TestList("Find",
[
@ -131,12 +137,14 @@ public static class SqliteCSharpTests
Expect.equal(Sqlite.Query.Find.ById("tbl"), "SELECT data FROM tbl WHERE data->>'Id' = @id",
"SELECT by ID query not correct");
}),
#pragma warning disable CS0618
TestCase("ByField succeeds", () =>
{
Expect.equal(Sqlite.Query.Find.ByField("tbl", Field.GE("Golf", 0)),
"SELECT data FROM tbl WHERE data->>'Golf' >= @field",
"SELECT data FROM tbl WHERE data->>'Golf' >= @field0",
"SELECT by JSON comparison query not correct");
})
#pragma warning restore CS0618
]),
TestList("Patch",
[
@ -146,12 +154,14 @@ public static class SqliteCSharpTests
"UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Id' = @id",
"UPDATE partial by ID statement not correct");
}),
#pragma warning disable CS0618
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 tbl SET data = json_patch(data, json(@data)) WHERE data->>'Part' <> @field0",
"UPDATE partial by JSON comparison query not correct");
})
#pragma warning restore CS0618
]),
TestList("RemoveFields",
[
@ -161,13 +171,15 @@ public static class SqliteCSharpTests
"UPDATE tbl SET data = json_remove(data, @name) WHERE data->>'Id' = @id",
"Remove field by ID query not correct");
}),
#pragma warning disable CS0618
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",
"UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data->>'Fly' < @field0",
"Remove field by field query not correct");
})
#pragma warning restore CS0618
]),
TestList("Delete",
[
@ -176,11 +188,13 @@ public static class SqliteCSharpTests
Expect.equal(Sqlite.Query.Delete.ById("tbl"), "DELETE FROM tbl WHERE data->>'Id' = @id",
"DELETE by ID query not correct");
}),
#pragma warning disable CS0618
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");
})
#pragma warning restore CS0618
])
]),
TestList("Parameters",
@ -197,6 +211,7 @@ public static class SqliteCSharpTests
Expect.equal(theParam.ParameterName, "@test", "The parameter name is incorrect");
Expect.equal(theParam.Value, "{\"Nice\":\"job\"}", "The parameter value is incorrect");
}),
#pragma warning disable CS0618
TestCase("AddField succeeds when adding a parameter", () =>
{
var paramList = Parameters.AddField("@field", Field.EQ("it", 99), []).ToList();
@ -210,6 +225,7 @@ public static class SqliteCSharpTests
var paramSeq = Parameters.AddField("@it", Field.EX("Coffee"), []);
Expect.isEmpty(paramSeq, "There should not have been any parameters added");
}),
#pragma warning restore CS0618
TestCase("None succeeds", () =>
{
Expect.isEmpty(Parameters.None, "The parameter list should have been empty");
@ -442,6 +458,7 @@ public static class SqliteCSharpTests
var theCount = await Count.All(SqliteDb.TableName);
Expect.equal(theCount, 5L, "There should have been 5 matching documents");
}),
#pragma warning disable CS0618
TestCase("ByField succeeds for numeric range", async () =>
{
await using var db = await SqliteDb.BuildDb();
@ -458,6 +475,7 @@ public static class SqliteCSharpTests
var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("Value", "aardvark", "apple"));
Expect.equal(theCount, 1L, "There should have been 1 matching document");
})
#pragma warning restore CS0618
]),
TestList("Exists",
[
@ -480,6 +498,7 @@ public static class SqliteCSharpTests
Expect.isFalse(exists, "There should not have been an existing document");
})
]),
#pragma warning disable CS0618
TestList("ByField",
[
TestCase("succeeds when documents exist", async () =>
@ -499,6 +518,7 @@ public static class SqliteCSharpTests
Expect.isFalse(exists, "There should not have been any existing documents");
})
])
#pragma warning restore CS0618
]),
TestList("Find",
[
@ -542,6 +562,7 @@ public static class SqliteCSharpTests
Expect.isNull(doc, "There should not have been a document returned");
})
]),
#pragma warning disable CS0618
TestList("ByField",
[
TestCase("succeeds when documents are found", async () =>
@ -590,6 +611,7 @@ public static class SqliteCSharpTests
Expect.isNull(doc, "There should not have been a document returned");
})
])
#pragma warning restore CS0618
]),
TestList("Update",
[
@ -676,6 +698,7 @@ public static class SqliteCSharpTests
await Patch.ById(SqliteDb.TableName, "test", new { Foo = "green" });
})
]),
#pragma warning disable CS0618
TestList("ByField",
[
TestCase("succeeds when a document is updated", async () =>
@ -698,6 +721,7 @@ public static class SqliteCSharpTests
await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
})
])
#pragma warning restore CS0618
]),
TestList("RemoveFields",
[
@ -730,6 +754,7 @@ public static class SqliteCSharpTests
await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Value" });
})
]),
#pragma warning disable CS0618
TestList("ByField",
[
TestCase("succeeds when a field is removed", async () =>
@ -758,6 +783,7 @@ public static class SqliteCSharpTests
await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" });
})
])
#pragma warning restore CS0618
]),
TestList("Delete",
[
@ -782,6 +808,7 @@ public static class SqliteCSharpTests
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
})
]),
#pragma warning disable CS0618
TestList("ByField",
[
TestCase("succeeds when documents are deleted", async () =>
@ -803,6 +830,7 @@ public static class SqliteCSharpTests
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
})
])
#pragma warning restore CS0618
]),
TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
]);

View File

@ -94,7 +94,7 @@ let unitTests =
test "byField succeeds" {
Expect.equal
(Query.Count.byField "tbl" (Field.EQ "thatField" 0))
"SELECT COUNT(*) AS it FROM tbl WHERE data->>'thatField' = @field"
"SELECT COUNT(*) AS it FROM tbl WHERE data->>'thatField' = @field0"
"JSON field text comparison count query not correct"
}
]
@ -108,7 +108,7 @@ let unitTests =
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"
"SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Test' < @field0) AS it"
"JSON field text comparison exists query not correct"
}
]
@ -122,7 +122,7 @@ let unitTests =
test "byField succeeds" {
Expect.equal
(Query.Find.byField "tbl" (Field.GE "Golf" 0))
"SELECT data FROM tbl WHERE data->>'Golf' >= @field"
"SELECT data FROM tbl WHERE data->>'Golf' >= @field0"
"SELECT by JSON comparison query not correct"
}
]
@ -136,7 +136,7 @@ let unitTests =
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 tbl SET data = json_patch(data, json(@data)) WHERE data->>'Part' <> @field0"
"UPDATE partial by JSON comparison query not correct"
}
]
@ -153,7 +153,7 @@ let unitTests =
"tbl"
(Field.GT "Fly" 0)
[ SqliteParameter("@name0", "one"); SqliteParameter("@name1", "two") ])
"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' > @field0"
"Remove field by field query not correct"
}
]
@ -187,7 +187,7 @@ let unitTests =
test "succeeds when adding a parameter" {
let paramList = addFieldParam "@field" (Field.EQ "it" 99) []
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.Value 99 "The parameter value is incorrect"
}