Daniel J. Summers 2c24e2e912 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
2024-08-19 23:30:38 +00:00

1111 lines
51 KiB
Forth

namespace BitBadger.Documents.Postgres
/// The type of index to generate for the document
[<Struct>]
type DocumentIndex =
/// A GIN index with standard operations (all operators supported)
| Full
/// A GIN index with JSONPath operations (optimized for @>, @?, @@ operators)
| Optimized
open Npgsql
/// Configuration for document handling
module Configuration =
/// The data source to use for query execution
let mutable private dataSourceValue : NpgsqlDataSource option = None
/// Register a data source to use for query execution (disposes the current one if it exists)
[<CompiledName "UseDataSource">]
let useDataSource source =
if Option.isSome dataSourceValue then dataSourceValue.Value.Dispose()
dataSourceValue <- Some source
/// Retrieve the currently configured data source
[<CompiledName "DataSource">]
let dataSource () =
match dataSourceValue with
| Some source -> source
| None -> invalidOp "Please provide a data source before attempting data access"
open Npgsql.FSharp
/// Helper functions
[<AutoOpen>]
module private Helpers =
/// Shorthand to retrieve the data source as SqlProps
let internal fromDataSource () =
Configuration.dataSource () |> Sql.fromDataSource
/// Execute a task and ignore the result
let internal ignoreTask<'T> (it : System.Threading.Tasks.Task<'T>) = backgroundTask {
let! _ = it
()
}
/// Create a number or string parameter, or use the given parameter derivation function if non-(numeric or string)
let internal parameterFor<'T> (value: 'T) (catchAllFunc: 'T -> SqlValue) =
match box value with
| :? int8 as it -> Sql.int8 it
| :? uint8 as it -> Sql.int8 (int8 it)
| :? int16 as it -> Sql.int16 it
| :? uint16 as it -> Sql.int16 (int16 it)
| :? int as it -> Sql.int it
| :? uint32 as it -> Sql.int (int it)
| :? int64 as it -> Sql.int64 it
| :? uint64 as it -> Sql.int64 (int64 it)
| :? decimal as it -> Sql.decimal it
| :? single as it -> Sql.double (double it)
| :? double as it -> Sql.double it
| :? string as it -> Sql.string it
| _ -> catchAllFunc value
open BitBadger.Documents
/// Functions for creating parameters
[<AutoOpen>]
module Parameters =
/// Create an ID parameter (name "@id")
[<CompiledName "Id">]
let idParam (key: 'TKey) =
"@id", parameterFor key (fun it -> Sql.string (string it))
/// Create a parameter with a JSON value
[<CompiledName "Json">]
let jsonParam (name: string) (it: 'TJson) =
name, Sql.jsonb (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 ($"{p}min",
parameterFor (List.head values) (fun v -> Sql.parameter (NpgsqlParameter($"{p}min", v))))
yield ($"{p}max",
parameterFor (List.last values) (fun v -> Sql.parameter (NpgsqlParameter($"{p}max", v))))
| _ ->
let p = name.Derive it.ParameterName
yield (p, parameterFor it.Value (fun v -> Sql.parameter (NpgsqlParameter(p, v)))) })
|> Seq.collect id
|> Seq.append parameters
|> Seq.toList
|> Seq.ofList
/// Append JSON field name parameters for the given field names to the given parameters
[<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)
/// An empty parameter sequence
[<CompiledName "None">]
let noParams =
Seq.empty<string * SqlValue>
/// Query construction functions
[<RequireQualifiedAccess>]
module Query =
/// 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()
let isNumeric (it: obj) =
match it with
| :? int8 | :? uint8 | :? int16 | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64
| :? decimal | :? single | :? double -> true
| _ -> false
fields
|> Seq.map (fun it ->
match it.Op with
| EX | NEX -> $"{it.Path PostgreSQL} {it.Op}"
| _ ->
let p = name.Derive it.ParameterName
let param, value =
match it.Op with
| BT -> $"{p}min AND {p}max", (it.Value :?> obj list)[0]
| _ -> p, it.Value
if isNumeric value then
$"({it.Path PostgreSQL})::numeric {it.Op} {param}"
else $"{it.Path PostgreSQL} {it.Op} {param}")
|> String.concat $" {howMatched} "
/// Create a WHERE clause fragment to implement an ID-based query
[<CompiledName "WhereById">]
let whereById<'TKey> (docId: 'TKey) =
whereByFields Any [ { Field.EQ (Configuration.idField ()) docId with ParameterName = Some "@id" } ]
/// Table and index definition queries
module Definition =
/// SQL statement to create a document table
[<CompiledName "EnsureTable">]
let ensureTable name =
Query.Definition.ensureTableFor name "JSONB"
/// SQL statement to create an index on JSON documents in the specified table
[<CompiledName "EnsureDocumentIndex">]
let ensureDocumentIndex (name: string) idxType =
let extraOps = match idxType with Full -> "" | Optimized -> " jsonb_path_ops"
let tableName = name.Split '.' |> Array.last
$"CREATE INDEX IF NOT EXISTS idx_{tableName}_document ON {name} USING GIN (data{extraOps})"
/// Create a WHERE clause fragment to implement a @> (JSON contains) condition
[<CompiledName "WhereDataContains">]
let whereDataContains paramName =
$"data @> %s{paramName}"
/// Create a WHERE clause fragment to implement a @? (JSON Path match) condition
[<CompiledName "WhereJsonPathMatches">]
let whereJsonPathMatches paramName =
$"data @? %s{paramName}::jsonpath"
/// Create an UPDATE statement to patch documents
[<CompiledName "Patch">]
let patch tableName =
$"UPDATE %s{tableName} SET data = data || @data"
/// Create an UPDATE statement to remove fields from documents
[<CompiledName "RemoveFields">]
let removeFields tableName =
$"UPDATE %s{tableName} SET data = data - @name"
/// Create a query by a document's ID
[<CompiledName "ById">]
let byId<'TKey> statement (docId: 'TKey) =
Query.statementWhere statement (whereById docId)
/// Create a query on JSON fields
[<CompiledName "ByFields">]
let byFields statement howMatched fields =
Query.statementWhere statement (whereByFields howMatched fields)
/// Create a JSON containment query
[<CompiledName "ByContains">]
let byContains statement =
Query.statementWhere statement (whereDataContains "@criteria")
/// Create a JSON Path match query
[<CompiledName "ByPathMatch">]
let byPathMatch statement =
Query.statementWhere statement (whereJsonPathMatches "@path")
/// Functions for dealing with results
[<AutoOpen>]
module Results =
/// Create a domain item from a document, specifying the field in which the document is found
[<CompiledName "FromDocument">]
let fromDocument<'T> field (row: RowReader) : 'T =
Configuration.serializer().Deserialize<'T>(row.string field)
/// Create a domain item from a document
[<CompiledName "FromData">]
let fromData<'T> row : 'T =
fromDocument "data" row
/// Extract a count from the column "it"
[<CompiledName "ToCount">]
let toCount (row: RowReader) =
row.int "it"
/// Extract a true/false value from the column "it"
[<CompiledName "ToExists">]
let toExists (row: RowReader) =
row.bool "it"
/// Versions of queries that accept SqlProps as the last parameter
module WithProps =
module FSharpList = Microsoft.FSharp.Collections.List
/// Commands to execute custom SQL queries
[<RequireQualifiedAccess>]
module Custom =
/// Execute a query that returns a list of results
[<CompiledName "FSharpList">]
let list<'TDoc> query parameters (mapFunc: RowReader -> 'TDoc) sqlProps =
Sql.query query sqlProps
|> Sql.parameters (List.ofSeq parameters)
|> Sql.executeAsync mapFunc
/// Execute a query that returns a list of results
let List<'TDoc>(query, parameters, mapFunc: System.Func<RowReader, 'TDoc>, sqlProps) = backgroundTask {
let! results = list<'TDoc> query parameters mapFunc.Invoke sqlProps
return ResizeArray results
}
/// Execute a query that returns one or no results; returns None if not found
[<CompiledName "FSharpSingle">]
let single<'TDoc> query parameters mapFunc sqlProps = backgroundTask {
let! results = list<'TDoc> query parameters mapFunc sqlProps
return FSharp.Collections.List.tryHead results
}
/// Execute a query that returns one or no results; returns null if not found
let Single<'TDoc when 'TDoc: null>(
query, parameters, mapFunc: System.Func<RowReader, 'TDoc>, sqlProps) = backgroundTask {
let! result = single<'TDoc> query parameters mapFunc.Invoke sqlProps
return Option.toObj result
}
/// Execute a query that returns no results
[<CompiledName "NonQuery">]
let nonQuery query parameters sqlProps =
Sql.query query sqlProps
|> Sql.parameters (FSharpList.ofSeq parameters)
|> Sql.executeNonQueryAsync
|> ignoreTask
/// Execute a query that returns a scalar value
[<CompiledName "FSharpScalar">]
let scalar<'T when 'T: struct> query parameters (mapFunc: RowReader -> 'T) sqlProps =
Sql.query query sqlProps
|> Sql.parameters (FSharpList.ofSeq parameters)
|> Sql.executeRowAsync mapFunc
/// Execute a query that returns a scalar value
let Scalar<'T when 'T: struct>(query, parameters, mapFunc: System.Func<RowReader, 'T>, sqlProps) =
scalar<'T> query parameters mapFunc.Invoke sqlProps
/// Table and index definition commands
module Definition =
/// Create a document table
[<CompiledName "EnsureTable">]
let ensureTable name sqlProps = backgroundTask {
do! Custom.nonQuery (Query.Definition.ensureTable name) [] sqlProps
do! Custom.nonQuery (Query.Definition.ensureKey name PostgreSQL) [] sqlProps
}
/// Create an index on documents in the specified table
[<CompiledName "EnsureDocumentIndex">]
let ensureDocumentIndex name idxType sqlProps =
Custom.nonQuery (Query.Definition.ensureDocumentIndex name idxType) [] sqlProps
/// Create an index on field(s) within documents in the specified table
[<CompiledName "EnsureFieldIndex">]
let ensureFieldIndex tableName indexName fields sqlProps =
Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields PostgreSQL) [] sqlProps
/// Commands to add documents
[<AutoOpen>]
module Document =
/// Insert a new document
[<CompiledName "Insert">]
let insert<'TDoc> tableName (document: 'TDoc) sqlProps =
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}')::numeric), 0) + 1 FROM {tableName}) || '"
| Guid -> $"\"{AutoId.GenerateGuid()}\""
| RandomString -> $"\"{AutoId.GenerateRandomString(Configuration.idStringLength ())}\""
| Disabled -> "@data"
|> function it -> $"""@data::jsonb || ('{{"{idField}":{it}}}')::jsonb"""
else "@data"
(Query.insert tableName).Replace("@data", dataParam)
Custom.nonQuery query [ jsonParam "@data" document ] sqlProps
/// 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) sqlProps =
Custom.nonQuery (Query.save tableName) [ jsonParam "@data" document ] sqlProps
/// Commands to count documents
[<RequireQualifiedAccess>]
module Count =
/// Count all documents in a table
[<CompiledName "All">]
let all tableName sqlProps =
Custom.scalar (Query.count tableName) [] toCount sqlProps
/// Count matching documents using JSON field comparisons (->> =)
[<CompiledName "ByFields">]
let byFields tableName howMatched fields sqlProps =
Custom.scalar
(Query.byFields (Query.count tableName) howMatched fields) (addFieldParams fields []) toCount sqlProps
/// Count matching documents using a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TContains) sqlProps =
Custom.scalar
(Query.byContains (Query.count tableName)) [ jsonParam "@criteria" criteria ] toCount sqlProps
/// Count matching documents using a JSON Path match query (@?)
[<CompiledName "ByJsonPath">]
let byJsonPath tableName jsonPath sqlProps =
Custom.scalar
(Query.byPathMatch (Query.count tableName)) [ "@path", Sql.string jsonPath ] toCount sqlProps
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
module Exists =
/// Determine if a document exists for the given ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) sqlProps =
Custom.scalar (Query.exists tableName (Query.whereById docId)) [ idParam docId ] toExists sqlProps
/// Determine if a document exists using JSON field comparisons (->> =)
[<CompiledName "ByFields">]
let byFields tableName howMatched fields sqlProps =
Custom.scalar
(Query.exists tableName (Query.whereByFields howMatched fields))
(addFieldParams fields [])
toExists
sqlProps
/// Determine if a document exists using a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TContains) sqlProps =
Custom.scalar
(Query.exists tableName (Query.whereDataContains "@criteria"))
[ jsonParam "@criteria" criteria ]
toExists
sqlProps
/// Determine if a document exists using a JSON Path match query (@?)
[<CompiledName "ByJsonPath">]
let byJsonPath tableName jsonPath sqlProps =
Custom.scalar
(Query.exists tableName (Query.whereJsonPathMatches "@path"))
[ "@path", Sql.string jsonPath ]
toExists
sqlProps
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
module Find =
/// Retrieve all documents in the given table
[<CompiledName "FSharpAll">]
let all<'TDoc> tableName sqlProps =
Custom.list<'TDoc> (Query.find tableName) [] fromData<'TDoc> sqlProps
/// Retrieve all documents in the given table
let All<'TDoc>(tableName, sqlProps) =
Custom.List<'TDoc>(Query.find tableName, [], fromData<'TDoc>, sqlProps)
/// Retrieve all documents in the given table ordered by the given fields in the document
[<CompiledName "FSharpAllOrdered">]
let allOrdered<'TDoc> tableName orderFields sqlProps =
Custom.list<'TDoc> (Query.find tableName + Query.orderBy orderFields PostgreSQL) [] fromData<'TDoc> sqlProps
/// Retrieve all documents in the given table ordered by the given fields in the document
let AllOrdered<'TDoc>(tableName, orderFields, sqlProps) =
Custom.List<'TDoc>(
Query.find tableName + Query.orderBy orderFields PostgreSQL, [], fromData<'TDoc>, sqlProps)
/// Retrieve a document by its ID (returns None if not found)
[<CompiledName "FSharpById">]
let byId<'TKey, 'TDoc> tableName (docId: 'TKey) sqlProps =
Custom.single (Query.byId (Query.find tableName) docId) [ idParam docId ] fromData<'TDoc> sqlProps
/// Retrieve a document by its ID (returns null if not found)
let ById<'TKey, 'TDoc when 'TDoc: null>(tableName, docId: 'TKey, sqlProps) =
Custom.Single<'TDoc>(
Query.byId (Query.find tableName) docId, [ idParam docId ], fromData<'TDoc>, sqlProps)
/// Retrieve documents matching JSON field comparisons (->> =)
[<CompiledName "FSharpByFields">]
let byFields<'TDoc> tableName howMatched fields sqlProps =
Custom.list<'TDoc>
(Query.byFields (Query.find tableName) howMatched fields)
(addFieldParams fields [])
fromData<'TDoc>
sqlProps
/// Retrieve documents matching JSON field comparisons (->> =)
let ByFields<'TDoc>(tableName, howMatched, fields, sqlProps) =
Custom.List<'TDoc>(
Query.byFields (Query.find tableName) howMatched fields,
addFieldParams fields [],
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching JSON field comparisons (->> =) ordered by the given fields in the document
[<CompiledName "FSharpByFieldsOrdered">]
let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields sqlProps =
Custom.list<'TDoc>
(Query.byFields (Query.find tableName) howMatched queryFields + Query.orderBy orderFields PostgreSQL)
(addFieldParams queryFields [])
fromData<'TDoc>
sqlProps
/// Retrieve documents matching JSON field comparisons (->> =) ordered by the given fields in the document
let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, sqlProps) =
Custom.List<'TDoc>(
Query.byFields (Query.find tableName) howMatched queryFields + Query.orderBy orderFields PostgreSQL,
addFieldParams queryFields [],
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching a JSON containment query (@>)
[<CompiledName "FSharpByContains">]
let byContains<'TDoc> tableName (criteria: obj) sqlProps =
Custom.list<'TDoc>
(Query.byContains (Query.find tableName)) [ jsonParam "@criteria" criteria ] fromData<'TDoc> sqlProps
/// Retrieve documents matching a JSON containment query (@>)
let ByContains<'TDoc>(tableName, criteria: obj, sqlProps) =
Custom.List<'TDoc>(
Query.byContains (Query.find tableName),
[ jsonParam "@criteria" criteria ],
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
[<CompiledName "FSharpByContainsOrdered">]
let byContainsOrdered<'TDoc> tableName (criteria: obj) orderFields sqlProps =
Custom.list<'TDoc>
(Query.byContains (Query.find tableName) + Query.orderBy orderFields PostgreSQL)
[ jsonParam "@criteria" criteria ]
fromData<'TDoc>
sqlProps
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
let ByContainsOrdered<'TDoc>(tableName, criteria: obj, orderFields, sqlProps) =
Custom.List<'TDoc>(
Query.byContains (Query.find tableName) + Query.orderBy orderFields PostgreSQL,
[ jsonParam "@criteria" criteria ],
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching a JSON Path match query (@?)
[<CompiledName "FSharpByJsonPath">]
let byJsonPath<'TDoc> tableName jsonPath sqlProps =
Custom.list<'TDoc>
(Query.byPathMatch (Query.find tableName)) [ "@path", Sql.string jsonPath ] fromData<'TDoc> sqlProps
/// Retrieve documents matching a JSON Path match query (@?)
let ByJsonPath<'TDoc>(tableName, jsonPath, sqlProps) =
Custom.List<'TDoc>(
Query.byPathMatch (Query.find tableName),
[ "@path", Sql.string jsonPath ],
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
[<CompiledName "FSharpByJsonPathOrdered">]
let byJsonPathOrdered<'TDoc> tableName jsonPath orderFields sqlProps =
Custom.list<'TDoc>
(Query.byPathMatch (Query.find tableName) + Query.orderBy orderFields PostgreSQL)
[ "@path", Sql.string jsonPath ]
fromData<'TDoc>
sqlProps
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
let ByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, sqlProps) =
Custom.List<'TDoc>(
Query.byPathMatch (Query.find tableName) + Query.orderBy orderFields PostgreSQL,
[ "@path", Sql.string jsonPath ],
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching JSON field comparisons (->> =); returns None if not found
[<CompiledName "FSharpFirstByFields">]
let firstByFields<'TDoc> tableName howMatched fields sqlProps =
Custom.single<'TDoc>
$"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1"
(addFieldParams fields [])
fromData<'TDoc>
sqlProps
/// Retrieve the first document matching JSON field comparisons (->> =); returns null if not found
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields, sqlProps) =
Custom.Single<'TDoc>(
$"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1",
addFieldParams fields [],
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching JSON field comparisons (->> =) ordered by the given fields in the
/// document; returns None if not found
[<CompiledName "FSharpFirstByFieldsOrdered">]
let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields sqlProps =
Custom.single<'TDoc>
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields PostgreSQL} LIMIT 1"
(addFieldParams queryFields [])
fromData<'TDoc>
sqlProps
/// Retrieve the first document matching JSON field comparisons (->> =) ordered by the given fields in the
/// document; returns null if not found
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields, sqlProps) =
Custom.Single<'TDoc>(
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields PostgreSQL} LIMIT 1",
addFieldParams queryFields [],
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
[<CompiledName "FSharpFirstByContains">]
let firstByContains<'TDoc> tableName (criteria: obj) sqlProps =
Custom.single<'TDoc>
$"{Query.byContains (Query.find tableName)} LIMIT 1"
[ jsonParam "@criteria" criteria ]
fromData<'TDoc>
sqlProps
/// Retrieve the first document matching a JSON containment query (@>); returns null if not found
let FirstByContains<'TDoc when 'TDoc: null>(tableName, criteria: obj, sqlProps) =
Custom.Single<'TDoc>(
$"{Query.byContains (Query.find tableName)} LIMIT 1",
[ jsonParam "@criteria" criteria ],
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the
/// document; returns None if not found
[<CompiledName "FSharpFirstByContainsOrdered">]
let firstByContainsOrdered<'TDoc> tableName (criteria: obj) orderFields sqlProps =
Custom.single<'TDoc>
$"{Query.byContains (Query.find tableName)}{Query.orderBy orderFields PostgreSQL} LIMIT 1"
[ jsonParam "@criteria" criteria ]
fromData<'TDoc>
sqlProps
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the
/// document; returns null if not found
let FirstByContainsOrdered<'TDoc when 'TDoc: null>(tableName, criteria: obj, orderFields, sqlProps) =
Custom.Single<'TDoc>(
$"{Query.byContains (Query.find tableName)}{Query.orderBy orderFields PostgreSQL} LIMIT 1",
[ jsonParam "@criteria" criteria ],
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
[<CompiledName "FSharpFirstByJsonPath">]
let firstByJsonPath<'TDoc> tableName jsonPath sqlProps =
Custom.single<'TDoc>
$"{Query.byPathMatch (Query.find tableName)} LIMIT 1"
[ "@path", Sql.string jsonPath ]
fromData<'TDoc>
sqlProps
/// Retrieve the first document matching a JSON Path match query (@?); returns null if not found
let FirstByJsonPath<'TDoc when 'TDoc: null>(tableName, jsonPath, sqlProps) =
Custom.Single<'TDoc>(
$"{Query.byPathMatch (Query.find tableName)} LIMIT 1",
[ "@path", Sql.string jsonPath ],
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the
/// document; returns None if not found
[<CompiledName "FSharpFirstByJsonPathOrdered">]
let firstByJsonPathOrdered<'TDoc> tableName jsonPath orderFields sqlProps =
Custom.single<'TDoc>
$"{Query.byPathMatch (Query.find tableName)}{Query.orderBy orderFields PostgreSQL} LIMIT 1"
[ "@path", Sql.string jsonPath ]
fromData<'TDoc>
sqlProps
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the
/// document; returns null if not found
let FirstByJsonPathOrdered<'TDoc when 'TDoc: null>(tableName, jsonPath, orderFields, sqlProps) =
Custom.Single<'TDoc>(
$"{Query.byPathMatch (Query.find tableName)}{Query.orderBy orderFields PostgreSQL} LIMIT 1",
[ "@path", Sql.string jsonPath ],
fromData<'TDoc>,
sqlProps)
/// Commands to update documents
[<RequireQualifiedAccess>]
module Update =
/// Update an entire document by its ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) (document: 'TDoc) sqlProps =
Custom.nonQuery
(Query.byId (Query.update tableName) docId) [ idParam docId; jsonParam "@data" document ] sqlProps
/// Update an entire document by its ID, using the provided function to obtain the ID from the document
[<CompiledName "FSharpByFunc">]
let byFunc tableName (idFunc: 'TDoc -> 'TKey) (document: 'TDoc) sqlProps =
byId tableName (idFunc document) document sqlProps
/// Update an entire document by its ID, using the provided function to obtain the ID from the document
let ByFunc(tableName, idFunc: System.Func<'TDoc, 'TKey>, document: 'TDoc, sqlProps) =
byFunc tableName idFunc.Invoke document sqlProps
/// Commands to patch (partially update) documents
[<RequireQualifiedAccess>]
module Patch =
/// Patch a document by its ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) (patch: 'TPatch) sqlProps =
Custom.nonQuery
(Query.byId (Query.patch tableName) docId) [ idParam docId; jsonParam "@data" patch ] sqlProps
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<CompiledName "ByFields">]
let byFields tableName howMatched fields (patch: 'TPatch) sqlProps =
Custom.nonQuery
(Query.byFields (Query.patch tableName) howMatched fields)
(addFieldParams fields [ jsonParam "@data" patch ])
sqlProps
/// Patch documents using a JSON containment query in the WHERE clause (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TContains) (patch: 'TPatch) sqlProps =
Custom.nonQuery
(Query.byContains (Query.patch tableName))
[ jsonParam "@data" patch; jsonParam "@criteria" criteria ]
sqlProps
/// Patch documents using a JSON Path match query in the WHERE clause (@?)
[<CompiledName "ByJsonPath">]
let byJsonPath tableName jsonPath (patch: 'TPatch) sqlProps =
Custom.nonQuery
(Query.byPathMatch (Query.patch tableName))
[ jsonParam "@data" patch; "@path", Sql.string jsonPath ]
sqlProps
/// Commands to remove fields from documents
[<RequireQualifiedAccess>]
module RemoveFields =
/// Remove fields from a document by the document's ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) fieldNames sqlProps =
Custom.nonQuery
(Query.byId (Query.removeFields tableName) docId) [ idParam docId; fieldNameParams fieldNames ] sqlProps
/// Remove fields from documents via a comparison on JSON fields in the document
[<CompiledName "ByFields">]
let byFields tableName howMatched fields fieldNames sqlProps =
Custom.nonQuery
(Query.byFields (Query.removeFields tableName) howMatched fields)
(addFieldParams fields [ fieldNameParams fieldNames ])
sqlProps
/// Remove fields from documents via a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TContains) fieldNames sqlProps =
Custom.nonQuery
(Query.byContains (Query.removeFields tableName))
[ jsonParam "@criteria" criteria; fieldNameParams fieldNames ]
sqlProps
/// Remove fields from documents via a JSON Path match query (@?)
[<CompiledName "FSharpByJsonPath">]
let byJsonPath tableName jsonPath fieldNames sqlProps =
Custom.nonQuery
(Query.byPathMatch (Query.removeFields tableName))
[ "@path", Sql.string jsonPath; fieldNameParams fieldNames ]
sqlProps
/// Commands to delete documents
[<RequireQualifiedAccess>]
module Delete =
/// Delete a document by its ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) sqlProps =
Custom.nonQuery (Query.byId (Query.delete tableName) docId) [ idParam docId ] sqlProps
/// Delete documents by matching a JSON field comparison query (->> =)
[<CompiledName "ByFields">]
let byFields tableName howMatched fields sqlProps =
Custom.nonQuery
(Query.byFields (Query.delete tableName) howMatched fields) (addFieldParams fields []) sqlProps
/// Delete documents by matching a JSON contains query (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TCriteria) sqlProps =
Custom.nonQuery (Query.byContains (Query.delete tableName)) [ jsonParam "@criteria" criteria ] sqlProps
/// Delete documents by matching a JSON Path match query (@?)
[<CompiledName "ByJsonPath">]
let byJsonPath tableName path sqlProps =
Custom.nonQuery (Query.byPathMatch (Query.delete tableName)) [ "@path", Sql.string path ] sqlProps
/// Commands to execute custom SQL queries
[<RequireQualifiedAccess>]
module Custom =
/// Execute a query that returns a list of results
[<CompiledName "FSharpList">]
let list<'TDoc> query parameters (mapFunc: RowReader -> 'TDoc) =
WithProps.Custom.list<'TDoc> query parameters mapFunc (fromDataSource ())
/// Execute a query that returns a list of results
let List<'TDoc>(query, parameters, mapFunc: System.Func<RowReader, 'TDoc>) =
WithProps.Custom.List<'TDoc>(query, parameters, mapFunc, fromDataSource ())
/// Execute a query that returns one or no results; returns None if not found
[<CompiledName "FSharpSingle">]
let single<'TDoc> query parameters (mapFunc: RowReader -> 'TDoc) =
WithProps.Custom.single<'TDoc> query parameters mapFunc (fromDataSource ())
/// Execute a query that returns one or no results; returns null if not found
let Single<'TDoc when 'TDoc: null>(query, parameters, mapFunc: System.Func<RowReader, 'TDoc>) =
WithProps.Custom.Single<'TDoc>(query, parameters, mapFunc, fromDataSource ())
/// Execute a query that returns no results
[<CompiledName "NonQuery">]
let nonQuery query parameters =
WithProps.Custom.nonQuery query parameters (fromDataSource ())
/// Execute a query that returns a scalar value
[<CompiledName "FSharpScalar">]
let scalar<'T when 'T: struct> query parameters (mapFunc: RowReader -> 'T) =
WithProps.Custom.scalar query parameters mapFunc (fromDataSource ())
/// Execute a query that returns a scalar value
let Scalar<'T when 'T: struct>(query, parameters, mapFunc: System.Func<RowReader, 'T>) =
WithProps.Custom.Scalar<'T>(query, parameters, mapFunc, fromDataSource ())
/// Table and index definition commands
[<RequireQualifiedAccess>]
module Definition =
/// Create a document table
[<CompiledName "EnsureTable">]
let ensureTable name =
WithProps.Definition.ensureTable name (fromDataSource ())
/// Create an index on documents in the specified table
[<CompiledName "EnsureDocumentIndex">]
let ensureDocumentIndex name idxType =
WithProps.Definition.ensureDocumentIndex name idxType (fromDataSource ())
/// Create an index on field(s) within documents in the specified table
[<CompiledName "EnsureFieldIndex">]
let ensureFieldIndex tableName indexName fields =
WithProps.Definition.ensureFieldIndex tableName indexName fields (fromDataSource ())
/// Document writing functions
[<AutoOpen>]
module Document =
/// Insert a new document
[<CompiledName "Insert">]
let insert<'TDoc> tableName (document: 'TDoc) =
WithProps.Document.insert<'TDoc> tableName document (fromDataSource ())
/// 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) =
WithProps.Document.save<'TDoc> tableName document (fromDataSource ())
/// Queries to count documents
[<RequireQualifiedAccess>]
module Count =
/// Count all documents in a table
[<CompiledName "All">]
let all tableName =
WithProps.Count.all tableName (fromDataSource ())
/// Count matching documents using a JSON field comparison query (->> =)
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
WithProps.Count.byFields tableName howMatched fields (fromDataSource ())
/// Count matching documents using a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName criteria =
WithProps.Count.byContains tableName criteria (fromDataSource ())
/// Count matching documents using a JSON Path match query (@?)
[<CompiledName "ByJsonPath">]
let byJsonPath tableName jsonPath =
WithProps.Count.byJsonPath tableName jsonPath (fromDataSource ())
/// Queries to determine if documents exist
[<RequireQualifiedAccess>]
module Exists =
/// Determine if a document exists for the given ID
[<CompiledName "ById">]
let byId tableName docId =
WithProps.Exists.byId tableName docId (fromDataSource ())
/// Determine if documents exist using a JSON field comparison query (->> =)
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
WithProps.Exists.byFields tableName howMatched fields (fromDataSource ())
/// Determine if documents exist using a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName criteria =
WithProps.Exists.byContains tableName criteria (fromDataSource ())
/// Determine if documents exist using a JSON Path match query (@?)
[<CompiledName "ByJsonPath">]
let byJsonPath tableName jsonPath =
WithProps.Exists.byJsonPath tableName jsonPath (fromDataSource ())
/// Commands to retrieve documents
[<RequireQualifiedAccess>]
module Find =
/// Retrieve all documents in the given table
[<CompiledName "FSharpAll">]
let all<'TDoc> tableName =
WithProps.Find.all<'TDoc> tableName (fromDataSource ())
/// Retrieve all documents in the given table
let All<'TDoc> tableName =
WithProps.Find.All<'TDoc>(tableName, fromDataSource ())
/// Retrieve all documents in the given table ordered by the given fields in the document
[<CompiledName "FSharpAllOrdered">]
let allOrdered<'TDoc> tableName orderFields =
WithProps.Find.allOrdered<'TDoc> tableName orderFields (fromDataSource ())
/// Retrieve all documents in the given table ordered by the given fields in the document
let AllOrdered<'TDoc> tableName orderFields =
WithProps.Find.AllOrdered<'TDoc>(tableName, orderFields, fromDataSource ())
/// Retrieve a document by its ID; returns None if not found
[<CompiledName "FSharpById">]
let byId<'TKey, 'TDoc> tableName docId =
WithProps.Find.byId<'TKey, 'TDoc> tableName docId (fromDataSource ())
/// Retrieve a document by its ID; returns null if not found
let ById<'TKey, 'TDoc when 'TDoc: null>(tableName, docId: 'TKey) =
WithProps.Find.ById<'TKey, 'TDoc>(tableName, docId, fromDataSource ())
/// Retrieve documents matching a JSON field comparison query (->> =)
[<CompiledName "FSharpByFields">]
let byFields<'TDoc> tableName howMatched fields =
WithProps.Find.byFields<'TDoc> tableName howMatched fields (fromDataSource ())
/// Retrieve documents matching a JSON field comparison query (->> =)
let ByFields<'TDoc>(tableName, howMatched, fields) =
WithProps.Find.ByFields<'TDoc>(tableName, howMatched, fields, fromDataSource ())
/// Retrieve documents matching a JSON field comparison query (->> =) ordered by the given fields in the document
[<CompiledName "FSharpByFieldsOrdered">]
let byFieldsOrdered<'TDoc> tableName howMatched queryField orderFields =
WithProps.Find.byFieldsOrdered<'TDoc> tableName howMatched queryField orderFields (fromDataSource ())
/// Retrieve documents matching a JSON field comparison query (->> =) ordered by the given fields in the document
let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields) =
WithProps.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, fromDataSource ())
/// Retrieve documents matching a JSON containment query (@>)
[<CompiledName "FSharpByContains">]
let byContains<'TDoc> tableName (criteria: obj) =
WithProps.Find.byContains<'TDoc> tableName criteria (fromDataSource ())
/// Retrieve documents matching a JSON containment query (@>)
let ByContains<'TDoc>(tableName, criteria: obj) =
WithProps.Find.ByContains<'TDoc>(tableName, criteria, fromDataSource ())
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
[<CompiledName "FSharpByContainsOrdered">]
let byContainsOrdered<'TDoc> tableName (criteria: obj) orderFields =
WithProps.Find.byContainsOrdered<'TDoc> tableName criteria orderFields (fromDataSource ())
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
let ByContainsOrdered<'TDoc>(tableName, criteria: obj, orderFields) =
WithProps.Find.ByContainsOrdered<'TDoc>(tableName, criteria, orderFields, fromDataSource ())
/// Retrieve documents matching a JSON Path match query (@?)
[<CompiledName "FSharpByJsonPath">]
let byJsonPath<'TDoc> tableName jsonPath =
WithProps.Find.byJsonPath<'TDoc> tableName jsonPath (fromDataSource ())
/// Retrieve documents matching a JSON Path match query (@?)
let ByJsonPath<'TDoc>(tableName, jsonPath) =
WithProps.Find.ByJsonPath<'TDoc>(tableName, jsonPath, fromDataSource ())
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
[<CompiledName "FSharpByJsonPathOrdered">]
let byJsonPathOrdered<'TDoc> tableName jsonPath orderFields =
WithProps.Find.byJsonPathOrdered<'TDoc> tableName jsonPath orderFields (fromDataSource ())
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
let ByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields) =
WithProps.Find.ByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, fromDataSource ())
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
[<CompiledName "FSharpFirstByFields">]
let firstByFields<'TDoc> tableName howMatched fields =
WithProps.Find.firstByFields<'TDoc> tableName howMatched fields (fromDataSource ())
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields) =
WithProps.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, fromDataSource ())
/// Retrieve the first document matching a JSON field comparison query (->> =) ordered by the given fields in the
/// document; returns None if not found
[<CompiledName "FSharpFirstByFieldsOrdered">]
let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
WithProps.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields (fromDataSource ())
/// Retrieve the first document matching a JSON field comparison query (->> =) ordered by the given fields in the
/// document; returns null if not found
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields) =
WithProps.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, fromDataSource ())
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
[<CompiledName "FSharpFirstByContains">]
let firstByContains<'TDoc> tableName (criteria: obj) =
WithProps.Find.firstByContains<'TDoc> tableName criteria (fromDataSource ())
/// Retrieve the first document matching a JSON containment query (@>); returns null if not found
let FirstByContains<'TDoc when 'TDoc: null>(tableName, criteria: obj) =
WithProps.Find.FirstByContains<'TDoc>(tableName, criteria, fromDataSource ())
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the document;
/// returns None if not found
[<CompiledName "FSharpFirstByContainsOrdered">]
let firstByContainsOrdered<'TDoc> tableName (criteria: obj) orderFields =
WithProps.Find.firstByContainsOrdered<'TDoc> tableName criteria orderFields (fromDataSource ())
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the document;
/// returns null if not found
let FirstByContainsOrdered<'TDoc when 'TDoc: null>(tableName, criteria: obj, orderFields) =
WithProps.Find.FirstByContainsOrdered<'TDoc>(tableName, criteria, orderFields, fromDataSource ())
/// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
[<CompiledName "FSharpFirstByJsonPath">]
let firstByJsonPath<'TDoc> tableName jsonPath =
WithProps.Find.firstByJsonPath<'TDoc> tableName jsonPath (fromDataSource ())
/// Retrieve the first document matching a JSON Path match query (@?); returns null if not found
let FirstByJsonPath<'TDoc when 'TDoc: null>(tableName, jsonPath) =
WithProps.Find.FirstByJsonPath<'TDoc>(tableName, jsonPath, fromDataSource ())
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the document;
/// returns None if not found
[<CompiledName "FSharpFirstByJsonPathOrdered">]
let firstByJsonPathOrdered<'TDoc> tableName jsonPath orderFields =
WithProps.Find.firstByJsonPathOrdered<'TDoc> tableName jsonPath orderFields (fromDataSource ())
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the document;
/// returns null if not found
let FirstByJsonPathOrdered<'TDoc when 'TDoc: null>(tableName, jsonPath, orderFields) =
WithProps.Find.FirstByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, fromDataSource ())
/// Commands to update documents
[<RequireQualifiedAccess>]
module Update =
/// Update an entire document by its ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) (document: 'TDoc) =
WithProps.Update.byId tableName docId document (fromDataSource ())
/// Update an entire document by its ID, using the provided function to obtain the ID from the document
[<CompiledName "FSharpFullFunc">]
let byFunc tableName (idFunc: 'TDoc -> 'TKey) (document: 'TDoc) =
WithProps.Update.byFunc tableName idFunc document (fromDataSource ())
/// Update an entire document by its ID, using the provided function to obtain the ID from the document
let ByFunc(tableName, idFunc: System.Func<'TDoc, 'TKey>, document: 'TDoc) =
WithProps.Update.ByFunc(tableName, idFunc, document, fromDataSource ())
/// Commands to patch (partially update) documents
[<RequireQualifiedAccess>]
module Patch =
/// Patch a document by its ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) (patch: 'TPatch) =
WithProps.Patch.byId tableName docId patch (fromDataSource ())
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<CompiledName "ByFields">]
let byFields tableName howMatched fields (patch: 'TPatch) =
WithProps.Patch.byFields tableName howMatched fields patch (fromDataSource ())
/// Patch documents using a JSON containment query in the WHERE clause (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TCriteria) (patch: 'TPatch) =
WithProps.Patch.byContains tableName criteria patch (fromDataSource ())
/// Patch documents using a JSON Path match query in the WHERE clause (@?)
[<CompiledName "ByJsonPath">]
let byJsonPath tableName jsonPath (patch: 'TPatch) =
WithProps.Patch.byJsonPath tableName jsonPath patch (fromDataSource ())
/// Commands to remove fields from documents
[<RequireQualifiedAccess>]
module RemoveFields =
/// Remove fields from a document by the document's ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) fieldNames =
WithProps.RemoveFields.byId tableName docId fieldNames (fromDataSource ())
/// Remove fields from documents via a comparison on JSON fields in the document
[<CompiledName "ByFields">]
let byFields tableName howMatched fields fieldNames =
WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (fromDataSource ())
/// Remove fields from documents via a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TContains) fieldNames =
WithProps.RemoveFields.byContains tableName criteria fieldNames (fromDataSource ())
/// Remove fields from documents via a JSON Path match query (@?)
[<CompiledName "ByJsonPath">]
let byJsonPath tableName jsonPath fieldNames =
WithProps.RemoveFields.byJsonPath tableName jsonPath fieldNames (fromDataSource ())
/// Commands to delete documents
[<RequireQualifiedAccess>]
module Delete =
/// Delete a document by its ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) =
WithProps.Delete.byId tableName docId (fromDataSource ())
/// Delete documents by matching a JSON field comparison query (->> =)
[<CompiledName "ByFields">]
let byFields tableName howMatched fields =
WithProps.Delete.byFields tableName howMatched fields (fromDataSource ())
/// Delete documents by matching a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TContains) =
WithProps.Delete.byContains tableName criteria (fromDataSource ())
/// Delete documents by matching a JSON Path match query (@?)
[<CompiledName "ByJsonPath">]
let byJsonPath tableName path =
WithProps.Delete.byJsonPath tableName path (fromDataSource ())