From fbc4e891bdf4ae808b0f056e86801fd833f5e46d Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Mon, 19 Aug 2024 22:23:22 -0400 Subject: [PATCH] Integrate v4 document library for SQLite - Eliminate warnings for PostgreSQL --- .../Postgres/PostgresCategoryData.fs | 5 +-- .../SQLite/SQLiteCategoryData.fs | 28 ++++++------ src/MyWebLog.Data/SQLite/SQLiteHelpers.fs | 38 ++++++---------- src/MyWebLog.Data/SQLite/SQLitePageData.fs | 45 +++++++++---------- src/MyWebLog.Data/SQLite/SQLitePostData.fs | 42 +++++++++-------- src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs | 16 +++---- src/MyWebLog.Data/SQLite/SQLiteThemeData.fs | 6 +-- src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs | 27 ++++++----- .../SQLite/SQLiteWebLogUserData.fs | 22 ++++----- 9 files changed, 102 insertions(+), 127 deletions(-) diff --git a/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs b/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs index 837f810..e93f364 100644 --- a/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs @@ -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 diff --git a/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs b/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs index 2bd61bf..47f0541 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs @@ -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 Table.Category webLogId conn + conn.findByFields 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 Table.Category catId webLogId conn + conn.findFirstByFields 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 ser (rdr.GetString 1)) diff --git a/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs b/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs index 0ee1c89..48b3ca7 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs @@ -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,33 +246,17 @@ 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 @@ -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 ] diff --git a/src/MyWebLog.Data/SQLite/SQLitePageData.fs b/src/MyWebLog.Data/SQLite/SQLitePageData.fs index d51d0a1..f12d61b 100644 --- a/src/MyWebLog.Data/SQLite/SQLitePageData.fs +++ b/src/MyWebLog.Data/SQLite/SQLitePageData.fs @@ -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 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 Table.Page pageId webLogId conn with + match! conn.findFirstByFields 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 Table.Page pageId webLogId conn with + match! conn.findFirstByFields 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 Table.Page webLogId conn + let! pages = conn.findByFields 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 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 diff --git a/src/MyWebLog.Data/SQLite/SQLitePostData.fs b/src/MyWebLog.Data/SQLite/SQLitePostData.fs index bae81ff..414048f 100644 --- a/src/MyWebLog.Data/SQLite/SQLitePostData.fs +++ b/src/MyWebLog.Data/SQLite/SQLitePostData.fs @@ -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 Table.Post postId webLogId conn with + match! conn.findFirstByFields 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 Table.Post postId webLogId conn with + match! conn.findFirstByFields 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 Table.Post webLogId conn + let! posts = conn.findByFields 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 } diff --git a/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs b/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs index 1c621ee..6638bb4 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs @@ -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 Table.TagMap tagMapId webLogId conn + conn.findFirstByFields 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 + conn.findFirstByFields + 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 Table.TagMap webLogId conn + conn.findByFields 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) diff --git a/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs b/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs index 0717dac..ff205f5 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs @@ -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 = diff --git a/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs b/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs index cb97c8b..c1f1496 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs @@ -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 Table.WebLog (Field.EQ (nameof WebLog.Empty.UrlBase) url) + conn.findFirstByFields Table.WebLog Any [ Field.EQ (nameof WebLog.Empty.UrlBase) url ] /// Find a web log by its ID let findById webLogId = diff --git a/src/MyWebLog.Data/SQLite/SQLiteWebLogUserData.fs b/src/MyWebLog.Data/SQLite/SQLiteWebLogUserData.fs index 68702d3..eb2334e 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteWebLogUserData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteWebLogUserData.fs @@ -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 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 Table.WebLogUser userId webLogId conn + conn.findFirstByFields 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 + 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 Table.WebLogUser webLogId conn + let! users = conn.findByFields 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)