diff --git a/src/MyWebLog.Data/Postgres/PostgresCache.fs b/src/MyWebLog.Data/Postgres/PostgresCache.fs index 70b79d8..14c359d 100644 --- a/src/MyWebLog.Data/Postgres/PostgresCache.fs +++ b/src/MyWebLog.Data/Postgres/PostgresCache.fs @@ -200,11 +200,11 @@ type DistributedCache (connStr : string) = } interface IDistributedCache with - member this.Get key = get key CancellationToken.None |> sync - member this.GetAsync (key, token) = get key token - member this.Refresh key = refresh key CancellationToken.None |> sync - member this.RefreshAsync (key, token) = refresh key token - member this.Remove key = remove key CancellationToken.None |> sync - member this.RemoveAsync (key, token) = remove key token - member this.Set (key, value, options) = set key value options CancellationToken.None |> sync - member this.SetAsync (key, value, options, token) = set key value options token + member _.Get key = get key CancellationToken.None |> sync + member _.GetAsync (key, token) = get key token + member _.Refresh key = refresh key CancellationToken.None |> sync + member _.RefreshAsync (key, token) = refresh key token + member _.Remove key = remove key CancellationToken.None |> sync + member _.RemoveAsync (key, token) = remove key token + member _.Set (key, value, options) = set key value options CancellationToken.None |> sync + member _.SetAsync (key, value, options, token) = set key value options token diff --git a/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs b/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs index 16bc955..e06f45b 100644 --- a/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs @@ -9,18 +9,15 @@ open Npgsql.FSharp.Documents /// PostgreSQL myWebLog category data implementation type PostgresCategoryData (source : NpgsqlDataSource) = - /// Shorthand for turning a web log ID into a string - let wls = WebLogId.toString - /// Count all categories for the given web log let countAll webLogId = Sql.fromDataSource source - |> Query.countByContains Table.Category {| WebLogId = wls webLogId |} + |> Query.countByContains Table.Category (webLogDoc webLogId) /// Count all top-level categories for the given web log let countTopLevel webLogId = Sql.fromDataSource source - |> Query.countByContains Table.Category {| WebLogId = wls webLogId; ParentId = None |} + |> Query.countByContains Table.Category {| webLogDoc webLogId with ParentId = None |} /// Retrieve all categories for the given web log in a DotLiquid-friendly format let findAllForView webLogId = backgroundTask { @@ -30,7 +27,7 @@ type PostgresCategoryData (source : NpgsqlDataSource) = {Query.selectFromTable Table.Category} WHERE {Query.whereDataContains "@criteria"} ORDER BY LOWER(data->>'{nameof Category.empty.Name}')""" - |> Sql.parameters [ "@criteria", webLogContains webLogId ] + |> Sql.parameters [ webLogContains webLogId ] |> Sql.executeAsync fromData let ordered = Utils.orderByHierarchy cats None None [] let counts = @@ -53,7 +50,7 @@ type PostgresCategoryData (source : NpgsqlDataSource) = AND ({catIdSql})""" |> Sql.parameters ( ("@criteria", - Query.jsonbDocParam {| WebLogId = wls webLogId; Status = PostStatus.toString Published |}) + Query.jsonbDocParam {| webLogDoc webLogId with Status = PostStatus.toString Published |}) :: catIdParams) |> Sql.executeRowAsync Map.toCount |> Async.AwaitTask diff --git a/src/MyWebLog.Data/Postgres/PostgresHelpers.fs b/src/MyWebLog.Data/Postgres/PostgresHelpers.fs index 5f06f5f..8802d09 100644 --- a/src/MyWebLog.Data/Postgres/PostgresHelpers.fs +++ b/src/MyWebLog.Data/Postgres/PostgresHelpers.fs @@ -68,16 +68,17 @@ open Npgsql open Npgsql.FSharp open Npgsql.FSharp.Documents -/// Create a WHERE clause fragment for the web log ID -let webLogWhere = "data ->> 'WebLogId' = @webLogId" - /// Create a SQL parameter for the web log ID let webLogIdParam webLogId = "@webLogId", Sql.string (WebLogId.toString webLogId) +/// Create an anonymous record with the given web log ID +let webLogDoc webLogId = + {| WebLogId = WebLogId.toString webLogId |} + /// Create a parameter for a web log document-contains query let webLogContains webLogId = - Query.jsonbDocParam {| WebLogId = WebLogId.toString webLogId |} + "@criteria", Query.jsonbDocParam (webLogDoc webLogId) /// The name of the field to select to be able to use Map.toCount let countName = "the_count" @@ -110,11 +111,11 @@ let jsonArrayInClause<'T> name (valueFunc : 'T -> string) (items : 'T list) = |> List.skip 1 |> List.fold (fun (itemS, itemP) it -> idx <- idx + 1 - $"{itemS} OR data -> '%s{name}' ? @{name}{idx}", + $"{itemS} OR data->'%s{name}' ? @{name}{idx}", ($"@{name}{idx}", Sql.jsonb (valueFunc it)) :: itemP) (Seq.ofList items |> Seq.map (fun it -> - $"data -> '{name}' ? @{name}0", [ $"@{name}0", Sql.string (valueFunc it) ]) + $"data->'{name}' ? @{name}0", [ $"@{name}0", Sql.string (valueFunc it) ]) |> Seq.head) /// Get the first result of the given query @@ -179,21 +180,21 @@ module Document = SELECT EXISTS ( SELECT 1 FROM %s{table} WHERE id = @id AND {Query.whereDataContains "@criteria"} ) AS {existsName}""" - |> Sql.parameters [ "@id", Sql.string (keyFunc key); webLogIdParam webLogId ] + |> Sql.parameters [ "@id", Sql.string (keyFunc key); webLogContains webLogId ] |> Sql.executeRowAsync Map.toExists /// Find a document by its ID for the given web log let findByIdAndWebLog<'TKey, 'TDoc> source table (key : 'TKey) (keyFunc : 'TKey -> string) webLogId = Sql.fromDataSource source |> Sql.query $"""{Query.selectFromTable table} WHERE id = @id AND {Query.whereDataContains "@criteria"}""" - |> Sql.parameters [ "@id", Sql.string (keyFunc key); "@criteria", webLogContains webLogId ] + |> Sql.parameters [ "@id", Sql.string (keyFunc key); webLogContains webLogId ] |> Sql.executeAsync fromData<'TDoc> |> tryHead /// Find a document by its ID for the given web log let findByWebLog<'TDoc> source table webLogId : Task<'TDoc list> = Sql.fromDataSource source - |> Query.findByContains table {| WebLogId = WebLogId.toString webLogId |} + |> Query.findByContains table (webLogDoc webLogId) /// Functions to support revisions @@ -209,13 +210,13 @@ module Revisions = /// Find all revisions for all posts for the given web log let findByWebLog<'TKey> source revTable entityTable (keyFunc : string -> 'TKey) webLogId = Sql.fromDataSource source - |> Sql.query $" + |> Sql.query $""" SELECT pr.* FROM %s{revTable} pr INNER JOIN %s{entityTable} p ON p.id = pr.{entityTable}_id - WHERE p.{webLogWhere} - ORDER BY as_of DESC" - |> Sql.parameters [ webLogIdParam webLogId ] + WHERE p.{Query.whereDataContains "@criteria"} + ORDER BY as_of DESC""" + |> Sql.parameters [ webLogContains webLogId ] |> Sql.executeAsync (fun row -> keyFunc (row.string $"{entityTable}_id"), Map.toRevision row) /// Parameters for a revision INSERT statement diff --git a/src/MyWebLog.Data/Postgres/PostgresPageData.fs b/src/MyWebLog.Data/Postgres/PostgresPageData.fs index 29e1bf7..7b7bf29 100644 --- a/src/MyWebLog.Data/Postgres/PostgresPageData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresPageData.fs @@ -11,9 +11,6 @@ type PostgresPageData (source : NpgsqlDataSource) = // SUPPORT FUNCTIONS - /// Shorthand for turning a web log ID into a string - let wls = WebLogId.toString - /// Append revisions to a page let appendPageRevisions (page : Page) = backgroundTask { let! revisions = Revisions.findByEntityId source Table.PageRevision Table.Page page.Id PageId.toString @@ -32,27 +29,28 @@ type PostgresPageData (source : NpgsqlDataSource) = let pageExists pageId webLogId = Document.existsByWebLog source Table.Page pageId PageId.toString webLogId + /// Select pages via a JSON document containment query + let pageByCriteria = + $"""{Query.selectFromTable Table.Page} WHERE {Query.whereDataContains "@criteria"}""" + // IMPLEMENTATION FUNCTIONS /// Get all pages for a web log (without text or revisions) let all webLogId = Sql.fromDataSource source - |> Sql.query $""" - {Query.selectFromTable Table.Page} - WHERE {Query.whereDataContains "@criteria"} - ORDER BY LOWER(data->>'{nameof Page.empty.Title}')""" - |> Sql.parameters [ "@criteria", webLogContains webLogId ] + |> Sql.query $"{pageByCriteria} ORDER BY LOWER(data->>'{nameof Page.empty.Title}')" + |> Sql.parameters [ webLogContains webLogId ] |> Sql.executeAsync fromData /// Count all pages for the given web log let countAll webLogId = Sql.fromDataSource source - |> Query.countByContains Table.Page {| WebLogId = wls webLogId |} + |> Query.countByContains Table.Page (webLogDoc webLogId) /// Count all pages shown in the page list for the given web log let countListed webLogId = Sql.fromDataSource source - |> Query.countByContains Table.Page {| WebLogId = wls webLogId; IsInPageList = true |} + |> Query.countByContains Table.Page {| webLogDoc webLogId with IsInPageList = true |} /// Find a page by its ID (without revisions) let findById pageId webLogId = @@ -79,7 +77,7 @@ type PostgresPageData (source : NpgsqlDataSource) = /// Find a page by its permalink for the given web log let findByPermalink permalink webLogId = Sql.fromDataSource source - |> Query.findByContains Table.Page {| WebLogId = wls webLogId; Permalink = Permalink.toString permalink |} + |> Query.findByContains Table.Page {| webLogDoc webLogId with Permalink = Permalink.toString permalink |} |> tryHead /// Find the current permalink within a set of potential prior permalinks for the given web log @@ -96,7 +94,7 @@ type PostgresPageData (source : NpgsqlDataSource) = FROM page WHERE {Query.whereDataContains "@criteria"} AND ({linkSql})""" - |> Sql.parameters (("@criteria", webLogContains webLogId) :: linkParams) + |> Sql.parameters (webLogContains webLogId :: linkParams) |> Sql.executeAsync Map.toPermalink |> tryHead } @@ -114,26 +112,18 @@ type PostgresPageData (source : NpgsqlDataSource) = /// Get all listed pages for the given web log (without revisions or text) let findListed webLogId = Sql.fromDataSource source - |> Sql.query $""" - {Query.selectFromTable Table.Page} - WHERE {Query.whereDataContains "@criteria"} - ORDER BY LOWER(data->>'{nameof Page.empty.Title}')""" - |> Sql.parameters [ "@criteria", Query.jsonbDocParam {| WebLogId = wls webLogId; IsInPageList = true |} ] + |> Sql.query $"{pageByCriteria} ORDER BY LOWER(data->>'{nameof Page.empty.Title}')" + |> Sql.parameters [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with IsInPageList = true |} ] |> Sql.executeAsync pageWithoutText /// Get a page of pages for the given web log (without revisions) let findPageOfPages webLogId pageNbr = Sql.fromDataSource source - |> Sql.query $""" - {Query.selectFromTable Table.Page} - WHERE {Query.whereDataContains "@criteria"} + |> Sql.query $" + {pageByCriteria} ORDER BY LOWER(data->>'{nameof Page.empty.Title}') - LIMIT @pageSize OFFSET @toSkip""" - |> Sql.parameters - [ "@criteria", webLogContains webLogId - "@pageSize", Sql.int 26 - "@toSkip", Sql.int ((pageNbr - 1) * 25) - ] + LIMIT @pageSize OFFSET @toSkip" + |> Sql.parameters [ webLogContains webLogId; "@pageSize", Sql.int 26; "@toSkip", Sql.int ((pageNbr - 1) * 25) ] |> Sql.executeAsync fromData /// The parameters for saving a page diff --git a/src/MyWebLog.Data/Postgres/PostgresPostData.fs b/src/MyWebLog.Data/Postgres/PostgresPostData.fs index f3cca98..3336314 100644 --- a/src/MyWebLog.Data/Postgres/PostgresPostData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresPostData.fs @@ -12,9 +12,6 @@ type PostgresPostData (source : NpgsqlDataSource) = // SUPPORT FUNCTIONS - /// Shorthand for turning a web log ID into a string - let wls = WebLogId.toString - /// Append revisions to a post let appendPostRevisions (post : Post) = backgroundTask { let! revisions = Revisions.findByEntityId source Table.PostRevision Table.Post post.Id PostId.toString @@ -45,7 +42,7 @@ type PostgresPostData (source : NpgsqlDataSource) = |> Sql.query $"""SELECT COUNT(id) AS {countName} FROM {Table.Post} WHERE {Query.whereDataContains "@criteria"}""" |> Sql.parameters - [ "@criteria", Query.jsonbDocParam {| WebLogId = wls webLogId; Status = PostStatus.toString status |} ] + [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = PostStatus.toString status |} ] |> Sql.executeRowAsync Map.toCount /// Find a post by its ID for the given web log (excluding revisions) @@ -57,7 +54,7 @@ type PostgresPostData (source : NpgsqlDataSource) = Sql.fromDataSource source |> Sql.query postsByCriteria |> Sql.parameters - [ "@criteria", Query.jsonbDocParam {| WebLogId = wls webLogId; Permalink = Permalink.toString permalink |} ] + [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Permalink = Permalink.toString permalink |} ] |> Sql.executeAsync fromData |> tryHead @@ -99,7 +96,7 @@ type PostgresPostData (source : NpgsqlDataSource) = FROM {Table.Post} WHERE {Query.whereDataContains "@criteria"} AND ({linkSql})""" - |> Sql.parameters (("@criteria", webLogContains webLogId) :: linkParams) + |> Sql.parameters (webLogContains webLogId :: linkParams) |> Sql.executeAsync Map.toPermalink |> tryHead } @@ -124,7 +121,7 @@ type PostgresPostData (source : NpgsqlDataSource) = ORDER BY published_on DESC LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" |> Sql.parameters ( - ("@criteria", Query.jsonbDocParam {| WebLogId = wls webLogId; Status = PostStatus.toString Published |}) + ("@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = PostStatus.toString Published |}) :: catParams) |> Sql.executeAsync fromData @@ -136,7 +133,7 @@ type PostgresPostData (source : NpgsqlDataSource) = ORDER BY data->>'{nameof Post.empty.PublishedOn}' DESC NULLS FIRST, data->>'{nameof Post.empty.UpdatedOn}' LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" - |> Sql.parameters [ "@criteria", webLogContains webLogId ] + |> Sql.parameters [ webLogContains webLogId ] |> Sql.executeAsync postWithoutText /// Get a page of published posts for the given web log (excludes revisions) @@ -147,7 +144,7 @@ type PostgresPostData (source : NpgsqlDataSource) = ORDER BY data->>'{nameof Post.empty.PublishedOn}' DESC LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" |> Sql.parameters - [ "@criteria", Query.jsonbDocParam {| WebLogId = wls webLogId; Status = PostStatus.toString Published |} ] + [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = PostStatus.toString Published |} ] |> Sql.executeAsync fromData /// Get a page of tagged posts for the given web log (excludes revisions and prior permalinks) @@ -159,7 +156,7 @@ type PostgresPostData (source : NpgsqlDataSource) = ORDER BY data->>'{nameof Post.empty.PublishedOn}' DESC LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" |> Sql.parameters - [ "@criteria", Query.jsonbDocParam {| WebLogId = wls webLogId; Status = PostStatus.toString Published |} + [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = PostStatus.toString Published |} "@tag", Sql.jsonb tag ] |> Sql.executeAsync fromData @@ -167,7 +164,7 @@ type PostgresPostData (source : NpgsqlDataSource) = /// Find the next newest and oldest post from a publish date for the given web log let findSurroundingPosts webLogId (publishedOn : Instant) = backgroundTask { let queryParams () = Sql.parameters [ - "@criteria", Query.jsonbDocParam {| WebLogId = wls webLogId; Status = PostStatus.toString Published |} + "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = PostStatus.toString Published |} typedParam "publishedOn" publishedOn ] let! older = diff --git a/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs b/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs index a576924..49ee19e 100644 --- a/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs @@ -9,9 +9,6 @@ open Npgsql.FSharp.Documents /// PostgreSQL myWebLog tag mapping data implementation type PostgresTagMapData (source : NpgsqlDataSource) = - /// Shorthand for turning a web log ID into a string - let wls = WebLogId.toString - /// A query to select tag map(s) by JSON document containment criteria let tagMapByCriteria = $"""{Query.selectFromTable Table.TagMap} WHERE {Query.whereDataContains "@criteria"}""" @@ -33,7 +30,7 @@ type PostgresTagMapData (source : NpgsqlDataSource) = let findByUrlValue (urlValue : string) webLogId = Sql.fromDataSource source |> Sql.query tagMapByCriteria - |> Sql.parameters [ "@criteria", Query.jsonbDocParam {| WebLogId = wls webLogId; UrlValue = urlValue |} ] + |> Sql.parameters [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with UrlValue = urlValue |} ] |> Sql.executeAsync fromData |> tryHead @@ -41,7 +38,7 @@ type PostgresTagMapData (source : NpgsqlDataSource) = let findByWebLog webLogId = Sql.fromDataSource source |> Sql.query $"{tagMapByCriteria} ORDER BY data->>'tag'" - |> Sql.parameters [ "@criteria", webLogContains webLogId ] + |> Sql.parameters [ webLogContains webLogId ] |> Sql.executeAsync fromData /// Find any tag mappings in a list of tags for the given web log @@ -49,7 +46,7 @@ type PostgresTagMapData (source : NpgsqlDataSource) = let tagSql, tagParams = jsonArrayInClause (nameof TagMap.empty.Tag) id tags Sql.fromDataSource source |> Sql.query $"{tagMapByCriteria} AND ({tagSql})" - |> Sql.parameters (("@criteria", webLogContains webLogId) :: tagParams) + |> Sql.parameters (webLogContains webLogId :: tagParams) |> Sql.executeAsync fromData /// The parameters for saving a tag mapping diff --git a/src/MyWebLog.Data/Postgres/PostgresWebLogData.fs b/src/MyWebLog.Data/Postgres/PostgresWebLogData.fs index 25a14ed..4e1dace 100644 --- a/src/MyWebLog.Data/Postgres/PostgresWebLogData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresWebLogData.fs @@ -33,7 +33,7 @@ type PostgresWebLogData (source : NpgsqlDataSource) = DELETE FROM {Table.Upload} WHERE web_log_id = @webLogId; DELETE FROM {Table.WebLogUser} WHERE {criteria}; DELETE FROM {Table.WebLog} WHERE id = @webLogId" - |> Sql.parameters [ webLogIdParam webLogId; "@criteria", webLogContains webLogId ] + |> Sql.parameters [ webLogIdParam webLogId; webLogContains webLogId ] |> Sql.executeNonQueryAsync () } diff --git a/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs b/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs index 68a29d9..58d8f7a 100644 --- a/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs @@ -9,9 +9,6 @@ open Npgsql.FSharp.Documents /// PostgreSQL myWebLog user data implementation type PostgresWebLogUserData (source : NpgsqlDataSource) = - /// Shorthand for making a web log ID into a string - let wls = WebLogId.toString - /// Query to get users by JSON document containment criteria let userByCriteria = $"""{Query.selectFromTable Table.WebLogUser} WHERE {Query.whereDataContains "@criteria"}""" @@ -51,7 +48,7 @@ type PostgresWebLogUserData (source : NpgsqlDataSource) = let findByEmail (email : string) webLogId = Sql.fromDataSource source |> Sql.query userByCriteria - |> Sql.parameters [ "@criteria", Query.jsonbDocParam {| WebLogId = wls webLogId; Email = email |} ] + |> Sql.parameters [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Email = email |} ] |> Sql.executeAsync fromData |> tryHead @@ -59,7 +56,7 @@ type PostgresWebLogUserData (source : NpgsqlDataSource) = let findByWebLog webLogId = Sql.fromDataSource source |> Sql.query $"{userByCriteria} ORDER BY LOWER(data->>'{nameof WebLogUser.empty.PreferredName}')" - |> Sql.parameters [ "@criteria", webLogContains webLogId ] + |> Sql.parameters [ webLogContains webLogId ] |> Sql.executeAsync fromData /// Find the names of users by their IDs for the given web log @@ -68,7 +65,7 @@ type PostgresWebLogUserData (source : NpgsqlDataSource) = let! users = Sql.fromDataSource source |> Sql.query $"{userByCriteria} {idSql}" - |> Sql.parameters (("@criteria", webLogContains webLogId) :: idParams) + |> Sql.parameters (webLogContains webLogId :: idParams) |> Sql.executeAsync fromData return users