diff --git a/src/Postgres/BitBadger.Documents.Postgres.fsproj b/src/Postgres/BitBadger.Documents.Postgres.fsproj index 79ba4a6..bc226fe 100644 --- a/src/Postgres/BitBadger.Documents.Postgres.fsproj +++ b/src/Postgres/BitBadger.Documents.Postgres.fsproj @@ -7,6 +7,8 @@ + + diff --git a/src/Postgres/Functions.fs b/src/Postgres/Functions.fs new file mode 100644 index 0000000..ef699a8 --- /dev/null +++ b/src/Postgres/Functions.fs @@ -0,0 +1,617 @@ +namespace BitBadger.Documents.Postgres + +/// 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: RowReader -> 'TDoc) = + WithProps.Custom.list<'TDoc> query parameters mapFunc (fromDataSource ()) + + /// 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) = + WithProps.Custom.List<'TDoc>(query, parameters, mapFunc, fromDataSource ()) + + /// 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: RowReader -> 'TDoc) = + WithProps.Custom.single<'TDoc> query parameters mapFunc (fromDataSource ()) + + /// 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) = + WithProps.Custom.Single<'TDoc>(query, parameters, mapFunc, fromDataSource ()) + + /// Execute a query that returns no results + /// The query to retrieve the results + /// Parameters to use for the query + [] + let nonQuery query parameters = + WithProps.Custom.nonQuery query parameters (fromDataSource ()) + + /// 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: RowReader -> 'T) = + WithProps.Custom.scalar query parameters mapFunc (fromDataSource ()) + + /// 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) = + WithProps.Custom.Scalar<'T>(query, parameters, mapFunc, fromDataSource ()) + + +/// Table and index definition commands +[] +module Definition = + + /// Create a document table + /// The table whose existence should be ensured (may include schema) + [] + let ensureTable name = + WithProps.Definition.ensureTable name (fromDataSource ()) + + /// Create an index on documents in the specified table + /// The table to be indexed (may include schema) + /// The type of document index to create + [] + let ensureDocumentIndex name idxType = + WithProps.Definition.ensureDocumentIndex name idxType (fromDataSource ()) + + /// 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 = + WithProps.Definition.ensureFieldIndex tableName indexName fields (fromDataSource ()) + + +/// Document writing 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) = + 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") + /// The table into which the document should be saved (may include schema) + /// The document to be saved + [] + let save<'TDoc> tableName (document: 'TDoc) = + WithProps.Document.save<'TDoc> tableName document (fromDataSource ()) + + +/// Queries 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 = + WithProps.Count.all tableName (fromDataSource ()) + + /// 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 = + WithProps.Count.byFields tableName howMatched fields (fromDataSource ()) + + /// Count matching documents using a JSON containment query (@>) + /// The table in which documents should be counted (may include schema) + /// The document to match with the containment query + /// The count of the documents in the table + [] + let byContains tableName criteria = + WithProps.Count.byContains tableName criteria (fromDataSource ()) + + /// Count matching documents using a JSON Path match query (@?) + /// The table in which documents should be counted (may include schema) + /// The JSON Path expression to be matched + /// The count of the documents in the table + [] + let byJsonPath tableName jsonPath = + WithProps.Count.byJsonPath tableName jsonPath (fromDataSource ()) + + +/// Queries 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 = + WithProps.Exists.byId tableName docId (fromDataSource ()) + + /// 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 = + WithProps.Exists.byFields tableName howMatched fields (fromDataSource ()) + + /// Determine if a document exists using a JSON containment query (@>) + /// The table in which existence should be checked (may include schema) + /// The document to match with the containment query + /// True if any matching documents exist, false if not + [] + let byContains tableName criteria = + WithProps.Exists.byContains tableName criteria (fromDataSource ()) + + /// Determine if a document exists using a JSON Path match query (@?) + /// The table in which existence should be checked (may include schema) + /// The JSON Path expression to be matched + /// True if any matching documents exist, false if not + [] + let byJsonPath tableName jsonPath = + WithProps.Exists.byJsonPath tableName jsonPath (fromDataSource ()) + + +/// 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 = + WithProps.Find.all<'TDoc> tableName (fromDataSource ()) + + /// 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 = + WithProps.Find.All<'TDoc>(tableName, fromDataSource ()) + + /// 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 = + WithProps.Find.allOrdered<'TDoc> tableName orderFields (fromDataSource ()) + + /// 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 = + WithProps.Find.AllOrdered<'TDoc>(tableName, orderFields, fromDataSource ()) + + /// 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 = + WithProps.Find.byId<'TKey, 'TDoc> tableName docId (fromDataSource ()) + + /// 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: 'TKey) = + WithProps.Find.ById<'TKey, 'TDoc>(tableName, docId, fromDataSource ()) + + /// 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 = + WithProps.Find.byFields<'TDoc> tableName howMatched fields (fromDataSource ()) + + /// 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) = + WithProps.Find.ByFields<'TDoc>(tableName, howMatched, fields, fromDataSource ()) + + /// + /// 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 = + WithProps.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields (fromDataSource ()) + + /// + /// 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) = + WithProps.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, fromDataSource ()) + + /// Retrieve documents matching a JSON containment query (@>) + /// The table from which documents should be retrieved (may include schema) + /// The document to match with the containment query + /// All documents matching the given containment query + [] + let byContains<'TDoc> tableName (criteria: obj) = + WithProps.Find.byContains<'TDoc> tableName criteria (fromDataSource ()) + + /// Retrieve documents matching a JSON containment query (@>) + /// The table from which documents should be retrieved (may include schema) + /// The document to match with the containment query + /// All documents matching the given 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 + /// + /// The table from which documents should be retrieved (may include schema) + /// The document to match with the containment query + /// Fields by which the results should be ordered + /// All documents matching the given containment query, ordered by the given fields + [] + 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 + /// + /// The table from which documents should be retrieved (may include schema) + /// The document to match with the containment query + /// Fields by which the results should be ordered + /// All documents matching the given containment query, ordered by the given fields + let ByContainsOrdered<'TDoc>(tableName, criteria: obj, orderFields) = + WithProps.Find.ByContainsOrdered<'TDoc>(tableName, criteria, orderFields, fromDataSource ()) + + /// Retrieve documents matching a JSON Path match query (@?) + /// The table from which documents should be retrieved (may include schema) + /// The JSON Path expression to match + /// All documents matching the given JSON Path expression + [] + let byJsonPath<'TDoc> tableName jsonPath = + WithProps.Find.byJsonPath<'TDoc> tableName jsonPath (fromDataSource ()) + + /// Retrieve documents matching a JSON Path match query (@?) + /// The table from which documents should be retrieved (may include schema) + /// The JSON Path expression to match + /// All documents matching the given JSON Path expression + 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 + /// + /// The table from which documents should be retrieved (may include schema) + /// The JSON Path expression to match + /// Fields by which the results should be ordered + /// All documents matching the given JSON Path expression, ordered by the given fields + [] + 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 + /// + /// The table from which documents should be retrieved (may include schema) + /// The JSON Path expression to match + /// Fields by which the results should be ordered + /// All documents matching the given JSON Path expression, ordered by the given fields + let ByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields) = + WithProps.Find.ByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, fromDataSource ()) + + /// 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 = + WithProps.Find.firstByFields<'TDoc> tableName howMatched fields (fromDataSource ()) + + /// 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) = + WithProps.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, fromDataSource ()) + + /// + /// 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 = + WithProps.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields (fromDataSource ()) + + /// + /// 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) = + WithProps.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, fromDataSource ()) + + /// Retrieve the first document matching a JSON containment query (@>) + /// The table from which a document should be retrieved (may include schema) + /// The document to match with the containment query + /// Some with the first document, or None if not found + [] + let firstByContains<'TDoc> tableName (criteria: obj) = + WithProps.Find.firstByContains<'TDoc> tableName criteria (fromDataSource ()) + + /// Retrieve the first document matching a JSON containment query (@>) + /// The table from which a document should be retrieved (may include schema) + /// The document to match with the containment query + /// The first document, or null if not found + let FirstByContains<'TDoc when 'TDoc: null and 'TDoc: not struct>(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 + /// + /// The table from which a document should be retrieved (may include schema) + /// The document to match with the containment query + /// Fields by which the results should be ordered + /// + /// Some with the first document ordered by the given fields, or None if not found + /// + [] + 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 + /// + /// The table from which a document should be retrieved (may include schema) + /// The document to match with the containment query + /// Fields by which the results should be ordered + /// The first document ordered by the given fields, or null if not found + let FirstByContainsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, criteria: obj, orderFields) = + WithProps.Find.FirstByContainsOrdered<'TDoc>(tableName, criteria, orderFields, fromDataSource ()) + + /// Retrieve the first document matching a JSON Path match query (@?) + /// The table from which a document should be retrieved (may include schema) + /// The JSON Path expression to match + /// Some with the first document, or None if not found + [] + let firstByJsonPath<'TDoc> tableName jsonPath = + WithProps.Find.firstByJsonPath<'TDoc> tableName jsonPath (fromDataSource ()) + + /// Retrieve the first document matching a JSON Path match query (@?) + /// The table from which a document should be retrieved (may include schema) + /// The JSON Path expression to match + /// The first document, or null if not found + let FirstByJsonPath<'TDoc when 'TDoc: null and 'TDoc: not struct>(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 + /// + /// The table from which a document should be retrieved (may include schema) + /// The JSON Path expression 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 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 + /// + /// The table from which a document should be retrieved (may include schema) + /// The JSON Path expression to match + /// Fields by which the results should be ordered + /// The first document ordered by the given fields, or null if not found + let FirstByJsonPathOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, jsonPath, orderFields) = + WithProps.Find.FirstByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, fromDataSource ()) + + +/// 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) = + WithProps.Update.byId tableName docId document (fromDataSource ()) + + /// + /// 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) = + WithProps.Update.byFunc tableName idFunc document (fromDataSource ()) + + /// + /// 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) = + WithProps.Update.ByFunc(tableName, idFunc, document, fromDataSource ()) + + +/// 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) = + WithProps.Patch.byId tableName docId patch (fromDataSource ()) + + /// + /// 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) = + WithProps.Patch.byFields tableName howMatched fields patch (fromDataSource ()) + + /// Patch documents using a JSON containment query in the WHERE clause (@>) + /// The table in which documents should be patched (may include schema) + /// The document to match the containment query + /// The partial document to patch the existing document + [] + 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 (@?) + /// The table in which documents should be patched (may include schema) + /// The JSON Path expression to match + /// The partial document to patch the existing document + [] + let byJsonPath tableName jsonPath (patch: 'TPatch) = + WithProps.Patch.byJsonPath tableName jsonPath patch (fromDataSource ()) + + +/// 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 = + WithProps.RemoveFields.byId tableName docId fieldNames (fromDataSource ()) + + /// 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 = + WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (fromDataSource ()) + + /// Remove fields from documents via a JSON containment query (@>) + /// The table in which documents should be modified (may include schema) + /// The document to match the containment query + /// One or more field names to remove from the matching documents + [] + let byContains tableName (criteria: 'TContains) fieldNames = + WithProps.RemoveFields.byContains tableName criteria fieldNames (fromDataSource ()) + + /// Remove fields from documents via a JSON Path match query (@?) + /// The table in which documents should be modified (may include schema) + /// The JSON Path expression to match + /// One or more field names to remove from the matching documents + [] + let byJsonPath tableName jsonPath fieldNames = + WithProps.RemoveFields.byJsonPath tableName jsonPath fieldNames (fromDataSource ()) + + +/// 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) = + WithProps.Delete.byId tableName docId (fromDataSource ()) + + /// 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 = + WithProps.Delete.byFields tableName howMatched fields (fromDataSource ()) + + /// Delete documents by matching a JSON contains query (@>) + /// The table in which documents should be deleted (may include schema) + /// The document to match the containment query + [] + let byContains tableName (criteria: 'TContains) = + WithProps.Delete.byContains tableName criteria (fromDataSource ()) + + /// Delete documents by matching a JSON Path match query (@?) + /// The table in which documents should be deleted (may include schema) + /// The JSON Path expression to match + [] + let byJsonPath tableName jsonPath = + WithProps.Delete.byJsonPath tableName jsonPath (fromDataSource ()) diff --git a/src/Postgres/Library.fs b/src/Postgres/Library.fs index d0ab978..edf03af 100644 --- a/src/Postgres/Library.fs +++ b/src/Postgres/Library.fs @@ -302,1213 +302,3 @@ module Results = [] 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 - [] - 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 SqlProps to use to execute the query - /// A list of results for the given query - [] - 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 - /// The query to retrieve the results - /// Parameters to use for the query - /// The mapping function between the document and the domain item - /// The SqlProps to use to execute the query - /// A list of results for the given query - let List<'TDoc>(query, parameters, mapFunc: System.Func, sqlProps) = backgroundTask { - let! results = list<'TDoc> query parameters mapFunc.Invoke sqlProps - return ResizeArray 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 SqlProps to use to execute the query - /// Some with the first matching result, or None if not found - [] - let single<'TDoc> query parameters mapFunc sqlProps = backgroundTask { - let! results = list<'TDoc> query parameters mapFunc sqlProps - return FSharpList.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 SqlProps 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, sqlProps) = backgroundTask { - let! result = single<'TDoc> query parameters mapFunc.Invoke sqlProps - return Option.toObj result - } - - /// Execute a query that returns no results - /// The query to retrieve the results - /// Parameters to use for the query - /// The SqlProps to use to execute the query - [] - 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 - /// The query to retrieve the value - /// Parameters to use for the query - /// The mapping function to obtain the value - /// The SqlProps to use to execute the query - /// The scalar value for the query - [] - 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 - /// The query to retrieve the value - /// Parameters to use for the query - /// The mapping function to obtain the value - /// The SqlProps to use to execute the query - /// The scalar value for the query - let Scalar<'T when 'T: struct>(query, parameters, mapFunc: System.Func, sqlProps) = - scalar<'T> query parameters mapFunc.Invoke sqlProps - - /// Table and index definition commands - module Definition = - - /// Create a document table - /// The table whose existence should be ensured (may include schema) - /// The SqlProps to use to execute the query - [] - 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 - /// The table to be indexed (may include schema) - /// The type of document index to create - /// The SqlProps to use to execute the query - [] - 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 - /// The table to be indexed (may include schema) - /// The name of the index to create - /// One or more fields to be indexed - /// The SqlProps to use to execute the query - [] - let ensureFieldIndex tableName indexName fields sqlProps = - Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields PostgreSQL) [] sqlProps - - /// 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 SqlProps to use to execute the query - [] - 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") - /// - /// The table into which the document should be saved (may include schema) - /// The document to be saved - /// The SqlProps to use to execute the query - [] - let save<'TDoc> tableName (document: 'TDoc) sqlProps = - Custom.nonQuery (Query.save tableName) [ jsonParam "@data" document ] sqlProps - - /// Commands to count documents - [] - module Count = - - /// Count all documents in a table - /// The table in which documents should be counted (may include schema) - /// The SqlProps to use to execute the query - /// The count of the documents in the table - [] - let all tableName sqlProps = - Custom.scalar (Query.count tableName) [] toCount sqlProps - - /// 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 SqlProps to use to execute the query - /// The count of matching documents in the table - [] - 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 (@>) - /// The table in which documents should be counted (may include schema) - /// The document to match with the containment query - /// The SqlProps to use to execute the query - /// The count of the documents in the table - [] - 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 (@?) - /// The table in which documents should be counted (may include schema) - /// The JSON Path expression to be matched - /// The SqlProps to use to execute the query - /// The count of the documents in the table - [] - let byJsonPath tableName jsonPath sqlProps = - Custom.scalar - (Query.byPathMatch (Query.count tableName)) [ "@path", Sql.string jsonPath ] toCount sqlProps - - /// 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 SqlProps to use to execute the query - /// True if a document exists, false if not - [] - 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 (->> =, 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 SqlProps to use to execute the query - /// True if any matching documents exist, false if not - [] - 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 (@>) - /// The table in which existence should be checked (may include schema) - /// The document to match with the containment query - /// The SqlProps to use to execute the query - /// True if any matching documents exist, false if not - [] - 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 (@?) - /// The table in which existence should be checked (may include schema) - /// The JSON Path expression to be matched - /// The SqlProps to use to execute the query - /// True if any matching documents exist, false if not - [] - 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 - [] - module Find = - - /// Retrieve all documents in the given table - /// The table from which documents should be retrieved (may include schema) - /// The SqlProps to use to execute the query - /// All documents from the given table - [] - let all<'TDoc> tableName sqlProps = - Custom.list<'TDoc> (Query.find tableName) [] fromData<'TDoc> sqlProps - - /// Retrieve all documents in the given table - /// The table from which documents should be retrieved (may include schema) - /// The SqlProps to use to execute the query - /// All documents from 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 - /// The table from which documents should be retrieved (may include schema) - /// Fields by which the results should be ordered - /// The SqlProps to use to execute the query - /// All documents from the given table, ordered by the given fields - [] - 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 - /// The table from which documents should be retrieved (may include schema) - /// Fields by which the results should be ordered - /// The SqlProps to use to execute the query - /// All documents from the given table, ordered by the given fields - 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 - /// The table from which a document should be retrieved (may include schema) - /// The ID of the document to retrieve - /// The SqlProps to use to execute the query - /// Some with the document if found, None otherwise - [] - 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 - /// The table from which a document should be retrieved (may include schema) - /// The ID of the document to retrieve - /// The SqlProps 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, sqlProps) = - Custom.Single<'TDoc>( - Query.byId (Query.find tableName) docId, [ idParam docId ], fromData<'TDoc>, sqlProps) - - /// 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 SqlProps to use to execute the query - /// All documents matching the given fields - [] - 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 (->> =, 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 SqlProps to use to execute the query - /// All documents matching the given fields - 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 (->> =, 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 SqlProps 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 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 (->> =, 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 SqlProps 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, 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 (@>) - /// The table from which documents should be retrieved (may include schema) - /// The document to match with the containment query - /// The SqlProps to use to execute the query - /// All documents matching the given 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 (@>) - /// The table from which documents should be retrieved (may include schema) - /// The document to match with the containment query - /// The SqlProps to use to execute the query - /// All documents matching the given 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 - /// - /// The table from which documents should be retrieved (may include schema) - /// The document to match with the containment query - /// Fields by which the results should be ordered - /// The SqlProps to use to execute the query - /// All documents matching the given containment query, ordered by the given fields - [] - 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 - /// - /// The table from which documents should be retrieved (may include schema) - /// The document to match with the containment query - /// Fields by which the results should be ordered - /// The SqlProps to use to execute the query - /// All documents matching the given containment query, ordered by the given fields - 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 (@?) - /// The table from which documents should be retrieved (may include schema) - /// The JSON Path expression to match - /// The SqlProps to use to execute the query - /// All documents matching the given JSON Path expression - [] - 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 (@?) - /// The table from which documents should be retrieved (may include schema) - /// The JSON Path expression to match - /// The SqlProps to use to execute the query - /// All documents matching the given JSON Path expression - 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 - /// - /// The table from which documents should be retrieved (may include schema) - /// The JSON Path expression to match - /// Fields by which the results should be ordered - /// The SqlProps to use to execute the query - /// All documents matching the given JSON Path expression, ordered by the given fields - [] - 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 - /// - /// The table from which documents should be retrieved (may include schema) - /// The JSON Path expression to match - /// Fields by which the results should be ordered - /// The SqlProps to use to execute the query - /// All documents matching the given JSON Path expression, ordered by the given fields - 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 (->> =, 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 SqlProps to use to execute the query - /// Some with the first document, or None if not found - [] - 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 (->> =, 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 SqlProps 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, 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 (->> =, 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 SqlProps 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 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 (->> =, 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 SqlProps 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, 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 (@>) - /// The table from which a document should be retrieved (may include schema) - /// The document to match with the containment query - /// The SqlProps to use to execute the query - /// Some with the first document, or None if not found - [] - 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 (@>) - /// The table from which a document should be retrieved (may include schema) - /// The document to match with the containment query - /// The SqlProps to use to execute the query - /// The first document, or null if not found - let FirstByContains<'TDoc when 'TDoc: null and 'TDoc: not struct>(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 - /// - /// The table from which a document should be retrieved (may include schema) - /// The document to match with the containment query - /// Fields by which the results should be ordered - /// The SqlProps to use to execute the query - /// - /// Some with the first document ordered by the given fields, or None if not found - /// - [] - 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 - /// - /// The table from which a document should be retrieved (may include schema) - /// The document to match with the containment query - /// Fields by which the results should be ordered - /// The SqlProps to use to execute the query - /// The first document ordered by the given fields, or null if not found - let FirstByContainsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>( - 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 (@?) - /// The table from which a document should be retrieved (may include schema) - /// The JSON Path expression to match - /// The SqlProps to use to execute the query - /// Some with the first document, or None if not found - [] - 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 (@?) - /// The table from which a document should be retrieved (may include schema) - /// The JSON Path expression to match - /// The SqlProps to use to execute the query - /// The first document, or null if not found - let FirstByJsonPath<'TDoc when 'TDoc: null and 'TDoc: not struct>(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 - /// - /// The table from which a document should be retrieved (may include schema) - /// The JSON Path expression to match - /// Fields by which the results should be ordered - /// The SqlProps to use to execute the query - /// - /// Some with the first document ordered by the given fields, or None if not found - /// - [] - 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 - /// - /// The table from which a document should be retrieved (may include schema) - /// The JSON Path expression to match - /// Fields by which the results should be ordered - /// The SqlProps to use to execute the query - /// The first document ordered by the given fields, or null if not found - let FirstByJsonPathOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>( - 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 - [] - 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 SqlProps to use to execute the query - [] - let byId tableName (docId: 'TKey) (document: 'TDoc) sqlProps = - Custom.nonQuery - (Query.byId (Query.update tableName) docId) [ idParam docId; jsonParam "@data" document ] sqlProps - - /// - /// 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 SqlProps to use to execute the query - [] - let byFunc tableName (idFunc: 'TDoc -> 'TKey) (document: 'TDoc) sqlProps = - byId tableName (idFunc document) document sqlProps - - /// - /// 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 SqlProps to use to execute the query - let ByFunc(tableName, idFunc: System.Func<'TDoc, 'TKey>, document: 'TDoc, sqlProps) = - byFunc tableName idFunc.Invoke document sqlProps - - /// 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 SqlProps to use to execute the query - [] - 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 (->> =, - /// 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 SqlProps to use to execute the query - [] - 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 (@>) - /// The table in which documents should be patched (may include schema) - /// The document to match the containment query - /// The partial document to patch the existing document - /// The SqlProps to use to execute the query - [] - 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 (@?) - /// The table in which documents should be patched (may include schema) - /// The JSON Path expression to match - /// The partial document to patch the existing document - /// The SqlProps to use to execute the query - [] - 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 - [] - 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 SqlProps to use to execute the query - [] - 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 - /// 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 SqlProps to use to execute the query - [] - 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 (@>) - /// The table in which documents should be modified (may include schema) - /// The document to match the containment query - /// One or more field names to remove from the matching documents - /// The SqlProps to use to execute the query - [] - 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 (@?) - /// The table in which documents should be modified (may include schema) - /// The JSON Path expression to match - /// One or more field names to remove from the matching documents - /// The SqlProps to use to execute the query - [] - let byJsonPath tableName jsonPath fieldNames sqlProps = - Custom.nonQuery - (Query.byPathMatch (Query.removeFields tableName)) - [ "@path", Sql.string jsonPath; fieldNameParams fieldNames ] - sqlProps - - /// 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 SqlProps to use to execute the query - [] - 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 (->> =, 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 SqlProps to use to execute the query - [] - 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 (@>) - /// The table in which documents should be deleted (may include schema) - /// The document to match the containment query - /// The SqlProps to use to execute the query - [] - 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 (@?) - /// The table in which documents should be deleted (may include schema) - /// The JSON Path expression to match - /// The SqlProps to use to execute the query - [] - let byJsonPath tableName jsonPath sqlProps = - Custom.nonQuery (Query.byPathMatch (Query.delete tableName)) [ "@path", Sql.string jsonPath ] sqlProps - - -/// Commands to execute custom SQL queries -[] -module Custom = - - /// Execute a query that returns a list of results - [] - 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) = - WithProps.Custom.List<'TDoc>(query, parameters, mapFunc, fromDataSource ()) - - /// Execute a query that returns one or no results; returns None if not found - [] - 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 and 'TDoc: not struct>( - query, parameters, mapFunc: System.Func) = - WithProps.Custom.Single<'TDoc>(query, parameters, mapFunc, fromDataSource ()) - - /// Execute a query that returns no results - [] - let nonQuery query parameters = - WithProps.Custom.nonQuery query parameters (fromDataSource ()) - - /// Execute a query that returns a scalar value - [] - 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) = - WithProps.Custom.Scalar<'T>(query, parameters, mapFunc, fromDataSource ()) - - -/// Table and index definition commands -[] -module Definition = - - /// Create a document table - [] - let ensureTable name = - WithProps.Definition.ensureTable name (fromDataSource ()) - - /// Create an index on documents in the specified table - [] - let ensureDocumentIndex name idxType = - WithProps.Definition.ensureDocumentIndex name idxType (fromDataSource ()) - - /// Create an index on field(s) within documents in the specified table - [] - let ensureFieldIndex tableName indexName fields = - WithProps.Definition.ensureFieldIndex tableName indexName fields (fromDataSource ()) - - -/// Document writing functions -[] -module Document = - - /// Insert a new document - [] - 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") - [] - let save<'TDoc> tableName (document: 'TDoc) = - WithProps.Document.save<'TDoc> tableName document (fromDataSource ()) - - -/// Queries to count documents -[] -module Count = - - /// Count all documents in a table - [] - let all tableName = - WithProps.Count.all tableName (fromDataSource ()) - - /// Count matching documents using a JSON field comparison query (->> =) - [] - let byFields tableName howMatched fields = - WithProps.Count.byFields tableName howMatched fields (fromDataSource ()) - - /// Count matching documents using a JSON containment query (@>) - [] - let byContains tableName criteria = - WithProps.Count.byContains tableName criteria (fromDataSource ()) - - /// Count matching documents using a JSON Path match query (@?) - [] - let byJsonPath tableName jsonPath = - WithProps.Count.byJsonPath tableName jsonPath (fromDataSource ()) - - -/// Queries to determine if documents exist -[] -module Exists = - - /// Determine if a document exists for the given ID - [] - let byId tableName docId = - WithProps.Exists.byId tableName docId (fromDataSource ()) - - /// Determine if documents exist using a JSON field comparison query (->> =) - [] - let byFields tableName howMatched fields = - WithProps.Exists.byFields tableName howMatched fields (fromDataSource ()) - - /// Determine if documents exist using a JSON containment query (@>) - [] - let byContains tableName criteria = - WithProps.Exists.byContains tableName criteria (fromDataSource ()) - - /// Determine if documents exist using a JSON Path match query (@?) - [] - let byJsonPath tableName jsonPath = - WithProps.Exists.byJsonPath tableName jsonPath (fromDataSource ()) - - -/// Commands to retrieve documents -[] -module Find = - - /// Retrieve all documents in the given table - [] - 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 - [] - 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 - [] - 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 and 'TDoc: not struct>(tableName, docId: 'TKey) = - WithProps.Find.ById<'TKey, 'TDoc>(tableName, docId, 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 (->> =) - 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 - [] - 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 (@>) - [] - 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 - [] - 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 (@?) - [] - 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 - [] - 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 - [] - 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 and 'TDoc: not struct>(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 - [] - 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 and 'TDoc: not struct>( - 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 - [] - 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 and 'TDoc: not struct>(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 - [] - 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 and 'TDoc: not struct>(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 - [] - 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 and 'TDoc: not struct>(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 - [] - 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 and 'TDoc: not struct>(tableName, jsonPath, orderFields) = - WithProps.Find.FirstByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, fromDataSource ()) - - -/// Commands to update documents -[] -module Update = - - /// Update an entire document by its ID - [] - 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 - [] - 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 -[] -module Patch = - - /// Patch a document by its ID - [] - 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 (->> =) - [] - 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 (@>) - [] - 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 (@?) - [] - let byJsonPath tableName jsonPath (patch: 'TPatch) = - WithProps.Patch.byJsonPath tableName jsonPath patch (fromDataSource ()) - - -/// Commands to remove fields from documents -[] -module RemoveFields = - - /// Remove fields from a document by the document's ID - [] - 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 - [] - let byFields tableName howMatched fields fieldNames = - WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (fromDataSource ()) - - /// Remove fields from documents via a JSON containment query (@>) - [] - let byContains tableName (criteria: 'TContains) fieldNames = - WithProps.RemoveFields.byContains tableName criteria fieldNames (fromDataSource ()) - - /// Remove fields from documents via a JSON Path match query (@?) - [] - let byJsonPath tableName jsonPath fieldNames = - WithProps.RemoveFields.byJsonPath tableName jsonPath fieldNames (fromDataSource ()) - - -/// Commands to delete documents -[] -module Delete = - - /// Delete a document by its ID - [] - let byId tableName (docId: 'TKey) = - WithProps.Delete.byId tableName docId (fromDataSource ()) - - /// Delete documents by matching a JSON field comparison query (->> =) - [] - let byFields tableName howMatched fields = - WithProps.Delete.byFields tableName howMatched fields (fromDataSource ()) - - /// Delete documents by matching a JSON containment query (@>) - [] - let byContains tableName (criteria: 'TContains) = - WithProps.Delete.byContains tableName criteria (fromDataSource ()) - - /// Delete documents by matching a JSON Path match query (@?) - [] - let byJsonPath tableName path = - WithProps.Delete.byJsonPath tableName path (fromDataSource ()) diff --git a/src/Postgres/WithProps.fs b/src/Postgres/WithProps.fs new file mode 100644 index 0000000..4e03a26 --- /dev/null +++ b/src/Postgres/WithProps.fs @@ -0,0 +1,840 @@ +/// Versions of queries that accept SqlProps as the last parameter +module BitBadger.Documents.Postgres.WithProps + +open BitBadger.Documents +open Npgsql.FSharp + +/// Commands to execute custom SQL queries +[] +module Custom = + + module FSharpList = Microsoft.FSharp.Collections.List + + /// 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 SqlProps to use to execute the query + /// A list of results for the given query + [] + let list<'TDoc> query parameters (mapFunc: RowReader -> 'TDoc) sqlProps = + Sql.query query sqlProps + |> Sql.parameters (FSharpList.ofSeq parameters) + |> Sql.executeAsync 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 SqlProps to use to execute the query + /// A list of results for the given query + let List<'TDoc>(query, parameters, mapFunc: System.Func, sqlProps) = backgroundTask { + let! results = list<'TDoc> query parameters mapFunc.Invoke sqlProps + return ResizeArray 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 SqlProps to use to execute the query + /// Some with the first matching result, or None if not found + [] + let single<'TDoc> query parameters mapFunc sqlProps = backgroundTask { + let! results = list<'TDoc> query parameters mapFunc sqlProps + return FSharpList.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 SqlProps 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, sqlProps) = backgroundTask { + let! result = single<'TDoc> query parameters mapFunc.Invoke sqlProps + return Option.toObj result + } + + /// Execute a query that returns no results + /// The query to retrieve the results + /// Parameters to use for the query + /// The SqlProps to use to execute the query + [] + 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 + /// The query to retrieve the value + /// Parameters to use for the query + /// The mapping function to obtain the value + /// The SqlProps to use to execute the query + /// The scalar value for the query + [] + 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 + /// The query to retrieve the value + /// Parameters to use for the query + /// The mapping function to obtain the value + /// The SqlProps to use to execute the query + /// The scalar value for the query + let Scalar<'T when 'T: struct>(query, parameters, mapFunc: System.Func, sqlProps) = + scalar<'T> query parameters mapFunc.Invoke sqlProps + +/// Table and index definition commands +module Definition = + + /// Create a document table + /// The table whose existence should be ensured (may include schema) + /// The SqlProps to use to execute the query + [] + 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 + /// The table to be indexed (may include schema) + /// The type of document index to create + /// The SqlProps to use to execute the query + [] + 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 + /// The table to be indexed (may include schema) + /// The name of the index to create + /// One or more fields to be indexed + /// The SqlProps to use to execute the query + [] + let ensureFieldIndex tableName indexName fields sqlProps = + Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields PostgreSQL) [] sqlProps + +/// 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 SqlProps to use to execute the query + [] + 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") + /// The table into which the document should be saved (may include schema) + /// The document to be saved + /// The SqlProps to use to execute the query + [] + let save<'TDoc> tableName (document: 'TDoc) sqlProps = + Custom.nonQuery (Query.save tableName) [ jsonParam "@data" document ] sqlProps + +/// Commands to count documents +[] +module Count = + + /// Count all documents in a table + /// The table in which documents should be counted (may include schema) + /// The SqlProps to use to execute the query + /// The count of the documents in the table + [] + let all tableName sqlProps = + Custom.scalar (Query.count tableName) [] toCount sqlProps + + /// 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 SqlProps to use to execute the query + /// The count of matching documents in the table + [] + 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 (@>) + /// The table in which documents should be counted (may include schema) + /// The document to match with the containment query + /// The SqlProps to use to execute the query + /// The count of the documents in the table + [] + 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 (@?) + /// The table in which documents should be counted (may include schema) + /// The JSON Path expression to be matched + /// The SqlProps to use to execute the query + /// The count of the documents in the table + [] + let byJsonPath tableName jsonPath sqlProps = + Custom.scalar + (Query.byPathMatch (Query.count tableName)) [ "@path", Sql.string jsonPath ] toCount sqlProps + +/// 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 SqlProps to use to execute the query + /// True if a document exists, false if not + [] + 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 (->> =, 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 SqlProps to use to execute the query + /// True if any matching documents exist, false if not + [] + 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 (@>) + /// The table in which existence should be checked (may include schema) + /// The document to match with the containment query + /// The SqlProps to use to execute the query + /// True if any matching documents exist, false if not + [] + 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 (@?) + /// The table in which existence should be checked (may include schema) + /// The JSON Path expression to be matched + /// The SqlProps to use to execute the query + /// True if any matching documents exist, false if not + [] + 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 +[] +module Find = + + /// Retrieve all documents in the given table + /// The table from which documents should be retrieved (may include schema) + /// The SqlProps to use to execute the query + /// All documents from the given table + [] + let all<'TDoc> tableName sqlProps = + Custom.list<'TDoc> (Query.find tableName) [] fromData<'TDoc> sqlProps + + /// Retrieve all documents in the given table + /// The table from which documents should be retrieved (may include schema) + /// The SqlProps to use to execute the query + /// All documents from 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 + /// The table from which documents should be retrieved (may include schema) + /// Fields by which the results should be ordered + /// The SqlProps to use to execute the query + /// All documents from the given table, ordered by the given fields + [] + 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 + /// The table from which documents should be retrieved (may include schema) + /// Fields by which the results should be ordered + /// The SqlProps to use to execute the query + /// All documents from the given table, ordered by the given fields + 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 + /// The table from which a document should be retrieved (may include schema) + /// The ID of the document to retrieve + /// The SqlProps to use to execute the query + /// Some with the document if found, None otherwise + [] + 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 + /// The table from which a document should be retrieved (may include schema) + /// The ID of the document to retrieve + /// The SqlProps 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, sqlProps) = + Custom.Single<'TDoc>( + Query.byId (Query.find tableName) docId, [ idParam docId ], fromData<'TDoc>, sqlProps) + + /// 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 SqlProps to use to execute the query + /// All documents matching the given fields + [] + 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 (->> =, 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 SqlProps to use to execute the query + /// All documents matching the given fields + 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 (->> =, 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 SqlProps 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 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 (->> =, 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 SqlProps 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, 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 (@>) + /// The table from which documents should be retrieved (may include schema) + /// The document to match with the containment query + /// The SqlProps to use to execute the query + /// All documents matching the given 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 (@>) + /// The table from which documents should be retrieved (may include schema) + /// The document to match with the containment query + /// The SqlProps to use to execute the query + /// All documents matching the given 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 + /// + /// The table from which documents should be retrieved (may include schema) + /// The document to match with the containment query + /// Fields by which the results should be ordered + /// The SqlProps to use to execute the query + /// All documents matching the given containment query, ordered by the given fields + [] + 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 + /// + /// The table from which documents should be retrieved (may include schema) + /// The document to match with the containment query + /// Fields by which the results should be ordered + /// The SqlProps to use to execute the query + /// All documents matching the given containment query, ordered by the given fields + 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 (@?) + /// The table from which documents should be retrieved (may include schema) + /// The JSON Path expression to match + /// The SqlProps to use to execute the query + /// All documents matching the given JSON Path expression + [] + 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 (@?) + /// The table from which documents should be retrieved (may include schema) + /// The JSON Path expression to match + /// The SqlProps to use to execute the query + /// All documents matching the given JSON Path expression + 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 + /// + /// The table from which documents should be retrieved (may include schema) + /// The JSON Path expression to match + /// Fields by which the results should be ordered + /// The SqlProps to use to execute the query + /// All documents matching the given JSON Path expression, ordered by the given fields + [] + 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 + /// + /// The table from which documents should be retrieved (may include schema) + /// The JSON Path expression to match + /// Fields by which the results should be ordered + /// The SqlProps to use to execute the query + /// All documents matching the given JSON Path expression, ordered by the given fields + 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 (->> =, 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 SqlProps to use to execute the query + /// Some with the first document, or None if not found + [] + 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 (->> =, 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 SqlProps 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, 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 (->> =, 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 SqlProps 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 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 (->> =, 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 SqlProps 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, 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 (@>) + /// The table from which a document should be retrieved (may include schema) + /// The document to match with the containment query + /// The SqlProps to use to execute the query + /// Some with the first document, or None if not found + [] + 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 (@>) + /// The table from which a document should be retrieved (may include schema) + /// The document to match with the containment query + /// The SqlProps to use to execute the query + /// The first document, or null if not found + let FirstByContains<'TDoc when 'TDoc: null and 'TDoc: not struct>(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 + /// + /// The table from which a document should be retrieved (may include schema) + /// The document to match with the containment query + /// Fields by which the results should be ordered + /// The SqlProps to use to execute the query + /// + /// Some with the first document ordered by the given fields, or None if not found + /// + [] + 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 + /// + /// The table from which a document should be retrieved (may include schema) + /// The document to match with the containment query + /// Fields by which the results should be ordered + /// The SqlProps to use to execute the query + /// The first document ordered by the given fields, or null if not found + let FirstByContainsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>( + 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 (@?) + /// The table from which a document should be retrieved (may include schema) + /// The JSON Path expression to match + /// The SqlProps to use to execute the query + /// Some with the first document, or None if not found + [] + 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 (@?) + /// The table from which a document should be retrieved (may include schema) + /// The JSON Path expression to match + /// The SqlProps to use to execute the query + /// The first document, or null if not found + let FirstByJsonPath<'TDoc when 'TDoc: null and 'TDoc: not struct>(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 + /// + /// The table from which a document should be retrieved (may include schema) + /// The JSON Path expression to match + /// Fields by which the results should be ordered + /// The SqlProps to use to execute the query + /// + /// Some with the first document ordered by the given fields, or None if not found + /// + [] + 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 + /// + /// The table from which a document should be retrieved (may include schema) + /// The JSON Path expression to match + /// Fields by which the results should be ordered + /// The SqlProps to use to execute the query + /// The first document ordered by the given fields, or null if not found + let FirstByJsonPathOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>( + 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 +[] +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 SqlProps to use to execute the query + [] + let byId tableName (docId: 'TKey) (document: 'TDoc) sqlProps = + Custom.nonQuery + (Query.byId (Query.update tableName) docId) [ idParam docId; jsonParam "@data" document ] sqlProps + + /// + /// 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 SqlProps to use to execute the query + [] + let byFunc tableName (idFunc: 'TDoc -> 'TKey) (document: 'TDoc) sqlProps = + byId tableName (idFunc document) document sqlProps + + /// + /// 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 SqlProps to use to execute the query + let ByFunc(tableName, idFunc: System.Func<'TDoc, 'TKey>, document: 'TDoc, sqlProps) = + byFunc tableName idFunc.Invoke document sqlProps + +/// 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 SqlProps to use to execute the query + [] + 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 (->> =, 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 SqlProps to use to execute the query + [] + 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 (@>) + /// The table in which documents should be patched (may include schema) + /// The document to match the containment query + /// The partial document to patch the existing document + /// The SqlProps to use to execute the query + [] + 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 (@?) + /// The table in which documents should be patched (may include schema) + /// The JSON Path expression to match + /// The partial document to patch the existing document + /// The SqlProps to use to execute the query + [] + 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 +[] +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 SqlProps to use to execute the query + [] + 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 + /// 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 SqlProps to use to execute the query + [] + 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 (@>) + /// The table in which documents should be modified (may include schema) + /// The document to match the containment query + /// One or more field names to remove from the matching documents + /// The SqlProps to use to execute the query + [] + 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 (@?) + /// The table in which documents should be modified (may include schema) + /// The JSON Path expression to match + /// One or more field names to remove from the matching documents + /// The SqlProps to use to execute the query + [] + let byJsonPath tableName jsonPath fieldNames sqlProps = + Custom.nonQuery + (Query.byPathMatch (Query.removeFields tableName)) + [ "@path", Sql.string jsonPath; fieldNameParams fieldNames ] + sqlProps + +/// 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 SqlProps to use to execute the query + [] + 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 (->> =, 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 SqlProps to use to execute the query + [] + 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 (@>) + /// The table in which documents should be deleted (may include schema) + /// The document to match the containment query + /// The SqlProps to use to execute the query + [] + 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 (@?) + /// The table in which documents should be deleted (may include schema) + /// The JSON Path expression to match + /// The SqlProps to use to execute the query + [] + let byJsonPath tableName jsonPath sqlProps = + Custom.nonQuery (Query.byPathMatch (Query.delete tableName)) [ "@path", Sql.string jsonPath ] sqlProps