Integrate v4 document library for SQLite

- Eliminate warnings for PostgreSQL
This commit is contained in:
Daniel J. Summers 2024-08-19 22:23:22 -04:00
parent cd450a05e5
commit fbc4e891bd
9 changed files with 102 additions and 127 deletions

View File

@ -2,7 +2,6 @@
open BitBadger.Documents
open BitBadger.Documents.Postgres
open BitBadger.Documents.Postgres.Compat
open Microsoft.Extensions.Logging
open MyWebLog
open MyWebLog.Data
@ -21,7 +20,7 @@ type PostgresCategoryData(log: ILogger) =
log.LogTrace "Category.countTopLevel"
Custom.scalar
$"""{Query.byContains (Query.count Table.Category)}
AND {Query.whereByField (Field.NEX (nameof Category.Empty.ParentId)) ""}"""
AND {Query.whereByFields Any [ Field.NEX (nameof Category.Empty.ParentId) ]}"""
[ webLogContains webLogId ]
toCount
@ -95,7 +94,7 @@ type PostgresCategoryData(log: ILogger) =
Query.byId (Query.removeFields Table.Category) "",
children
|> List.map (fun child ->
[ idParam child.Id; fieldNameParam [ nameof Category.Empty.ParentId ] ])
[ idParam child.Id; fieldNameParams [ nameof Category.Empty.ParentId ] ])
let! _ =
Configuration.dataSource ()
|> Sql.fromDataSource

View File

@ -3,7 +3,6 @@ namespace MyWebLog.Data.SQLite
open System.Threading.Tasks
open BitBadger.Documents
open BitBadger.Documents.Sqlite
open BitBadger.Documents.Sqlite.Compat
open Microsoft.Data.Sqlite
open Microsoft.Extensions.Logging
open MyWebLog
@ -24,15 +23,14 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge
/// Count all top-level categories for the given web log
let countTopLevel webLogId =
log.LogTrace "Category.countTopLevel"
let fields = [ webLogField webLogId; Field.NEX parentIdField ]
conn.customScalar
$"{Document.Query.countByWebLog Table.Category} AND data ->> '{parentIdField}' IS NULL"
[ webLogParam webLogId ]
(toCount >> int)
(Query.byFields (Query.count Table.Category) All fields) (addFieldParams fields []) (toCount >> int)
/// Find all categories for the given web log
let findByWebLog webLogId =
log.LogTrace "Category.findByWebLog"
Document.findByWebLog<Category> Table.Category webLogId conn
conn.findByFields<Category> Table.Category Any [ webLogField webLogId ]
/// Retrieve all categories for the given web log in a DotLiquid-friendly format
let findAllForView webLogId = backgroundTask {
@ -51,10 +49,10 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge
|> List.ofSeq
|> inJsonArray Table.Post (nameof Post.Empty.CategoryIds) "catId"
let query = $"""
SELECT COUNT(DISTINCT data ->> '{nameof Post.Empty.Id}')
SELECT COUNT(DISTINCT data->>'{nameof Post.Empty.Id}')
FROM {Table.Post}
WHERE {Document.Query.whereByWebLog}
AND {Query.whereByField (Field.EQ (nameof Post.Empty.Status) "") $"'{string Published}'"}
AND data->>'{nameof Post.Empty.Status}' = '{string Published}'
AND {catSql}"""
let! postCount = conn.customScalar query (webLogParam webLogId :: catParams) toCount
return it.Id, int postCount
@ -70,9 +68,9 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge
}
/// Find a category by its ID for the given web log
let findById catId webLogId =
let findById (catId: CategoryId) webLogId =
log.LogTrace "Category.findById"
Document.findByIdAndWebLog<CategoryId, Category> Table.Category catId webLogId conn
conn.findFirstByFields<Category> Table.Category All [ idField catId; webLogField webLogId ]
/// Delete a category
let delete catId webLogId = backgroundTask {
@ -80,22 +78,22 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge
match! findById catId webLogId with
| Some cat ->
// Reassign any children to the category's parent category
let! children = conn.countByField Table.Category (Field.EQ parentIdField (string catId))
let! children = conn.countByFields Table.Category Any [ Field.EQ parentIdField (string catId) ]
if children > 0L then
let parent = Field.EQ parentIdField (string catId)
let parent = [ Field.EQ parentIdField (string catId) ]
match cat.ParentId with
| Some _ -> do! conn.patchByField Table.Category parent {| ParentId = cat.ParentId |}
| None -> do! conn.removeFieldsByField Table.Category parent [ parentIdField ]
| Some _ -> do! conn.patchByFields Table.Category Any parent {| ParentId = cat.ParentId |}
| None -> do! conn.removeFieldsByFields Table.Category Any parent [ parentIdField ]
// Delete the category off all posts where it is assigned, and the category itself
let catIdField = nameof Post.Empty.CategoryIds
let! posts =
conn.customList
$"SELECT data ->> '{nameof Post.Empty.Id}', data -> '{catIdField}'
$"SELECT data->>'{nameof Post.Empty.Id}', data->'{catIdField}'
FROM {Table.Post}
WHERE {Document.Query.whereByWebLog}
AND EXISTS
(SELECT 1
FROM json_each({Table.Post}.data -> '{catIdField}')
FROM json_each({Table.Post}.data->'{catIdField}')
WHERE json_each.value = @id)"
[ idParam catId; webLogParam webLogId ]
(fun rdr -> rdr.GetString 0, Utils.deserialize<string list> ser (rdr.GetString 1))

View File

@ -218,6 +218,10 @@ module Map =
Data = data }
open BitBadger.Documents
open BitBadger.Documents.Sqlite
open BitBadger.Documents.Sqlite.WithConn
/// Create a named parameter
let sqlParam name (value: obj) =
SqliteParameter(name, value)
@ -226,11 +230,13 @@ let sqlParam name (value: obj) =
let webLogParam (webLogId: WebLogId) =
sqlParam "@webLogId" (string webLogId)
/// Create a field for an ID value
let idField<'T> (idValue: 'T) =
{ Field.EQ "Id" (string idValue) with ParameterName = Some "@id" }
open BitBadger.Documents
open BitBadger.Documents.Sqlite
open BitBadger.Documents.Sqlite.Compat
open BitBadger.Documents.Sqlite.WithConn
/// Create a web log field
let webLogField (webLogId: WebLogId) =
{ Field.EQ "WebLogId" (string webLogId) with ParameterName = Some "@webLogId" }
/// Functions for manipulating documents
module Document =
@ -240,34 +246,18 @@ module Document =
/// Fragment to add a web log ID condition to a WHERE clause (parameter @webLogId)
let whereByWebLog =
Query.whereByField (Field.EQ "WebLogId" "") "@webLogId"
/// A SELECT query to count documents for a given web log ID
let countByWebLog table =
Query.statementWhere (Query.count table) whereByWebLog
/// A query to select from a table by the document's ID and its web log ID
let selectByIdAndWebLog table =
Query.statementWhere (Query.find table) $"""{Query.whereById "@id"} AND {whereByWebLog}"""
Query.whereByFields Any [ { Field.EQ "WebLogId" "" with ParameterName = Some "@webLogId" } ]
/// A query to select from a table by its web log ID
let selectByWebLog table =
$"{Query.selectFromTable table} WHERE {whereByWebLog}"
Query.statementWhere (Query.find table) whereByWebLog
/// Count documents for the given web log ID
let countByWebLog table (webLogId: WebLogId) conn = backgroundTask {
let! count = Count.byFields table Any [ Field.EQ "WebLogId" (string webLogId) ] conn
let! count = Count.byFields table Any [ webLogField webLogId ] conn
return int count
}
/// Find a document by its ID and web log ID
let findByIdAndWebLog<'TKey, 'TDoc> table (key: 'TKey) webLogId conn =
Custom.single (Query.selectByIdAndWebLog table) [ idParam key; webLogParam webLogId ] fromData<'TDoc> conn
/// Find documents for the given web log
let findByWebLog<'TDoc> table (webLogId: WebLogId) conn =
Find.byFields<'TDoc> table Any [ Field.EQ "WebLogId" (string webLogId) ] conn
/// Functions to support revisions
module Revisions =
@ -285,7 +275,7 @@ module Revisions =
Custom.list
$"SELECT pr.*
FROM %s{revTable} pr
INNER JOIN %s{entityTable} p ON p.data ->> 'Id' = pr.{entityTable}_id
INNER JOIN %s{entityTable} p ON p.data->>'Id' = pr.{entityTable}_id
WHERE p.{Document.Query.whereByWebLog}
ORDER BY as_of DESC"
[ webLogParam webLogId ]

View File

@ -3,7 +3,6 @@ namespace MyWebLog.Data.SQLite
open System.Threading.Tasks
open BitBadger.Documents
open BitBadger.Documents.Sqlite
open BitBadger.Documents.Sqlite.Compat
open Microsoft.Data.Sqlite
open Microsoft.Extensions.Logging
open MyWebLog
@ -19,7 +18,7 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
let pgListName = nameof Page.Empty.IsInPageList
/// The JSON field for the title of the page
let titleField = $"data ->> '{nameof Page.Empty.Title}'"
let titleField = $"data->>'{nameof Page.Empty.Title}'"
// SUPPORT FUNCTIONS
@ -51,9 +50,10 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
/// Get all pages for a web log (without text, metadata, revisions, or prior permalinks)
let all webLogId =
log.LogTrace "Page.all"
let field = [ webLogField webLogId ]
conn.customList
$"{Query.selectFromTable Table.Page} WHERE {Document.Query.whereByWebLog} ORDER BY LOWER({titleField})"
[ webLogParam webLogId ]
$"{Query.byFields (Query.find Table.Page) Any field} ORDER BY LOWER({titleField})"
(addFieldParams field [])
(fun rdr -> { fromData<Page> rdr with Text = ""; Metadata = []; PriorPermalinks = [] })
/// Count all pages for the given web log
@ -64,23 +64,22 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
/// Count all pages shown in the page list for the given web log
let countListed webLogId =
log.LogTrace "Page.countListed"
let fields = [ webLogField webLogId; Field.EQ pgListName true ]
conn.customScalar
$"""{Document.Query.countByWebLog Table.Page} AND {Query.whereByField (Field.EQ pgListName "") "true"}"""
[ webLogParam webLogId ]
(toCount >> int)
(Query.byFields (Query.count Table.Page) All fields) (addFieldParams fields []) (toCount >> int)
/// Find a page by its ID (without revisions and prior permalinks)
let findById pageId webLogId = backgroundTask {
let findById (pageId: PageId) webLogId = backgroundTask {
log.LogTrace "Page.findById"
match! Document.findByIdAndWebLog<PageId, Page> Table.Page pageId webLogId conn with
match! conn.findFirstByFields<Page> Table.Page All [ idField pageId; webLogField webLogId ] with
| Some page -> return Some { page with PriorPermalinks = [] }
| None -> return None
}
/// Find a complete page by its ID
let findFullById pageId webLogId = backgroundTask {
let findFullById (pageId: PageId) webLogId = backgroundTask {
log.LogTrace "Page.findFullById"
match! Document.findByIdAndWebLog<PageId, Page> Table.Page pageId webLogId conn with
match! conn.findFirstByFields<Page> Table.Page All [ idField pageId; webLogField webLogId ] with
| Some page ->
let! page = appendPageRevisions page
return Some page
@ -94,7 +93,7 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
match! findById pageId webLogId with
| Some _ ->
do! conn.customNonQuery
$"DELETE FROM {Table.PageRevision} WHERE page_id = @id;
$"{Query.delete Table.PageRevision} WHERE page_id = @id;
{Query.byId (Query.delete Table.Page) (string pageId)}"
[ idParam pageId ]
return true
@ -104,18 +103,16 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
/// Find a page by its permalink for the given web log
let findByPermalink (permalink: Permalink) webLogId =
log.LogTrace "Page.findByPermalink"
let linkParam = Field.EQ linkName (string permalink)
let fields = [ webLogField webLogId; Field.EQ linkName (string permalink) ]
conn.customSingle
$"""{Document.Query.selectByWebLog Table.Page} AND {Query.whereByField linkParam "@link"}"""
(addFieldParam "@link" linkParam [ webLogParam webLogId ])
pageWithoutLinks
(Query.byFields (Query.find Table.Page) All fields) (addFieldParams fields []) pageWithoutLinks
/// Find the current permalink within a set of potential prior permalinks for the given web log
let findCurrentPermalink (permalinks: Permalink list) webLogId =
log.LogTrace "Page.findCurrentPermalink"
let linkSql, linkParams = inJsonArray Table.Page (nameof Page.Empty.PriorPermalinks) "link" permalinks
conn.customSingle
$"SELECT data ->> '{linkName}' AS permalink
$"SELECT data->>'{linkName}' AS permalink
FROM {Table.Page}
WHERE {Document.Query.whereByWebLog} AND {linkSql}"
(webLogParam webLogId :: linkParams)
@ -124,7 +121,7 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
/// Get all complete pages for the given web log
let findFullByWebLog webLogId = backgroundTask {
log.LogTrace "Page.findFullByWebLog"
let! pages = Document.findByWebLog<Page> Table.Page webLogId conn
let! pages = conn.findByFields<Page> Table.Page Any [ webLogField webLogId ]
let! withRevs = pages |> List.map appendPageRevisions |> Task.WhenAll
return List.ofArray withRevs
}
@ -132,18 +129,20 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
/// Get all listed pages for the given web log (without revisions or text)
let findListed webLogId =
log.LogTrace "Page.findListed"
let fields = [ webLogField webLogId; Field.EQ pgListName true ]
conn.customList
$"""{Document.Query.selectByWebLog Table.Page} AND {Query.whereByField (Field.EQ pgListName "") "true"}
ORDER BY LOWER({titleField})"""
[ webLogParam webLogId ]
$"{Query.byFields (Query.find Table.Page) All fields} ORDER BY LOWER({titleField})"
(addFieldParams fields [])
(fun rdr -> { fromData<Page> rdr with Text = "" })
/// Get a page of pages for the given web log (without revisions)
let findPageOfPages webLogId pageNbr =
log.LogTrace "Page.findPageOfPages"
let field = [ webLogField webLogId ]
conn.customList
$"{Document.Query.selectByWebLog Table.Page} ORDER BY LOWER({titleField}) LIMIT @pageSize OFFSET @toSkip"
[ webLogParam webLogId; SqliteParameter("@pageSize", 26); SqliteParameter("@toSkip", (pageNbr - 1) * 25) ]
$"{Query.byFields (Query.find Table.Page) Any field} ORDER BY LOWER({titleField})
LIMIT @pageSize OFFSET @toSkip"
(addFieldParams field [ sqlParam "@pageSize" 26; sqlParam "@toSkip" ((pageNbr - 1) * 25) ])
(fun rdr -> { pageWithoutLinks rdr with Metadata = [] })
/// Update a page

View File

@ -3,7 +3,6 @@ namespace MyWebLog.Data.SQLite
open System.Threading.Tasks
open BitBadger.Documents
open BitBadger.Documents.Sqlite
open BitBadger.Documents.Sqlite.Compat
open Microsoft.Data.Sqlite
open Microsoft.Extensions.Logging
open MyWebLog
@ -17,7 +16,7 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
let linkName = nameof Post.Empty.Permalink
/// The JSON field for when the post was published
let publishField = $"data ->> '{nameof Post.Empty.PublishedOn}'"
let publishField = $"data->>'{nameof Post.Empty.PublishedOn}'"
/// The name of the JSON field for the post's status
let statName = nameof Post.Empty.Status
@ -44,7 +43,7 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
/// The SELECT statement to retrieve published posts with a web log ID parameter
let publishedPostByWebLog =
$"""{postByWebLog} AND {Query.whereByField (Field.EQ statName "") $"'{string Published}'"}"""
$"{postByWebLog} AND data->>'{statName}' = '{string Published}'"
/// Update a post's revisions
let updatePostRevisions (postId: PostId) oldRevs newRevs =
@ -63,16 +62,14 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
/// Count posts in a status for the given web log
let countByStatus (status: PostStatus) webLogId =
log.LogTrace "Post.countByStatus"
let statParam = Field.EQ statName (string status)
let fields = [ webLogField webLogId; Field.EQ statName (string status) ]
conn.customScalar
$"""{Document.Query.countByWebLog Table.Post} AND {Query.whereByField statParam "@status"}"""
(addFieldParam "@status" statParam [ webLogParam webLogId ])
(toCount >> int)
(Query.byFields (Query.count Table.Post) All fields) (addFieldParams fields []) (toCount >> int)
/// Find a post by its ID for the given web log (excluding revisions)
let findById postId webLogId = backgroundTask {
let findById (postId: PostId) webLogId = backgroundTask {
log.LogTrace "Post.findById"
match! Document.findByIdAndWebLog<PostId, Post> Table.Post postId webLogId conn with
match! conn.findFirstByFields<Post> Table.Post All [ idField postId; webLogField webLogId ] with
| Some post -> return Some { post with PriorPermalinks = [] }
| None -> return None
}
@ -80,16 +77,14 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
/// Find a post by its permalink for the given web log (excluding revisions)
let findByPermalink (permalink: Permalink) webLogId =
log.LogTrace "Post.findByPermalink"
let linkParam = Field.EQ linkName (string permalink)
let fields = [ webLogField webLogId; Field.EQ linkName (string permalink) ]
conn.customSingle
$"""{Document.Query.selectByWebLog Table.Post} AND {Query.whereByField linkParam "@link"}"""
(addFieldParam "@link" linkParam [ webLogParam webLogId ])
postWithoutLinks
(Query.byFields (Query.find Table.Post) All fields) (addFieldParams fields []) postWithoutLinks
/// Find a complete post by its ID for the given web log
let findFullById postId webLogId = backgroundTask {
log.LogTrace "Post.findFullById"
match! Document.findByIdAndWebLog<PostId, Post> Table.Post postId webLogId conn with
match! conn.findFirstByFields<Post> Table.Post All [ idField postId; webLogField webLogId ] with
| Some post ->
let! post = appendPostRevisions post
return Some post
@ -102,9 +97,11 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
match! findById postId webLogId with
| Some _ ->
do! conn.customNonQuery
$"""DELETE FROM {Table.PostRevision} WHERE post_id = @id;
DELETE FROM {Table.PostComment}
WHERE {Query.whereByField (Field.EQ (nameof Comment.Empty.PostId) "") "@id"};
$"""{Query.delete Table.PostRevision} WHERE post_id = @id;
{Query.byFields
(Query.delete Table.PostComment)
Any
[ { Field.EQ (nameof Comment.Empty.PostId) postId with ParameterName = Some "@id" }]};
{Query.byId (Query.delete Table.Post) (string postId)}"""
[ idParam postId ]
return true
@ -116,7 +113,7 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
log.LogTrace "Post.findCurrentPermalink"
let linkSql, linkParams = inJsonArray Table.Post (nameof Post.Empty.PriorPermalinks) "link" permalinks
conn.customSingle
$"SELECT data ->> '{linkName}' AS permalink
$"SELECT data->>'{linkName}' AS permalink
FROM {Table.Post}
WHERE {Document.Query.whereByWebLog} AND {linkSql}"
(webLogParam webLogId :: linkParams)
@ -125,7 +122,7 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
/// Get all complete posts for the given web log
let findFullByWebLog webLogId = backgroundTask {
log.LogTrace "Post.findFullByWebLog"
let! posts = Document.findByWebLog<Post> Table.Post webLogId conn
let! posts = conn.findByFields<Post> Table.Post Any [ webLogField webLogId ]
let! withRevs = posts |> List.map appendPostRevisions |> Task.WhenAll
return List.ofArray withRevs
}
@ -146,7 +143,7 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
log.LogTrace "Post.findPageOfPosts"
conn.customList
$"{postByWebLog}
ORDER BY {publishField} DESC NULLS FIRST, data ->> '{nameof Post.Empty.UpdatedOn}'
ORDER BY {publishField} DESC NULLS FIRST, data->>'{nameof Post.Empty.UpdatedOn}'
LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}"
[ webLogParam webLogId ]
postWithoutText
@ -175,15 +172,16 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
/// Find the next newest and oldest post from a publish date for the given web log
let findSurroundingPosts webLogId (publishedOn : Instant) = backgroundTask {
log.LogTrace "Post.findSurroundingPosts"
let parameters = [ webLogParam webLogId; sqlParam "@publishedOn" (instantParam publishedOn) ]
let! older =
conn.customSingle
$"{publishedPostByWebLog} AND {publishField} < @publishedOn ORDER BY {publishField} DESC LIMIT 1"
[ webLogParam webLogId; SqliteParameter("@publishedOn", instantParam publishedOn) ]
parameters
postWithoutLinks
let! newer =
conn.customSingle
$"{publishedPostByWebLog} AND {publishField} > @publishedOn ORDER BY {publishField} LIMIT 1"
[ webLogParam webLogId; SqliteParameter("@publishedOn", instantParam publishedOn) ]
parameters
postWithoutLinks
return older, newer
}

View File

@ -2,7 +2,6 @@ namespace MyWebLog.Data.SQLite
open BitBadger.Documents
open BitBadger.Documents.Sqlite
open BitBadger.Documents.Sqlite.Compat
open Microsoft.Data.Sqlite
open Microsoft.Extensions.Logging
open MyWebLog
@ -12,9 +11,9 @@ open MyWebLog.Data
type SQLiteTagMapData(conn: SqliteConnection, log: ILogger) =
/// Find a tag mapping by its ID for the given web log
let findById tagMapId webLogId =
let findById (tagMapId: TagMapId) webLogId =
log.LogTrace "TagMap.findById"
Document.findByIdAndWebLog<TagMapId, TagMap> Table.TagMap tagMapId webLogId conn
conn.findFirstByFields<TagMap> Table.TagMap All [ idField tagMapId; webLogField webLogId ]
/// Delete a tag mapping for the given web log
let delete tagMapId webLogId = backgroundTask {
@ -29,21 +28,18 @@ type SQLiteTagMapData(conn: SqliteConnection, log: ILogger) =
/// Find a tag mapping by its URL value for the given web log
let findByUrlValue (urlValue: string) webLogId =
log.LogTrace "TagMap.findByUrlValue"
let urlParam = Field.EQ (nameof TagMap.Empty.UrlValue) urlValue
conn.customSingle
$"""{Document.Query.selectByWebLog Table.TagMap} AND {Query.whereByField urlParam "@urlValue"}"""
(addFieldParam "@urlValue" urlParam [ webLogParam webLogId ])
fromData<TagMap>
conn.findFirstByFields<TagMap>
Table.TagMap All [ webLogField webLogId; Field.EQ (nameof TagMap.Empty.UrlValue) urlValue ]
/// Get all tag mappings for the given web log
let findByWebLog webLogId =
log.LogTrace "TagMap.findByWebLog"
Document.findByWebLog<TagMap> Table.TagMap webLogId conn
conn.findByFields<TagMap> Table.TagMap Any [ webLogField webLogId ]
/// Find any tag mappings in a list of tags for the given web log
let findMappingForTags (tags: string list) webLogId =
log.LogTrace "TagMap.findMappingForTags"
let mapSql, mapParams = inClause $"AND data ->> '{nameof TagMap.Empty.Tag}'" "tag" id tags
let mapSql, mapParams = inClause $"AND data->>'{nameof TagMap.Empty.Tag}'" "tag" id tags
conn.customList
$"{Document.Query.selectByWebLog Table.TagMap} {mapSql}"
(webLogParam webLogId :: mapParams)

View File

@ -11,7 +11,7 @@ open MyWebLog.Data
type SQLiteThemeData(conn : SqliteConnection, log: ILogger) =
/// The JSON field for the theme ID
let idField = $"data ->> '{nameof Theme.Empty.Id}'"
let idField = $"data->>'{nameof Theme.Empty.Id}'"
/// Convert a document to a theme with no template text
let withoutTemplateText (rdr: SqliteDataReader) =
@ -48,7 +48,7 @@ type SQLiteThemeData(conn : SqliteConnection, log: ILogger) =
match! findByIdWithoutText themeId with
| Some _ ->
do! conn.customNonQuery
$"DELETE FROM {Table.ThemeAsset} WHERE theme_id = @id;
$"{Query.delete Table.ThemeAsset} WHERE theme_id = @id;
{Query.byId (Query.delete Table.Theme) (string themeId)}"
[ idParam themeId ]
return true
@ -87,7 +87,7 @@ type SQLiteThemeAssetData(conn : SqliteConnection, log: ILogger) =
/// Delete all assets for the given theme
let deleteByTheme (themeId: ThemeId) =
log.LogTrace "ThemeAsset.deleteByTheme"
conn.customNonQuery $"DELETE FROM {Table.ThemeAsset} WHERE theme_id = @id" [ idParam themeId ]
conn.customNonQuery $"{Query.delete Table.ThemeAsset} WHERE theme_id = @id" [ idParam themeId ]
/// Find a theme asset by its ID
let findById assetId =

View File

@ -2,7 +2,6 @@ namespace MyWebLog.Data.SQLite
open BitBadger.Documents
open BitBadger.Documents.Sqlite
open BitBadger.Documents.Sqlite.Compat
open Microsoft.Data.Sqlite
open Microsoft.Extensions.Logging
open MyWebLog
@ -24,25 +23,25 @@ type SQLiteWebLogData(conn: SqliteConnection, log: ILogger) =
/// Delete a web log by its ID
let delete webLogId =
log.LogTrace "WebLog.delete"
let webLogMatches = Query.whereByField (Field.EQ "WebLogId" "") "@webLogId"
let subQuery table = $"(SELECT data ->> 'Id' FROM {table} WHERE {webLogMatches})"
let webLogMatches = Query.whereByFields Any [ { Field.EQ "WebLogId" "" with ParameterName = Some "@webLogId" } ]
let subQuery table = $"(SELECT data->>'Id' FROM {table} WHERE {webLogMatches})"
Custom.nonQuery
$"""DELETE FROM {Table.PostComment} WHERE data ->> 'PostId' IN {subQuery Table.Post};
DELETE FROM {Table.PostRevision} WHERE post_id IN {subQuery Table.Post};
DELETE FROM {Table.PageRevision} WHERE page_id IN {subQuery Table.Page};
DELETE FROM {Table.Post} WHERE {webLogMatches};
DELETE FROM {Table.Page} WHERE {webLogMatches};
DELETE FROM {Table.Category} WHERE {webLogMatches};
DELETE FROM {Table.TagMap} WHERE {webLogMatches};
DELETE FROM {Table.Upload} WHERE web_log_id = @webLogId;
DELETE FROM {Table.WebLogUser} WHERE {webLogMatches};
DELETE FROM {Table.WebLog} WHERE {Query.whereById "@webLogId"}"""
$"""{Query.delete Table.PostComment} WHERE data ->> 'PostId' IN {subQuery Table.Post};
{Query.delete Table.PostRevision} WHERE post_id IN {subQuery Table.Post};
{Query.delete Table.PageRevision} WHERE page_id IN {subQuery Table.Page};
{Query.delete Table.Post} WHERE {webLogMatches};
{Query.delete Table.Page} WHERE {webLogMatches};
{Query.delete Table.Category} WHERE {webLogMatches};
{Query.delete Table.TagMap} WHERE {webLogMatches};
{Query.delete Table.Upload} WHERE web_log_id = @webLogId;
{Query.delete Table.WebLogUser} WHERE {webLogMatches};
{Query.delete Table.WebLog} WHERE {Query.whereById "@webLogId"}"""
[ webLogParam webLogId ]
/// Find a web log by its host (URL base)
let findByHost (url: string) =
log.LogTrace "WebLog.findByHost"
conn.findFirstByField<WebLog> Table.WebLog (Field.EQ (nameof WebLog.Empty.UrlBase) url)
conn.findFirstByFields<WebLog> Table.WebLog Any [ Field.EQ (nameof WebLog.Empty.UrlBase) url ]
/// Find a web log by its ID
let findById webLogId =

View File

@ -2,7 +2,6 @@ namespace MyWebLog.Data.SQLite
open BitBadger.Documents
open BitBadger.Documents.Sqlite
open BitBadger.Documents.Sqlite.Compat
open Microsoft.Data.Sqlite
open Microsoft.Extensions.Logging
open MyWebLog
@ -17,17 +16,18 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) =
conn.insert<WebLogUser> Table.WebLogUser user
/// Find a user by their ID for the given web log
let findById userId webLogId =
let findById (userId: WebLogUserId) webLogId =
log.LogTrace "WebLogUser.findById"
Document.findByIdAndWebLog<WebLogUserId, WebLogUser> Table.WebLogUser userId webLogId conn
conn.findFirstByFields<WebLogUser> Table.WebLogUser All [ idField userId; webLogField webLogId ]
/// Delete a user if they have no posts or pages
let delete userId webLogId = backgroundTask {
log.LogTrace "WebLogUser.delete"
match! findById userId webLogId with
| Some _ ->
let! pageCount = conn.countByField Table.Page (Field.EQ (nameof Page.Empty.AuthorId) (string userId))
let! postCount = conn.countByField Table.Post (Field.EQ (nameof Post.Empty.AuthorId) (string userId))
let author = [ Field.EQ (nameof Page.Empty.AuthorId) (string userId) ]
let! pageCount = conn.countByFields Table.Page Any author
let! postCount = conn.countByFields Table.Post Any author
if pageCount + postCount > 0 then
return Error "User has pages or posts; cannot delete"
else
@ -39,24 +39,20 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) =
/// Find a user by their e-mail address for the given web log
let findByEmail (email: string) webLogId =
log.LogTrace "WebLogUser.findByEmail"
let emailParam = Field.EQ (nameof WebLogUser.Empty.Email) email
conn.customSingle
$"""{Document.Query.selectByWebLog Table.WebLogUser}
AND {Query.whereByField emailParam "@email"}"""
(addFieldParam "@email" emailParam [ webLogParam webLogId ])
fromData<WebLogUser>
conn.findFirstByFields
Table.WebLogUser All [ webLogField webLogId; Field.EQ (nameof WebLogUser.Empty.Email) email ]
/// Get all users for the given web log
let findByWebLog webLogId = backgroundTask {
log.LogTrace "WebLogUser.findByWebLog"
let! users = Document.findByWebLog<WebLogUser> Table.WebLogUser webLogId conn
let! users = conn.findByFields<WebLogUser> Table.WebLogUser Any [ webLogField webLogId ]
return users |> List.sortBy _.PreferredName.ToLowerInvariant()
}
/// Find the names of users by their IDs for the given web log
let findNames webLogId (userIds: WebLogUserId list) =
log.LogTrace "WebLogUser.findNames"
let nameSql, nameParams = inClause $"AND data ->> '{nameof WebLogUser.Empty.Id}'" "id" string userIds
let nameSql, nameParams = inClause $"AND data->>'{nameof WebLogUser.Empty.Id}'" "id" string userIds
conn.customList
$"{Document.Query.selectByWebLog Table.WebLogUser} {nameSql}"
(webLogParam webLogId :: nameParams)