From 0032d15c0a4612eac6ef56120e65e9c487f3527c Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Tue, 17 Sep 2024 08:05:30 -0400 Subject: [PATCH] Update for doc lib v4-rc4 --- src/MyWebLog.Data/MyWebLog.Data.fsproj | 6 +- .../Postgres/PostgresCategoryData.fs | 41 +++++++------- src/MyWebLog.Data/Postgres/PostgresHelpers.fs | 21 ------- .../Postgres/PostgresPageData.fs | 17 +++--- .../Postgres/PostgresPostData.fs | 23 ++++---- .../Postgres/PostgresTagMapData.fs | 9 +-- .../Postgres/PostgresThemeData.fs | 2 +- .../Postgres/PostgresWebLogUserData.fs | 6 +- .../SQLite/SQLiteCategoryData.fs | 40 +++++++------ src/MyWebLog.Data/SQLite/SQLiteHelpers.fs | 56 ++----------------- src/MyWebLog.Data/SQLite/SQLitePageData.fs | 20 +++---- src/MyWebLog.Data/SQLite/SQLitePostData.fs | 39 +++++++------ src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs | 9 +-- src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs | 5 +- .../SQLite/SQLiteWebLogUserData.fs | 11 ++-- src/MyWebLog.Domain/MyWebLog.Domain.fsproj | 2 +- 16 files changed, 119 insertions(+), 188 deletions(-) diff --git a/src/MyWebLog.Data/MyWebLog.Data.fsproj b/src/MyWebLog.Data/MyWebLog.Data.fsproj index 03014eb..c6b77a0 100644 --- a/src/MyWebLog.Data/MyWebLog.Data.fsproj +++ b/src/MyWebLog.Data/MyWebLog.Data.fsproj @@ -5,14 +5,14 @@ - - + + - + diff --git a/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs b/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs index 1c1eec5..d991c42 100644 --- a/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs @@ -20,37 +20,41 @@ type PostgresCategoryData(log: ILogger) = log.LogTrace "Category.countTopLevel" Custom.scalar $"""{Query.byContains (Query.count Table.Category)} - AND {Query.whereByFields Any [ Field.NEX (nameof Category.Empty.ParentId) ]}""" + AND {Query.whereByFields Any [ Field.NotExists (nameof Category.Empty.ParentId) ]}""" [ webLogContains webLogId ] toCount + /// Find all categories for the given web log + let findByWebLog webLogId = + log.LogTrace "Category.findByWebLog" + Find.byContains Table.Category (webLogDoc webLogId) + /// Retrieve all categories for the given web log in a DotLiquid-friendly format let findAllForView webLogId = backgroundTask { log.LogTrace "Category.findAllForView" - let! cats = - Custom.list - $"{selectWithCriteria Table.Category} ORDER BY LOWER(data->>'{nameof Category.Empty.Name}')" - [ webLogContains webLogId ] - fromData - let ordered = Utils.orderByHierarchy cats None None [] - let counts = + let! cats = findByWebLog webLogId + let ordered = Utils.orderByHierarchy (cats |> List.sortBy _.Name.ToLowerInvariant()) None None [] + let counts = ordered |> Seq.map (fun it -> // Parent category post counts include posts in subcategories - let catIdSql, catIdParams = + let catIdField = ordered |> Seq.filter (fun cat -> cat.ParentNames |> Array.contains it.Name) |> Seq.map _.Id |> Seq.append (Seq.singleton it.Id) - |> List.ofSeq - |> arrayContains (nameof Post.Empty.CategoryIds) id + |> Seq.map box + |> Field.InArray (nameof Post.Empty.CategoryIds) Table.Post + let query = + (Query.statementWhere + (Query.count Table.Post) + $"""{Query.whereDataContains "@criteria"} AND {Query.whereByFields All [ catIdField ]}""") + .Replace("(*)", $"(DISTINCT data->>'{nameof Post.Empty.Id}')") let postCount = Custom.scalar - $"""SELECT COUNT(DISTINCT data->>'{nameof Post.Empty.Id}') AS it - FROM {Table.Post} - WHERE {Query.whereDataContains "@criteria"} - AND {catIdSql}""" - [ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |}; catIdParams ] + query + (addFieldParams + [ catIdField ] [ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |} ]) toCount |> Async.AwaitTask |> Async.RunSynchronously @@ -71,11 +75,6 @@ type PostgresCategoryData(log: ILogger) = log.LogTrace "Category.findById" Document.findByIdAndWebLog Table.Category catId webLogId - /// Find all categories for the given web log - let findByWebLog webLogId = - log.LogTrace "Category.findByWebLog" - Find.byContains Table.Category (webLogDoc webLogId) - /// Delete a category let delete catId webLogId = backgroundTask { log.LogTrace "Category.delete" diff --git a/src/MyWebLog.Data/Postgres/PostgresHelpers.fs b/src/MyWebLog.Data/Postgres/PostgresHelpers.fs index c14610c..4972ff3 100644 --- a/src/MyWebLog.Data/Postgres/PostgresHelpers.fs +++ b/src/MyWebLog.Data/Postgres/PostgresHelpers.fs @@ -85,27 +85,6 @@ let webLogContains webLogId = let selectWithCriteria tableName = Query.byContains (Query.find tableName) -/// Create the SQL and parameters for an IN clause -let inClause<'T> colNameAndPrefix paramName (items: 'T list) = - if List.isEmpty items then "", [] - else - let mutable idx = 0 - items - |> List.skip 1 - |> List.fold (fun (itemS, itemP) it -> - idx <- idx + 1 - $"{itemS}, @%s{paramName}{idx}", ($"@%s{paramName}{idx}", Sql.string (string it)) :: itemP) - (Seq.ofList items - |> Seq.map (fun it -> - $"%s{colNameAndPrefix} IN (@%s{paramName}0", [ $"@%s{paramName}0", Sql.string (string it) ]) - |> Seq.head) - |> function sql, ps -> $"{sql})", ps - -/// Create the SQL and parameters for match-any array query -let arrayContains<'T> name (valueFunc: 'T -> string) (items: 'T list) = - $"data['{name}'] ?| @{name}Values", - ($"@{name}Values", Sql.stringArray (items |> List.map valueFunc |> Array.ofList)) - /// Get the first result of the given query let tryHead<'T> (query: Task<'T list>) = backgroundTask { let! results = query diff --git a/src/MyWebLog.Data/Postgres/PostgresPageData.fs b/src/MyWebLog.Data/Postgres/PostgresPageData.fs index ba757e4..2c8b113 100644 --- a/src/MyWebLog.Data/Postgres/PostgresPageData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresPageData.fs @@ -111,15 +111,14 @@ type PostgresPageData(log: ILogger) = log.LogTrace "Page.findCurrentPermalink" if List.isEmpty permalinks then return None else - let linkSql, linkParam = arrayContains (nameof Page.Empty.PriorPermalinks) string permalinks - return! - Custom.single - $"""SELECT data->>'{nameof Page.Empty.Permalink}' AS permalink - FROM page - WHERE {Query.whereDataContains "@criteria"} - AND {linkSql}""" - [ webLogContains webLogId; linkParam ] - Map.toPermalink + let linkField = + Field.InArray (nameof Page.Empty.PriorPermalinks) Table.Page (List.map (string >> box) permalinks) + let query = + (Query.statementWhere + (Query.find Table.Page) + $"""{Query.whereDataContains "@criteria"} AND {Query.whereByFields All [ linkField ]}""") + .Replace("SELECT data", $"SELECT data->>'{nameof Page.Empty.Permalink}' AS permalink") + return! Custom.single query (addFieldParams [ linkField ] [ webLogContains webLogId ]) Map.toPermalink } /// Get all complete pages for the given web log diff --git a/src/MyWebLog.Data/Postgres/PostgresPostData.fs b/src/MyWebLog.Data/Postgres/PostgresPostData.fs index c12f0e9..9360cb2 100644 --- a/src/MyWebLog.Data/Postgres/PostgresPostData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresPostData.fs @@ -97,15 +97,14 @@ type PostgresPostData(log: ILogger) = log.LogTrace "Post.findCurrentPermalink" if List.isEmpty permalinks then return None else - let linkSql, linkParam = arrayContains (nameof Post.Empty.PriorPermalinks) string permalinks - return! - Custom.single - $"""SELECT data->>'{nameof Post.Empty.Permalink}' AS permalink - FROM {Table.Post} - WHERE {Query.whereDataContains "@criteria"} - AND {linkSql}""" - [ webLogContains webLogId; linkParam ] - Map.toPermalink + let linkField = + Field.InArray (nameof Post.Empty.PriorPermalinks) Table.Post (List.map (string >> box) permalinks) + let query = + (Query.statementWhere + (Query.find Table.Post) + $"""{Query.whereDataContains "@criteria"} AND {Query.whereByFields All [ linkField ]}""") + .Replace("SELECT data", $"SELECT data->>'{nameof Post.Empty.Permalink}' AS permalink") + return! Custom.single query (addFieldParams [ linkField ] [ webLogContains webLogId ]) Map.toPermalink } /// Get all complete posts for the given web log @@ -122,13 +121,13 @@ type PostgresPostData(log: ILogger) = /// Get a page of categorized posts for the given web log (excludes revisions) let findPageOfCategorizedPosts webLogId (categoryIds: CategoryId list) pageNbr postsPerPage = log.LogTrace "Post.findPageOfCategorizedPosts" - let catSql, catParam = arrayContains (nameof Post.Empty.CategoryIds) string categoryIds + let catIdField = Field.InArray (nameof Post.Empty.CategoryIds) Table.Post (List.map (string >> box) categoryIds) Custom.list $"""{selectWithCriteria Table.Post} - AND {catSql} + AND {Query.whereByFields All [ catIdField ]} {Query.orderBy [ Field.Named $"{nameof Post.Empty.PublishedOn} DESC" ] PostgreSQL} LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}""" - [ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |}; catParam ] + (addFieldParams [ catIdField] [ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |} ]) postWithoutLinks /// Get a page of posts for the given web log (excludes text and revisions) diff --git a/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs b/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs index ca160f3..e74c66c 100644 --- a/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs @@ -6,6 +6,7 @@ open Microsoft.Extensions.Logging open MyWebLog open MyWebLog.Data open Npgsql.FSharp +open Npgsql.Internal.Postgres /// PostgreSQL myWebLog tag mapping data implementation type PostgresTagMapData(log: ILogger) = @@ -36,12 +37,12 @@ type PostgresTagMapData(log: ILogger) = Find.byContainsOrdered Table.TagMap (webLogDoc webLogId) [ Field.Named (nameof TagMap.Empty.Tag) ] /// Find any tag mappings in a list of tags for the given web log - let findMappingForTags tags webLogId = + let findMappingForTags (tags: string list) webLogId = log.LogTrace "TagMap.findMappingForTags" - let tagSql, tagParam = arrayContains (nameof TagMap.Empty.Tag) id tags + let tagField = Field.InArray (nameof TagMap.Empty.Tag) Table.TagMap (List.map box tags) Custom.list - $"{selectWithCriteria Table.TagMap} AND {tagSql}" - [ webLogContains webLogId; tagParam ] + $"{selectWithCriteria Table.TagMap} AND {Query.whereByFields All [ tagField ]}" + (addFieldParams [ tagField ] [ webLogContains webLogId ]) fromData /// Save a tag mapping diff --git a/src/MyWebLog.Data/Postgres/PostgresThemeData.fs b/src/MyWebLog.Data/Postgres/PostgresThemeData.fs index 727aecd..ee81aa6 100644 --- a/src/MyWebLog.Data/Postgres/PostgresThemeData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresThemeData.fs @@ -17,7 +17,7 @@ type PostgresThemeData(log: ILogger) = /// Retrieve all themes (except 'admin'; excludes template text) let all () = log.LogTrace "Theme.all" - let fields = [ Field.NE (nameof Theme.Empty.Id) "admin" ] + let fields = [ Field.NotEqual (nameof Theme.Empty.Id) "admin" ] Custom.list (Query.byFields (Query.find Table.Theme) Any fields + Query.orderBy [ Field.Named (nameof Theme.Empty.Id) ] PostgreSQL) diff --git a/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs b/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs index 3507d83..37d1258 100644 --- a/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs @@ -55,11 +55,11 @@ type PostgresWebLogUserData(log: ILogger) = /// Find the names of users by their IDs for the given web log let findNames webLogId (userIds: WebLogUserId list) = backgroundTask { log.LogTrace "WebLogUser.findNames" - let idSql, idParams = inClause $"AND data->>'{nameof WebLogUser.Empty.Id}'" "id" userIds + let idField = Field.In (nameof WebLogUser.Empty.Id) (List.map (string >> box) userIds) let! users = Custom.list - $"{selectWithCriteria Table.WebLogUser} {idSql}" - (webLogContains webLogId :: idParams) + $"{selectWithCriteria Table.WebLogUser} AND {Query.whereByFields All [ idField ]}" + (addFieldParams [ idField ] [ webLogContains webLogId ]) fromData return users |> List.map (fun u -> { Name = string u.Id; Value = u.DisplayName }) } diff --git a/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs b/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs index 8faf3ae..29c96e2 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs @@ -25,7 +25,7 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge /// Count all top-level categories for the given web log let countTopLevel webLogId = backgroundTask { log.LogTrace "Category.countTopLevel" - let! count = conn.countByFields Table.Category All [ webLogField webLogId; Field.NEX parentIdField ] + let! count = conn.countByFields Table.Category All [ webLogField webLogId; Field.NotExists parentIdField ] return int count } @@ -43,20 +43,20 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge ordered |> Seq.map (fun it -> backgroundTask { // Parent category post counts include posts in subcategories - let catSql, catParams = + let childCats = ordered |> Seq.filter (fun cat -> cat.ParentNames |> Array.contains it.Name) |> Seq.map _.Id |> Seq.append (Seq.singleton it.Id) - |> List.ofSeq - |> inJsonArray Table.Post (nameof Post.Empty.CategoryIds) "catId" - let query = $""" - SELECT COUNT(DISTINCT data->>'{nameof Post.Empty.Id}') - FROM {Table.Post} - WHERE {Document.Query.whereByWebLog} - AND data->>'{nameof Post.Empty.Status}' = '{string Published}' - AND {catSql}""" - let! postCount = conn.customScalar query (webLogParam webLogId :: catParams) toCount + |> Seq.map box + let fields = + [ webLogField webLogId + Field.Equal (nameof Post.Empty.Status) (string Published) + Field.InArray (nameof Post.Empty.CategoryIds) Table.Post childCats ] + let query = + (Query.statementWhere (Query.count Table.Post) (Query.whereByFields All fields)) + .Replace("(*)", $"(DISTINCT data->>'{nameof Post.Empty.Id}')") + let! postCount = conn.customScalar query (addFieldParams fields []) toCount return it.Id, int postCount }) |> Task.WhenAll @@ -80,24 +80,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.countByFields Table.Category Any [ Field.EQ parentIdField (string catId) ] + let! children = conn.countByFields Table.Category Any [ Field.Equal parentIdField (string catId) ] if children > 0L then - let parent = [ Field.EQ parentIdField (string catId) ] + let parent = [ Field.Equal parentIdField (string catId) ] match cat.ParentId with | 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 fields = [ webLogField webLogId; Field.InArray catIdField Table.Post [ string catId ] ] + let query = + (Query.statementWhere (Query.find Table.Post) (Query.whereByFields All fields)) + .Replace("SELECT data", $"SELECT data->>'{nameof Post.Empty.Id}', data->'{catIdField}'") let! posts = conn.customList - $"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}') - WHERE json_each.value = @id)" - [ idParam catId; webLogParam webLogId ] + query + (addFieldParams fields []) (fun rdr -> rdr.GetString 0, Utils.deserialize ser (rdr.GetString 1)) for postId, cats in posts do do! conn.patchById diff --git a/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs b/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs index f071b93..13e90e8 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs @@ -82,39 +82,6 @@ let instantParam = let maybeInstant = Option.map instantParam >> maybe -/// Create the SQL and parameters for an EXISTS applied to a JSON array -let inJsonArray<'T> table jsonField paramName (items: 'T list) = - if List.isEmpty items then "", [] - else - let mutable idx = 0 - items - |> List.skip 1 - |> List.fold (fun (itemS, itemP) it -> - idx <- idx + 1 - $"{itemS}, @%s{paramName}{idx}", (SqliteParameter($"@%s{paramName}{idx}", string it) :: itemP)) - (Seq.ofList items - |> Seq.map (fun it -> $"(@%s{paramName}0", [ SqliteParameter($"@%s{paramName}0", string it) ]) - |> Seq.head) - |> function - sql, ps -> - $"EXISTS (SELECT 1 FROM json_each(%s{table}.data, '$.%s{jsonField}') WHERE value IN {sql}))", ps - -/// Create the SQL and parameters for an IN clause -let inClause<'T> colNameAndPrefix paramName (valueFunc: 'T -> string) (items: 'T list) = - if List.isEmpty items then "", [] - else - let mutable idx = 0 - items - |> List.skip 1 - |> List.fold (fun (itemS, itemP) it -> - idx <- idx + 1 - $"{itemS}, @%s{paramName}{idx}", (SqliteParameter ($"@%s{paramName}{idx}", valueFunc it) :: itemP)) - (Seq.ofList items - |> Seq.map (fun it -> - $"%s{colNameAndPrefix} IN (@%s{paramName}0", [ SqliteParameter ($"@%s{paramName}0", valueFunc it) ]) - |> Seq.head) - |> function sql, ps -> $"{sql})", ps - /// Functions to map domain items from a data reader module Map = @@ -219,8 +186,6 @@ module Map = open BitBadger.Documents -open BitBadger.Documents.Sqlite -open BitBadger.Documents.Sqlite.WithConn /// Create a named parameter let sqlParam name (value: obj) = @@ -232,26 +197,15 @@ let webLogParam (webLogId: WebLogId) = /// Create a field for an ID value let idField<'T> (idValue: 'T) = - { Field.EQ "Id" (string idValue) with ParameterName = Some "@id" } + { Field.Equal "Id" (string idValue) with ParameterName = Some "@id" } /// Create a web log field let webLogField (webLogId: WebLogId) = - { Field.EQ "WebLogId" (string webLogId) with ParameterName = Some "@webLogId" } + { Field.Equal "WebLogId" (string webLogId) with ParameterName = Some "@webLogId" } -/// Functions for manipulating documents -module Document = - - /// Queries to assist with document manipulation - module Query = - - /// Fragment to add a web log ID condition to a WHERE clause (parameter @webLogId) - let 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.statementWhere (Query.find table) whereByWebLog +open BitBadger.Documents.Sqlite +open BitBadger.Documents.Sqlite.WithConn /// Functions to support revisions module Revisions = @@ -270,7 +224,7 @@ module Revisions = $"SELECT pr.* FROM %s{revTable} pr INNER JOIN %s{entityTable} p ON p.data->>'Id' = pr.{entityTable}_id - WHERE p.{Document.Query.whereByWebLog} + WHERE p.{Query.whereByFields Any [ webLogField webLogId ]} ORDER BY as_of DESC" [ webLogParam webLogId ] (fun rdr -> keyFunc (Map.getString $"{entityTable}_id" rdr), Map.toRevision rdr) diff --git a/src/MyWebLog.Data/SQLite/SQLitePageData.fs b/src/MyWebLog.Data/SQLite/SQLitePageData.fs index de3144e..73f0b54 100644 --- a/src/MyWebLog.Data/SQLite/SQLitePageData.fs +++ b/src/MyWebLog.Data/SQLite/SQLitePageData.fs @@ -68,7 +68,7 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) = /// Count all pages shown in the page list for the given web log let countListed webLogId = backgroundTask { log.LogTrace "Page.countListed" - let! count = conn.countByFields Table.Page All [ webLogField webLogId; Field.EQ pgListName true ] + let! count = conn.countByFields Table.Page All [ webLogField webLogId; Field.Equal pgListName true ] return int count } @@ -107,20 +107,20 @@ 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 fields = [ webLogField webLogId; Field.EQ linkName (string permalink) ] + let fields = [ webLogField webLogId; Field.Equal linkName (string permalink) ] conn.customSingle (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 - FROM {Table.Page} - WHERE {Document.Query.whereByWebLog} AND {linkSql}" - (webLogParam webLogId :: linkParams) - Map.toPermalink + let fields = + [ webLogField webLogId + Field.InArray (nameof Page.Empty.PriorPermalinks) Table.Page (List.map (string >> box) permalinks) ] + let query = + (Query.statementWhere (Query.find Table.Page) (Query.whereByFields All fields)) + .Replace("SELECT data", $"SELECT data->>'{linkName}' AS permalink") + conn.customSingle query (addFieldParams fields []) Map.toPermalink /// Get all complete pages for the given web log let findFullByWebLog webLogId = backgroundTask { @@ -133,7 +133,7 @@ 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 ] + let fields = [ webLogField webLogId; Field.Equal pgListName true ] conn.customList (sortedPages fields) (addFieldParams fields []) (fun rdr -> { fromData rdr with Text = "" }) diff --git a/src/MyWebLog.Data/SQLite/SQLitePostData.fs b/src/MyWebLog.Data/SQLite/SQLitePostData.fs index 66659d5..c8f827d 100644 --- a/src/MyWebLog.Data/SQLite/SQLitePostData.fs +++ b/src/MyWebLog.Data/SQLite/SQLitePostData.fs @@ -31,7 +31,10 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = } /// The SELECT statement to retrieve posts with a web log ID parameter - let postByWebLog = Document.Query.selectByWebLog Table.Post + let postByWebLog = + Query.statementWhere + (Query.find Table.Post) + (Query.whereByFields Any [ { Field.Equal "WebLogId" "" with ParameterName = Some "@webLogId" } ]) /// Return a post with no revisions or prior permalinks let postWithoutLinks rdr = @@ -62,7 +65,7 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = /// Count posts in a status for the given web log let countByStatus (status: PostStatus) webLogId = backgroundTask { log.LogTrace "Post.countByStatus" - let! count = conn.countByFields Table.Post All [ webLogField webLogId; Field.EQ statName (string status) ] + let! count = conn.countByFields Table.Post All [ webLogField webLogId; Field.Equal statName (string status) ] return int count } @@ -77,7 +80,7 @@ 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 fields = [ webLogField webLogId; Field.EQ linkName (string permalink) ] + let fields = [ webLogField webLogId; Field.Equal linkName (string permalink) ] conn.customSingle (Query.byFields (Query.find Table.Post) All fields) (addFieldParams fields []) postWithoutLinks @@ -111,13 +114,13 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = /// Find the current permalink from a list of potential prior permalinks for the given web log let findCurrentPermalink (permalinks: Permalink list) webLogId = log.LogTrace "Post.findCurrentPermalink" - let linkSql, linkParams = inJsonArray Table.Post (nameof Post.Empty.PriorPermalinks) "link" permalinks - conn.customSingle - $"SELECT data->>'{linkName}' AS permalink - FROM {Table.Post} - WHERE {Document.Query.whereByWebLog} AND {linkSql}" - (webLogParam webLogId :: linkParams) - Map.toPermalink + let fields = + [ webLogField webLogId + Field.InArray (nameof Post.Empty.PriorPermalinks) Table.Post (List.map (string >> box) permalinks) ] + let query = + (Query.statementWhere (Query.find Table.Post) (Query.whereByFields All fields)) + .Replace("SELECT data", $"SELECT data->>'{linkName}' AS permalink") + conn.customSingle query (addFieldParams fields []) Map.toPermalink /// Get all complete posts for the given web log let findFullByWebLog webLogId = backgroundTask { @@ -130,12 +133,12 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = /// Get a page of categorized posts for the given web log (excludes revisions) let findPageOfCategorizedPosts webLogId (categoryIds: CategoryId list) pageNbr postsPerPage = log.LogTrace "Post.findPageOfCategorizedPosts" - let catSql, catParams = inJsonArray Table.Post (nameof Post.Empty.CategoryIds) "catId" categoryIds + let catIdField = Field.InArray (nameof Post.Empty.CategoryIds) Table.Post (List.map (string >> box) categoryIds) conn.customList - $"""{publishedPostByWebLog} AND {catSql} + $"""{publishedPostByWebLog} AND {Query.whereByFields Any [ catIdField ]} {Query.orderBy [ Field.Named $"{publishName} DESC" ] SQLite} LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}""" - (webLogParam webLogId :: catParams) + (addFieldParams [ webLogField webLogId; catIdField ] []) postWithoutLinks /// Get a page of posts for the given web log (excludes text and revisions) @@ -162,12 +165,12 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = /// Get a page of tagged posts for the given web log (excludes revisions) let findPageOfTaggedPosts webLogId (tag : string) pageNbr postsPerPage = log.LogTrace "Post.findPageOfTaggedPosts" - let tagSql, tagParams = inJsonArray Table.Post (nameof Post.Empty.Tags) "tag" [ tag ] + let tagField = Field.InArray (nameof Post.Empty.Tags) Table.Post [ tag ] conn.customList - $"""{publishedPostByWebLog} AND {tagSql} + $"""{publishedPostByWebLog} AND {Query.whereByFields Any [ tagField ]} {Query.orderBy [ Field.Named $"{publishName} DESC" ] SQLite} LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}""" - (webLogParam webLogId :: tagParams) + (addFieldParams [ webLogField webLogId; tagField ] []) postWithoutLinks /// Find the next newest and oldest post from a publish date for the given web log @@ -176,8 +179,8 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = let adjacent op order = let fields = [ webLogField webLogId - Field.EQ (nameof Post.Empty.Status) (string Published) - (if op = "<" then Field.LT else Field.GT) publishName (instantParam publishedOn) + Field.Equal (nameof Post.Empty.Status) (string Published) + (if op = "<" then Field.Less else Field.Greater) publishName (instantParam publishedOn) ] conn.customSingle (Query.byFields (Query.find Table.Post) All fields diff --git a/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs b/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs index 6638bb4..cbd8007 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs @@ -29,7 +29,7 @@ type SQLiteTagMapData(conn: SqliteConnection, log: ILogger) = let findByUrlValue (urlValue: string) webLogId = log.LogTrace "TagMap.findByUrlValue" conn.findFirstByFields - Table.TagMap All [ webLogField webLogId; Field.EQ (nameof TagMap.Empty.UrlValue) urlValue ] + Table.TagMap All [ webLogField webLogId; Field.Equal (nameof TagMap.Empty.UrlValue) urlValue ] /// Get all tag mappings for the given web log let findByWebLog webLogId = @@ -39,11 +39,8 @@ type SQLiteTagMapData(conn: SqliteConnection, log: ILogger) = /// 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 - conn.customList - $"{Document.Query.selectByWebLog Table.TagMap} {mapSql}" - (webLogParam webLogId :: mapParams) - fromData + conn.findByFields + Table.TagMap All [ webLogField webLogId; Field.In (nameof TagMap.Empty.Tag) (List.map box tags) ] /// Save a tag mapping let save (tagMap: TagMap) = diff --git a/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs b/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs index c1f1496..40b7f6b 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs @@ -23,7 +23,8 @@ type SQLiteWebLogData(conn: SqliteConnection, log: ILogger) = /// Delete a web log by its ID let delete webLogId = log.LogTrace "WebLog.delete" - let webLogMatches = Query.whereByFields Any [ { Field.EQ "WebLogId" "" with ParameterName = Some "@webLogId" } ] + let webLogMatches = + Query.whereByFields Any [ { Field.Equal "WebLogId" "" with ParameterName = Some "@webLogId" } ] let subQuery table = $"(SELECT data->>'Id' FROM {table} WHERE {webLogMatches})" Custom.nonQuery $"""{Query.delete Table.PostComment} WHERE data ->> 'PostId' IN {subQuery Table.Post}; @@ -41,7 +42,7 @@ type SQLiteWebLogData(conn: SqliteConnection, log: ILogger) = /// Find a web log by its host (URL base) let findByHost (url: string) = log.LogTrace "WebLog.findByHost" - conn.findFirstByFields Table.WebLog Any [ Field.EQ (nameof WebLog.Empty.UrlBase) url ] + conn.findFirstByFields Table.WebLog Any [ Field.Equal (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 eb2334e..4390ef9 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteWebLogUserData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteWebLogUserData.fs @@ -25,7 +25,7 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) = log.LogTrace "WebLogUser.delete" match! findById userId webLogId with | Some _ -> - let author = [ Field.EQ (nameof Page.Empty.AuthorId) (string userId) ] + let author = [ Field.Equal (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 @@ -40,7 +40,7 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) = let findByEmail (email: string) webLogId = log.LogTrace "WebLogUser.findByEmail" conn.findFirstByFields - Table.WebLogUser All [ webLogField webLogId; Field.EQ (nameof WebLogUser.Empty.Email) email ] + Table.WebLogUser All [ webLogField webLogId; Field.Equal (nameof WebLogUser.Empty.Email) email ] /// Get all users for the given web log let findByWebLog webLogId = backgroundTask { @@ -52,10 +52,11 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) = /// 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 fields = [ webLogField webLogId; Field.In (nameof WebLogUser.Empty.Id) (List.map (string >> box) userIds) ] + let query = Query.statementWhere (Query.find Table.WebLogUser) (Query.whereByFields All fields) conn.customList - $"{Document.Query.selectByWebLog Table.WebLogUser} {nameSql}" - (webLogParam webLogId :: nameParams) + query + (addFieldParams fields []) (fun rdr -> let user = fromData rdr { Name = string user.Id; Value = user.DisplayName }) diff --git a/src/MyWebLog.Domain/MyWebLog.Domain.fsproj b/src/MyWebLog.Domain/MyWebLog.Domain.fsproj index 7682c35..8e647aa 100644 --- a/src/MyWebLog.Domain/MyWebLog.Domain.fsproj +++ b/src/MyWebLog.Domain/MyWebLog.Domain.fsproj @@ -10,7 +10,7 @@ - +