WIP on SQLite data
- Add table definitions - Convert categories - WIP on pages
This commit is contained in:
parent
554e22f998
commit
2fc2714287
@ -1,4 +1,4 @@
|
||||
namespace MyWebLog.Data
|
||||
namespace MyWebLog.Dataa
|
||||
|
||||
open LiteDB
|
||||
open MyWebLog
|
||||
|
@ -6,9 +6,94 @@ open Microsoft.Data.Sqlite
|
||||
open MyWebLog
|
||||
open MyWebLog.ViewModels
|
||||
|
||||
type SQLiteData (conn : SqliteConnection) =
|
||||
[<AutoOpen>]
|
||||
module private SqliteHelpers =
|
||||
do ()
|
||||
|
||||
member _.x = ""
|
||||
/// Run a command that returns a count
|
||||
let count (cmd : SqliteCommand) = backgroundTask {
|
||||
let! it = cmd.ExecuteScalarAsync ()
|
||||
return it :?> int
|
||||
}
|
||||
|
||||
/// Create a list of items from the given data reader
|
||||
let toList<'T> (it : SqliteDataReader -> 'T) (rdr : SqliteDataReader) =
|
||||
seq { while rdr.Read () do it rdr }
|
||||
|> List.ofSeq
|
||||
|
||||
/// Verify that the web log ID matches before returning an item
|
||||
let verifyWebLog<'T> webLogId (prop : 'T -> WebLogId) (it : SqliteDataReader -> 'T) (rdr : SqliteDataReader) =
|
||||
if rdr.Read () then
|
||||
let item = it rdr
|
||||
if prop item = webLogId then Some item else None
|
||||
else
|
||||
None
|
||||
|
||||
/// Execute a command that returns no data
|
||||
let write (cmd : SqliteCommand) = backgroundTask {
|
||||
let! _ = cmd.ExecuteNonQueryAsync ()
|
||||
()
|
||||
}
|
||||
|
||||
/// Functions to map domain items from a data reader
|
||||
module Map =
|
||||
|
||||
/// Get a boolean value from a data reader
|
||||
let getBoolean (rdr : SqliteDataReader) col = rdr.GetBoolean (rdr.GetOrdinal col)
|
||||
|
||||
/// Get a date/time value from a data reader
|
||||
let getDateTime (rdr : SqliteDataReader) col = rdr.GetDateTime (rdr.GetOrdinal col)
|
||||
|
||||
/// Get a string value from a data reader
|
||||
let getString (rdr : SqliteDataReader) col = rdr.GetString (rdr.GetOrdinal col)
|
||||
|
||||
/// Get a possibly null string value from a data reader
|
||||
let tryString (rdr : SqliteDataReader) col =
|
||||
if rdr.IsDBNull (rdr.GetOrdinal col) then None else Some (getString rdr col)
|
||||
|
||||
/// Create a category from the current row in the given data reader
|
||||
let toCategory (rdr : SqliteDataReader) : Category =
|
||||
{ id = CategoryId (getString rdr "id")
|
||||
webLogId = WebLogId (getString rdr "web_log_id")
|
||||
name = getString rdr "name"
|
||||
slug = getString rdr "slug"
|
||||
description = tryString rdr "description"
|
||||
parentId = tryString rdr "parent_id" |> Option.map CategoryId
|
||||
}
|
||||
|
||||
/// Create a meta item from the current row in the given data reader
|
||||
let toMetaItem (rdr : SqliteDataReader) : MetaItem =
|
||||
{ name = getString rdr "name"
|
||||
value = getString rdr "value"
|
||||
}
|
||||
|
||||
/// Create a permalink from the current row in the given data reader
|
||||
let toPermalink (rdr : SqliteDataReader) : Permalink =
|
||||
Permalink (getString rdr "permalink")
|
||||
|
||||
/// Create a page from the current row in the given data reader
|
||||
let toPage (rdr : SqliteDataReader) : Page =
|
||||
{ Page.empty with
|
||||
id = PageId (getString rdr "id")
|
||||
webLogId = WebLogId (getString rdr "web_log_id")
|
||||
authorId = WebLogUserId (getString rdr "author_id")
|
||||
title = getString rdr "title"
|
||||
permalink = toPermalink rdr
|
||||
publishedOn = getDateTime rdr "published_on"
|
||||
updatedOn = getDateTime rdr "updated_on"
|
||||
showInPageList = getBoolean rdr "show_in_page_list"
|
||||
template = tryString rdr "template"
|
||||
text = getString rdr "page_text"
|
||||
}
|
||||
|
||||
/// Create a revision from the current row in the given data reader
|
||||
let toRevision (rdr : SqliteDataReader) : Revision =
|
||||
{ asOf = getDateTime rdr "as_of"
|
||||
text = MarkupText.parse (getString rdr "revision_text")
|
||||
}
|
||||
|
||||
|
||||
type SQLiteData (conn : SqliteConnection) =
|
||||
|
||||
// /// Shorthand for accessing the collections in the LiteDB database
|
||||
// let Collection = {|
|
||||
@ -23,6 +108,45 @@ type SQLiteData (conn : SqliteConnection) =
|
||||
// WebLogUser = db.GetCollection<WebLogUser> "WebLogUser"
|
||||
// |}
|
||||
|
||||
/// Add parameters for category INSERT or UPDATE statements
|
||||
let addCategoryParameters (cmd : SqliteCommand) (cat : Category) =
|
||||
[ cmd.Parameters.AddWithValue ("@id", CategoryId.toString cat.id)
|
||||
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString cat.webLogId)
|
||||
cmd.Parameters.AddWithValue ("@name", cat.name)
|
||||
cmd.Parameters.AddWithValue ("@slug", cat.slug)
|
||||
cmd.Parameters.AddWithValue ("@description",
|
||||
match cat.description with Some d -> d :> obj | None -> DBNull.Value)
|
||||
cmd.Parameters.AddWithValue ("@parentId",
|
||||
match cat.parentId with Some (CategoryId parentId) -> parentId :> obj | None -> DBNull.Value)
|
||||
] |> ignore
|
||||
|
||||
/// Add parameters for page INSERT or UPDATE statements
|
||||
let addPageParameters (cmd : SqliteCommand) (page : Page) =
|
||||
[ cmd.Parameters.AddWithValue ("@id", PageId.toString page.id)
|
||||
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString page.webLogId)
|
||||
cmd.Parameters.AddWithValue ("@authorId", WebLogUserId.toString page.authorId)
|
||||
cmd.Parameters.AddWithValue ("@title", page.title)
|
||||
cmd.Parameters.AddWithValue ("@permalink", Permalink.toString page.permalink)
|
||||
cmd.Parameters.AddWithValue ("@publishedOn", page.publishedOn)
|
||||
cmd.Parameters.AddWithValue ("@updatedOn", page.updatedOn)
|
||||
cmd.Parameters.AddWithValue ("@showInPageList", page.showInPageList)
|
||||
cmd.Parameters.AddWithValue ("@template",
|
||||
match page.template with Some t -> t :> obj | None -> DBNull.Value)
|
||||
cmd.Parameters.AddWithValue ("@text", page.text)
|
||||
] |> ignore
|
||||
|
||||
/// Add a web log ID parameter
|
||||
let addWebLogId (cmd : SqliteCommand) webLogId =
|
||||
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
|
||||
|
||||
/// Append meta items to a page
|
||||
let appendPageMeta (page : Page) = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT name, value FROM page_meta WHERE page_id = @id"
|
||||
use! rdr = cmd.ExecuteReaderAsync ()
|
||||
return { page with metadata = toList Map.toMetaItem rdr }
|
||||
}
|
||||
|
||||
/// Return a page with no revisions or prior permalinks
|
||||
let pageWithoutRevisions (page : Page) =
|
||||
{ page with revisions = []; priorPermalinks = [] }
|
||||
@ -43,6 +167,8 @@ type SQLiteData (conn : SqliteConnection) =
|
||||
let postWithoutText post =
|
||||
{ postWithoutRevisions post with text = "" }
|
||||
|
||||
|
||||
|
||||
/// The connection for this instance
|
||||
member _.Conn = conn
|
||||
|
||||
@ -54,59 +180,65 @@ type SQLiteData (conn : SqliteConnection) =
|
||||
member _.add cat = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"INSERT INTO Category VALUES (@id, @webLogId, @name, @slug, @description, @parentId)"
|
||||
[ cmd.Parameters.AddWithValue ("@id", CategoryId.toString cat.id)
|
||||
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString cat.webLogId)
|
||||
cmd.Parameters.AddWithValue ("@name", cat.name)
|
||||
cmd.Parameters.AddWithValue ("@slug", cat.slug)
|
||||
cmd.Parameters.AddWithValue ("@description",
|
||||
match cat.description with
|
||||
| Some d -> d :> obj
|
||||
| None -> DBNull.Value)
|
||||
cmd.Parameters.AddWithValue ("@parentId",
|
||||
match cat.parentId with
|
||||
| Some (CategoryId parentId) -> parentId :> obj
|
||||
| None -> DBNull.Value)
|
||||
]
|
||||
|> ignore
|
||||
"INSERT INTO category VALUES (@id, @webLogId, @name, @slug, @description, @parentId)"
|
||||
addCategoryParameters cmd cat
|
||||
let! _ = cmd.ExecuteNonQueryAsync ()
|
||||
()
|
||||
}
|
||||
|
||||
member _.countAll webLogId = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT COUNT(id) FROM Category WHERE webLogId = @webLpgId"
|
||||
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
|
||||
let! result = cmd.ExecuteScalarAsync ()
|
||||
return result :?> int
|
||||
cmd.CommandText <- "SELECT COUNT(id) FROM category WHERE web_log_id = @webLogId"
|
||||
addWebLogId cmd webLogId
|
||||
return! count cmd
|
||||
}
|
||||
|
||||
member _.countTopLevel webLogId =
|
||||
Collection.Category.Count(fun cat -> cat.webLogId = webLogId && Option.isNone cat.parentId)
|
||||
|> Task.FromResult
|
||||
member _.countTopLevel webLogId = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"SELECT COUNT(id) FROM category WHERE web_log_id = @webLogId AND parent_id IS NULL"
|
||||
addWebLogId cmd webLogId
|
||||
return! count cmd
|
||||
}
|
||||
|
||||
member _.findAllForView webLogId = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT * FROM category WHERE web_log_id = @webLogId"
|
||||
addWebLogId cmd webLogId
|
||||
use! rdr = cmd.ExecuteReaderAsync ()
|
||||
let cats =
|
||||
Collection.Category.Find (fun cat -> cat.webLogId = webLogId)
|
||||
seq {
|
||||
while rdr.Read () do
|
||||
Map.toCategory rdr
|
||||
}
|
||||
|> Seq.sortBy (fun cat -> cat.name.ToLowerInvariant ())
|
||||
|> List.ofSeq
|
||||
let ordered = orderByHierarchy cats None None []
|
||||
if not rdr.IsClosed then do! rdr.CloseAsync ()
|
||||
let ordered = Utils.orderByHierarchy cats None None []
|
||||
let! counts =
|
||||
ordered
|
||||
|> Seq.map (fun it -> backgroundTask {
|
||||
// Parent category post counts include posts in subcategories
|
||||
let catIds =
|
||||
ordered
|
||||
|> Seq.filter (fun cat -> cat.parentNames |> Array.contains it.name)
|
||||
|> Seq.map (fun cat -> cat.id :> obj)
|
||||
|> Seq.append (Seq.singleton it.id)
|
||||
|> List.ofSeq
|
||||
let count =
|
||||
Collection.Post.Count (fun p ->
|
||||
p.webLogId = webLogId
|
||||
&& p.status = Published
|
||||
&& p.categoryIds |> List.exists (fun cId -> catIds |> List.contains cId))
|
||||
return it.id, count
|
||||
cmd.Parameters.Clear ()
|
||||
addWebLogId cmd webLogId
|
||||
cmd.CommandText <-
|
||||
"""SELECT COUNT(DISTINCT p.id)
|
||||
FROM post p
|
||||
INNER JOIN post_category pc ON pc.post_id = p.id
|
||||
WHERE p.web_log_id = @webLogId
|
||||
AND p.status = 'Published'
|
||||
AND pc.category_id IN ("""
|
||||
ordered
|
||||
|> Seq.filter (fun cat -> cat.parentNames |> Array.contains it.name)
|
||||
|> Seq.map (fun cat -> cat.id)
|
||||
|> Seq.append (Seq.singleton it.id)
|
||||
|> Seq.iteri (fun idx item ->
|
||||
if idx > 0 then cmd.CommandText <- $"{cmd.CommandText}, "
|
||||
cmd.CommandText <- $"{cmd.CommandText}@catId{idx}"
|
||||
cmd.Parameters.AddWithValue ($"@catId{idx}", item) |> ignore)
|
||||
cmd.CommandText <- $"{cmd.CommandText})"
|
||||
let! postCount = count cmd
|
||||
return it.id, postCount
|
||||
})
|
||||
|> Task.WhenAll
|
||||
return
|
||||
@ -121,38 +253,65 @@ type SQLiteData (conn : SqliteConnection) =
|
||||
|> Array.ofSeq
|
||||
}
|
||||
|
||||
member _.findById catId webLogId =
|
||||
Collection.Category.FindById (CategoryIdMapping.toBson catId)
|
||||
|> verifyWebLog webLogId (fun c -> c.webLogId)
|
||||
member _.findById catId webLogId = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT * FROM category WHERE id = @id"
|
||||
cmd.Parameters.AddWithValue ("@id", CategoryId.toString catId) |> ignore
|
||||
use! rdr = cmd.ExecuteReaderAsync ()
|
||||
return verifyWebLog<Category> webLogId (fun c -> c.webLogId) Map.toCategory rdr
|
||||
}
|
||||
|
||||
member _.findByWebLog webLogId =
|
||||
Collection.Category.Find (fun c -> c.webLogId = webLogId)
|
||||
|> toList
|
||||
member _.findByWebLog webLogId = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT * FROM category WHERE web_log_id = @webLogId"
|
||||
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
|
||||
use! rdr = cmd.ExecuteReaderAsync ()
|
||||
return toList Map.toCategory rdr
|
||||
}
|
||||
|
||||
member this.delete catId webLogId = backgroundTask {
|
||||
match! this.findById catId webLogId with
|
||||
| Some _ ->
|
||||
use cmd = conn.CreateCommand ()
|
||||
// Delete the category off all posts where it is assigned
|
||||
Collection.Post.Find (fun p -> p.webLogId = webLogId && p.categoryIds |> List.contains catId)
|
||||
|> Seq.map (fun p ->
|
||||
{ p with categoryIds = p.categoryIds |> List.filter (fun cId -> cId <> catId) })
|
||||
|> Collection.Post.Update
|
||||
|> ignore
|
||||
cmd.CommandText <-
|
||||
"""DELETE FROM post_category
|
||||
WHERE category_id = @id
|
||||
AND post_id IN (SELECT id FROM post WHERE web_log_id = @webLogId)"""
|
||||
let catIdParameter = cmd.Parameters.AddWithValue ("@id", CategoryId.toString catId)
|
||||
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
|
||||
do! write cmd
|
||||
// Delete the category itself
|
||||
let _ = Collection.Category.Delete (CategoryIdMapping.toBson catId)
|
||||
do! checkpoint ()
|
||||
cmd.CommandText <- "DELETE FROM category WHERE id = @id"
|
||||
cmd.Parameters.Clear ()
|
||||
cmd.Parameters.Add catIdParameter |> ignore
|
||||
do! write cmd
|
||||
return true
|
||||
| None -> return false
|
||||
}
|
||||
|
||||
member _.restore cats = backgroundTask {
|
||||
let _ = Collection.Category.InsertBulk cats
|
||||
do! checkpoint ()
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"INSERT INTO category VALUES (@id, @webLogId, @name, @slug, @description, @parentId)"
|
||||
for cat in cats do
|
||||
cmd.Parameters.Clear ()
|
||||
addCategoryParameters cmd cat
|
||||
do! write cmd
|
||||
}
|
||||
|
||||
member _.update cat = backgroundTask {
|
||||
let _ = Collection.Category.Update cat
|
||||
do! checkpoint ()
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"""UPDATE category
|
||||
SET name = @name,
|
||||
slug = @slug,
|
||||
description = @description,
|
||||
parent_id = @parentId
|
||||
WHERE id = @id
|
||||
AND web_log_id = @webLogId"""
|
||||
addCategoryParameters cmd cat
|
||||
do! write cmd
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,56 +319,135 @@ type SQLiteData (conn : SqliteConnection) =
|
||||
new IPageData with
|
||||
|
||||
member _.add page = backgroundTask {
|
||||
let _ = Collection.Page.Insert page
|
||||
do! checkpoint ()
|
||||
use cmd = conn.CreateCommand ()
|
||||
// The page itself
|
||||
cmd.CommandText <-
|
||||
"""INSERT INTO page
|
||||
VALUES (@id, @webLogId, @authorId, @title, @permalink, @publishedOn, @updatedOn,
|
||||
@showInPageList, @template, @text)"""
|
||||
addPageParameters cmd page
|
||||
do! write cmd
|
||||
// Metadata
|
||||
cmd.CommandText <- "INSERT INTO page_meta VALUES (@pageId, @name, @value)"
|
||||
for meta in page.metadata do
|
||||
cmd.Parameters.Clear ()
|
||||
[ cmd.Parameters.AddWithValue ("@pageId", PageId.toString page.id)
|
||||
cmd.Parameters.AddWithValue ("@name", meta.name)
|
||||
cmd.Parameters.AddWithValue ("@value", meta.value)
|
||||
] |> ignore
|
||||
do! write cmd
|
||||
// Revisions
|
||||
cmd.CommandText <- "INSERT INTO page_revision VALUES (@pageId, @asOf, @text)"
|
||||
for rev in page.revisions do
|
||||
cmd.Parameters.Clear ()
|
||||
[ cmd.Parameters.AddWithValue ("@pageId", PageId.toString page.id)
|
||||
cmd.Parameters.AddWithValue ("@asOf", rev.asOf)
|
||||
cmd.Parameters.AddWithValue ("@text", MarkupText.toString rev.text)
|
||||
] |> ignore
|
||||
do! write cmd
|
||||
}
|
||||
|
||||
member _.all webLogId =
|
||||
Collection.Page.Find (fun p -> p.webLogId = webLogId)
|
||||
|> Seq.map pageWithoutText
|
||||
|> Seq.sortBy pageSort
|
||||
|> toList
|
||||
member _.all webLogId = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT * FROM page WHERE web_log_id = @webLogId ORDER BY LOWER(title)"
|
||||
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
|
||||
let noText rdr = { Map.toPage rdr with text = "" }
|
||||
use! rdr = cmd.ExecuteReaderAsync ()
|
||||
return toList noText rdr
|
||||
}
|
||||
|
||||
member _.countAll webLogId =
|
||||
Collection.Page.Count (fun p -> p.webLogId = webLogId)
|
||||
|> Task.FromResult
|
||||
|
||||
member _.countListed webLogId =
|
||||
Collection.Page.Count (fun p -> p.webLogId = webLogId && p.showInPageList)
|
||||
|> Task.FromResult
|
||||
|
||||
member _.findFullById pageId webLogId =
|
||||
Collection.Page.FindById (PageIdMapping.toBson pageId)
|
||||
|> verifyWebLog webLogId (fun it -> it.webLogId)
|
||||
|
||||
member this.findById pageId webLogId = backgroundTask {
|
||||
let! page = this.findFullById pageId webLogId
|
||||
return page |> Option.map pageWithoutRevisions
|
||||
member _.countAll webLogId = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT COUNT(id) FROM page WHERE web_log_id = @webLogId"
|
||||
addWebLogId cmd webLogId
|
||||
return! count cmd
|
||||
}
|
||||
|
||||
member _.countListed webLogId = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"""SELECT COUNT(id)
|
||||
FROM page
|
||||
WHERE web_log_id = @webLogId
|
||||
AND show_in_page_list = @showInPageList"""
|
||||
addWebLogId cmd webLogId
|
||||
cmd.Parameters.AddWithValue ("@showInPageList", true) |> ignore
|
||||
return! count cmd
|
||||
}
|
||||
|
||||
member _.findById pageId webLogId = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT * FROM page WHERE id = @id"
|
||||
cmd.Parameters.AddWithValue ("@id", PageId.toString pageId) |> ignore
|
||||
use! rdr = cmd.ExecuteReaderAsync ()
|
||||
match verifyWebLog<Page> webLogId (fun it -> it.webLogId) Map.toPage rdr with
|
||||
| Some page ->
|
||||
let! page = appendPageMeta page
|
||||
return Some page
|
||||
| None -> return None
|
||||
}
|
||||
|
||||
member this.findFullById pageId webLogId = backgroundTask {
|
||||
match! this.findById pageId webLogId with
|
||||
| Some page ->
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT * FROM page_permalink WHERE page_id = @pageId"
|
||||
cmd.Parameters.AddWithValue ("@pageId", PageId.toString page.id) |> ignore
|
||||
use! linkRdr = cmd.ExecuteReaderAsync ()
|
||||
let page = { page with priorPermalinks = toList Map.toPermalink linkRdr }
|
||||
cmd.CommandText <- "SELECT * FROM page_revision WHERE page_id = @pageId"
|
||||
use! revRdr = cmd.ExecuteReaderAsync ()
|
||||
return Some { page with revisions = toList Map.toRevision revRdr }
|
||||
| None -> return None
|
||||
}
|
||||
|
||||
member this.delete pageId webLogId = backgroundTask {
|
||||
match! this.findById pageId webLogId with
|
||||
| Some _ ->
|
||||
let _ = Collection.Page.Delete (PageIdMapping.toBson pageId)
|
||||
do! checkpoint ()
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "DELETE FROM page_revision WHERE page_id = @id"
|
||||
cmd.Parameters.AddWithValue ("@id", PageId.toString pageId) |> ignore
|
||||
do! write cmd
|
||||
cmd.CommandText <- "DELETE FROM page_permalink WHERE page_id = @id"
|
||||
do! write cmd
|
||||
cmd.CommandText <- "DELETE FROM page_meta WHERE page_id = @id"
|
||||
do! write cmd
|
||||
cmd.CommandText <- "DELETE FROM page WHERE id = @id"
|
||||
do! write cmd
|
||||
return true
|
||||
| None -> return false
|
||||
}
|
||||
|
||||
member _.findByPermalink permalink webLogId = backgroundTask {
|
||||
let! page =
|
||||
Collection.Page.Find (fun p -> p.webLogId = webLogId && p.permalink = permalink)
|
||||
|> tryFirst
|
||||
return page |> Option.map pageWithoutRevisions
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT * FROM page WHERE web_log_id = @webLogId AND permalink = @link"
|
||||
addWebLogId cmd webLogId
|
||||
cmd.Parameters.AddWithValue ("@link", Permalink.toString permalink) |> ignore
|
||||
use! rdr = cmd.ExecuteReaderAsync ()
|
||||
if rdr.Read () then
|
||||
let! page = appendPageMeta (Map.toPage rdr)
|
||||
return Some page
|
||||
else
|
||||
return None
|
||||
}
|
||||
|
||||
member _.findCurrentPermalink permalinks webLogId = backgroundTask {
|
||||
let! result =
|
||||
Collection.Page.Find (fun p ->
|
||||
p.webLogId = webLogId
|
||||
&& p.priorPermalinks |> List.exists (fun link -> permalinks |> List.contains link))
|
||||
|> tryFirst
|
||||
return result |> Option.map (fun pg -> pg.permalink)
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"""SELECT p.permalink
|
||||
FROM page p
|
||||
INNER JOIN page_permalink pp ON pp.page_id = p.id
|
||||
WHERE p.web_log_id = @webLogId
|
||||
AND pp.permalink IN ("""
|
||||
permalinks
|
||||
|> List.iteri (fun idx link ->
|
||||
if idx > 0 then cmd.CommandText <- $"{cmd.CommandText}, "
|
||||
cmd.CommandText <- $"{cmd.CommandText}@link{idx}"
|
||||
cmd.Parameters.AddWithValue ($"@link{idx}", Permalink.toString link) |> ignore)
|
||||
cmd.CommandText <- $"{cmd.CommandText})"
|
||||
addWebLogId cmd webLogId
|
||||
use! rdr = cmd.ExecuteReaderAsync ()
|
||||
return if rdr.Read () then Some (Map.toPermalink rdr) else None
|
||||
}
|
||||
|
||||
member _.findFullByWebLog webLogId =
|
||||
@ -265,7 +503,7 @@ type SQLiteData (conn : SqliteConnection) =
|
||||
|
||||
member _.findFullById postId webLogId =
|
||||
Collection.Post.FindById (PostIdMapping.toBson postId)
|
||||
|> verifyWebLog webLogId (fun p -> p.webLogId)
|
||||
//|> verifyWebLog webLogId (fun p -> p.webLogId)
|
||||
|
||||
member this.delete postId webLogId = backgroundTask {
|
||||
match! this.findFullById postId webLogId with
|
||||
@ -357,7 +595,7 @@ type SQLiteData (conn : SqliteConnection) =
|
||||
|
||||
member _.findById tagMapId webLogId =
|
||||
Collection.TagMap.FindById (TagMapIdMapping.toBson tagMapId)
|
||||
|> verifyWebLog webLogId (fun tm -> tm.webLogId)
|
||||
//|> verifyWebLog webLogId (fun tm -> tm.webLogId)
|
||||
|
||||
member this.delete tagMapId webLogId = backgroundTask {
|
||||
match! this.findById tagMapId webLogId with
|
||||
@ -516,7 +754,7 @@ type SQLiteData (conn : SqliteConnection) =
|
||||
|
||||
member _.findById userId webLogId =
|
||||
Collection.WebLogUser.FindById (WebLogUserIdMapping.toBson userId)
|
||||
|> verifyWebLog webLogId (fun u -> u.webLogId)
|
||||
// |> verifyWebLog webLogId (fun u -> u.webLogId)
|
||||
|
||||
member _.findByWebLog webLogId =
|
||||
Collection.WebLogUser.Find (fun wlu -> wlu.webLogId = webLogId)
|
||||
@ -540,16 +778,223 @@ type SQLiteData (conn : SqliteConnection) =
|
||||
|
||||
member _.startUp () = backgroundTask {
|
||||
|
||||
let _ = Collection.Category.EnsureIndex (fun c -> c.webLogId)
|
||||
let _ = Collection.Comment.EnsureIndex (fun c -> c.postId)
|
||||
let _ = Collection.Page.EnsureIndex (fun p -> p.webLogId)
|
||||
let _ = Collection.Page.EnsureIndex (fun p -> p.authorId)
|
||||
let _ = Collection.Post.EnsureIndex (fun p -> p.webLogId)
|
||||
let _ = Collection.Post.EnsureIndex (fun p -> p.authorId)
|
||||
let _ = Collection.TagMap.EnsureIndex (fun tm -> tm.webLogId)
|
||||
let _ = Collection.WebLog.EnsureIndex (fun wl -> wl.urlBase)
|
||||
let _ = Collection.WebLogUser.EnsureIndex (fun wlu -> wlu.webLogId)
|
||||
let tableExists table = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <- "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = @table"
|
||||
cmd.Parameters.AddWithValue ("@table", table) |> ignore
|
||||
let! count = cmd.ExecuteScalarAsync ()
|
||||
return (count :?> int) = 1
|
||||
}
|
||||
|
||||
do! checkpoint ()
|
||||
let! exists = tableExists "theme"
|
||||
if not exists then
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE theme (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
version TEXT NOT NULL)"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE theme_template (
|
||||
theme_id TEXT NOT NULL REFERENCES theme (id),
|
||||
name TEXT NOT NULL,
|
||||
template TEXT NOT NULL,
|
||||
PRIMARY KEY (theme_id, name))"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE theme_asset (
|
||||
theme_id TEXT NOT NULL REFERENCES theme (id),
|
||||
path TEXT NOT NULL,
|
||||
updated_on TEXT NOT NULL,
|
||||
data BINARY NOT NULL,
|
||||
PRIMARY KEY (theme_id, path))"""
|
||||
do! write cmd
|
||||
|
||||
let! exists = tableExists "web_log"
|
||||
if not exists then
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE web_log (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
subtitle TEXT,
|
||||
default_page TEXT NOT NULL,
|
||||
theme_id TEXT NOT NULL REFERENCES theme (id),
|
||||
url_base TEXT NOT NULL,
|
||||
time_zone TEXT NOT NULL,
|
||||
auto_htmx INTEGER NOT NULL DEFAULT 0)"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE web_log_rss (
|
||||
web_log_id TEXT PRIMARY KEY REFERENCES web_log (id),
|
||||
feed_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
feed_name TEXT NOT NULL,
|
||||
items_in_feed INTEGER,
|
||||
category_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
tag_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
copyright TEXT)"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE web_log_feed (
|
||||
id TEXT PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
source TEXT NOT NULL,
|
||||
path TEXT NOT NULL)"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE web_log_feed_podcast (
|
||||
feed_id TEXT PRIMARY KEY REFERENCES web_log_feed (id),
|
||||
title TEXT NOT NULL,
|
||||
subtitle TEXT,
|
||||
items_in_feed INTEGER NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
displayed_author TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
image_url TEXT NOT NULL,
|
||||
itunes_category TEXT NOT NULL,
|
||||
itunes_subcategory TEXT,
|
||||
explicit TEXT NOT NULL,
|
||||
default_media_type TEXT,
|
||||
media_base_url TEXT)"""
|
||||
do! write cmd
|
||||
|
||||
let! exists = tableExists "category"
|
||||
if not exists then
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE category (
|
||||
id TEXT PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
parent_id TEXT)"""
|
||||
do! write cmd
|
||||
|
||||
let! exists = tableExists "web_log_user"
|
||||
if not exists then
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE web_log_user (
|
||||
id TEXT PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
user_name TEXT NOT NULL,
|
||||
first_name TEXT NOT NULL,
|
||||
last_name TEXT NOT NULL,
|
||||
preferred_name TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
salt TEXT NOT NULL,
|
||||
url TEXT,
|
||||
authorization_level TEXT NOT NULL)"""
|
||||
do! write cmd
|
||||
|
||||
let! exists = tableExists "page"
|
||||
if not exists then
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE page (
|
||||
id TEXT PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
author_id TEXT NOT NULL REFERENCES web_log_user (id),
|
||||
title TEXT NOT NULL,
|
||||
permalink TEXT NOT NULL,
|
||||
published_on TEXT NOT NULL,
|
||||
updated_on TEXT NOT NULL,
|
||||
show_in_page_list INTEGER NOT NULL DEFAULT 0,
|
||||
template TEXT,
|
||||
page_text TEXT NOT NULL)"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE page_meta (
|
||||
page_id TEXT NOT NULL REFERENCES page (id),
|
||||
name TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
PRIMARY KEY (page_id, name, value))"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE page_permalink (
|
||||
page_id TEXT NOT NULL REFERENCES page (id),
|
||||
permalink TEXT NOT NULL,
|
||||
PRIMARY KEY (page_id, permalink))"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE page_revision (
|
||||
page_id TEXT NOT NULL REFERENCES page (id),
|
||||
as_of TEXT NOT NULL,
|
||||
revision_text TEXT NOT NULL,
|
||||
PRIMARY KEY (page_id, as_of))"""
|
||||
do! write cmd
|
||||
|
||||
let! exists = tableExists "post"
|
||||
if not exists then
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE post (
|
||||
id TEXT PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
author_id TEXT NOT NULL REFERENCES web_log_user (id),
|
||||
status TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
permalink TEXT NOT NULL,
|
||||
published_on TEXT,
|
||||
updated_on TEXT NOT NULL,
|
||||
template TEXT,
|
||||
post_text TEXT NOT NULL)"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE post_category (
|
||||
post_id TEXT NOT NULL REFERENCES post (id),
|
||||
category_id TEXT NOT NULL REFERENCES category (id),
|
||||
PRIMARY KEY (post_id, category_id))"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE post_tag (
|
||||
post_id TEXT NOT NULL REFERENCES post (id),
|
||||
tag TEXT NOT NULL,
|
||||
PRIMARY KEY (post_id, tag))"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE post_meta (
|
||||
post_id TEXT NOT NULL REFERENCES post (id),
|
||||
name TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
PRIMARY KEY (post_id, name, value))"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE post_permalink (
|
||||
post_id TEXT NOT NULL REFERENCES post (id),
|
||||
permalink TEXT NOT NULL,
|
||||
PRIMARY KEY (post_id, permalink))"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE post_revision (
|
||||
post_id TEXT NOT NULL REFERENCES post (id),
|
||||
as_of TEXT NOT NULL,
|
||||
revision_text TEXT NOT NULL,
|
||||
PRIMARY KEY (page_id, as_of))"""
|
||||
do! write cmd
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE post_comment (
|
||||
id TEXT PRIMARY KEY,
|
||||
post_id TEXT NOT NULL REFERENCES post(id),
|
||||
in_reply_to_id TEXT,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
url TEXT,
|
||||
status TEXT NOT NULL,
|
||||
posted_on TEXT NOT NULL,
|
||||
comment_text TEXT NOT NULL)"""
|
||||
do! write cmd
|
||||
|
||||
let! exists = tableExists "tag_map"
|
||||
if not exists then
|
||||
use cmd = conn.CreateCommand ()
|
||||
cmd.CommandText <-
|
||||
"""CREATE TABLE tag_map (
|
||||
id TEXT PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
tag TEXT NOT NULL,
|
||||
url_value TEXT NOT NULL)"""
|
||||
do! write cmd
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user