Version 4 rc1 (#6)

Changes in this version:
- **BREAKING CHANGE**: All `*byField`/`*ByField` functions are now `*byFields`/`*ByFields`, and take a `FieldMatch` case before the list of fields. The `Compat` namespace in both libraries will assist in this transition. In support of this change, the `Field` parameter name is optional; the library will generate parameter names for it if they are not specified.
- **BREAKING CHANGE**: The `Query` namespaces have had some significant work, particularly from the full-query perspective. Most have been broken up into the base query and modifiers `by*` that will combine the base query with the `WHERE` clause needed to satisfy the criteria.
- **FEATURE / BREAKING CHANGE**: PostgreSQL document fields will now be cast to numeric if the parameter value passed to the query is numeric. This drove the `Query` breaking changes, as the fields need to have their intended value for the library to generate the appropriate SQL. Additionally, if code assumes the library can be given something like `8` and transform it to `"8"`, this is no longer the case.
- **FEATURE**: All `Find` queries (except `byId`/`ById`) now have a version with the `Ordered` suffix. These take a list of fields by which the query should be ordered. A new `Field` method called `Named` can assist with creating these fields. Prefixing the field name with `n:` will cast the field to numeric in PostgreSQL (and will be ignored by SQLite); adding " DESC" to the field name will sort it descending (Z-A, high to low) instead of ascending (A-Z, low to high).
- **BREAKING CHANGE** (PostgreSQL only): `fieldNameParam`/`Parameters.FieldName` are now plural. The function still only generates one parameter, but the name is now the same between PostgreSQL and SQLite. The goal of this library is to abstract the differences away as much as practical, and this furthers that end. There are functions with these names in the `Compat` namespace.
- **FEATURE**: In the F# v3 library, lists of parameters were expected to be F#'s `List` type, and the C# version took either `List<T>` or `IEnumerable<T>`. In this version, these all expect `seq`/`IEnumerable<T>`. F#'s `List` satisfies the `seq` constraints, so this should not be a breaking change.
- **FEATURE**: `Field`s now may have qualifiers; this allows tables to be aliased when joining multiple tables (as all have the same `data` column). F# users can use `with` to specify this at creation, and both F# and C# can use the `WithQualifier` method to create a field with the qualifier specified. Parameter names for fields may be specified in a similar way, substituting `ParameterName` for `Qualifier`.

Reviewed-on: #6
This commit was merged in pull request #6.
This commit is contained in:
2024-08-19 23:30:38 +00:00
parent 039761fcca
commit 2c24e2e912
27 changed files with 8162 additions and 4943 deletions

View File

@@ -31,20 +31,47 @@ module Configuration =
[<RequireQualifiedAccess>]
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 a comparison on fields in a JSON document
[<CompiledName "WhereByFields">]
let whereByFields (howMatched: FieldMatch) fields =
let name = ParameterName()
fields
|> Seq.map (fun it ->
match it.Op with
| EX | NEX -> $"{it.Path SQLite} {it.Op}"
| BT ->
let p = name.Derive it.ParameterName
$"{it.Path SQLite} {it.Op} {p}min AND {p}max"
| _ -> $"{it.Path SQLite} {it.Op} {name.Derive it.ParameterName}")
|> String.concat $" {howMatched} "
/// Create a WHERE clause fragment to implement an ID-based query
[<CompiledName "WhereById">]
let whereById paramName =
whereByField (Field.EQ (Configuration.idField ()) 0) paramName
whereByFields Any [ { Field.EQ (Configuration.idField ()) 0 with ParameterName = Some paramName } ]
/// Create an UPDATE statement to patch documents
[<CompiledName "Patch">]
let patch tableName =
$"UPDATE %s{tableName} SET data = json_patch(data, json(@data))"
/// Create an UPDATE statement to remove fields from documents
[<CompiledName "RemoveFields">]
let removeFields tableName (parameters: SqliteParameter seq) =
let paramNames = parameters |> Seq.map _.ParameterName |> String.concat ", "
$"UPDATE %s{tableName} SET data = json_remove(data, {paramNames})"
/// Create a query by a document's ID
[<CompiledName "ById">]
let byId<'TKey> statement (docId: 'TKey) =
Query.statementWhere
statement
(whereByFields Any [ { Field.EQ (Configuration.idField ()) docId with ParameterName = Some "@id" } ])
/// Create a query on JSON fields
[<CompiledName "ByFields">]
let byFields statement howMatched fields =
Query.statementWhere statement (whereByFields howMatched fields)
/// Data definition
module Definition =
@@ -53,107 +80,7 @@ module Query =
[<CompiledName "EnsureTable">]
let ensureTable name =
Query.Definition.ensureTableFor name "TEXT"
/// Query to update a document
[<CompiledName "Update">]
let update tableName =
$"""UPDATE %s{tableName} SET data = @data WHERE {whereById "@id"}"""
/// Queries for counting documents
module Count =
/// Query to count all documents in a table
[<CompiledName "All">]
let all tableName =
$"SELECT COUNT(*) AS it FROM %s{tableName}"
/// Query to count matching documents using a text comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field =
$"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereByField field "@field"}"""
/// Queries for determining document existence
module Exists =
/// Query to determine if a document exists for the given ID
[<CompiledName "ById">]
let byId tableName =
$"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereById "@id"}) AS it"""
/// Query to determine if documents exist using a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field =
$"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereByField field "@field"}) AS it"""
/// Queries for retrieving documents
module Find =
/// Query to retrieve a document by its ID
[<CompiledName "ById">]
let byId tableName =
$"""{Query.selectFromTable tableName} WHERE {whereById "@id"}"""
/// Query to retrieve documents using a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field =
$"""{Query.selectFromTable tableName} WHERE {whereByField field "@field"}"""
/// Document patching (partial update) queries
module Patch =
/// Create an UPDATE statement to patch documents
let internal update tableName whereClause =
$"UPDATE %s{tableName} SET data = json_patch(data, json(@data)) WHERE %s{whereClause}"
/// Query to patch (partially update) a document by its ID
[<CompiledName "ById">]
let byId tableName =
whereById "@id" |> update tableName
/// Query to patch (partially update) a document via a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field =
whereByField field "@field" |> update tableName
/// Queries to remove fields from documents
module RemoveFields =
/// Create an UPDATE statement to remove parameters
let internal update tableName (parameters: SqliteParameter list) whereClause =
let paramNames = parameters |> List.map _.ParameterName |> String.concat ", "
$"UPDATE %s{tableName} SET data = json_remove(data, {paramNames}) WHERE {whereClause}"
/// Query to remove fields from a document by the document's ID
[<CompiledName "FSharpById">]
let byId tableName parameters =
whereById "@id" |> update tableName parameters
/// Query to remove fields from a document by the document's ID
let ById(tableName, parameters) =
byId tableName (List.ofSeq parameters)
/// Query to remove fields from documents via a comparison on a JSON field within the document
[<CompiledName "FSharpByField">]
let byField tableName field parameters =
whereByField field "@field" |> update tableName parameters
/// Query to remove fields from documents via a comparison on a JSON field within the document
let ByField(tableName, field, parameters) =
byField tableName field (List.ofSeq parameters)
/// Queries to delete documents
module Delete =
/// Query to delete a document by its ID
[<CompiledName "ById">]
let byId tableName =
$"""DELETE FROM %s{tableName} WHERE {whereById "@id"}"""
/// Query to delete documents using a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field =
$"""DELETE FROM %s{tableName} WHERE {whereByField field "@field"}"""
/// Parameter handling helpers
[<AutoOpen>]
@@ -169,39 +96,39 @@ module Parameters =
let jsonParam name (it: 'TJson) =
SqliteParameter(name, Configuration.serializer().Serialize it)
/// Create JSON field parameters
[<CompiledName "AddFields">]
let addFieldParams fields parameters =
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
|> 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 =
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")
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
/// 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">]
@@ -323,23 +250,42 @@ module WithConn =
[<CompiledName "EnsureTable">]
let ensureTable name conn = backgroundTask {
do! Custom.nonQuery (Query.Definition.ensureTable name) [] conn
do! Custom.nonQuery (Query.Definition.ensureKey name) [] conn
do! Custom.nonQuery (Query.Definition.ensureKey name SQLite) [] conn
}
/// Create an index on a document table
[<CompiledName "EnsureFieldIndex">]
let ensureFieldIndex tableName indexName fields conn =
Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields) [] conn
Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields SQLite) [] conn
/// Insert a new document
[<CompiledName "Insert">]
let insert<'TDoc> tableName (document: 'TDoc) conn =
Custom.nonQuery (Query.insert tableName) [ jsonParam "@data" document ] conn
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
[<CompiledName "Save">]
let save<'TDoc> tableName (document: 'TDoc) conn =
Custom.nonQuery (Query.save tableName) [ jsonParam "@data" document ] conn
/// Commands to add documents
[<AutoOpen>]
module Document =
/// Insert a new document
[<CompiledName "Insert">]
let insert<'TDoc> tableName (document: 'TDoc) conn =
let query =
match Configuration.autoIdStrategy () with
| Disabled -> Query.insert tableName
| strategy ->
let idField = Configuration.idField ()
let dataParam =
if AutoId.NeedsAutoId strategy document idField then
match strategy with
| Number -> $"(SELECT coalesce(max(data->>'{idField}'), 0) + 1 FROM {tableName})"
| Guid -> $"'{AutoId.GenerateGuid()}'"
| RandomString -> $"'{AutoId.GenerateRandomString(Configuration.idStringLength ())}'"
| Disabled -> "@data"
|> function it -> $"json_set(@data, '$.{idField}', {it})"
else "@data"
(Query.insert tableName).Replace("@data", dataParam)
Custom.nonQuery query [ jsonParam "@data" document ] conn
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
[<CompiledName "Save">]
let save<'TDoc> tableName (document: 'TDoc) conn =
Custom.nonQuery (Query.save tableName) [ jsonParam "@data" document ] conn
/// Commands to count documents
[<RequireQualifiedAccess>]
@@ -348,12 +294,13 @@ module WithConn =
/// Count all documents in a table
[<CompiledName "All">]
let all tableName conn =
Custom.scalar (Query.Count.all tableName) [] toCount conn
Custom.scalar (Query.count tableName) [] toCount conn
/// Count matching documents using a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field conn =
Custom.scalar (Query.Count.byField tableName field) (addFieldParam "@field" field []) toCount conn
/// Count matching documents using a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields conn =
Custom.scalar
(Query.byFields (Query.count tableName) howMatched fields) (addFieldParams fields []) toCount conn
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
@@ -362,12 +309,16 @@ module WithConn =
/// Determine if a document exists for the given ID
[<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 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 tableName (Query.whereById "@id")) [ 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 tableName (Query.whereByFields howMatched fields))
(addFieldParams fields [])
toExists
conn
/// Commands to retrieve documents
[<RequireQualifiedAccess>]
@@ -376,42 +327,99 @@ module WithConn =
/// Retrieve all documents in the given table
[<CompiledName "FSharpAll">]
let all<'TDoc> tableName conn =
Custom.list<'TDoc> (Query.selectFromTable tableName) [] fromData<'TDoc> conn
Custom.list<'TDoc> (Query.find tableName) [] fromData<'TDoc> conn
/// Retrieve all documents in the given table
let All<'TDoc>(tableName, conn) =
Custom.List(Query.selectFromTable tableName, [], fromData<'TDoc>, conn)
Custom.List(Query.find tableName, [], fromData<'TDoc>, conn)
/// Retrieve all documents in the given table ordered by the given fields in the document
[<CompiledName "FSharpAllOrdered">]
let allOrdered<'TDoc> tableName orderFields conn =
Custom.list<'TDoc> (Query.find tableName + Query.orderBy orderFields SQLite) [] fromData<'TDoc> conn
/// Retrieve all documents in the given table ordered by the given fields in the document
let AllOrdered<'TDoc>(tableName, orderFields, conn) =
Custom.List(Query.find tableName + Query.orderBy orderFields SQLite, [], fromData<'TDoc>, conn)
/// Retrieve a document by its ID (returns None if not found)
[<CompiledName "FSharpById">]
let byId<'TKey, 'TDoc> tableName (docId: 'TKey) conn =
Custom.single<'TDoc> (Query.Find.byId tableName) [ idParam docId ] fromData<'TDoc> conn
Custom.single<'TDoc> (Query.byId (Query.find tableName) docId) [ idParam docId ] fromData<'TDoc> conn
/// Retrieve a document by its ID (returns null if not found)
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 a JSON field
[<CompiledName "FSharpByField">]
let byField<'TDoc> tableName field conn =
Custom.Single<'TDoc>(Query.byId (Query.find tableName) docId, [ 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.byField tableName field) (addFieldParam "@field" field []) fromData<'TDoc> conn
(Query.byFields (Query.find tableName) howMatched fields)
(addFieldParams fields [])
fromData<'TDoc>
conn
/// Retrieve documents via a comparison on a JSON field
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, addFieldParam "@field" field [], fromData<'TDoc>, conn)
Query.byFields (Query.find tableName) howMatched fields,
addFieldParams fields [],
fromData<'TDoc>,
conn)
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<CompiledName "FSharpFirstByField">]
let firstByField<'TDoc> tableName field conn =
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
[<CompiledName "FSharpByFieldsOrdered">]
let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn =
Custom.list<'TDoc>
(Query.byFields (Query.find tableName) howMatched queryFields + Query.orderBy orderFields SQLite)
(addFieldParams queryFields [])
fromData<'TDoc>
conn
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn) =
Custom.List<'TDoc>(
Query.byFields (Query.find tableName) howMatched queryFields + Query.orderBy orderFields SQLite,
addFieldParams queryFields [],
fromData<'TDoc>,
conn)
/// Retrieve documents via a comparison on JSON fields, returning only the first result
[<CompiledName "FSharpFirstByFields">]
let firstByFields<'TDoc> tableName howMatched fields conn =
Custom.single
$"{Query.Find.byField tableName field} LIMIT 1" (addFieldParam "@field" field []) fromData<'TDoc> conn
$"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1"
(addFieldParams fields [])
fromData<'TDoc>
conn
/// Retrieve documents via a comparison on a JSON field, returning only the first result
let FirstByField<'TDoc when 'TDoc: null>(tableName, 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", addFieldParam "@field" field [], fromData<'TDoc>, conn)
$"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1",
addFieldParams fields [],
fromData<'TDoc>,
conn)
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning
/// only the first result
[<CompiledName "FSharpFirstByFieldsOrdered">]
let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn =
Custom.single
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields SQLite} LIMIT 1"
(addFieldParams queryFields [])
fromData<'TDoc>
conn
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning
/// only the first result
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields, conn) =
Custom.Single(
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields SQLite} LIMIT 1",
addFieldParams queryFields [],
fromData<'TDoc>,
conn)
/// Commands to update documents
[<RequireQualifiedAccess>]
@@ -420,7 +428,10 @@ module WithConn =
/// Update an entire document by its ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) (document: 'TDoc) conn =
Custom.nonQuery (Query.update tableName) [ idParam docId; jsonParam "@data" document ] conn
Custom.nonQuery
(Query.statementWhere (Query.update tableName) (Query.whereById "@id"))
[ idParam docId; jsonParam "@data" document ]
conn
/// Update an entire document by its ID, using the provided function to obtain the ID from the document
[<CompiledName "FSharpByFunc">]
@@ -438,38 +449,38 @@ module WithConn =
/// Patch a document by its ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) (patch: 'TPatch) conn =
Custom.nonQuery (Query.Patch.byId tableName) [ idParam docId; jsonParam "@data" patch ] conn
/// Patch documents using a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field (patch: 'TPatch) (conn: SqliteConnection) =
Custom.nonQuery
(Query.Patch.byField tableName field) (addFieldParam "@field" field [ jsonParam "@data" patch ]) conn
(Query.byId (Query.patch tableName) docId) [ idParam docId; jsonParam "@data" patch ] conn
/// Patch documents using a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields (patch: 'TPatch) conn =
Custom.nonQuery
(Query.byFields (Query.patch tableName) howMatched fields)
(addFieldParams fields [ jsonParam "@data" patch ])
conn
/// Commands to remove fields from documents
[<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
Custom.nonQuery
(Query.byId (Query.removeFields tableName nameParams) docId)
(idParam docId |> Seq.singleton |> Seq.append nameParams)
conn
/// Remove fields from a document by the document's ID
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 =
/// 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.byField tableName field nameParams) (addFieldParam "@field" field 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
(Query.byFields (Query.removeFields tableName nameParams) howMatched fields)
(addFieldParams fields nameParams)
conn
/// Commands to delete documents
[<RequireQualifiedAccess>]
@@ -478,12 +489,12 @@ module WithConn =
/// Delete a document by its ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) conn =
Custom.nonQuery (Query.Delete.byId tableName) [ idParam docId ] conn
/// 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.byId (Query.delete tableName) docId) [ idParam docId ] conn
/// Delete documents by matching a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields conn =
Custom.nonQuery (Query.byFields (Query.delete tableName) howMatched fields) (addFieldParams fields []) conn
/// Commands to execute custom SQL queries
@@ -529,6 +540,7 @@ module Custom =
use conn = Configuration.dbConn ()
WithConn.Custom.Scalar<'T>(query, parameters, mapFunc, conn)
/// Functions to create tables and indexes
[<RequireQualifiedAccess>]
module Definition =
@@ -545,6 +557,7 @@ module Definition =
use conn = Configuration.dbConn ()
WithConn.Definition.ensureFieldIndex tableName indexName fields conn
/// Document insert/save functions
[<AutoOpen>]
module Document =
@@ -553,13 +566,14 @@ module Document =
[<CompiledName "Insert">]
let insert<'TDoc> tableName (document: 'TDoc) =
use conn = Configuration.dbConn ()
WithConn.insert tableName document conn
WithConn.Document.insert tableName document conn
/// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
[<CompiledName "Save">]
let save<'TDoc> tableName (document: 'TDoc) =
use conn = Configuration.dbConn ()
WithConn.save tableName document conn
WithConn.Document.save tableName document conn
/// Commands to count documents
[<RequireQualifiedAccess>]
@@ -571,11 +585,12 @@ module Count =
use conn = Configuration.dbConn ()
WithConn.Count.all tableName conn
/// Count matching documents using a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field =
/// Count matching documents using a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
use conn = Configuration.dbConn ()
WithConn.Count.byField tableName field conn
WithConn.Count.byFields tableName howMatched fields conn
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
@@ -586,12 +601,13 @@ 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 a JSON field
[<CompiledName "ByField">]
let byField tableName field =
/// Determine if a document exists using a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
use conn = Configuration.dbConn ()
WithConn.Exists.byField tableName field conn
WithConn.Exists.byFields tableName howMatched fields conn
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
@@ -608,6 +624,17 @@ module Find =
use conn = Configuration.dbConn ()
WithConn.Find.All<'TDoc>(tableName, conn)
/// Retrieve all documents in the given table ordered by the given fields in the document
[<CompiledName "FSharpAllOrdered">]
let allOrdered<'TDoc> tableName orderFields =
use conn = Configuration.dbConn ()
WithConn.Find.allOrdered<'TDoc> tableName orderFields conn
/// Retrieve all documents in the given table ordered by the given fields in the document
let AllOrdered<'TDoc> tableName orderFields =
use conn = Configuration.dbConn ()
WithConn.Find.AllOrdered<'TDoc>(tableName, orderFields, conn)
/// Retrieve a document by its ID (returns None if not found)
[<CompiledName "FSharpById">]
let byId<'TKey, 'TDoc> tableName docId =
@@ -619,27 +646,52 @@ module Find =
use conn = Configuration.dbConn ()
WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
/// Retrieve documents via a comparison on a JSON field
[<CompiledName "FSharpByField">]
let byField<'TDoc> tableName field =
/// Retrieve documents via a comparison on JSON fields
[<CompiledName "FSharpByFields">]
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 JSON fields
let ByFields<'TDoc>(tableName, howMatched, fields) =
use conn = Configuration.dbConn ()
WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn)
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
[<CompiledName "FSharpByFieldsOrdered">]
let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
use conn = Configuration.dbConn ()
WithConn.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields) =
use conn = Configuration.dbConn ()
WithConn.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
/// Retrieve documents via a comparison on JSON fields, returning only the first result
[<CompiledName "FSharpFirstByFields">]
let firstByFields<'TDoc> tableName howMatched fields =
use conn = Configuration.dbConn ()
WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn
/// Retrieve documents via a comparison on JSON fields, returning only the first result
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields) =
use conn = Configuration.dbConn ()
WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn)
/// Retrieve documents via a comparison on a JSON field
let ByField<'TDoc>(tableName, field) =
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning only
/// the first result
[<CompiledName "FSharpFirstByFieldsOrdered">]
let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
use conn = Configuration.dbConn ()
WithConn.Find.ByField<'TDoc>(tableName, field, conn)
WithConn.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning only
/// the first result
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields) =
use conn = Configuration.dbConn ()
WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<CompiledName "FSharpFirstByField">]
let firstByField<'TDoc> tableName field =
use conn = Configuration.dbConn ()
WithConn.Find.firstByField<'TDoc> tableName field conn
/// Retrieve documents via a comparison on a JSON field, returning only the first result
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
use conn = Configuration.dbConn ()
WithConn.Find.FirstByField<'TDoc>(tableName, field, conn)
/// Commands to update documents
[<RequireQualifiedAccess>]
@@ -662,6 +714,7 @@ module Update =
use conn = Configuration.dbConn ()
WithConn.Update.ByFunc(tableName, idFunc, document, conn)
/// Commands to patch (partially update) documents
[<RequireQualifiedAccess>]
module Patch =
@@ -672,37 +725,29 @@ module Patch =
use conn = Configuration.dbConn ()
WithConn.Patch.byId tableName docId patch conn
/// Patch documents using a comparison on a JSON field in the WHERE clause
[<CompiledName "ByField">]
let byField tableName field (patch: 'TPatch) =
/// 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.byField tableName field patch conn
WithConn.Patch.byFields tableName howMatched fields 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 =
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)
/// Remove field from documents via a comparison on a JSON field in the document
[<CompiledName "FSharpByField">]
let byField tableName field fieldNames =
use conn = Configuration.dbConn ()
WithConn.RemoveFields.byField tableName field fieldNames conn
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn
/// Remove field from documents via a comparison on a JSON field in the document
let ByField(tableName, field, fieldNames) =
use conn = Configuration.dbConn ()
WithConn.RemoveFields.ByField(tableName, field, fieldNames, conn)
/// Commands to delete documents
[<RequireQualifiedAccess>]
@@ -713,9 +758,9 @@ module Delete =
let byId tableName (docId: 'TKey) =
use conn = Configuration.dbConn ()
WithConn.Delete.byId tableName docId conn
/// Delete documents by matching a comparison on a JSON field
[<CompiledName "ByField">]
let byField tableName field =
/// Delete documents by matching a comparison on JSON fields
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
use conn = Configuration.dbConn ()
WithConn.Delete.byField tableName field conn
WithConn.Delete.byFields tableName howMatched fields conn