From cc3e41ddc5b9e4858202701bfad455e290d351b9 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Thu, 22 Aug 2024 23:00:25 -0400 Subject: [PATCH] Incorporate doc lib v4 ordering --- src/MyWebLog.Data/MyWebLog.Data.fsproj | 8 +- src/MyWebLog.Data/Postgres/PostgresHelpers.fs | 2 +- .../Postgres/PostgresPageData.fs | 14 +- .../Postgres/PostgresPostData.fs | 41 +++-- .../Postgres/PostgresThemeData.fs | 8 +- .../Postgres/PostgresWebLogUserData.fs | 6 +- .../SQLite/SQLiteCategoryData.fs | 14 +- src/MyWebLog.Data/SQLite/SQLiteHelpers.fs | 6 - src/MyWebLog.Data/SQLite/SQLitePageData.fs | 29 ++-- src/MyWebLog.Data/SQLite/SQLitePostData.fs | 55 +++--- src/MyWebLog.Data/SQLite/SQLiteThemeData.fs | 10 +- src/MyWebLog.Data/SQLiteData.fs | 162 +++++++++--------- src/MyWebLog.Domain/MyWebLog.Domain.fsproj | 2 +- src/MyWebLog.Tests/MyWebLog.Tests.fsproj | 2 +- src/MyWebLog/MyWebLog.fsproj | 6 +- 15 files changed, 184 insertions(+), 181 deletions(-) diff --git a/src/MyWebLog.Data/MyWebLog.Data.fsproj b/src/MyWebLog.Data/MyWebLog.Data.fsproj index cb6c042..03014eb 100644 --- a/src/MyWebLog.Data/MyWebLog.Data.fsproj +++ b/src/MyWebLog.Data/MyWebLog.Data.fsproj @@ -5,9 +5,9 @@ - - - + + + @@ -15,7 +15,7 @@ - + diff --git a/src/MyWebLog.Data/Postgres/PostgresHelpers.fs b/src/MyWebLog.Data/Postgres/PostgresHelpers.fs index 1e2b483..c14610c 100644 --- a/src/MyWebLog.Data/Postgres/PostgresHelpers.fs +++ b/src/MyWebLog.Data/Postgres/PostgresHelpers.fs @@ -83,7 +83,7 @@ let webLogContains webLogId = /// A SQL string to select data from a table with the given JSON document contains criteria let selectWithCriteria tableName = - $"""{Query.find tableName} WHERE {Query.whereDataContains "@criteria"}""" + Query.byContains (Query.find tableName) /// Create the SQL and parameters for an IN clause let inClause<'T> colNameAndPrefix paramName (items: 'T list) = diff --git a/src/MyWebLog.Data/Postgres/PostgresPageData.fs b/src/MyWebLog.Data/Postgres/PostgresPageData.fs index d14da27..ba757e4 100644 --- a/src/MyWebLog.Data/Postgres/PostgresPageData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresPageData.fs @@ -33,6 +33,10 @@ type PostgresPageData(log: ILogger) = log.LogTrace "Page.pageExists" Document.existsByWebLog Table.Page pageId webLogId + /// The query to get all pages ordered by title + let sortedPages = + selectWithCriteria Table.Page + Query.orderBy [ Field.Named $"i:{nameof Page.Empty.Title}" ] PostgreSQL + // IMPLEMENTATION FUNCTIONS /// Add a page @@ -47,7 +51,7 @@ type PostgresPageData(log: ILogger) = let all webLogId = log.LogTrace "Page.all" Custom.list - $"{selectWithCriteria Table.Page} ORDER BY LOWER(data->>'{nameof Page.Empty.Title}')" + sortedPages [ webLogContains webLogId ] (fun row -> { fromData row with Text = ""; Metadata = []; PriorPermalinks = [] }) @@ -133,17 +137,13 @@ type PostgresPageData(log: ILogger) = let findListed webLogId = log.LogTrace "Page.findListed" Custom.list - $"{selectWithCriteria Table.Page} ORDER BY LOWER(data->>'{nameof Page.Empty.Title}')" - [ jsonParam "@criteria" {| webLogDoc webLogId with IsInPageList = true |} ] - pageWithoutText + sortedPages [ jsonParam "@criteria" {| webLogDoc webLogId with IsInPageList = true |} ] pageWithoutText /// Get a page of pages for the given web log (without revisions) let findPageOfPages webLogId pageNbr = log.LogTrace "Page.findPageOfPages" Custom.list - $"{selectWithCriteria Table.Page} - ORDER BY LOWER(data->>'{nameof Page.Empty.Title}') - LIMIT @pageSize OFFSET @toSkip" + $"{sortedPages} LIMIT @pageSize OFFSET @toSkip" [ webLogContains webLogId; "@pageSize", Sql.int 26; "@toSkip", Sql.int ((pageNbr - 1) * 25) ] (fun row -> { fromData row with Metadata = []; PriorPermalinks = [] }) diff --git a/src/MyWebLog.Data/Postgres/PostgresPostData.fs b/src/MyWebLog.Data/Postgres/PostgresPostData.fs index b986280..c12f0e9 100644 --- a/src/MyWebLog.Data/Postgres/PostgresPostData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresPostData.fs @@ -124,21 +124,24 @@ type PostgresPostData(log: ILogger) = log.LogTrace "Post.findPageOfCategorizedPosts" let catSql, catParam = arrayContains (nameof Post.Empty.CategoryIds) string categoryIds Custom.list - $"{selectWithCriteria Table.Post} - AND {catSql} - ORDER BY data->>'{nameof Post.Empty.PublishedOn}' DESC - LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" + $"""{selectWithCriteria Table.Post} + AND {catSql} + {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 ] postWithoutLinks /// Get a page of posts for the given web log (excludes text and revisions) let findPageOfPosts webLogId pageNbr postsPerPage = log.LogTrace "Post.findPageOfPosts" + let order = + Query.orderBy + [ Field.Named $"{nameof Post.Empty.PublishedOn} DESC NULLS FIRST" + Field.Named (nameof Post.Empty.UpdatedOn) ] + PostgreSQL Custom.list - $"{selectWithCriteria Table.Post} - ORDER BY data->>'{nameof Post.Empty.PublishedOn}' DESC NULLS FIRST, - data->>'{nameof Post.Empty.UpdatedOn}' - LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" + $"{selectWithCriteria Table.Post}{order} + LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" [ webLogContains webLogId ] postWithoutText @@ -146,9 +149,9 @@ type PostgresPostData(log: ILogger) = let findPageOfPublishedPosts webLogId pageNbr postsPerPage = log.LogTrace "Post.findPageOfPublishedPosts" Custom.list - $"{selectWithCriteria Table.Post} - ORDER BY data->>'{nameof Post.Empty.PublishedOn}' DESC - LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" + $"""{selectWithCriteria Table.Post} + {Query.orderBy [ Field.Named $"{nameof Post.Empty.PublishedOn} DESC" ] PostgreSQL} + LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}""" [ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |} ] postWithoutLinks @@ -156,10 +159,10 @@ type PostgresPostData(log: ILogger) = let findPageOfTaggedPosts webLogId (tag: string) pageNbr postsPerPage = log.LogTrace "Post.findPageOfTaggedPosts" Custom.list - $"{selectWithCriteria Table.Post} - AND data['{nameof Post.Empty.Tags}'] @> @tag - ORDER BY data->>'{nameof Post.Empty.PublishedOn}' DESC - LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" + $"""{selectWithCriteria Table.Post} + AND data['{nameof Post.Empty.Tags}'] @> @tag + {Query.orderBy [ Field.Named $"{nameof Post.Empty.PublishedOn} DESC" ] PostgreSQL} + LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}""" [ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |}; jsonParam "@tag" [| tag |] ] postWithoutLinks @@ -170,10 +173,10 @@ type PostgresPostData(log: ILogger) = [ jsonParam "@criteria" {| webLogDoc webLogId with Status = Published |} "@publishedOn", Sql.timestamptz (publishedOn.ToDateTimeOffset()) ] let query op direction = - $"{selectWithCriteria Table.Post} - AND (data->>'{nameof Post.Empty.PublishedOn}')::timestamp with time zone %s{op} @publishedOn - ORDER BY data->>'{nameof Post.Empty.PublishedOn}' %s{direction} - LIMIT 1" + $"""{selectWithCriteria Table.Post} + AND (data->>'{nameof Post.Empty.PublishedOn}')::timestamp with time zone %s{op} @publishedOn + {Query.orderBy [ Field.Named $"{nameof Post.Empty.PublishedOn} %s{direction}" ] PostgreSQL} + LIMIT 1""" let! older = Custom.list (query "<" "DESC") (queryParams ()) postWithoutLinks let! newer = Custom.list (query ">" "") (queryParams ()) postWithoutLinks return List.tryHead older, List.tryHead newer diff --git a/src/MyWebLog.Data/Postgres/PostgresThemeData.fs b/src/MyWebLog.Data/Postgres/PostgresThemeData.fs index 6942f3b..727aecd 100644 --- a/src/MyWebLog.Data/Postgres/PostgresThemeData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresThemeData.fs @@ -17,11 +17,11 @@ 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" ] Custom.list - $"{Query.find Table.Theme} - WHERE data->>'{nameof Theme.Empty.Id}' <> 'admin' - ORDER BY data->>'{nameof Theme.Empty.Id}'" - [] + (Query.byFields (Query.find Table.Theme) Any fields + + Query.orderBy [ Field.Named (nameof Theme.Empty.Id) ] PostgreSQL) + (addFieldParams fields []) withoutTemplateText /// Does a given theme exist? diff --git a/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs b/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs index caca8ba..3507d83 100644 --- a/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs @@ -49,10 +49,8 @@ type PostgresWebLogUserData(log: ILogger) = /// Get all users for the given web log let findByWebLog webLogId = log.LogTrace "WebLogUser.findByWebLog" - Custom.list - $"{selectWithCriteria Table.WebLogUser} ORDER BY LOWER(data->>'{nameof WebLogUser.Empty.PreferredName}')" - [ webLogContains webLogId ] - fromData + Find.byContainsOrdered + Table.WebLogUser (webLogDoc webLogId) [ Field.Named $"i:{nameof WebLogUser.Empty.PreferredName}" ] /// Find the names of users by their IDs for the given web log let findNames webLogId (userIds: WebLogUserId list) = backgroundTask { diff --git a/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs b/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs index 47f0541..8faf3ae 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs @@ -16,16 +16,18 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge let parentIdField = nameof Category.Empty.ParentId /// Count all categories for the given web log - let countAll webLogId = + let countAll webLogId = backgroundTask { log.LogTrace "Category.countAll" - Document.countByWebLog Table.Category webLogId conn + let! count = conn.countByFields Table.Category Any [ webLogField webLogId ] + return int count + } /// Count all top-level categories for the given web log - let countTopLevel webLogId = + let countTopLevel webLogId = backgroundTask { log.LogTrace "Category.countTopLevel" - let fields = [ webLogField webLogId; Field.NEX parentIdField ] - conn.customScalar - (Query.byFields (Query.count Table.Category) All fields) (addFieldParams fields []) (toCount >> int) + let! count = conn.countByFields Table.Category All [ webLogField webLogId; Field.NEX parentIdField ] + return int count + } /// Find all categories for the given web log let findByWebLog webLogId = diff --git a/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs b/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs index 48b3ca7..f071b93 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteHelpers.fs @@ -251,12 +251,6 @@ module Document = /// A query to select from a table by its web log ID let selectByWebLog table = 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 [ webLogField webLogId ] conn - return int count - } /// Functions to support revisions diff --git a/src/MyWebLog.Data/SQLite/SQLitePageData.fs b/src/MyWebLog.Data/SQLite/SQLitePageData.fs index f12d61b..de3144e 100644 --- a/src/MyWebLog.Data/SQLite/SQLitePageData.fs +++ b/src/MyWebLog.Data/SQLite/SQLitePageData.fs @@ -17,8 +17,10 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) = /// The JSON field name for the "is in page list" flag let pgListName = nameof Page.Empty.IsInPageList - /// The JSON field for the title of the page - let titleField = $"data->>'{nameof Page.Empty.Title}'" + /// Query to return pages sorted by title + let sortedPages fields = + Query.byFields (Query.find Table.Page) All fields + + Query.orderBy [ Field.Named $"i:{nameof Page.Empty.Title}" ] SQLite // SUPPORT FUNCTIONS @@ -52,21 +54,23 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) = log.LogTrace "Page.all" let field = [ webLogField webLogId ] conn.customList - $"{Query.byFields (Query.find Table.Page) Any field} ORDER BY LOWER({titleField})" + (sortedPages field) (addFieldParams field []) (fun rdr -> { fromData rdr with Text = ""; Metadata = []; PriorPermalinks = [] }) /// Count all pages for the given web log - let countAll webLogId = + let countAll webLogId = backgroundTask { log.LogTrace "Page.countAll" - Document.countByWebLog Table.Page webLogId conn + let! count = conn.countByFields Table.Page Any [ webLogField webLogId ] + return int count + } /// Count all pages shown in the page list for the given web log - let countListed webLogId = + let countListed webLogId = backgroundTask { log.LogTrace "Page.countListed" - let fields = [ webLogField webLogId; Field.EQ pgListName true ] - conn.customScalar - (Query.byFields (Query.count Table.Page) All fields) (addFieldParams fields []) (toCount >> int) + let! count = conn.countByFields Table.Page All [ webLogField webLogId; Field.EQ pgListName true ] + return int count + } /// Find a page by its ID (without revisions and prior permalinks) let findById (pageId: PageId) webLogId = backgroundTask { @@ -131,17 +135,14 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) = log.LogTrace "Page.findListed" let fields = [ webLogField webLogId; Field.EQ pgListName true ] conn.customList - $"{Query.byFields (Query.find Table.Page) All fields} ORDER BY LOWER({titleField})" - (addFieldParams fields []) - (fun rdr -> { fromData rdr with Text = "" }) + (sortedPages fields) (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 - $"{Query.byFields (Query.find Table.Page) Any field} ORDER BY LOWER({titleField}) - LIMIT @pageSize OFFSET @toSkip" + $"{sortedPages field} LIMIT @pageSize OFFSET @toSkip" (addFieldParams field [ sqlParam "@pageSize" 26; sqlParam "@toSkip" ((pageNbr - 1) * 25) ]) (fun rdr -> { pageWithoutLinks rdr with Metadata = [] }) diff --git a/src/MyWebLog.Data/SQLite/SQLitePostData.fs b/src/MyWebLog.Data/SQLite/SQLitePostData.fs index 414048f..66659d5 100644 --- a/src/MyWebLog.Data/SQLite/SQLitePostData.fs +++ b/src/MyWebLog.Data/SQLite/SQLitePostData.fs @@ -16,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 publishName = nameof Post.Empty.PublishedOn /// The name of the JSON field for the post's status let statName = nameof Post.Empty.Status @@ -60,11 +60,11 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = } /// Count posts in a status for the given web log - let countByStatus (status: PostStatus) webLogId = + let countByStatus (status: PostStatus) webLogId = backgroundTask { log.LogTrace "Post.countByStatus" - let fields = [ webLogField webLogId; Field.EQ statName (string status) ] - conn.customScalar - (Query.byFields (Query.count Table.Post) All fields) (addFieldParams fields []) (toCount >> int) + let! count = conn.countByFields Table.Post All [ webLogField webLogId; Field.EQ statName (string status) ] + return int count + } /// Find a post by its ID for the given web log (excluding revisions) let findById (postId: PostId) webLogId = backgroundTask { @@ -132,19 +132,20 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = log.LogTrace "Post.findPageOfCategorizedPosts" let catSql, catParams = inJsonArray Table.Post (nameof Post.Empty.CategoryIds) "catId" categoryIds conn.customList - $"{publishedPostByWebLog} AND {catSql} - ORDER BY {publishField} DESC - LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" + $"""{publishedPostByWebLog} AND {catSql} + {Query.orderBy [ Field.Named $"{publishName} DESC" ] SQLite} + LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}""" (webLogParam webLogId :: catParams) postWithoutLinks /// Get a page of posts for the given web log (excludes text and revisions) let findPageOfPosts webLogId pageNbr postsPerPage = log.LogTrace "Post.findPageOfPosts" + let order = + Query.orderBy + [ Field.Named $"{publishName} DESC NULLS FIRST"; Field.Named (nameof Post.Empty.UpdatedOn) ] SQLite conn.customList - $"{postByWebLog} - ORDER BY {publishField} DESC NULLS FIRST, data->>'{nameof Post.Empty.UpdatedOn}' - LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" + $"{postByWebLog}{order} LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" [ webLogParam webLogId ] postWithoutText @@ -152,9 +153,9 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = let findPageOfPublishedPosts webLogId pageNbr postsPerPage = log.LogTrace "Post.findPageOfPublishedPosts" conn.customList - $"{publishedPostByWebLog} - ORDER BY {publishField} DESC - LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" + $"""{publishedPostByWebLog} + {Query.orderBy [ Field.Named $"{publishName} DESC" ] SQLite} + LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}""" [ webLogParam webLogId ] postWithoutLinks @@ -163,26 +164,28 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = log.LogTrace "Post.findPageOfTaggedPosts" let tagSql, tagParams = inJsonArray Table.Post (nameof Post.Empty.Tags) "tag" [ tag ] conn.customList - $"{publishedPostByWebLog} AND {tagSql} - ORDER BY {publishField} DESC - LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" + $"""{publishedPostByWebLog} AND {tagSql} + {Query.orderBy [ Field.Named $"{publishName} DESC" ] SQLite} + LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}""" (webLogParam webLogId :: tagParams) postWithoutLinks /// 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 = + 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) + ] conn.customSingle - $"{publishedPostByWebLog} AND {publishField} < @publishedOn ORDER BY {publishField} DESC LIMIT 1" - parameters - postWithoutLinks - let! newer = - conn.customSingle - $"{publishedPostByWebLog} AND {publishField} > @publishedOn ORDER BY {publishField} LIMIT 1" - parameters + (Query.byFields (Query.find Table.Post) All fields + + Query.orderBy [ Field.Named (publishName + order) ] SQLite + " LIMIT 1") + (addFieldParams fields []) postWithoutLinks + let! older = adjacent "<" " DESC" + let! newer = adjacent ">" "" return older, newer } diff --git a/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs b/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs index ff205f5..580fcb7 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs @@ -10,8 +10,8 @@ open MyWebLog.Data /// SQLite myWebLog theme data implementation type SQLiteThemeData(conn : SqliteConnection, log: ILogger) = - /// The JSON field for the theme ID - let idField = $"data->>'{nameof Theme.Empty.Id}'" + /// The name of the theme ID field + let idName = nameof Theme.Empty.Id /// Convert a document to a theme with no template text let withoutTemplateText (rdr: SqliteDataReader) = @@ -25,7 +25,11 @@ type SQLiteThemeData(conn : SqliteConnection, log: ILogger) = /// Retrieve all themes (except 'admin'; excludes template text) let all () = log.LogTrace "Theme.all" - conn.customList $"{Query.find Table.Theme} WHERE {idField} <> 'admin' ORDER BY {idField}" [] withoutTemplateText + let fields = [ Field.NE idName "admin" ] + conn.customList + (Query.byFields (Query.find Table.Theme) Any fields + Query.orderBy [ Field.Named idName ] SQLite) + (addFieldParams fields []) + withoutTemplateText /// Does a given theme exist? let exists (themeId: ThemeId) = diff --git a/src/MyWebLog.Data/SQLiteData.fs b/src/MyWebLog.Data/SQLiteData.fs index 158ed7c..1540808 100644 --- a/src/MyWebLog.Data/SQLiteData.fs +++ b/src/MyWebLog.Data/SQLiteData.fs @@ -1,7 +1,6 @@ namespace MyWebLog.Data open System -open System.Threading.Tasks open BitBadger.Documents open BitBadger.Documents.Sqlite open Microsoft.Data.Sqlite @@ -24,108 +23,107 @@ type SQLiteData(conn: SqliteConnection, log: ILogger, ser: JsonSeria let needsTable table = not (List.contains table tables) - let jsonTable table = - $"{Query.Definition.ensureTable table}; {Query.Definition.ensureKey table SQLite}" + let creatingTable = "Creating {Table} table..." - let tasks = - seq { - // Theme tables - if needsTable Table.Theme then jsonTable Table.Theme - if needsTable Table.ThemeAsset then + // Theme tables + if needsTable Table.Theme then + log.LogInformation(creatingTable, Table.Theme) + do! conn.ensureTable Table.Theme + + if needsTable Table.ThemeAsset then + log.LogInformation(creatingTable, Table.ThemeAsset) + do! conn.customNonQuery $"CREATE TABLE {Table.ThemeAsset} ( theme_id TEXT NOT NULL, path TEXT NOT NULL, updated_on TEXT NOT NULL, data BLOB NOT NULL, - PRIMARY KEY (theme_id, path))" - - // Web log table - if needsTable Table.WebLog then jsonTable Table.WebLog - - // Category table - if needsTable Table.Category then - $"""{jsonTable Table.Category}; - {Query.Definition.ensureIndexOn - Table.Category "web_log" [ nameof Category.Empty.WebLogId ] SQLite}""" - - // Web log user table - if needsTable Table.WebLogUser then - $"""{jsonTable Table.WebLogUser}; - {Query.Definition.ensureIndexOn - Table.WebLogUser - "email" - [ nameof WebLogUser.Empty.WebLogId; nameof WebLogUser.Empty.Email ] - SQLite}""" - - // Page tables - if needsTable Table.Page then - $"""{jsonTable Table.Page}; - {Query.Definition.ensureIndexOn Table.Page "author" [ nameof Page.Empty.AuthorId ] SQLite}; - {Query.Definition.ensureIndexOn - Table.Page - "permalink" - [ nameof Page.Empty.WebLogId; nameof Page.Empty.Permalink ] - SQLite}""" - if needsTable Table.PageRevision then + PRIMARY KEY (theme_id, path))" [] + + // Web log table + if needsTable Table.WebLog then + log.LogInformation(creatingTable, Table.WebLog) + do! conn.ensureTable Table.WebLog + + // Category table + if needsTable Table.Category then + log.LogInformation(creatingTable, Table.Category) + do! conn.ensureTable Table.Category + do! conn.ensureFieldIndex Table.Category "web_log" [ nameof Category.Empty.WebLogId ] + + // Web log user table + if needsTable Table.WebLogUser then + log.LogInformation(creatingTable, Table.WebLogUser) + do! conn.ensureTable Table.WebLogUser + do! conn.ensureFieldIndex + Table.WebLogUser "email" [ nameof WebLogUser.Empty.WebLogId; nameof WebLogUser.Empty.Email ] + + // Page tables + if needsTable Table.Page then + log.LogInformation(creatingTable, Table.Page) + do! conn.ensureTable Table.Page + do! conn.ensureFieldIndex Table.Page "author" [ nameof Page.Empty.AuthorId ] + do! conn.ensureFieldIndex Table.Page "permalink" [ nameof Page.Empty.WebLogId; nameof Page.Empty.Permalink ] + + if needsTable Table.PageRevision then + log.LogInformation(creatingTable, Table.PageRevision) + do! conn.customNonQuery $"CREATE TABLE {Table.PageRevision} ( page_id TEXT NOT NULL, as_of TEXT NOT NULL, revision_text TEXT NOT NULL, - PRIMARY KEY (page_id, as_of))" - - // Post tables - if needsTable Table.Post then - $"""{jsonTable Table.Post}; - {Query.Definition.ensureIndexOn Table.Post "author" [ nameof Post.Empty.AuthorId ] SQLite}; - {Query.Definition.ensureIndexOn - Table.Post "permalink" [ nameof Post.Empty.WebLogId; nameof Post.Empty.Permalink ] SQLite}; - {Query.Definition.ensureIndexOn - Table.Post - "status" - [ nameof Post.Empty.WebLogId; nameof Post.Empty.Status; nameof Post.Empty.UpdatedOn ] - SQLite}""" - // TODO: index categories by post? - if needsTable Table.PostRevision then + PRIMARY KEY (page_id, as_of))" [] + + // Post tables + if needsTable Table.Post then + log.LogInformation(creatingTable, Table.Post) + do! conn.ensureTable Table.Post + do! conn.ensureFieldIndex Table.Post "author" [ nameof Post.Empty.AuthorId ] + do! conn.ensureFieldIndex Table.Post "permalink" [ nameof Post.Empty.WebLogId; nameof Post.Empty.Permalink ] + do! conn.ensureFieldIndex + Table.Post + "status" + [ nameof Post.Empty.WebLogId; nameof Post.Empty.Status; nameof Post.Empty.UpdatedOn ] + // TODO: index categories by post? + + if needsTable Table.PostRevision then + log.LogInformation(creatingTable, Table.PostRevision) + do! conn.customNonQuery $"CREATE TABLE {Table.PostRevision} ( post_id TEXT NOT NULL, as_of TEXT NOT NULL, revision_text TEXT NOT NULL, - PRIMARY KEY (post_id, as_of))" - if needsTable Table.PostComment then - $"""{jsonTable Table.PostComment}; - {Query.Definition.ensureIndexOn - Table.PostComment "post" [ nameof Comment.Empty.PostId ] SQLite}""" - - // Tag map table - if needsTable Table.TagMap then - $"""{jsonTable Table.TagMap}; - {Query.Definition.ensureIndexOn - Table.TagMap - "url" - [ nameof TagMap.Empty.WebLogId; nameof TagMap.Empty.UrlValue ] - SQLite}""" - - // Uploaded file table - if needsTable Table.Upload then + PRIMARY KEY (post_id, as_of))" [] + + if needsTable Table.PostComment then + log.LogInformation(creatingTable, Table.PostComment) + do! conn.ensureTable Table.PostComment + do! conn.ensureFieldIndex Table.PostComment "post" [ nameof Comment.Empty.PostId ] + + // Tag map table + if needsTable Table.TagMap then + log.LogInformation(creatingTable, Table.TagMap) + do! conn.ensureTable Table.TagMap + do! conn.ensureFieldIndex Table.TagMap "url" [ nameof TagMap.Empty.WebLogId; nameof TagMap.Empty.UrlValue ] + + // Uploaded file table + if needsTable Table.Upload then + log.LogInformation(creatingTable, Table.Upload) + do! conn.customNonQuery $"CREATE TABLE {Table.Upload} ( id TEXT PRIMARY KEY, web_log_id TEXT NOT NULL, path TEXT NOT NULL, updated_on TEXT NOT NULL, data BLOB NOT NULL); - CREATE INDEX idx_{Table.Upload}_path ON {Table.Upload} (web_log_id, path)" - - // Database version table - if needsTable Table.DbVersion then - $"CREATE TABLE {Table.DbVersion} (id TEXT PRIMARY KEY); - INSERT INTO {Table.DbVersion} VALUES ('{Utils.Migration.currentDbVersion}')" - } - |> Seq.map (fun sql -> - log.LogInformation $"""Creating {(sql.Replace("IF NOT EXISTS ", "").Split ' ')[2]} table...""" - conn.customNonQuery sql []) + CREATE INDEX idx_{Table.Upload}_path ON {Table.Upload} (web_log_id, path)" [] - let! _ = Task.WhenAll tasks - () + // Database version table + if needsTable Table.DbVersion then + log.LogInformation(creatingTable, Table.DbVersion) + do! conn.customNonQuery + $"CREATE TABLE {Table.DbVersion} (id TEXT PRIMARY KEY); + INSERT INTO {Table.DbVersion} VALUES ('{Utils.Migration.currentDbVersion}')" [] } /// Set the database version to the specified version diff --git a/src/MyWebLog.Domain/MyWebLog.Domain.fsproj b/src/MyWebLog.Domain/MyWebLog.Domain.fsproj index 1eecef4..7682c35 100644 --- a/src/MyWebLog.Domain/MyWebLog.Domain.fsproj +++ b/src/MyWebLog.Domain/MyWebLog.Domain.fsproj @@ -11,7 +11,7 @@ - + diff --git a/src/MyWebLog.Tests/MyWebLog.Tests.fsproj b/src/MyWebLog.Tests/MyWebLog.Tests.fsproj index 64edcd9..fd362a3 100644 --- a/src/MyWebLog.Tests/MyWebLog.Tests.fsproj +++ b/src/MyWebLog.Tests/MyWebLog.Tests.fsproj @@ -28,7 +28,7 @@ - + diff --git a/src/MyWebLog/MyWebLog.fsproj b/src/MyWebLog/MyWebLog.fsproj index 9bb4bce..06b4bf1 100644 --- a/src/MyWebLog/MyWebLog.fsproj +++ b/src/MyWebLog/MyWebLog.fsproj @@ -32,12 +32,12 @@ - - + + - +