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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user