Version 2.1 #41
|
@ -9,7 +9,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BitBadger.Npgsql.FSharp.Documents" Version="2.0.0" />
|
||||
<PackageReference Include="BitBadger.Documents.Postgres" Version="3.0.0-rc-1" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||
|
|
|
@ -2,7 +2,7 @@ namespace MyWebLog.Data.Postgres
|
|||
|
||||
open System.Threading
|
||||
open System.Threading.Tasks
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open Microsoft.Extensions.Caching.Distributed
|
||||
open NodaTime
|
||||
|
||||
|
@ -48,9 +48,11 @@ type DistributedCache () =
|
|||
task {
|
||||
let! exists =
|
||||
Custom.scalar
|
||||
$"SELECT EXISTS
|
||||
(SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'session')
|
||||
AS {existsName}" [] Map.toExists
|
||||
"SELECT EXISTS
|
||||
(SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'session')
|
||||
AS it"
|
||||
[]
|
||||
toExists
|
||||
if not exists then
|
||||
do! Custom.nonQuery
|
||||
"CREATE TABLE session (
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
namespace MyWebLog.Data.Postgres
|
||||
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open Microsoft.Extensions.Logging
|
||||
open MyWebLog
|
||||
open MyWebLog.Data
|
||||
|
@ -41,13 +42,12 @@ type PostgresCategoryData(log: ILogger) =
|
|||
|> arrayContains (nameof Post.Empty.CategoryIds) id
|
||||
let postCount =
|
||||
Custom.scalar
|
||||
$"""SELECT COUNT(DISTINCT data ->> '{nameof Post.Empty.Id}') AS {countName}
|
||||
$"""SELECT COUNT(DISTINCT data ->> '{nameof Post.Empty.Id}') AS it
|
||||
FROM {Table.Post}
|
||||
WHERE {Query.whereDataContains "@criteria"}
|
||||
AND {catIdSql}"""
|
||||
[ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published |}
|
||||
catIdParams ]
|
||||
Map.toCount
|
||||
[ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |}; catIdParams ]
|
||||
toCount
|
||||
|> Async.AwaitTask
|
||||
|> Async.RunSynchronously
|
||||
it.Id, postCount)
|
||||
|
@ -85,33 +85,32 @@ type PostgresCategoryData(log: ILogger) =
|
|||
Configuration.dataSource ()
|
||||
|> Sql.fromDataSource
|
||||
|> Sql.executeTransactionAsync
|
||||
[ Query.Update.partialById Table.Category,
|
||||
[ Query.Patch.byId Table.Category,
|
||||
children
|
||||
|> List.map (fun child ->
|
||||
[ "@id", Sql.string (string child.Id)
|
||||
"@data", Query.jsonbDocParam {| ParentId = cat.ParentId |} ]) ]
|
||||
[ idParam child.Id; jsonParam "@data" {| ParentId = cat.ParentId |} ]) ]
|
||||
()
|
||||
// Delete the category off all posts where it is assigned
|
||||
let! posts =
|
||||
Custom.list
|
||||
$"SELECT data FROM {Table.Post} WHERE data -> '{nameof Post.Empty.CategoryIds}' @> @id"
|
||||
[ "@id", Query.jsonbDocParam [| string catId |] ]
|
||||
[ jsonParam "@id" [| string catId |] ]
|
||||
fromData<Post>
|
||||
if not (List.isEmpty posts) then
|
||||
let! _ =
|
||||
Configuration.dataSource ()
|
||||
|> Sql.fromDataSource
|
||||
|> Sql.executeTransactionAsync
|
||||
[ Query.Update.partialById Table.Post,
|
||||
[ Query.Patch.byId Table.Post,
|
||||
posts
|
||||
|> List.map (fun post ->
|
||||
[ "@id", Sql.string (string post.Id)
|
||||
"@data", Query.jsonbDocParam
|
||||
{| CategoryIds = post.CategoryIds
|
||||
|> List.filter (fun cat -> cat <> catId) |} ]) ]
|
||||
[ idParam post.Id
|
||||
jsonParam
|
||||
"@data"
|
||||
{| CategoryIds = post.CategoryIds |> List.filter (fun cat -> cat <> catId) |} ]) ]
|
||||
()
|
||||
// Delete the category itself
|
||||
do! Delete.byId Table.Category (string catId)
|
||||
do! Delete.byId Table.Category catId
|
||||
return if hasChildren then ReassignedChildCategories else CategoryDeleted
|
||||
| None -> return CategoryNotFound
|
||||
}
|
||||
|
@ -129,7 +128,7 @@ type PostgresCategoryData(log: ILogger) =
|
|||
Configuration.dataSource ()
|
||||
|> Sql.fromDataSource
|
||||
|> Sql.executeTransactionAsync [
|
||||
Query.insert Table.Category, cats |> List.map (fun c -> [ "@data", Query.jsonbDocParam c ])
|
||||
Query.insert Table.Category, cats |> List.map (fun c -> [ jsonParam "@data" c ])
|
||||
]
|
||||
()
|
||||
}
|
||||
|
|
|
@ -61,7 +61,8 @@ module Table =
|
|||
|
||||
open System
|
||||
open System.Threading.Tasks
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open MyWebLog
|
||||
open MyWebLog.Data
|
||||
open NodaTime
|
||||
|
@ -78,13 +79,7 @@ let webLogDoc (webLogId: WebLogId) =
|
|||
|
||||
/// Create a parameter for a web log document-contains query
|
||||
let webLogContains webLogId =
|
||||
"@criteria", Query.jsonbDocParam (webLogDoc webLogId)
|
||||
|
||||
/// The name of the field to select to be able to use Map.toCount
|
||||
let countName = "the_count"
|
||||
|
||||
/// The name of the field to select to be able to use Map.toExists
|
||||
let existsName = "does_exist"
|
||||
jsonParam "@criteria" (webLogDoc webLogId)
|
||||
|
||||
/// A SQL string to select data from a table with the given JSON document contains criteria
|
||||
let selectWithCriteria tableName =
|
||||
|
@ -129,14 +124,6 @@ let optParam<'T> name (it: 'T option) =
|
|||
/// Mapping functions for SQL queries
|
||||
module Map =
|
||||
|
||||
/// Get a count from a row
|
||||
let toCount (row: RowReader) =
|
||||
row.int countName
|
||||
|
||||
/// Get a true/false value as to whether an item exists
|
||||
let toExists (row: RowReader) =
|
||||
row.bool existsName
|
||||
|
||||
/// Create a permalink from the current row
|
||||
let toPermalink (row: RowReader) =
|
||||
Permalink (row.string "permalink")
|
||||
|
@ -168,9 +155,9 @@ module Document =
|
|||
Custom.scalar
|
||||
$"""SELECT EXISTS (
|
||||
SELECT 1 FROM %s{table} WHERE {Query.whereById "@id"} AND {Query.whereDataContains "@criteria"}
|
||||
) AS {existsName}"""
|
||||
) AS it"""
|
||||
[ "@id", Sql.string (string key); webLogContains webLogId ]
|
||||
Map.toExists
|
||||
toExists
|
||||
|
||||
/// Find a document by its ID for the given web log
|
||||
let findByIdAndWebLog<'TKey, 'TDoc> table (key: 'TKey) webLogId =
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
namespace MyWebLog.Data.Postgres
|
||||
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open Microsoft.Extensions.Logging
|
||||
open MyWebLog
|
||||
open MyWebLog.Data
|
||||
|
@ -76,7 +77,7 @@ type PostgresPageData(log: ILogger) =
|
|||
do! Custom.nonQuery
|
||||
$"""DELETE FROM {Table.PageRevision} WHERE page_id = @id;
|
||||
DELETE FROM {Table.Page} WHERE {Query.whereById "@id"}"""
|
||||
[ "@id", Sql.string (string pageId) ]
|
||||
[ idParam pageId ]
|
||||
return true
|
||||
| false -> return false
|
||||
}
|
||||
|
@ -119,7 +120,7 @@ type PostgresPageData(log: ILogger) =
|
|||
log.LogTrace "Page.findListed"
|
||||
Custom.list
|
||||
$"{selectWithCriteria Table.Page} ORDER BY LOWER(data ->> '{nameof Page.Empty.Title}')"
|
||||
[ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with IsInPageList = true |} ]
|
||||
[ jsonParam "@criteria" {| webLogDoc webLogId with IsInPageList = true |} ]
|
||||
pageWithoutText
|
||||
|
||||
/// Get a page of pages for the given web log (without revisions)
|
||||
|
@ -141,7 +142,7 @@ type PostgresPageData(log: ILogger) =
|
|||
|> Sql.fromDataSource
|
||||
|> Sql.executeTransactionAsync
|
||||
[ Query.insert Table.Page,
|
||||
pages |> List.map (fun page -> [ "@data", Query.jsonbDocParam { page with Revisions = [] } ])
|
||||
pages |> List.map (fun page -> [ jsonParam "@data" { page with Revisions = [] } ])
|
||||
Revisions.insertSql Table.PageRevision,
|
||||
revisions |> List.map (fun (pageId, rev) -> Revisions.revParams pageId rev) ]
|
||||
()
|
||||
|
@ -161,7 +162,7 @@ type PostgresPageData(log: ILogger) =
|
|||
log.LogTrace "Page.updatePriorPermalinks"
|
||||
match! pageExists pageId webLogId with
|
||||
| true ->
|
||||
do! Update.partialById Table.Page (string pageId) {| PriorPermalinks = permalinks |}
|
||||
do! Patch.byId Table.Page pageId {| PriorPermalinks = permalinks |}
|
||||
return true
|
||||
| false -> return false
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
namespace MyWebLog.Data.Postgres
|
||||
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open Microsoft.Extensions.Logging
|
||||
open MyWebLog
|
||||
open MyWebLog.Data
|
||||
|
@ -50,7 +51,7 @@ type PostgresPostData(log: ILogger) =
|
|||
log.LogTrace "Post.findByPermalink"
|
||||
Custom.single
|
||||
(selectWithCriteria Table.Post)
|
||||
[ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Permalink = permalink |} ]
|
||||
[ jsonParam "@criteria" {| webLogDoc webLogId with Permalink = permalink |} ]
|
||||
fromData<Post>
|
||||
|
||||
/// Find a complete post by its ID for the given web log
|
||||
|
@ -72,7 +73,7 @@ type PostgresPostData(log: ILogger) =
|
|||
$"""DELETE FROM {Table.PostComment} WHERE {Query.whereDataContains "@criteria"};
|
||||
DELETE FROM {Table.PostRevision} WHERE post_id = @id;
|
||||
DELETE FROM {Table.Post} WHERE {Query.whereById "@id"}"""
|
||||
[ "@id", Sql.string (string postId); "@criteria", Query.jsonbDocParam {| PostId = postId |} ]
|
||||
[ idParam postId; jsonParam "@criteria" {| PostId = postId |} ]
|
||||
return true
|
||||
| false -> return false
|
||||
}
|
||||
|
@ -113,7 +114,7 @@ type PostgresPostData(log: ILogger) =
|
|||
AND {catSql}
|
||||
ORDER BY data ->> '{nameof Post.Empty.PublishedOn}' DESC
|
||||
LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}"
|
||||
[ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published |}; catParam ]
|
||||
[ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |}; catParam ]
|
||||
fromData<Post>
|
||||
|
||||
/// Get a page of posts for the given web log (excludes text and revisions)
|
||||
|
@ -134,7 +135,7 @@ type PostgresPostData(log: ILogger) =
|
|||
$"{selectWithCriteria Table.Post}
|
||||
ORDER BY data ->> '{nameof Post.Empty.PublishedOn}' DESC
|
||||
LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}"
|
||||
[ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published |} ]
|
||||
[ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |} ]
|
||||
fromData<Post>
|
||||
|
||||
/// Get a page of tagged posts for the given web log (excludes revisions and prior permalinks)
|
||||
|
@ -145,15 +146,14 @@ type PostgresPostData(log: ILogger) =
|
|||
AND data['{nameof Post.Empty.Tags}'] @> @tag
|
||||
ORDER BY data ->> '{nameof Post.Empty.PublishedOn}' DESC
|
||||
LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}"
|
||||
[ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published |}
|
||||
"@tag", Query.jsonbDocParam [| tag |] ]
|
||||
[ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |}; jsonParam "@tag" [| tag |] ]
|
||||
fromData<Post>
|
||||
|
||||
/// Find the next newest and oldest post from a publish date for the given web log
|
||||
let findSurroundingPosts webLogId publishedOn = backgroundTask {
|
||||
log.LogTrace "Post.findSurroundingPosts"
|
||||
let queryParams () =
|
||||
[ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published |}
|
||||
[ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |}
|
||||
"@publishedOn", Sql.string ((InstantPattern.General.Format publishedOn)[..19]) ]
|
||||
let pubField = nameof Post.Empty.PublishedOn
|
||||
let! older =
|
||||
|
@ -192,7 +192,7 @@ type PostgresPostData(log: ILogger) =
|
|||
|> Sql.fromDataSource
|
||||
|> Sql.executeTransactionAsync
|
||||
[ Query.insert Table.Post,
|
||||
posts |> List.map (fun post -> [ "@data", Query.jsonbDocParam { post with Revisions = [] } ])
|
||||
posts |> List.map (fun post -> [ jsonParam "@data" { post with Revisions = [] } ])
|
||||
Revisions.insertSql Table.PostRevision,
|
||||
revisions |> List.map (fun (postId, rev) -> Revisions.revParams postId rev) ]
|
||||
()
|
||||
|
@ -203,7 +203,7 @@ type PostgresPostData(log: ILogger) =
|
|||
log.LogTrace "Post.updatePriorPermalinks"
|
||||
match! postExists postId webLogId with
|
||||
| true ->
|
||||
do! Update.partialById Table.Post (string postId) {| PriorPermalinks = permalinks |}
|
||||
do! Patch.byId Table.Post postId {| PriorPermalinks = permalinks |}
|
||||
return true
|
||||
| false -> return false
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
namespace MyWebLog.Data.Postgres
|
||||
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open Microsoft.Extensions.Logging
|
||||
open MyWebLog
|
||||
open MyWebLog.Data
|
||||
|
@ -19,7 +20,7 @@ type PostgresTagMapData(log: ILogger) =
|
|||
log.LogTrace "TagMap.delete"
|
||||
let! exists = Document.existsByWebLog Table.TagMap tagMapId webLogId
|
||||
if exists then
|
||||
do! Delete.byId Table.TagMap (string tagMapId)
|
||||
do! Delete.byId Table.TagMap tagMapId
|
||||
return true
|
||||
else return false
|
||||
}
|
||||
|
@ -58,7 +59,7 @@ type PostgresTagMapData(log: ILogger) =
|
|||
|> Sql.fromDataSource
|
||||
|> Sql.executeTransactionAsync
|
||||
[ Query.insert Table.TagMap,
|
||||
tagMaps |> List.map (fun tagMap -> [ "@data", Query.jsonbDocParam tagMap ]) ]
|
||||
tagMaps |> List.map (fun tagMap -> [ jsonParam "@data" tagMap ]) ]
|
||||
()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
namespace MyWebLog.Data.Postgres
|
||||
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open Microsoft.Extensions.Logging
|
||||
open MyWebLog
|
||||
open MyWebLog.Data
|
||||
open Npgsql.FSharp
|
||||
|
||||
/// PostreSQL myWebLog theme data implementation
|
||||
type PostgresThemeData(log: ILogger) =
|
||||
|
@ -25,17 +25,17 @@ type PostgresThemeData(log: ILogger) =
|
|||
/// Does a given theme exist?
|
||||
let exists (themeId: ThemeId) =
|
||||
log.LogTrace "Theme.exists"
|
||||
Exists.byId Table.Theme (string themeId)
|
||||
Exists.byId Table.Theme themeId
|
||||
|
||||
/// Find a theme by its ID
|
||||
let findById (themeId: ThemeId) =
|
||||
log.LogTrace "Theme.findById"
|
||||
Find.byId<Theme> Table.Theme (string themeId)
|
||||
Find.byId<ThemeId, Theme> Table.Theme themeId
|
||||
|
||||
/// Find a theme by its ID (excludes the text of templates)
|
||||
let findByIdWithoutText (themeId: ThemeId) =
|
||||
log.LogTrace "Theme.findByIdWithoutText"
|
||||
Custom.single (Query.Find.byId Table.Theme) [ "@id", Sql.string (string themeId) ] withoutTemplateText
|
||||
Custom.single (Query.Find.byId Table.Theme) [ idParam themeId ] withoutTemplateText
|
||||
|
||||
/// Delete a theme by its ID
|
||||
let delete themeId = backgroundTask {
|
||||
|
@ -45,7 +45,7 @@ type PostgresThemeData(log: ILogger) =
|
|||
do! Custom.nonQuery
|
||||
$"""DELETE FROM {Table.ThemeAsset} WHERE theme_id = @id;
|
||||
DELETE FROM {Table.Theme} WHERE {Query.whereById "@id"}"""
|
||||
[ "@id", Sql.string (string themeId) ]
|
||||
[ idParam themeId ]
|
||||
return true
|
||||
| false -> return false
|
||||
}
|
||||
|
@ -75,32 +75,29 @@ type PostgresThemeAssetData(log: ILogger) =
|
|||
/// Delete all assets for the given theme
|
||||
let deleteByTheme (themeId: ThemeId) =
|
||||
log.LogTrace "ThemeAsset.deleteByTheme"
|
||||
Custom.nonQuery $"DELETE FROM {Table.ThemeAsset} WHERE theme_id = @id" [ "@id", Sql.string (string themeId) ]
|
||||
Custom.nonQuery $"DELETE FROM {Table.ThemeAsset} WHERE theme_id = @id" [ idParam themeId ]
|
||||
|
||||
/// Find a theme asset by its ID
|
||||
let findById assetId =
|
||||
log.LogTrace "ThemeAsset.findById"
|
||||
let (ThemeAssetId (ThemeId themeId, path)) = assetId
|
||||
Custom.single
|
||||
$"SELECT * FROM {Table.ThemeAsset} WHERE theme_id = @themeId AND path = @path"
|
||||
[ "@themeId", Sql.string themeId; "@path", Sql.string path ]
|
||||
$"SELECT * FROM {Table.ThemeAsset} WHERE theme_id = @id AND path = @path"
|
||||
[ idParam themeId; "@path", Sql.string path ]
|
||||
(Map.toThemeAsset true)
|
||||
|
||||
/// Get theme assets for the given theme (excludes data)
|
||||
let findByTheme (themeId: ThemeId) =
|
||||
log.LogTrace "ThemeAsset.findByTheme"
|
||||
Custom.list
|
||||
$"SELECT theme_id, path, updated_on FROM {Table.ThemeAsset} WHERE theme_id = @themeId"
|
||||
[ "@themeId", Sql.string (string themeId) ]
|
||||
$"SELECT theme_id, path, updated_on FROM {Table.ThemeAsset} WHERE theme_id = @id"
|
||||
[ idParam themeId ]
|
||||
(Map.toThemeAsset false)
|
||||
|
||||
/// Get theme assets for the given theme
|
||||
let findByThemeWithData (themeId: ThemeId) =
|
||||
log.LogTrace "ThemeAsset.findByThemeWithData"
|
||||
Custom.list
|
||||
$"SELECT * FROM {Table.ThemeAsset} WHERE theme_id = @themeId"
|
||||
[ "@themeId", Sql.string (string themeId) ]
|
||||
(Map.toThemeAsset true)
|
||||
Custom.list $"SELECT * FROM {Table.ThemeAsset} WHERE theme_id = @id" [ idParam themeId ] (Map.toThemeAsset true)
|
||||
|
||||
/// Save a theme asset
|
||||
let save (asset: ThemeAsset) =
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
namespace MyWebLog.Data.Postgres
|
||||
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open Microsoft.Extensions.Logging
|
||||
open MyWebLog
|
||||
open MyWebLog.Data
|
||||
|
@ -21,7 +22,7 @@ type PostgresUploadData(log: ILogger) =
|
|||
let upParams (upload: Upload) =
|
||||
[ webLogIdParam upload.WebLogId
|
||||
typedParam "updatedOn" upload.UpdatedOn
|
||||
"@id", Sql.string (string upload.Id)
|
||||
idParam upload.Id
|
||||
"@path", Sql.string (string upload.Path)
|
||||
"@data", Sql.bytea upload.Data ]
|
||||
|
||||
|
@ -33,7 +34,7 @@ type PostgresUploadData(log: ILogger) =
|
|||
/// Delete an uploaded file by its ID
|
||||
let delete uploadId webLogId = backgroundTask {
|
||||
log.LogTrace "Upload.delete"
|
||||
let idParam = [ "@id", Sql.string (string uploadId) ]
|
||||
let idParam = [ idParam uploadId ]
|
||||
let! path =
|
||||
Custom.single
|
||||
$"SELECT path FROM {Table.Upload} WHERE id = @id AND web_log_id = @webLogId"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
namespace MyWebLog.Data.Postgres
|
||||
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open Microsoft.Extensions.Logging
|
||||
open MyWebLog
|
||||
open MyWebLog.Data
|
||||
|
@ -46,13 +47,13 @@ type PostgresWebLogData(log: ILogger) =
|
|||
/// Find a web log by its ID
|
||||
let findById (webLogId: WebLogId) =
|
||||
log.LogTrace "WebLog.findById"
|
||||
Find.byId<WebLog> Table.WebLog (string webLogId)
|
||||
Find.byId<WebLogId, WebLog> Table.WebLog webLogId
|
||||
|
||||
/// Update redirect rules for a web log
|
||||
let updateRedirectRules (webLog: WebLog) = backgroundTask {
|
||||
log.LogTrace "WebLog.updateRedirectRules"
|
||||
match! findById webLog.Id with
|
||||
| Some _ -> do! Update.partialById Table.WebLog (string webLog.Id) {| RedirectRules = webLog.RedirectRules |}
|
||||
| Some _ -> do! Patch.byId Table.WebLog webLog.Id {| RedirectRules = webLog.RedirectRules |}
|
||||
| None -> ()
|
||||
}
|
||||
|
||||
|
@ -60,14 +61,14 @@ type PostgresWebLogData(log: ILogger) =
|
|||
let updateRssOptions (webLog: WebLog) = backgroundTask {
|
||||
log.LogTrace "WebLog.updateRssOptions"
|
||||
match! findById webLog.Id with
|
||||
| Some _ -> do! Update.partialById Table.WebLog (string webLog.Id) {| Rss = webLog.Rss |}
|
||||
| Some _ -> do! Patch.byId Table.WebLog webLog.Id {| Rss = webLog.Rss |}
|
||||
| None -> ()
|
||||
}
|
||||
|
||||
/// Update settings for a web log
|
||||
let updateSettings (webLog: WebLog) =
|
||||
log.LogTrace "WebLog.updateSettings"
|
||||
Update.full Table.WebLog (string webLog.Id) webLog
|
||||
Update.byId Table.WebLog webLog.Id webLog
|
||||
|
||||
interface IWebLogData with
|
||||
member _.Add webLog = add webLog
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
namespace MyWebLog.Data.Postgres
|
||||
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open Microsoft.Extensions.Logging
|
||||
open MyWebLog
|
||||
open MyWebLog.Data
|
||||
|
@ -24,13 +25,13 @@ type PostgresWebLogUserData(log: ILogger) =
|
|||
Custom.scalar
|
||||
$" SELECT ( EXISTS (SELECT 1 FROM {Table.Page} WHERE {criteria})
|
||||
OR EXISTS (SELECT 1 FROM {Table.Post} WHERE {criteria})
|
||||
) AS {existsName}"
|
||||
[ "@criteria", Query.jsonbDocParam {| AuthorId = userId |} ]
|
||||
Map.toExists
|
||||
) AS it"
|
||||
[ jsonParam "@criteria" {| AuthorId = userId |} ]
|
||||
toExists
|
||||
if isAuthor then
|
||||
return Error "User has pages or posts; cannot delete"
|
||||
else
|
||||
do! Delete.byId Table.WebLogUser (string userId)
|
||||
do! Delete.byId Table.WebLogUser userId
|
||||
return Ok true
|
||||
| None -> return Error "User does not exist"
|
||||
}
|
||||
|
@ -67,8 +68,7 @@ type PostgresWebLogUserData(log: ILogger) =
|
|||
Configuration.dataSource ()
|
||||
|> Sql.fromDataSource
|
||||
|> Sql.executeTransactionAsync
|
||||
[ Query.insert Table.WebLogUser,
|
||||
users |> List.map (fun user -> Query.docParameters (string user.Id) user) ]
|
||||
[ Query.insert Table.WebLogUser, users |> List.map (fun user -> [ jsonParam "@data" user ]) ]
|
||||
()
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ type PostgresWebLogUserData(log: ILogger) =
|
|||
let setLastSeen (userId: WebLogUserId) webLogId = backgroundTask {
|
||||
log.LogTrace "WebLogUser.setLastSeen"
|
||||
match! Document.existsByWebLog Table.WebLogUser userId webLogId with
|
||||
| true -> do! Update.partialById Table.WebLogUser (string userId) {| LastSeenOn = Some (Noda.now ()) |}
|
||||
| true -> do! Patch.byId Table.WebLogUser userId {| LastSeenOn = Some (Noda.now ()) |}
|
||||
| false -> ()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
namespace MyWebLog.Data
|
||||
|
||||
open BitBadger.Npgsql.Documents
|
||||
open BitBadger.Npgsql.FSharp.Documents
|
||||
open BitBadger.Documents
|
||||
open BitBadger.Documents.Postgres
|
||||
open Microsoft.Extensions.Logging
|
||||
open MyWebLog
|
||||
open MyWebLog.Data.Postgres
|
||||
|
@ -31,8 +31,8 @@ type PostgresData(log: ILogger<PostgresData>, ser: JsonSerializer) =
|
|||
// Theme tables
|
||||
if needsTable Table.Theme then
|
||||
isNew <- true
|
||||
Definition.createTable Table.Theme
|
||||
Definition.createKey Table.Theme
|
||||
Query.Definition.ensureTable Table.Theme
|
||||
Query.Definition.ensureKey Table.Theme
|
||||
if needsTable Table.ThemeAsset then
|
||||
$"CREATE TABLE {Table.ThemeAsset} (
|
||||
theme_id TEXT NOT NULL,
|
||||
|
@ -43,30 +43,29 @@ type PostgresData(log: ILogger<PostgresData>, ser: JsonSerializer) =
|
|||
|
||||
// Web log table
|
||||
if needsTable Table.WebLog then
|
||||
Definition.createTable Table.WebLog
|
||||
Definition.createKey Table.WebLog
|
||||
Definition.createIndex Table.WebLog Optimized
|
||||
Query.Definition.ensureTable Table.WebLog
|
||||
Query.Definition.ensureKey Table.WebLog
|
||||
Query.Definition.ensureDocumentIndex Table.WebLog Optimized
|
||||
|
||||
// Category table
|
||||
if needsTable Table.Category then
|
||||
Definition.createTable Table.Category
|
||||
Definition.createKey Table.Category
|
||||
Definition.createIndex Table.Category Optimized
|
||||
Query.Definition.ensureTable Table.Category
|
||||
Query.Definition.ensureKey Table.Category
|
||||
Query.Definition.ensureDocumentIndex Table.Category Optimized
|
||||
|
||||
// Web log user table
|
||||
if needsTable Table.WebLogUser then
|
||||
Definition.createTable Table.WebLogUser
|
||||
Definition.createKey Table.WebLogUser
|
||||
Definition.createIndex Table.WebLogUser Optimized
|
||||
Query.Definition.ensureTable Table.WebLogUser
|
||||
Query.Definition.ensureKey Table.WebLogUser
|
||||
Query.Definition.ensureDocumentIndex Table.WebLogUser Optimized
|
||||
|
||||
// Page tables
|
||||
if needsTable Table.Page then
|
||||
Definition.createTable Table.Page
|
||||
Definition.createKey Table.Page
|
||||
$"CREATE INDEX page_web_log_idx ON {Table.Page} ((data ->> '{nameof Page.Empty.WebLogId}'))"
|
||||
$"CREATE INDEX page_author_idx ON {Table.Page} ((data ->> '{nameof Page.Empty.AuthorId}'))"
|
||||
$"CREATE INDEX page_permalink_idx ON {Table.Page}
|
||||
((data ->> '{nameof Page.Empty.WebLogId}'), (data ->> '{nameof Page.Empty.Permalink}'))"
|
||||
Query.Definition.ensureTable Table.Page
|
||||
Query.Definition.ensureKey Table.Page
|
||||
Query.Definition.ensureIndexOn Table.Page "author" [ nameof Page.Empty.AuthorId ]
|
||||
Query.Definition.ensureIndexOn
|
||||
Table.Page "permalink" [ nameof Page.Empty.WebLogId; nameof Page.Empty.Permalink ]
|
||||
if needsTable Table.PageRevision then
|
||||
$"CREATE TABLE {Table.PageRevision} (
|
||||
page_id TEXT NOT NULL,
|
||||
|
@ -76,15 +75,15 @@ type PostgresData(log: ILogger<PostgresData>, ser: JsonSerializer) =
|
|||
|
||||
// Post tables
|
||||
if needsTable Table.Post then
|
||||
Definition.createTable Table.Post
|
||||
Definition.createKey Table.Post
|
||||
$"CREATE INDEX post_web_log_idx ON {Table.Post} ((data ->> '{nameof Post.Empty.WebLogId}'))"
|
||||
$"CREATE INDEX post_author_idx ON {Table.Post} ((data ->> '{nameof Post.Empty.AuthorId}'))"
|
||||
$"CREATE INDEX post_status_idx ON {Table.Post}
|
||||
((data ->> '{nameof Post.Empty.WebLogId}'), (data ->> '{nameof Post.Empty.Status}'),
|
||||
(data ->> '{nameof Post.Empty.UpdatedOn}'))"
|
||||
$"CREATE INDEX post_permalink_idx ON {Table.Post}
|
||||
((data ->> '{nameof Post.Empty.WebLogId}'), (data ->> '{nameof Post.Empty.Permalink}'))"
|
||||
Query.Definition.ensureTable Table.Post
|
||||
Query.Definition.ensureKey Table.Post
|
||||
Query.Definition.ensureIndexOn Table.Post "author" [ nameof Post.Empty.AuthorId ]
|
||||
Query.Definition.ensureIndexOn
|
||||
Table.Post "permalink" [ nameof Post.Empty.WebLogId; nameof Post.Empty.Permalink ]
|
||||
Query.Definition.ensureIndexOn
|
||||
Table.Post
|
||||
"status"
|
||||
[ nameof Post.Empty.WebLogId; nameof Post.Empty.Status; nameof Post.Empty.UpdatedOn ]
|
||||
$"CREATE INDEX post_category_idx ON {Table.Post} USING GIN ((data['{nameof Post.Empty.CategoryIds}']))"
|
||||
$"CREATE INDEX post_tag_idx ON {Table.Post} USING GIN ((data['{nameof Post.Empty.Tags}']))"
|
||||
if needsTable Table.PostRevision then
|
||||
|
@ -94,16 +93,15 @@ type PostgresData(log: ILogger<PostgresData>, ser: JsonSerializer) =
|
|||
revision_text TEXT NOT NULL,
|
||||
PRIMARY KEY (post_id, as_of))"
|
||||
if needsTable Table.PostComment then
|
||||
Definition.createTable Table.PostComment
|
||||
Definition.createKey Table.PostComment
|
||||
$"CREATE INDEX post_comment_post_idx ON {Table.PostComment}
|
||||
((data ->> '{nameof Comment.Empty.PostId}'))"
|
||||
Query.Definition.ensureTable Table.PostComment
|
||||
Query.Definition.ensureKey Table.PostComment
|
||||
Query.Definition.ensureIndexOn Table.PostComment "post" [ nameof Comment.Empty.PostId ]
|
||||
|
||||
// Tag map table
|
||||
if needsTable Table.TagMap then
|
||||
Definition.createTable Table.TagMap
|
||||
Definition.createKey Table.TagMap
|
||||
Definition.createIndex Table.TagMap Optimized
|
||||
Query.Definition.ensureTable Table.TagMap
|
||||
Query.Definition.ensureKey Table.TagMap
|
||||
Query.Definition.ensureDocumentIndex Table.TagMap Optimized
|
||||
|
||||
// Uploaded file table
|
||||
if needsTable Table.Upload then
|
||||
|
|
|
@ -50,14 +50,12 @@ type RedirectRuleMiddleware(next: RequestDelegate, log: ILogger<RedirectRuleMidd
|
|||
|
||||
|
||||
open System
|
||||
open BitBadger.Documents
|
||||
open Microsoft.Extensions.DependencyInjection
|
||||
open MyWebLog.Data
|
||||
open Newtonsoft.Json
|
||||
open Npgsql
|
||||
|
||||
// The PostgreSQL document library
|
||||
module Postgres = BitBadger.Npgsql.FSharp.Documents
|
||||
|
||||
// The SQLite document library
|
||||
module Sqlite = BitBadger.Sqlite.FSharp.Documents
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user