Version 4 #6

Merged
danieljsummers merged 30 commits from version-four into main 2024-08-19 23:30:39 +00:00
2 changed files with 150 additions and 67 deletions
Showing only changes of commit d131eda56e - Show all commits

View File

@ -62,31 +62,47 @@ module Parameters =
[<CompiledName "Json">]
let jsonParam (name: string) (it: 'TJson) =
name, Sql.jsonb (Configuration.serializer().Serialize it)
/// Convert the fields to their parameters
let private convertFieldsToParameters fields =
let name = ParameterName()
fields
|> Seq.map (fun it ->
seq {
match it.Op with
| EX | NEX -> ()
| BT ->
let p = name.Derive it.ParameterName
let values = it.Value :?> obj list
yield ($"{p}min", Sql.parameter (NpgsqlParameter($"{p}min", List.head values)))
yield ($"{p}max", Sql.parameter (NpgsqlParameter($"{p}max", List.last values)))
| _ ->
let p = name.Derive it.ParameterName
yield (p, Sql.parameter (NpgsqlParameter(p, 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
/// Create a JSON field parameter
[<CompiledName "FSharpAddField">]
[<System.Obsolete "Use addFieldParams instead; will be removed in v4">]
let addFieldParam name field parameters =
match field.Op with
| 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
addFieldParams [ { field with ParameterName = Some name } ] parameters
/// Create a JSON field parameter
[<System.Obsolete "Use AddFields instead; will be removed in v4">]
let AddField name field parameters =
match field.Op with
| 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
AddFields [ { field with ParameterName = Some name } ] parameters
/// Append JSON field name parameters for the given field names to the given parameters
[<CompiledName "FSharpFieldName">]
@ -134,7 +150,7 @@ module Query =
/// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
[<CompiledName "WhereByField">]
//[<Obsolete "Use whereByFields / WhereByFields instead">]
[<System.Obsolete "Use WhereByFields instead; will be removed in v4">]
let whereByField field paramName =
whereByFields [ { field with ParameterName = Some paramName } ] Any
@ -184,7 +200,8 @@ module Query =
/// 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"}"""
whereByFields [ { field with ParameterName = Some "@field" } ] Any
|> sprintf "SELECT COUNT(*) AS it FROM %s WHERE %s" tableName
/// Query to count matching documents using a JSON containment query (@>)
[<CompiledName "ByContains">]
@ -207,7 +224,8 @@ module Query =
/// 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"""
whereByFields [ { field with ParameterName = Some "@field" } ] Any
|> sprintf "SELECT EXISTS (SELECT 1 FROM %s WHERE %s) AS it" tableName
/// Query to determine if documents exist using a JSON containment query (@>)
[<CompiledName "ByContains">]
@ -230,7 +248,8 @@ module Query =
/// Query to retrieve documents using a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field =
$"""{Query.selectFromTable tableName} WHERE {whereByField field "@field"}"""
whereByFields [ { field with ParameterName = Some "@field" } ] Any
|> sprintf "%s WHERE %s" (Query.selectFromTable tableName)
/// Query to retrieve documents using a JSON containment query (@>)
[<CompiledName "ByContains">]
@ -257,7 +276,7 @@ module Query =
/// Query to patch documents match a JSON field comparison (->> =)
[<CompiledName "ByField">]
let byField tableName field =
whereByField field "@field" |> update tableName
whereByFields [ { field with ParameterName = Some "@field" } ] Any |> update tableName
/// Query to patch documents matching a JSON containment query (@>)
[<CompiledName "ByContains">]
@ -284,7 +303,7 @@ module Query =
/// Query to remove fields from documents via a comparison on a JSON field within the document
[<CompiledName "ByField">]
let byField tableName field =
whereByField field "@field" |> update tableName
whereByFields [ { field with ParameterName = Some "@field" } ] Any |> update tableName
/// Query to patch documents matching a JSON containment query (@>)
[<CompiledName "ByContains">]
@ -307,7 +326,8 @@ module Query =
/// 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"}"""
whereByFields [ { field with ParameterName = Some "@field" } ] Any
|> sprintf "DELETE FROM %s WHERE %s" tableName
/// Query to delete documents using a JSON containment query (@>)
[<CompiledName "ByContains">]
@ -444,7 +464,11 @@ module WithProps =
/// Count matching documents using a JSON field comparison (->> =)
[<CompiledName "ByField">]
let byField tableName field sqlProps =
Custom.scalar (Query.Count.byField tableName field) (addFieldParam "@field" field []) toCount sqlProps
Custom.scalar
(Query.Count.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
toCount
sqlProps
/// Count matching documents using a JSON containment query (@>)
[<CompiledName "ByContains">]
@ -468,7 +492,11 @@ module WithProps =
/// Determine if a document exists using a JSON field comparison (->> =)
[<CompiledName "ByField">]
let byField tableName field sqlProps =
Custom.scalar (Query.Exists.byField tableName field) (addFieldParam "@field" field []) toExists sqlProps
Custom.scalar
(Query.Exists.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
toExists
sqlProps
/// Determine if a document exists using a JSON containment query (@>)
[<CompiledName "ByContains">]
@ -506,12 +534,18 @@ module WithProps =
[<CompiledName "FSharpByField">]
let byField<'TDoc> tableName field sqlProps =
Custom.list<'TDoc>
(Query.Find.byField tableName field) (addFieldParam "@field" field []) fromData<'TDoc> sqlProps
(Query.Find.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
fromData<'TDoc>
sqlProps
/// Retrieve documents matching a JSON field comparison (->> =)
let ByField<'TDoc>(tableName, field, sqlProps) =
Custom.List<'TDoc>(
Query.Find.byField tableName field, addFieldParam "@field" field [], fromData<'TDoc>, sqlProps)
Query.Find.byField tableName field,
addFieldParams [ { field with ParameterName = Some "@field" } ] [],
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching a JSON containment query (@>)
[<CompiledName "FSharpByContains">]
@ -540,7 +574,7 @@ module WithProps =
let firstByField<'TDoc> tableName field sqlProps =
Custom.single<'TDoc>
$"{Query.Find.byField tableName field} LIMIT 1"
(addFieldParam "@field" field [])
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
fromData<'TDoc>
sqlProps
@ -548,7 +582,7 @@ module WithProps =
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, sqlProps) =
Custom.Single<'TDoc>(
$"{Query.Find.byField tableName field} LIMIT 1",
addFieldParam "@field" field [],
addFieldParams [ { field with ParameterName = Some "@field" } ] [],
fromData<'TDoc>,
sqlProps)
@ -612,7 +646,7 @@ module WithProps =
let byField tableName field (patch: 'TPatch) sqlProps =
Custom.nonQuery
(Query.Patch.byField tableName field)
(addFieldParam "@field" field [ jsonParam "@data" patch ])
(addFieldParams [ { field with ParameterName = Some "@field" } ] [ jsonParam "@data" patch ])
sqlProps
/// Patch documents using a JSON containment query in the WHERE clause (@>)
@ -645,7 +679,7 @@ module WithProps =
let byField tableName field fieldNames sqlProps =
Custom.nonQuery
(Query.RemoveFields.byField tableName field)
(addFieldParam "@field" field [ fieldNameParam fieldNames ])
(addFieldParams [ { field with ParameterName = Some "@field" } ] [ fieldNameParam fieldNames ])
sqlProps
/// Remove fields from documents via a comparison on a JSON field in the document
@ -688,7 +722,10 @@ module WithProps =
/// Delete documents by matching a JSON field comparison query (->> =)
[<CompiledName "ByField">]
let byField tableName field sqlProps =
Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam "@field" field []) sqlProps
Custom.nonQuery
(Query.Delete.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
sqlProps
/// Delete documents by matching a JSON contains query (@>)
[<CompiledName "ByContains">]

View File

@ -51,6 +51,7 @@ module Query =
/// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
[<CompiledName "WhereByField">]
[<System.Obsolete "Use WhereByFields instead; will be removed in v4">]
let whereByField field paramName =
whereByFields [ { field with ParameterName = Some paramName } ] Any
@ -83,7 +84,8 @@ module Query =
/// 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"}"""
whereByFields [ { field with ParameterName = Some "@field" } ] Any
|> sprintf "SELECT COUNT(*) AS it FROM %s WHERE %s" tableName
/// Queries for determining document existence
module Exists =
@ -96,7 +98,8 @@ module Query =
/// 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"""
whereByFields [ { field with ParameterName = Some "@field" } ] Any
|> sprintf "SELECT EXISTS (SELECT 1 FROM %s WHERE %s) AS it" tableName
/// Queries for retrieving documents
module Find =
@ -109,7 +112,8 @@ module Query =
/// Query to retrieve documents using a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field =
$"""{Query.selectFromTable tableName} WHERE {whereByField field "@field"}"""
whereByFields [ { field with ParameterName = Some "@field" } ] Any
|> sprintf "%s WHERE %s" (Query.selectFromTable tableName)
/// Document patching (partial update) queries
module Patch =
@ -126,7 +130,7 @@ module Query =
/// Query to patch (partially update) a document via a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field =
whereByField field "@field" |> update tableName
whereByFields [ { field with ParameterName = Some "@field" } ] Any |> update tableName
/// Queries to remove fields from documents
module RemoveFields =
@ -148,7 +152,7 @@ module Query =
/// Query to remove fields from documents via a comparison on a JSON field within the document
[<CompiledName "FSharpByField">]
let byField tableName field parameters =
whereByField field "@field" |> update tableName parameters
whereByFields [ { field with ParameterName = Some "@field" } ] Any |> update tableName parameters
/// Query to remove fields from documents via a comparison on a JSON field within the document
let ByField(tableName, field, parameters) =
@ -165,7 +169,8 @@ module Query =
/// 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"}"""
whereByFields [ { field with ParameterName = Some "@field" } ] Any
|> sprintf "DELETE FROM %s WHERE %s" tableName
/// Parameter handling helpers
@ -182,30 +187,44 @@ module Parameters =
let jsonParam name (it: 'TJson) =
SqliteParameter(name, Configuration.serializer().Serialize it)
/// Convert the fields to their parameters
let private convertFieldsToParameters fields =
let name = ParameterName()
fields
|> Seq.map (fun it ->
seq {
match it.Op with
| EX | NEX -> ()
| BT ->
let p = name.Derive it.ParameterName
let values = it.Value :?> obj list
yield SqliteParameter($"{p}min", List.head values)
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
/// Create a JSON field parameter (name "@field")
[<CompiledName "FSharpAddField">]
[<System.Obsolete "Use addFieldParams instead; will be removed in v4">]
let addFieldParam name field parameters =
match field.Op with
| 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
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) =
match field.Op with
| EX | NEX -> parameters
| BT ->
let values = field.Value :?> obj list
// let min = SqliteParameter($"{name}min", SqliteType.Integer)
// min.Value <- values[0]
// let max = SqliteParameter($"{name}max", SqliteType.Integer)
// max.Value <- values[1]
[ SqliteParameter($"{name}min", values[0]); SqliteParameter($"{name}max", values[1]) ]
// [min; max]
|> Seq.append parameters
| _ -> SqliteParameter(name, field.Value) |> Seq.singleton |> Seq.append parameters
AddFields [ { field with ParameterName = Some name } ] parameters
/// Append JSON field name parameters for the given field names to the given parameters
[<CompiledName "FSharpFieldNames">]
@ -366,7 +385,11 @@ module WithConn =
/// Count matching documents using a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field conn =
Custom.scalar (Query.Count.byField tableName field) (addFieldParam "@field" field []) toCount conn
Custom.scalar
(Query.Count.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
toCount
conn
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
@ -380,7 +403,11 @@ module WithConn =
/// Determine if a document exists using a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field conn =
Custom.scalar (Query.Exists.byField tableName field) (addFieldParam "@field" field []) toExists conn
Custom.scalar
(Query.Exists.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
toExists
conn
/// Commands to retrieve documents
[<RequireQualifiedAccess>]
@ -408,23 +435,35 @@ module WithConn =
[<CompiledName "FSharpByField">]
let byField<'TDoc> tableName field conn =
Custom.list<'TDoc>
(Query.Find.byField tableName field) (addFieldParam "@field" field []) fromData<'TDoc> conn
(Query.Find.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
fromData<'TDoc>
conn
/// Retrieve documents via a comparison on a JSON field
let ByField<'TDoc>(tableName, field, conn) =
Custom.List<'TDoc>(
Query.Find.byField tableName field, addFieldParam "@field" field [], fromData<'TDoc>, conn)
Query.Find.byField tableName field,
addFieldParams [ { field with ParameterName = Some "@field" } ] [],
fromData<'TDoc>,
conn)
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<CompiledName "FSharpFirstByField">]
let firstByField<'TDoc> tableName field conn =
Custom.single
$"{Query.Find.byField tableName field} LIMIT 1" (addFieldParam "@field" field []) fromData<'TDoc> conn
$"{Query.Find.byField tableName field} LIMIT 1"
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
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) =
Custom.Single(
$"{Query.Find.byField tableName field} LIMIT 1", addFieldParam "@field" field [], fromData<'TDoc>, conn)
$"{Query.Find.byField tableName field} LIMIT 1",
addFieldParams [ { field with ParameterName = Some "@field" } ] [],
fromData<'TDoc>,
conn)
/// Commands to update documents
[<RequireQualifiedAccess>]
@ -457,7 +496,9 @@ module WithConn =
[<CompiledName "ByField">]
let byField tableName field (patch: 'TPatch) (conn: SqliteConnection) =
Custom.nonQuery
(Query.Patch.byField tableName field) (addFieldParam "@field" field [ jsonParam "@data" patch ]) conn
(Query.Patch.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [ jsonParam "@data" patch ])
conn
/// Commands to remove fields from documents
[<RequireQualifiedAccess>]
@ -478,7 +519,9 @@ module WithConn =
let byField tableName field fieldNames conn =
let nameParams = fieldNameParams "@name" fieldNames
Custom.nonQuery
(Query.RemoveFields.byField tableName field nameParams) (addFieldParam "@field" field nameParams) conn
(Query.RemoveFields.byField tableName field nameParams)
(addFieldParams [ { field with ParameterName = Some "@field" } ] nameParams)
conn
/// Remove fields from documents via a comparison on a JSON field in the document
let ByField(tableName, field, fieldNames, conn) =
@ -496,7 +539,10 @@ module WithConn =
/// Delete documents by matching a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field conn =
Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam "@field" field []) conn
Custom.nonQuery
(Query.Delete.byField tableName field)
(addFieldParams [ { field with ParameterName = Some "@field" } ] [])
conn
/// Commands to execute custom SQL queries