diff --git a/src/Postgres/Library.fs b/src/Postgres/Library.fs
index beb9542..7a5520e 100644
--- a/src/Postgres/Library.fs
+++ b/src/Postgres/Library.fs
@@ -2,6 +2,7 @@
open System.IO
open System.Text
+open System.Threading.Tasks
/// The type of index to generate for the document
[]
@@ -342,12 +343,13 @@ module Results =
/// The query from which JSON should be extracted
[]
let writeJsonArray (writer: StreamWriter) (mapFunc: RowReader -> string) sqlProps = backgroundTask {
+ let await (it: Task) = it.ConfigureAwait(false).GetAwaiter().GetResult()
do! writer.WriteAsync "["
let mutable isFirst = true
do! sqlProps
|> Sql.iterAsync (fun it ->
- if isFirst then isFirst <- false else writer.Write ","
- writer.WriteAsync(mapFunc it).ConfigureAwait(false).GetAwaiter().GetResult())
+ if isFirst then isFirst <- false else await (writer.WriteAsync ",")
+ (mapFunc >> writer.WriteAsync >> await) it)
do! writer.WriteAsync "]"
}
diff --git a/src/Sqlite/BitBadger.Documents.Sqlite.fsproj b/src/Sqlite/BitBadger.Documents.Sqlite.fsproj
index e19b49d..c830a84 100644
--- a/src/Sqlite/BitBadger.Documents.Sqlite.fsproj
+++ b/src/Sqlite/BitBadger.Documents.Sqlite.fsproj
@@ -8,6 +8,8 @@
+
+
diff --git a/src/Sqlite/Functions.fs b/src/Sqlite/Functions.fs
new file mode 100644
index 0000000..24c3316
--- /dev/null
+++ b/src/Sqlite/Functions.fs
@@ -0,0 +1,418 @@
+namespace BitBadger.Documents.Sqlite
+
+open Microsoft.Data.Sqlite
+
+/// Commands to execute custom SQL queries
+[]
+module Custom =
+
+ /// Execute a query that returns a list of results
+ /// The query to retrieve the results
+ /// Parameters to use for the query
+ /// The mapping function between the document and the domain item
+ /// A list of results for the given query
+ []
+ let list<'TDoc> query parameters (mapFunc: SqliteDataReader -> 'TDoc) =
+ use conn = Configuration.dbConn ()
+ WithConn.Custom.list<'TDoc> query parameters mapFunc conn
+
+ /// Execute a query that returns a list of results
+ /// The query to retrieve the results
+ /// Parameters to use for the query
+ /// The mapping function between the document and the domain item
+ /// A list of results for the given query
+ let List<'TDoc>(query, parameters, mapFunc: System.Func) =
+ use conn = Configuration.dbConn ()
+ WithConn.Custom.List<'TDoc>(query, parameters, mapFunc, conn)
+
+ /// Execute a query that returns one or no results
+ /// The query to retrieve the results
+ /// Parameters to use for the query
+ /// The mapping function between the document and the domain item
+ /// Some with the first matching result, or None if not found
+ []
+ let single<'TDoc> query parameters (mapFunc: SqliteDataReader -> 'TDoc) =
+ use conn = Configuration.dbConn ()
+ WithConn.Custom.single<'TDoc> query parameters mapFunc conn
+
+ /// Execute a query that returns one or no results
+ /// The query to retrieve the results
+ /// Parameters to use for the query
+ /// The mapping function between the document and the domain item
+ /// The first matching result, or null if not found
+ let Single<'TDoc when 'TDoc: null and 'TDoc: not struct>(
+ query, parameters, mapFunc: System.Func) =
+ use conn = Configuration.dbConn ()
+ WithConn.Custom.Single<'TDoc>(query, parameters, mapFunc, conn)
+
+ /// Execute a query that returns no results
+ /// The query to retrieve the results
+ /// Parameters to use for the query
+ []
+ let nonQuery query parameters =
+ use conn = Configuration.dbConn ()
+ WithConn.Custom.nonQuery query parameters conn
+
+ /// Execute a query that returns a scalar value
+ /// The query to retrieve the value
+ /// Parameters to use for the query
+ /// The mapping function to obtain the value
+ /// The scalar value for the query
+ []
+ let scalar<'T when 'T: struct> query parameters (mapFunc: SqliteDataReader -> 'T) =
+ use conn = Configuration.dbConn ()
+ WithConn.Custom.scalar<'T> query parameters mapFunc conn
+
+ /// Execute a query that returns a scalar value
+ /// The query to retrieve the value
+ /// Parameters to use for the query
+ /// The mapping function to obtain the value
+ /// The scalar value for the query
+ let Scalar<'T when 'T: struct>(query, parameters, mapFunc: System.Func) =
+ use conn = Configuration.dbConn ()
+ WithConn.Custom.Scalar<'T>(query, parameters, mapFunc, conn)
+
+
+/// Functions to create tables and indexes
+[]
+module Definition =
+
+ /// Create a document table
+ /// The table whose existence should be ensured (may include schema)
+ []
+ let ensureTable name =
+ use conn = Configuration.dbConn ()
+ WithConn.Definition.ensureTable name conn
+
+ /// Create an index on field(s) within documents in the specified table
+ /// The table to be indexed (may include schema)
+ /// The name of the index to create
+ /// One or more fields to be indexed
+ []
+ let ensureFieldIndex tableName indexName fields =
+ use conn = Configuration.dbConn ()
+ WithConn.Definition.ensureFieldIndex tableName indexName fields conn
+
+
+/// Document insert/save functions
+[]
+module Document =
+
+ /// Insert a new document
+ /// The table into which the document should be inserted (may include schema)
+ /// The document to be inserted
+ []
+ let insert<'TDoc> tableName (document: 'TDoc) =
+ use conn = Configuration.dbConn ()
+ WithConn.Document.insert tableName document conn
+
+ /// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
+ /// The table into which the document should be saved (may include schema)
+ /// The document to be saved
+ []
+ let save<'TDoc> tableName (document: 'TDoc) =
+ use conn = Configuration.dbConn ()
+ WithConn.Document.save tableName document conn
+
+
+/// Commands to count documents
+[]
+module Count =
+
+ /// Count all documents in a table
+ /// The table in which documents should be counted (may include schema)
+ /// The count of the documents in the table
+ []
+ let all tableName =
+ use conn = Configuration.dbConn ()
+ WithConn.Count.all tableName conn
+
+ /// Count matching documents using JSON field comparisons (->> =, etc.)
+ /// The table in which documents should be counted (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The count of matching documents in the table
+ []
+ let byFields tableName howMatched fields =
+ use conn = Configuration.dbConn ()
+ WithConn.Count.byFields tableName howMatched fields conn
+
+
+/// Commands to determine if documents exist
+[]
+module Exists =
+
+ /// Determine if a document exists for the given ID
+ /// The table in which existence should be checked (may include schema)
+ /// The ID of the document whose existence should be checked
+ /// True if a document exists, false if not
+ []
+ let byId tableName (docId: 'TKey) =
+ use conn = Configuration.dbConn ()
+ WithConn.Exists.byId tableName docId conn
+
+ /// Determine if a document exists using JSON field comparisons (->> =, etc.)
+ /// The table in which existence should be checked (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// True if any matching documents exist, false if not
+ []
+ let byFields tableName howMatched fields =
+ use conn = Configuration.dbConn ()
+ WithConn.Exists.byFields tableName howMatched fields conn
+
+
+/// Commands to retrieve documents
+[]
+module Find =
+
+ /// Retrieve all documents in the given table
+ /// The table from which documents should be retrieved (may include schema)
+ /// All documents from the given table
+ []
+ let all<'TDoc> tableName =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.all<'TDoc> tableName conn
+
+ /// Retrieve all documents in the given table
+ /// The table from which documents should be retrieved (may include schema)
+ /// All documents from the given table
+ let All<'TDoc> tableName =
+ 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
+ /// The table from which documents should be retrieved (may include schema)
+ /// Fields by which the results should be ordered
+ /// All documents from the given table, ordered by the given fields
+ []
+ 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
+ /// The table from which documents should be retrieved (may include schema)
+ /// Fields by which the results should be ordered
+ /// All documents from the given table, ordered by the given fields
+ let AllOrdered<'TDoc> tableName orderFields =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.AllOrdered<'TDoc>(tableName, orderFields, conn)
+
+ /// Retrieve a document by its ID
+ /// The table from which a document should be retrieved (may include schema)
+ /// The ID of the document to retrieve
+ /// Some with the document if found, None otherwise
+ []
+ let byId<'TKey, 'TDoc> tableName docId =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn
+
+ /// Retrieve a document by its ID
+ /// The table from which a document should be retrieved (may include schema)
+ /// The ID of the document to retrieve
+ /// The document if found, null otherwise
+ let ById<'TKey, 'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, docId) =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
+
+ /// Retrieve documents matching JSON field comparisons (->> =, etc.)
+ /// The table from which documents should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// All documents matching the given fields
+ []
+ let byFields<'TDoc> tableName howMatched fields =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.byFields<'TDoc> tableName howMatched fields conn
+
+ /// Retrieve documents matching JSON field comparisons (->> =, etc.)
+ /// The table from which documents should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// All documents matching the given fields
+ let ByFields<'TDoc>(tableName, howMatched, fields) =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn)
+
+ ///
+ /// Retrieve documents matching JSON field comparisons (->> =, etc.) ordered by the given fields in
+ /// the document
+ ///
+ /// The table from which documents should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// Fields by which the results should be ordered
+ /// All documents matching the given fields, ordered by the other given fields
+ []
+ let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
+
+ ///
+ /// Retrieve documents matching JSON field comparisons (->> =, etc.) ordered by the given fields in
+ /// the document
+ ///
+ /// The table from which documents should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// Fields by which the results should be ordered
+ /// All documents matching the given fields, ordered by the other given fields
+ let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields) =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
+
+ /// Retrieve the first document matching JSON field comparisons (->> =, etc.)
+ /// The table from which a document should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// Some with the first document, or None if not found
+ []
+ let firstByFields<'TDoc> tableName howMatched fields =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn
+
+ /// Retrieve the first document matching JSON field comparisons (->> =, etc.)
+ /// The table from which a document should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The first document, or null if not found
+ let FirstByFields<'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, howMatched, fields) =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn)
+
+ ///
+ /// Retrieve the first document matching JSON field comparisons (->> =, etc.) ordered by the given
+ /// fields in the document
+ ///
+ /// The table from which a document should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// Fields by which the results should be ordered
+ ///
+ /// Some with the first document ordered by the given fields, or None if not found
+ ///
+ []
+ let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
+
+ ///
+ /// Retrieve the first document matching JSON field comparisons (->> =, etc.) ordered by the given
+ /// fields in the document
+ ///
+ /// The table from which a document should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// Fields by which the results should be ordered
+ /// The first document ordered by the given fields, or null if not found
+ let FirstByFieldsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>(
+ tableName, howMatched, queryFields, orderFields) =
+ use conn = Configuration.dbConn ()
+ WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
+
+
+/// Commands to update documents
+[]
+module Update =
+
+ /// Update (replace) an entire document by its ID
+ /// The table in which a document should be updated (may include schema)
+ /// The ID of the document to be updated (replaced)
+ /// The new document
+ []
+ let byId tableName (docId: 'TKey) (document: 'TDoc) =
+ use conn = Configuration.dbConn ()
+ WithConn.Update.byId tableName docId document conn
+
+ ///
+ /// Update (replace) an entire document by its ID, using the provided function to obtain the ID from the document
+ ///
+ /// The table in which a document should be updated (may include schema)
+ /// The function to obtain the ID of the document
+ /// The new document
+ []
+ let byFunc tableName (idFunc: 'TDoc -> 'TKey) (document: 'TDoc) =
+ use conn = Configuration.dbConn ()
+ WithConn.Update.byFunc tableName idFunc document conn
+
+ ///
+ /// Update (replace) an entire document by its ID, using the provided function to obtain the ID from the document
+ ///
+ /// The table in which a document should be updated (may include schema)
+ /// The function to obtain the ID of the document
+ /// The new document
+ let ByFunc(tableName, idFunc: System.Func<'TDoc, 'TKey>, document: 'TDoc) =
+ use conn = Configuration.dbConn ()
+ WithConn.Update.ByFunc(tableName, idFunc, document, conn)
+
+
+/// Commands to patch (partially update) documents
+[]
+module Patch =
+
+ /// Patch a document by its ID
+ /// The table in which a document should be patched (may include schema)
+ /// The ID of the document to patch
+ /// The partial document to patch the existing document
+ []
+ let byId tableName (docId: 'TKey) (patch: 'TPatch) =
+ use conn = Configuration.dbConn ()
+ WithConn.Patch.byId tableName docId patch conn
+
+ ///
+ /// Patch documents using a JSON field comparison query in the WHERE clause (->> =, etc.)
+ ///
+ /// The table in which documents should be patched (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The partial document to patch the existing document
+ []
+ let byFields tableName howMatched fields (patch: 'TPatch) =
+ use conn = Configuration.dbConn ()
+ WithConn.Patch.byFields tableName howMatched fields patch conn
+
+
+/// Commands to remove fields from documents
+[]
+module RemoveFields =
+
+ /// Remove fields from a document by the document's ID
+ /// The table in which a document should be modified (may include schema)
+ /// The ID of the document to modify
+ /// One or more field names to remove from the document
+ []
+ let byId tableName (docId: 'TKey) fieldNames =
+ use conn = Configuration.dbConn ()
+ WithConn.RemoveFields.byId tableName docId fieldNames conn
+
+ /// Remove fields from documents via a comparison on JSON fields in the document
+ /// The table in which documents should be modified (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// One or more field names to remove from the matching documents
+ []
+ let byFields tableName howMatched fields fieldNames =
+ use conn = Configuration.dbConn ()
+ WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn
+
+
+/// Commands to delete documents
+[]
+module Delete =
+
+ /// Delete a document by its ID
+ /// The table in which a document should be deleted (may include schema)
+ /// The ID of the document to delete
+ []
+ let byId tableName (docId: 'TKey) =
+ use conn = Configuration.dbConn ()
+ WithConn.Delete.byId tableName docId conn
+
+ /// Delete documents by matching a JSON field comparison query (->> =, etc.)
+ /// The table in which documents should be deleted (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ []
+ let byFields tableName howMatched fields =
+ use conn = Configuration.dbConn ()
+ WithConn.Delete.byFields tableName howMatched fields conn
diff --git a/src/Sqlite/Library.fs b/src/Sqlite/Library.fs
index 215a62b..8e74554 100644
--- a/src/Sqlite/Library.fs
+++ b/src/Sqlite/Library.fs
@@ -1,5 +1,7 @@
namespace BitBadger.Documents.Sqlite
+open System.IO
+open System.Text
open BitBadger.Documents
open Microsoft.Data.Sqlite
@@ -36,12 +38,10 @@ module Configuration =
[]
module Query =
- ///
- /// Create a WHERE clause fragment to implement a comparison on fields in a JSON document
- ///
+ /// Create a WHERE clause fragment to implement a comparison on fields in a JSON document
/// How the fields should be matched
/// The fields for the comparisons
- /// A WHERE clause implementing the comparisons for the given fields
+ /// A WHERE clause implementing the comparisons for the given fields
[]
let whereByFields (howMatched: FieldMatch) fields =
let name = ParameterName()
@@ -63,21 +63,21 @@ module Query =
| _ -> $"{it.Path SQLite AsSql} {it.Comparison.OpSql} {name.Derive it.ParameterName}")
|> String.concat $" {howMatched} "
- /// Create a WHERE clause fragment to implement an ID-based query
+ /// Create a WHERE clause fragment to implement an ID-based query
/// The ID of the document
- /// A WHERE clause fragment identifying a document by its ID
+ /// A WHERE clause fragment identifying a document by its ID
[]
let whereById (docId: 'TKey) =
whereByFields Any [ { Field.Equal (Configuration.idField ()) docId with ParameterName = Some "@id" } ]
- /// Create an UPDATE statement to patch documents
+ /// Create an UPDATE statement to patch documents
/// The table to be updated
/// A query to patch documents
[]
let patch tableName =
$"UPDATE %s{tableName} SET data = json_patch(data, json(@data))"
- /// Create an UPDATE statement to remove fields from documents
+ /// Create an UPDATE statement to remove fields from documents
/// The table to be updated
/// The parameters with the field names to be removed
/// A query to remove fields from documents
@@ -136,7 +136,7 @@ module Parameters =
SqliteParameter(name, Configuration.serializer().Serialize it)
/// Create JSON field parameters
- /// The Fields to convert to parameters
+ /// The Fields to convert to parameters
/// The current parameters for the query
/// A unified sequence of parameter names and values
[]
@@ -169,7 +169,7 @@ module Parameters =
/// Append JSON field name parameters for the given field names to the given parameters
/// The name of the parameter to use for each field
/// The names of fields to be addressed
- /// The name (@name) and parameter value for the field names
+ /// The name (@name) and parameter value for the field names
[]
let fieldNameParams paramName fieldNames =
fieldNames
@@ -189,14 +189,14 @@ module Results =
/// Create a domain item from a document, specifying the field in which the document is found
/// The field name containing the JSON document
- /// A SqliteDataReader set to the row with the document to be constructed
+ /// A SqliteDataReader set to the row with the document to be constructed
/// The constructed domain item
[]
let fromDocument<'TDoc> field (rdr: SqliteDataReader) : 'TDoc =
Configuration.serializer().Deserialize<'TDoc>(rdr.GetString(rdr.GetOrdinal field))
/// Create a domain item from a document
- /// A SqliteDataReader set to the row with the document to be constructed
+ /// A SqliteDataReader set to the row with the document to be constructed
/// The constructed domain item
[]
let fromData<'TDoc> rdr =
@@ -232,20 +232,86 @@ module Results =
}
/// Extract a count from the first column
- /// A SqliteDataReader set to the row with the count to retrieve
+ /// A SqliteDataReader set to the row with the count to retrieve
/// The count from the row
[]
let toCount (rdr: SqliteDataReader) =
rdr.GetInt64 0
/// Extract a true/false value from the first column
- /// A SqliteDataReader set to the row with the true/false value to retrieve
+ /// A SqliteDataReader set to the row with the true/false value to retrieve
/// The true/false value from the row
/// SQLite implements boolean as 1 = true, 0 = false
[]
let toExists rdr =
toCount rdr > 0L
+ /// Retrieve a JSON document, specifying the field in which the document is found
+ /// The field name containing the JSON document
+ /// A SqliteDataReader set to the row with the document to be constructed
+ /// The JSON document (an empty JSON document if not found)
+ []
+ let jsonFromDocument field (rdr: SqliteDataReader) =
+ try
+ let idx = rdr.GetOrdinal field
+ if rdr.IsDBNull idx then "{}" else rdr.GetString idx
+ with :? System.IndexOutOfRangeException -> "{}"
+
+ /// Retrieve a JSON document
+ /// A SqliteDataReader set to the row with the document to be constructed
+ /// The JSON document (an empty JSON document if not found)
+ []
+ let jsonFromData rdr =
+ jsonFromDocument "data" rdr
+
+ ///
+ /// Create a JSON array for the results of the given command, using the specified mapping function
+ ///
+ /// The command to execute
+ /// The mapping function to extract JSON from the query's results
+ /// A JSON array of items from the reader
+ []
+ let toJsonArray (cmd: SqliteCommand) (mapFunc: SqliteDataReader -> string) = backgroundTask {
+ use! rdr = cmd.ExecuteReaderAsync()
+ let it = StringBuilder "["
+ while! rdr.ReadAsync() do
+ if it.Length > 2 then ignore (it.Append ",")
+ it.Append(mapFunc rdr) |> ignore
+ return it.Append("]").ToString()
+ }
+
+ ///
+ /// Create a JSON array for the results of the given command, using the specified mapping function
+ ///
+ /// The command to execute
+ /// The mapping function to extract JSON from the query's results
+ /// A JSON array of items from the reader
+ let ToJsonArray (cmd: SqliteCommand) (mapFunc: System.Func) =
+ toJsonArray cmd mapFunc.Invoke
+
+ /// Write a JSON array of items for the results of a query to the given StreamWriter
+ /// The command to execute
+ /// The StreamWriter to which results should be written
+ /// The mapping function to extract JSON from the query's results
+ []
+ let writeJsonArray (cmd: SqliteCommand) (writer: StreamWriter) (mapFunc: SqliteDataReader -> string) =
+ backgroundTask {
+ use! rdr = cmd.ExecuteReaderAsync()
+ do! writer.WriteAsync "["
+ let mutable isFirst = true
+ while! rdr.ReadAsync() do
+ if isFirst then isFirst <- false else do! writer.WriteAsync ","
+ do! writer.WriteAsync(mapFunc rdr)
+ do! writer.WriteAsync "]"
+ }
+
+ /// Write a JSON array of items for the results of a query to the given StreamWriter
+ /// The command to execute
+ /// The StreamWriter to which results should be written
+ /// The mapping function to extract JSON from the query's results
+ let WriteJsonArray (cmd: SqliteCommand) (writer: StreamWriter) (mapFunc: System.Func) =
+ writeJsonArray cmd writer mapFunc.Invoke
+
[]
module internal Helpers =
@@ -256,928 +322,3 @@ module internal Helpers =
let! _ = cmd.ExecuteNonQueryAsync()
()
}
-
-
-/// Versions of queries that accept a SqliteConnection as the last parameter
-module WithConn =
-
- /// Commands to execute custom SQL queries
- []
- module Custom =
-
- /// Execute a query that returns a list of results
- /// The query to retrieve the results
- /// Parameters to use for the query
- /// The mapping function between the document and the domain item
- /// The SqliteConnection to use to execute the query
- /// A list of results for the given query
- []
- let list<'TDoc> query (parameters: SqliteParameter seq) (mapFunc: SqliteDataReader -> 'TDoc)
- (conn: SqliteConnection) =
- use cmd = conn.CreateCommand()
- cmd.CommandText <- query
- cmd.Parameters.AddRange parameters
- toCustomList<'TDoc> cmd mapFunc
-
- /// Execute a query that returns a list of results
- /// The query to retrieve the results
- /// Parameters to use for the query
- /// The mapping function between the document and the domain item
- /// The SqliteConnection to use to execute the query
- /// A list of results for the given query
- let List<'TDoc>(
- query, parameters: SqliteParameter seq, mapFunc: System.Func,
- conn: SqliteConnection
- ) =
- use cmd = conn.CreateCommand()
- cmd.CommandText <- query
- cmd.Parameters.AddRange parameters
- ToCustomList<'TDoc>(cmd, mapFunc)
-
- /// Execute a query that returns one or no results
- /// The query to retrieve the results
- /// Parameters to use for the query
- /// The mapping function between the document and the domain item
- /// The SqliteConnection to use to execute the query
- /// Some with the first matching result, or None if not found
- []
- let single<'TDoc> query parameters (mapFunc: SqliteDataReader -> 'TDoc) conn = backgroundTask {
- let! results = list query parameters mapFunc conn
- return FSharp.Collections.List.tryHead results
- }
-
- /// Execute a query that returns one or no results
- /// The query to retrieve the results
- /// Parameters to use for the query
- /// The mapping function between the document and the domain item
- /// The SqliteConnection to use to execute the query
- /// The first matching result, or null if not found
- let Single<'TDoc when 'TDoc: null and 'TDoc: not struct>(
- query, parameters, mapFunc: System.Func, conn
- ) = backgroundTask {
- let! result = single<'TDoc> query parameters mapFunc.Invoke conn
- return Option.toObj result
- }
-
- /// Execute a query that returns no results
- /// The query to retrieve the results
- /// Parameters to use for the query
- /// The SqliteConnection to use to execute the query
- []
- let nonQuery query (parameters: SqliteParameter seq) (conn: SqliteConnection) =
- use cmd = conn.CreateCommand()
- cmd.CommandText <- query
- cmd.Parameters.AddRange parameters
- write cmd
-
- /// Execute a query that returns a scalar value
- /// The query to retrieve the value
- /// Parameters to use for the query
- /// The mapping function to obtain the value
- /// The SqliteConnection to use to execute the query
- /// The scalar value for the query
- []
- let scalar<'T when 'T : struct> query (parameters: SqliteParameter seq) (mapFunc: SqliteDataReader -> 'T)
- (conn: SqliteConnection) = backgroundTask {
- use cmd = conn.CreateCommand()
- cmd.CommandText <- query
- cmd.Parameters.AddRange parameters
- use! rdr = cmd.ExecuteReaderAsync()
- let! isFound = rdr.ReadAsync()
- return if isFound then mapFunc rdr else Unchecked.defaultof<'T>
- }
-
- /// Execute a query that returns a scalar value
- /// The query to retrieve the value
- /// Parameters to use for the query
- /// The mapping function to obtain the value
- /// The SqliteConnection to use to execute the query
- /// The scalar value for the query
- let Scalar<'T when 'T: struct>(query, parameters, mapFunc: System.Func, conn) =
- scalar<'T> query parameters mapFunc.Invoke conn
-
- /// Functions to create tables and indexes
- []
- module Definition =
-
- /// Create a document table
- /// The table whose existence should be ensured (may include schema)
- /// The SqliteConnection to use to execute the query
- []
- let ensureTable name conn = backgroundTask {
- do! Custom.nonQuery (Query.Definition.ensureTable name) [] conn
- do! Custom.nonQuery (Query.Definition.ensureKey name SQLite) [] conn
- }
-
- /// Create an index on field(s) within documents in the specified table
- /// The table to be indexed (may include schema)
- /// The name of the index to create
- /// One or more fields to be indexed
- /// The SqliteConnection to use to execute the query
- []
- let ensureFieldIndex tableName indexName fields conn =
- Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields SQLite) [] conn
-
- /// Commands to add documents
- []
- module Document =
-
- /// Insert a new document
- /// The table into which the document should be inserted (may include schema)
- /// The document to be inserted
- /// The SqliteConnection to use to execute the query
- []
- 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")
- ///
- /// The table into which the document should be saved (may include schema)
- /// The document to be saved
- /// The SqliteConnection to use to execute the query
- []
- let save<'TDoc> tableName (document: 'TDoc) conn =
- Custom.nonQuery (Query.save tableName) [ jsonParam "@data" document ] conn
-
- /// Commands to count documents
- []
- module Count =
-
- /// Count all documents in a table
- /// The table in which documents should be counted (may include schema)
- /// The SqliteConnection to use to execute the query
- /// The count of the documents in the table
- []
- let all tableName conn =
- Custom.scalar (Query.count tableName) [] toCount conn
-
- /// Count matching documents using JSON field comparisons (->> =, etc.)
- /// The table in which documents should be counted (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The SqliteConnection to use to execute the query
- /// The count of matching documents in the table
- []
- 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
- []
- module Exists =
-
- /// Determine if a document exists for the given ID
- /// The table in which existence should be checked (may include schema)
- /// The ID of the document whose existence should be checked
- /// The SqliteConnection to use to execute the query
- /// True if a document exists, false if not
- []
- let byId tableName (docId: 'TKey) conn =
- Custom.scalar (Query.exists tableName (Query.whereById docId)) [ idParam docId ] toExists conn
-
- /// Determine if a document exists using JSON field comparisons (->> =, etc.)
- /// The table in which existence should be checked (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The SqliteConnection to use to execute the query
- /// True if any matching documents exist, false if not
- []
- let byFields tableName howMatched fields conn =
- Custom.scalar
- (Query.exists tableName (Query.whereByFields howMatched fields))
- (addFieldParams fields [])
- toExists
- conn
-
- /// Commands to retrieve documents
- []
- module Find =
-
- /// Retrieve all documents in the given table
- /// The table from which documents should be retrieved (may include schema)
- /// The SqliteConnection to use to execute the query
- /// All documents from the given table
- []
- let all<'TDoc> tableName conn =
- Custom.list<'TDoc> (Query.find tableName) [] fromData<'TDoc> conn
-
- /// Retrieve all documents in the given table
- /// The table from which documents should be retrieved (may include schema)
- /// The SqliteConnection to use to execute the query
- /// All documents from the given table
- let All<'TDoc>(tableName, conn) =
- Custom.List(Query.find tableName, [], fromData<'TDoc>, conn)
-
- /// Retrieve all documents in the given table ordered by the given fields in the document
- /// The table from which documents should be retrieved (may include schema)
- /// Fields by which the results should be ordered
- /// The SqliteConnection to use to execute the query
- /// All documents from the given table, ordered by the given fields
- []
- 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
- /// The table from which documents should be retrieved (may include schema)
- /// Fields by which the results should be ordered
- /// The SqliteConnection to use to execute the query
- /// All documents from the given table, ordered by the given fields
- let AllOrdered<'TDoc>(tableName, orderFields, conn) =
- Custom.List(Query.find tableName + Query.orderBy orderFields SQLite, [], fromData<'TDoc>, conn)
-
- /// Retrieve a document by its ID
- /// The table from which a document should be retrieved (may include schema)
- /// The ID of the document to retrieve
- /// The SqliteConnection to use to execute the query
- /// Some with the document if found, None otherwise
- []
- let byId<'TKey, 'TDoc> tableName (docId: 'TKey) conn =
- Custom.single<'TDoc> (Query.byId (Query.find tableName) docId) [ idParam docId ] fromData<'TDoc> conn
-
- /// Retrieve a document by its ID
- /// The table from which a document should be retrieved (may include schema)
- /// The ID of the document to retrieve
- /// The SqliteConnection to use to execute the query
- /// The document if found, null otherwise
- let ById<'TKey, 'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, docId: 'TKey, conn) =
- Custom.Single<'TDoc>(Query.byId (Query.find tableName) docId, [ idParam docId ], fromData<'TDoc>, conn)
-
- /// Retrieve documents matching JSON field comparisons (->> =, etc.)
- /// The table from which documents should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The SqliteConnection to use to execute the query
- /// All documents matching the given fields
- []
- let byFields<'TDoc> tableName howMatched fields conn =
- Custom.list<'TDoc>
- (Query.byFields (Query.find tableName) howMatched fields)
- (addFieldParams fields [])
- fromData<'TDoc>
- conn
-
- /// Retrieve documents matching JSON field comparisons (->> =, etc.)
- /// The table from which documents should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The SqliteConnection to use to execute the query
- /// All documents matching the given fields
- let ByFields<'TDoc>(tableName, howMatched, fields, conn) =
- Custom.List<'TDoc>(
- Query.byFields (Query.find tableName) howMatched fields,
- addFieldParams fields [],
- fromData<'TDoc>,
- conn)
-
- ///
- /// Retrieve documents matching JSON field comparisons (->> =, etc.) ordered by the given fields
- /// in the document
- ///
- /// The table from which documents should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// Fields by which the results should be ordered
- /// The SqliteConnection to use to execute the query
- /// All documents matching the given fields, ordered by the other given fields
- []
- 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 matching JSON field comparisons (->> =, etc.) ordered by the given fields
- /// in the document
- ///
- /// The table from which documents should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// Fields by which the results should be ordered
- /// The SqliteConnection to use to execute the query
- /// All documents matching the given fields, ordered by the other given fields
- 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 the first document matching JSON field comparisons (->> =, etc.)
- /// The table from which a document should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The SqliteConnection to use to execute the query
- /// Some with the first document, or None if not found
- []
- let firstByFields<'TDoc> tableName howMatched fields conn =
- Custom.single
- $"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1"
- (addFieldParams fields [])
- fromData<'TDoc>
- conn
-
- /// Retrieve the first document matching JSON field comparisons (->> =, etc.)
- /// The table from which a document should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The SqliteConnection to use to execute the query
- /// The first document, or null if not found
- let FirstByFields<'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, howMatched, fields, conn) =
- Custom.Single(
- $"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1",
- addFieldParams fields [],
- fromData<'TDoc>,
- conn)
-
- ///
- /// Retrieve the first document matching JSON field comparisons (->> =, etc.) ordered by the
- /// given fields in the document
- ///
- /// The table from which a document should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// Fields by which the results should be ordered
- /// The SqliteConnection to use to execute the query
- ///
- /// Some with the first document ordered by the given fields, or None if not found
- ///
- []
- 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 the first document matching JSON field comparisons (->> =, etc.) ordered by the
- /// given fields in the document
- ///
- /// The table from which a document should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// Fields by which the results should be ordered
- /// The SqliteConnection to use to execute the query
- /// The first document ordered by the given fields, or null if not found
- let FirstByFieldsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>(
- 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
- []
- module Update =
-
- /// Update (replace) an entire document by its ID
- /// The table in which a document should be updated (may include schema)
- /// The ID of the document to be updated (replaced)
- /// The new document
- /// The SqliteConnection to use to execute the query
- []
- let byId tableName (docId: 'TKey) (document: 'TDoc) conn =
- Custom.nonQuery
- (Query.statementWhere (Query.update tableName) (Query.whereById docId))
- [ idParam docId; jsonParam "@data" document ]
- conn
-
- ///
- /// Update (replace) an entire document by its ID, using the provided function to obtain the ID from the
- /// document
- ///
- /// The table in which a document should be updated (may include schema)
- /// The function to obtain the ID of the document
- /// The new document
- /// The SqliteConnection to use to execute the query
- []
- let byFunc tableName (idFunc: 'TDoc -> 'TKey) (document: 'TDoc) conn =
- byId tableName (idFunc document) document conn
-
- ///
- /// Update (replace) an entire document by its ID, using the provided function to obtain the ID from the
- /// document
- ///
- /// The table in which a document should be updated (may include schema)
- /// The function to obtain the ID of the document
- /// The new document
- /// The SqliteConnection to use to execute the query
- let ByFunc(tableName, idFunc: System.Func<'TDoc, 'TKey>, document: 'TDoc, conn) =
- byFunc tableName idFunc.Invoke document conn
-
- /// Commands to patch (partially update) documents
- []
- module Patch =
-
- /// Patch a document by its ID
- /// The table in which a document should be patched (may include schema)
- /// The ID of the document to patch
- /// The partial document to patch the existing document
- /// The SqliteConnection to use to execute the query
- []
- let byId tableName (docId: 'TKey) (patch: 'TPatch) conn =
- Custom.nonQuery
- (Query.byId (Query.patch tableName) docId) [ idParam docId; jsonParam "@data" patch ] conn
-
- ///
- /// Patch documents using a JSON field comparison query in the WHERE clause (->> =,
- /// etc.)
- ///
- /// The table in which documents should be patched (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The partial document to patch the existing document
- /// The SqliteConnection to use to execute the query
- []
- 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
- []
- module RemoveFields =
-
- /// Remove fields from a document by the document's ID
- /// The table in which a document should be modified (may include schema)
- /// The ID of the document to modify
- /// One or more field names to remove from the document
- /// The SqliteConnection to use to execute the query
- []
- let byId tableName (docId: 'TKey) fieldNames conn =
- let nameParams = fieldNameParams "@name" fieldNames
- Custom.nonQuery
- (Query.byId (Query.removeFields tableName nameParams) docId)
- (idParam docId |> Seq.singleton |> Seq.append nameParams)
- conn
-
- /// Remove fields from documents via a comparison on JSON fields in the document
- /// The table in which documents should be modified (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// One or more field names to remove from the matching documents
- /// The SqliteConnection to use to execute the query
- []
- let byFields tableName howMatched fields fieldNames conn =
- let nameParams = fieldNameParams "@name" fieldNames
- Custom.nonQuery
- (Query.byFields (Query.removeFields tableName nameParams) howMatched fields)
- (addFieldParams fields nameParams)
- conn
-
- /// Commands to delete documents
- []
- module Delete =
-
- /// Delete a document by its ID
- /// The table in which a document should be deleted (may include schema)
- /// The ID of the document to delete
- /// The SqliteConnection to use to execute the query
- []
- let byId tableName (docId: 'TKey) conn =
- Custom.nonQuery (Query.byId (Query.delete tableName) docId) [ idParam docId ] conn
-
- /// Delete documents by matching a JSON field comparison query (->> =, etc.)
- /// The table in which documents should be deleted (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The SqliteConnection to use to execute the query
- []
- let byFields tableName howMatched fields conn =
- Custom.nonQuery (Query.byFields (Query.delete tableName) howMatched fields) (addFieldParams fields []) conn
-
-
-/// Commands to execute custom SQL queries
-[]
-module Custom =
-
- /// Execute a query that returns a list of results
- /// The query to retrieve the results
- /// Parameters to use for the query
- /// The mapping function between the document and the domain item
- /// A list of results for the given query
- []
- let list<'TDoc> query parameters (mapFunc: SqliteDataReader -> 'TDoc) =
- use conn = Configuration.dbConn ()
- WithConn.Custom.list<'TDoc> query parameters mapFunc conn
-
- /// Execute a query that returns a list of results
- /// The query to retrieve the results
- /// Parameters to use for the query
- /// The mapping function between the document and the domain item
- /// A list of results for the given query
- let List<'TDoc>(query, parameters, mapFunc: System.Func) =
- use conn = Configuration.dbConn ()
- WithConn.Custom.List<'TDoc>(query, parameters, mapFunc, conn)
-
- /// Execute a query that returns one or no results
- /// The query to retrieve the results
- /// Parameters to use for the query
- /// The mapping function between the document and the domain item
- /// Some with the first matching result, or None if not found
- []
- let single<'TDoc> query parameters (mapFunc: SqliteDataReader -> 'TDoc) =
- use conn = Configuration.dbConn ()
- WithConn.Custom.single<'TDoc> query parameters mapFunc conn
-
- /// Execute a query that returns one or no results
- /// The query to retrieve the results
- /// Parameters to use for the query
- /// The mapping function between the document and the domain item
- /// The first matching result, or null if not found
- let Single<'TDoc when 'TDoc: null and 'TDoc: not struct>(
- query, parameters, mapFunc: System.Func) =
- use conn = Configuration.dbConn ()
- WithConn.Custom.Single<'TDoc>(query, parameters, mapFunc, conn)
-
- /// Execute a query that returns no results
- /// The query to retrieve the results
- /// Parameters to use for the query
- []
- let nonQuery query parameters =
- use conn = Configuration.dbConn ()
- WithConn.Custom.nonQuery query parameters conn
-
- /// Execute a query that returns a scalar value
- /// The query to retrieve the value
- /// Parameters to use for the query
- /// The mapping function to obtain the value
- /// The scalar value for the query
- []
- let scalar<'T when 'T: struct> query parameters (mapFunc: SqliteDataReader -> 'T) =
- use conn = Configuration.dbConn ()
- WithConn.Custom.scalar<'T> query parameters mapFunc conn
-
- /// Execute a query that returns a scalar value
- /// The query to retrieve the value
- /// Parameters to use for the query
- /// The mapping function to obtain the value
- /// The scalar value for the query
- let Scalar<'T when 'T: struct>(query, parameters, mapFunc: System.Func) =
- use conn = Configuration.dbConn ()
- WithConn.Custom.Scalar<'T>(query, parameters, mapFunc, conn)
-
-
-/// Functions to create tables and indexes
-[]
-module Definition =
-
- /// Create a document table
- /// The table whose existence should be ensured (may include schema)
- []
- let ensureTable name =
- use conn = Configuration.dbConn ()
- WithConn.Definition.ensureTable name conn
-
- /// Create an index on field(s) within documents in the specified table
- /// The table to be indexed (may include schema)
- /// The name of the index to create
- /// One or more fields to be indexed
- []
- let ensureFieldIndex tableName indexName fields =
- use conn = Configuration.dbConn ()
- WithConn.Definition.ensureFieldIndex tableName indexName fields conn
-
-
-/// Document insert/save functions
-[]
-module Document =
-
- /// Insert a new document
- /// The table into which the document should be inserted (may include schema)
- /// The document to be inserted
- []
- let insert<'TDoc> tableName (document: 'TDoc) =
- use conn = Configuration.dbConn ()
- WithConn.Document.insert tableName document conn
-
- /// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
- /// The table into which the document should be saved (may include schema)
- /// The document to be saved
- []
- let save<'TDoc> tableName (document: 'TDoc) =
- use conn = Configuration.dbConn ()
- WithConn.Document.save tableName document conn
-
-
-/// Commands to count documents
-[]
-module Count =
-
- /// Count all documents in a table
- /// The table in which documents should be counted (may include schema)
- /// The count of the documents in the table
- []
- let all tableName =
- use conn = Configuration.dbConn ()
- WithConn.Count.all tableName conn
-
- /// Count matching documents using JSON field comparisons (->> =, etc.)
- /// The table in which documents should be counted (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The count of matching documents in the table
- []
- let byFields tableName howMatched fields =
- use conn = Configuration.dbConn ()
- WithConn.Count.byFields tableName howMatched fields conn
-
-
-/// Commands to determine if documents exist
-[]
-module Exists =
-
- /// Determine if a document exists for the given ID
- /// The table in which existence should be checked (may include schema)
- /// The ID of the document whose existence should be checked
- /// True if a document exists, false if not
- []
- let byId tableName (docId: 'TKey) =
- use conn = Configuration.dbConn ()
- WithConn.Exists.byId tableName docId conn
-
- /// Determine if a document exists using JSON field comparisons (->> =, etc.)
- /// The table in which existence should be checked (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// True if any matching documents exist, false if not
- []
- let byFields tableName howMatched fields =
- use conn = Configuration.dbConn ()
- WithConn.Exists.byFields tableName howMatched fields conn
-
-
-/// Commands to retrieve documents
-[]
-module Find =
-
- /// Retrieve all documents in the given table
- /// The table from which documents should be retrieved (may include schema)
- /// All documents from the given table
- []
- let all<'TDoc> tableName =
- use conn = Configuration.dbConn ()
- WithConn.Find.all<'TDoc> tableName conn
-
- /// Retrieve all documents in the given table
- /// The table from which documents should be retrieved (may include schema)
- /// All documents from the given table
- let All<'TDoc> tableName =
- 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
- /// The table from which documents should be retrieved (may include schema)
- /// Fields by which the results should be ordered
- /// All documents from the given table, ordered by the given fields
- []
- 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
- /// The table from which documents should be retrieved (may include schema)
- /// Fields by which the results should be ordered
- /// All documents from the given table, ordered by the given fields
- let AllOrdered<'TDoc> tableName orderFields =
- use conn = Configuration.dbConn ()
- WithConn.Find.AllOrdered<'TDoc>(tableName, orderFields, conn)
-
- /// Retrieve a document by its ID
- /// The table from which a document should be retrieved (may include schema)
- /// The ID of the document to retrieve
- /// Some with the document if found, None otherwise
- []
- let byId<'TKey, 'TDoc> tableName docId =
- use conn = Configuration.dbConn ()
- WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn
-
- /// Retrieve a document by its ID
- /// The table from which a document should be retrieved (may include schema)
- /// The ID of the document to retrieve
- /// The document if found, null otherwise
- let ById<'TKey, 'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, docId) =
- use conn = Configuration.dbConn ()
- WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
-
- /// Retrieve documents matching JSON field comparisons (->> =, etc.)
- /// The table from which documents should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// All documents matching the given fields
- []
- let byFields<'TDoc> tableName howMatched fields =
- use conn = Configuration.dbConn ()
- WithConn.Find.byFields<'TDoc> tableName howMatched fields conn
-
- /// Retrieve documents matching JSON field comparisons (->> =, etc.)
- /// The table from which documents should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// All documents matching the given fields
- let ByFields<'TDoc>(tableName, howMatched, fields) =
- use conn = Configuration.dbConn ()
- WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn)
-
- ///
- /// Retrieve documents matching JSON field comparisons (->> =, etc.) ordered by the given fields in
- /// the document
- ///
- /// The table from which documents should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// Fields by which the results should be ordered
- /// All documents matching the given fields, ordered by the other given fields
- []
- let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
- use conn = Configuration.dbConn ()
- WithConn.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
-
- ///
- /// Retrieve documents matching JSON field comparisons (->> =, etc.) ordered by the given fields in
- /// the document
- ///
- /// The table from which documents should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// Fields by which the results should be ordered
- /// All documents matching the given fields, ordered by the other given fields
- let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields) =
- use conn = Configuration.dbConn ()
- WithConn.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
-
- /// Retrieve the first document matching JSON field comparisons (->> =, etc.)
- /// The table from which a document should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// Some with the first document, or None if not found
- []
- let firstByFields<'TDoc> tableName howMatched fields =
- use conn = Configuration.dbConn ()
- WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn
-
- /// Retrieve the first document matching JSON field comparisons (->> =, etc.)
- /// The table from which a document should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The first document, or null if not found
- let FirstByFields<'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, howMatched, fields) =
- use conn = Configuration.dbConn ()
- WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn)
-
- ///
- /// Retrieve the first document matching JSON field comparisons (->> =, etc.) ordered by the given
- /// fields in the document
- ///
- /// The table from which a document should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// Fields by which the results should be ordered
- ///
- /// Some with the first document ordered by the given fields, or None if not found
- ///
- []
- let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
- use conn = Configuration.dbConn ()
- WithConn.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
-
- ///
- /// Retrieve the first document matching JSON field comparisons (->> =, etc.) ordered by the given
- /// fields in the document
- ///
- /// The table from which a document should be retrieved (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// Fields by which the results should be ordered
- /// The first document ordered by the given fields, or null if not found
- let FirstByFieldsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>(
- tableName, howMatched, queryFields, orderFields) =
- use conn = Configuration.dbConn ()
- WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
-
-
-/// Commands to update documents
-[]
-module Update =
-
- /// Update (replace) an entire document by its ID
- /// The table in which a document should be updated (may include schema)
- /// The ID of the document to be updated (replaced)
- /// The new document
- []
- let byId tableName (docId: 'TKey) (document: 'TDoc) =
- use conn = Configuration.dbConn ()
- WithConn.Update.byId tableName docId document conn
-
- ///
- /// Update (replace) an entire document by its ID, using the provided function to obtain the ID from the document
- ///
- /// The table in which a document should be updated (may include schema)
- /// The function to obtain the ID of the document
- /// The new document
- []
- let byFunc tableName (idFunc: 'TDoc -> 'TKey) (document: 'TDoc) =
- use conn = Configuration.dbConn ()
- WithConn.Update.byFunc tableName idFunc document conn
-
- ///
- /// Update (replace) an entire document by its ID, using the provided function to obtain the ID from the document
- ///
- /// The table in which a document should be updated (may include schema)
- /// The function to obtain the ID of the document
- /// The new document
- let ByFunc(tableName, idFunc: System.Func<'TDoc, 'TKey>, document: 'TDoc) =
- use conn = Configuration.dbConn ()
- WithConn.Update.ByFunc(tableName, idFunc, document, conn)
-
-
-/// Commands to patch (partially update) documents
-[]
-module Patch =
-
- /// Patch a document by its ID
- /// The table in which a document should be patched (may include schema)
- /// The ID of the document to patch
- /// The partial document to patch the existing document
- []
- let byId tableName (docId: 'TKey) (patch: 'TPatch) =
- use conn = Configuration.dbConn ()
- WithConn.Patch.byId tableName docId patch conn
-
- ///
- /// Patch documents using a JSON field comparison query in the WHERE clause (->> =, etc.)
- ///
- /// The table in which documents should be patched (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// The partial document to patch the existing document
- []
- let byFields tableName howMatched fields (patch: 'TPatch) =
- use conn = Configuration.dbConn ()
- WithConn.Patch.byFields tableName howMatched fields patch conn
-
-
-/// Commands to remove fields from documents
-[]
-module RemoveFields =
-
- /// Remove fields from a document by the document's ID
- /// The table in which a document should be modified (may include schema)
- /// The ID of the document to modify
- /// One or more field names to remove from the document
- []
- let byId tableName (docId: 'TKey) fieldNames =
- use conn = Configuration.dbConn ()
- WithConn.RemoveFields.byId tableName docId fieldNames conn
-
- /// Remove fields from documents via a comparison on JSON fields in the document
- /// The table in which documents should be modified (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- /// One or more field names to remove from the matching documents
- []
- let byFields tableName howMatched fields fieldNames =
- use conn = Configuration.dbConn ()
- WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn
-
-
-/// Commands to delete documents
-[]
-module Delete =
-
- /// Delete a document by its ID
- /// The table in which a document should be deleted (may include schema)
- /// The ID of the document to delete
- []
- let byId tableName (docId: 'TKey) =
- use conn = Configuration.dbConn ()
- WithConn.Delete.byId tableName docId conn
-
- /// Delete documents by matching a JSON field comparison query (->> =, etc.)
- /// The table in which documents should be deleted (may include schema)
- /// Whether to match any or all of the field conditions
- /// The field conditions to match
- []
- let byFields tableName howMatched fields =
- use conn = Configuration.dbConn ()
- WithConn.Delete.byFields tableName howMatched fields conn
diff --git a/src/Sqlite/WithConn.fs b/src/Sqlite/WithConn.fs
new file mode 100644
index 0000000..763678b
--- /dev/null
+++ b/src/Sqlite/WithConn.fs
@@ -0,0 +1,510 @@
+/// Versions of queries that accept a SqliteConnection as the last parameter
+module BitBadger.Documents.Sqlite.WithConn
+
+open BitBadger.Documents
+open Microsoft.Data.Sqlite
+
+/// Commands to execute custom SQL queries
+[]
+module Custom =
+
+ /// Execute a query that returns a list of results
+ /// The query to retrieve the results
+ /// Parameters to use for the query
+ /// The mapping function between the document and the domain item
+ /// The SqliteConnection to use to execute the query
+ /// A list of results for the given query
+ []
+ let list<'TDoc> query (parameters: SqliteParameter seq) (mapFunc: SqliteDataReader -> 'TDoc)
+ (conn: SqliteConnection) =
+ use cmd = conn.CreateCommand()
+ cmd.CommandText <- query
+ cmd.Parameters.AddRange parameters
+ toCustomList<'TDoc> cmd mapFunc
+
+ /// Execute a query that returns a list of results
+ /// The query to retrieve the results
+ /// Parameters to use for the query
+ /// The mapping function between the document and the domain item
+ /// The SqliteConnection to use to execute the query
+ /// A list of results for the given query
+ let List<'TDoc>(
+ query, parameters: SqliteParameter seq, mapFunc: System.Func,
+ conn: SqliteConnection
+ ) =
+ use cmd = conn.CreateCommand()
+ cmd.CommandText <- query
+ cmd.Parameters.AddRange parameters
+ ToCustomList<'TDoc>(cmd, mapFunc)
+
+ /// Execute a query that returns one or no results
+ /// The query to retrieve the results
+ /// Parameters to use for the query
+ /// The mapping function between the document and the domain item
+ /// The SqliteConnection to use to execute the query
+ /// Some with the first matching result, or None if not found
+ []
+ let single<'TDoc> query parameters (mapFunc: SqliteDataReader -> 'TDoc) conn = backgroundTask {
+ let! results = list query parameters mapFunc conn
+ return FSharp.Collections.List.tryHead results
+ }
+
+ /// Execute a query that returns one or no results
+ /// The query to retrieve the results
+ /// Parameters to use for the query
+ /// The mapping function between the document and the domain item
+ /// The SqliteConnection to use to execute the query
+ /// The first matching result, or null if not found
+ let Single<'TDoc when 'TDoc: null and 'TDoc: not struct>(
+ query, parameters, mapFunc: System.Func, conn
+ ) = backgroundTask {
+ let! result = single<'TDoc> query parameters mapFunc.Invoke conn
+ return Option.toObj result
+ }
+
+ /// Execute a query that returns no results
+ /// The query to retrieve the results
+ /// Parameters to use for the query
+ /// The SqliteConnection to use to execute the query
+ []
+ let nonQuery query (parameters: SqliteParameter seq) (conn: SqliteConnection) =
+ use cmd = conn.CreateCommand()
+ cmd.CommandText <- query
+ cmd.Parameters.AddRange parameters
+ write cmd
+
+ /// Execute a query that returns a scalar value
+ /// The query to retrieve the value
+ /// Parameters to use for the query
+ /// The mapping function to obtain the value
+ /// The SqliteConnection to use to execute the query
+ /// The scalar value for the query
+ []
+ let scalar<'T when 'T : struct> query (parameters: SqliteParameter seq) (mapFunc: SqliteDataReader -> 'T)
+ (conn: SqliteConnection) = backgroundTask {
+ use cmd = conn.CreateCommand()
+ cmd.CommandText <- query
+ cmd.Parameters.AddRange parameters
+ use! rdr = cmd.ExecuteReaderAsync()
+ let! isFound = rdr.ReadAsync()
+ return if isFound then mapFunc rdr else Unchecked.defaultof<'T>
+ }
+
+ /// Execute a query that returns a scalar value
+ /// The query to retrieve the value
+ /// Parameters to use for the query
+ /// The mapping function to obtain the value
+ /// The SqliteConnection to use to execute the query
+ /// The scalar value for the query
+ let Scalar<'T when 'T: struct>(query, parameters, mapFunc: System.Func, conn) =
+ scalar<'T> query parameters mapFunc.Invoke conn
+
+/// Functions to create tables and indexes
+[]
+module Definition =
+
+ /// Create a document table
+ /// The table whose existence should be ensured (may include schema)
+ /// The SqliteConnection to use to execute the query
+ []
+ let ensureTable name conn = backgroundTask {
+ do! Custom.nonQuery (Query.Definition.ensureTable name) [] conn
+ do! Custom.nonQuery (Query.Definition.ensureKey name SQLite) [] conn
+ }
+
+ /// Create an index on field(s) within documents in the specified table
+ /// The table to be indexed (may include schema)
+ /// The name of the index to create
+ /// One or more fields to be indexed
+ /// The SqliteConnection to use to execute the query
+ []
+ let ensureFieldIndex tableName indexName fields conn =
+ Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields SQLite) [] conn
+
+/// Commands to add documents
+[]
+module Document =
+
+ /// Insert a new document
+ /// The table into which the document should be inserted (may include schema)
+ /// The document to be inserted
+ /// The SqliteConnection to use to execute the query
+ []
+ 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")
+ ///
+ /// The table into which the document should be saved (may include schema)
+ /// The document to be saved
+ /// The SqliteConnection to use to execute the query
+ []
+ let save<'TDoc> tableName (document: 'TDoc) conn =
+ Custom.nonQuery (Query.save tableName) [ jsonParam "@data" document ] conn
+
+/// Commands to count documents
+[]
+module Count =
+
+ /// Count all documents in a table
+ /// The table in which documents should be counted (may include schema)
+ /// The SqliteConnection to use to execute the query
+ /// The count of the documents in the table
+ []
+ let all tableName conn =
+ Custom.scalar (Query.count tableName) [] toCount conn
+
+ /// Count matching documents using JSON field comparisons (->> =, etc.)
+ /// The table in which documents should be counted (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The SqliteConnection to use to execute the query
+ /// The count of matching documents in the table
+ []
+ 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
+[]
+module Exists =
+
+ /// Determine if a document exists for the given ID
+ /// The table in which existence should be checked (may include schema)
+ /// The ID of the document whose existence should be checked
+ /// The SqliteConnection to use to execute the query
+ /// True if a document exists, false if not
+ []
+ let byId tableName (docId: 'TKey) conn =
+ Custom.scalar (Query.exists tableName (Query.whereById docId)) [ idParam docId ] toExists conn
+
+ /// Determine if a document exists using JSON field comparisons (->> =, etc.)
+ /// The table in which existence should be checked (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The SqliteConnection to use to execute the query
+ /// True if any matching documents exist, false if not
+ []
+ let byFields tableName howMatched fields conn =
+ Custom.scalar
+ (Query.exists tableName (Query.whereByFields howMatched fields))
+ (addFieldParams fields [])
+ toExists
+ conn
+
+/// Commands to retrieve documents
+[]
+module Find =
+
+ /// Retrieve all documents in the given table
+ /// The table from which documents should be retrieved (may include schema)
+ /// The SqliteConnection to use to execute the query
+ /// All documents from the given table
+ []
+ let all<'TDoc> tableName conn =
+ Custom.list<'TDoc> (Query.find tableName) [] fromData<'TDoc> conn
+
+ /// Retrieve all documents in the given table
+ /// The table from which documents should be retrieved (may include schema)
+ /// The SqliteConnection to use to execute the query
+ /// All documents from the given table
+ let All<'TDoc>(tableName, conn) =
+ Custom.List(Query.find tableName, [], fromData<'TDoc>, conn)
+
+ /// Retrieve all documents in the given table ordered by the given fields in the document
+ /// The table from which documents should be retrieved (may include schema)
+ /// Fields by which the results should be ordered
+ /// The SqliteConnection to use to execute the query
+ /// All documents from the given table, ordered by the given fields
+ []
+ 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
+ /// The table from which documents should be retrieved (may include schema)
+ /// Fields by which the results should be ordered
+ /// The SqliteConnection to use to execute the query
+ /// All documents from the given table, ordered by the given fields
+ let AllOrdered<'TDoc>(tableName, orderFields, conn) =
+ Custom.List(Query.find tableName + Query.orderBy orderFields SQLite, [], fromData<'TDoc>, conn)
+
+ /// Retrieve a document by its ID
+ /// The table from which a document should be retrieved (may include schema)
+ /// The ID of the document to retrieve
+ /// The SqliteConnection to use to execute the query
+ /// Some with the document if found, None otherwise
+ []
+ let byId<'TKey, 'TDoc> tableName (docId: 'TKey) conn =
+ Custom.single<'TDoc> (Query.byId (Query.find tableName) docId) [ idParam docId ] fromData<'TDoc> conn
+
+ /// Retrieve a document by its ID
+ /// The table from which a document should be retrieved (may include schema)
+ /// The ID of the document to retrieve
+ /// The SqliteConnection to use to execute the query
+ /// The document if found, null otherwise
+ let ById<'TKey, 'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, docId: 'TKey, conn) =
+ Custom.Single<'TDoc>(Query.byId (Query.find tableName) docId, [ idParam docId ], fromData<'TDoc>, conn)
+
+ /// Retrieve documents matching JSON field comparisons (->> =, etc.)
+ /// The table from which documents should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The SqliteConnection to use to execute the query
+ /// All documents matching the given fields
+ []
+ let byFields<'TDoc> tableName howMatched fields conn =
+ Custom.list<'TDoc>
+ (Query.byFields (Query.find tableName) howMatched fields)
+ (addFieldParams fields [])
+ fromData<'TDoc>
+ conn
+
+ /// Retrieve documents matching JSON field comparisons (->> =, etc.)
+ /// The table from which documents should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The SqliteConnection to use to execute the query
+ /// All documents matching the given fields
+ let ByFields<'TDoc>(tableName, howMatched, fields, conn) =
+ Custom.List<'TDoc>(
+ Query.byFields (Query.find tableName) howMatched fields,
+ addFieldParams fields [],
+ fromData<'TDoc>,
+ conn)
+
+ ///
+ /// Retrieve documents matching JSON field comparisons (->> =, etc.) ordered by the given fields
+ /// in the document
+ ///
+ /// The table from which documents should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// Fields by which the results should be ordered
+ /// The SqliteConnection to use to execute the query
+ /// All documents matching the given fields, ordered by the other given fields
+ []
+ 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 matching JSON field comparisons (->> =, etc.) ordered by the given fields
+ /// in the document
+ ///
+ /// The table from which documents should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// Fields by which the results should be ordered
+ /// The SqliteConnection to use to execute the query
+ /// All documents matching the given fields, ordered by the other given fields
+ 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 the first document matching JSON field comparisons (->> =, etc.)
+ /// The table from which a document should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The SqliteConnection to use to execute the query
+ /// Some with the first document, or None if not found
+ []
+ let firstByFields<'TDoc> tableName howMatched fields conn =
+ Custom.single
+ $"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1"
+ (addFieldParams fields [])
+ fromData<'TDoc>
+ conn
+
+ /// Retrieve the first document matching JSON field comparisons (->> =, etc.)
+ /// The table from which a document should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The SqliteConnection to use to execute the query
+ /// The first document, or null if not found
+ let FirstByFields<'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, howMatched, fields, conn) =
+ Custom.Single(
+ $"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1",
+ addFieldParams fields [],
+ fromData<'TDoc>,
+ conn)
+
+ ///
+ /// Retrieve the first document matching JSON field comparisons (->> =, etc.) ordered by the
+ /// given fields in the document
+ ///
+ /// The table from which a document should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// Fields by which the results should be ordered
+ /// The SqliteConnection to use to execute the query
+ ///
+ /// Some with the first document ordered by the given fields, or None if not found
+ ///
+ []
+ 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 the first document matching JSON field comparisons (->> =, etc.) ordered by the
+ /// given fields in the document
+ ///
+ /// The table from which a document should be retrieved (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// Fields by which the results should be ordered
+ /// The SqliteConnection to use to execute the query
+ /// The first document ordered by the given fields, or null if not found
+ let FirstByFieldsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>(
+ 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
+[]
+module Update =
+
+ /// Update (replace) an entire document by its ID
+ /// The table in which a document should be updated (may include schema)
+ /// The ID of the document to be updated (replaced)
+ /// The new document
+ /// The SqliteConnection to use to execute the query
+ []
+ let byId tableName (docId: 'TKey) (document: 'TDoc) conn =
+ Custom.nonQuery
+ (Query.statementWhere (Query.update tableName) (Query.whereById docId))
+ [ idParam docId; jsonParam "@data" document ]
+ conn
+
+ ///
+ /// Update (replace) an entire document by its ID, using the provided function to obtain the ID from the
+ /// document
+ ///
+ /// The table in which a document should be updated (may include schema)
+ /// The function to obtain the ID of the document
+ /// The new document
+ /// The SqliteConnection to use to execute the query
+ []
+ let byFunc tableName (idFunc: 'TDoc -> 'TKey) (document: 'TDoc) conn =
+ byId tableName (idFunc document) document conn
+
+ ///
+ /// Update (replace) an entire document by its ID, using the provided function to obtain the ID from the
+ /// document
+ ///
+ /// The table in which a document should be updated (may include schema)
+ /// The function to obtain the ID of the document
+ /// The new document
+ /// The SqliteConnection to use to execute the query
+ let ByFunc(tableName, idFunc: System.Func<'TDoc, 'TKey>, document: 'TDoc, conn) =
+ byFunc tableName idFunc.Invoke document conn
+
+/// Commands to patch (partially update) documents
+[]
+module Patch =
+
+ /// Patch a document by its ID
+ /// The table in which a document should be patched (may include schema)
+ /// The ID of the document to patch
+ /// The partial document to patch the existing document
+ /// The SqliteConnection to use to execute the query
+ []
+ let byId tableName (docId: 'TKey) (patch: 'TPatch) conn =
+ Custom.nonQuery
+ (Query.byId (Query.patch tableName) docId) [ idParam docId; jsonParam "@data" patch ] conn
+
+ ///
+ /// Patch documents using a JSON field comparison query in the WHERE clause (->> =,
+ /// etc.)
+ ///
+ /// The table in which documents should be patched (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The partial document to patch the existing document
+ /// The SqliteConnection to use to execute the query
+ []
+ 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
+[]
+module RemoveFields =
+
+ /// Remove fields from a document by the document's ID
+ /// The table in which a document should be modified (may include schema)
+ /// The ID of the document to modify
+ /// One or more field names to remove from the document
+ /// The SqliteConnection to use to execute the query
+ []
+ let byId tableName (docId: 'TKey) fieldNames conn =
+ let nameParams = fieldNameParams "@name" fieldNames
+ Custom.nonQuery
+ (Query.byId (Query.removeFields tableName nameParams) docId)
+ (idParam docId |> Seq.singleton |> Seq.append nameParams)
+ conn
+
+ /// Remove fields from documents via a comparison on JSON fields in the document
+ /// The table in which documents should be modified (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// One or more field names to remove from the matching documents
+ /// The SqliteConnection to use to execute the query
+ []
+ let byFields tableName howMatched fields fieldNames conn =
+ let nameParams = fieldNameParams "@name" fieldNames
+ Custom.nonQuery
+ (Query.byFields (Query.removeFields tableName nameParams) howMatched fields)
+ (addFieldParams fields nameParams)
+ conn
+
+/// Commands to delete documents
+[]
+module Delete =
+
+ /// Delete a document by its ID
+ /// The table in which a document should be deleted (may include schema)
+ /// The ID of the document to delete
+ /// The SqliteConnection to use to execute the query
+ []
+ let byId tableName (docId: 'TKey) conn =
+ Custom.nonQuery (Query.byId (Query.delete tableName) docId) [ idParam docId ] conn
+
+ /// Delete documents by matching a JSON field comparison query (->> =, etc.)
+ /// The table in which documents should be deleted (may include schema)
+ /// Whether to match any or all of the field conditions
+ /// The field conditions to match
+ /// The SqliteConnection to use to execute the query
+ []
+ let byFields tableName howMatched fields conn =
+ Custom.nonQuery (Query.byFields (Query.delete tableName) howMatched fields) (addFieldParams fields []) conn