always get tags via subquery instead of separate query

This commit is contained in:
Jon Schoning 2021-07-24 20:21:13 -05:00
parent c98030139b
commit 6545aaea17
2 changed files with 127 additions and 154 deletions

View file

@ -6,7 +6,6 @@ import Handler.Common
import Import
import qualified Text.Blaze.Html5 as H
import Yesod.RssFeed
import qualified Database.Esqueleto.Experimental as E
import qualified Data.Map as Map
getUserR :: UserNameP -> Handler Html
@ -40,14 +39,11 @@ _getUser unamep@(UserNameP uname) sharedp' filterp' (TagsP pathtags) = do
queryp = "query" :: Text
mquery <- lookupGetParam queryp
let mqueryp = fmap (\q -> (queryp, q)) mquery
(bcount, bmarks, alltags) <-
runDB $
do Entity userId user <- getBy404 (UniqueUserName uname)
(bcount, btmarks) <- runDB $ do
Entity userId user <- getBy404 (UniqueUserName uname)
when (not isowner && userPrivacyLock user)
(redirect (AuthR LoginR))
(cnt, bm) <- bookmarksQuery userId sharedp filterp pathtags mquery limit page
tg <- tagsQuery bm
pure (cnt, bm, tg)
bookmarksTagsQuery userId sharedp filterp pathtags mquery limit page
when (bcount == 0) (case filterp of FilterSingle _ -> notFound; _ -> pure ())
mroute <- getCurrentRoute
tagCloudMode <- getTagCloudMode isowner pathtags
@ -60,14 +56,18 @@ _getUser unamep@(UserNameP uname) sharedp' filterp' (TagsP pathtags) = do
rssLink (UserFeedR unamep) "feed"
$(widgetFile "user")
toWidgetBody [julius|
app.dat.bmarks = #{ toJSON $ toBookmarkFormList bmarks alltags } || [];
app.dat.bmarks = #{ toJSON $ toBookmarkFormList btmarks } || [];
app.dat.isowner = #{ isowner };
app.userR = "@{UserR unamep}";
app.tagCloudMode = #{ toJSON $ tagCloudMode } || {};
|]
toWidget [julius|
PS['Main'].renderTagCloud('##{rawJS tagCloudRenderEl}')(app.tagCloudMode)();
PS['Main'].renderBookmarks('##{rawJS renderEl}')(app.dat.bmarks)();
setTimeout(() => {
PS['Main'].renderBookmarks('##{rawJS renderEl}')(app.dat.bmarks)();
}, 0);
setTimeout(() => {
PS['Main'].renderTagCloud('##{rawJS tagCloudRenderEl}')(app.tagCloudMode)();
}, 0);
|]
-- Form
@ -98,24 +98,17 @@ _updateTagCloudMode mode =
TagCloudModeRelated _ _ -> setTagCloudMode mode
TagCloudModeNone -> notFound
bookmarkToRssEntry :: (Entity Bookmark, [Text]) -> FeedEntry Text
bookmarkToRssEntry :: (Entity Bookmark, Maybe Text) -> FeedEntry Text
bookmarkToRssEntry ((Entity entryId entry), tags) =
FeedEntry
{ feedEntryLink = bookmarkHref entry
, feedEntryUpdated = bookmarkTime entry
, feedEntryTitle = bookmarkDescription entry
, feedEntryContent = toHtml (bookmarkExtended entry)
, feedEntryCategories = map (EntryCategory Nothing Nothing) tags
, feedEntryCategories = map (EntryCategory Nothing Nothing) (maybe [] words tags)
, feedEntryEnclosure = Nothing
}
toBookmarkWithTagsList :: [Entity Bookmark] -> [Entity BookmarkTag] -> [(Entity Bookmark, [Text])]
toBookmarkWithTagsList bs as = do
b <- bs
let bid = E.entityKey b
let btags = filter ((==) bid . bookmarkTagBookmarkId . E.entityVal) as
pure $ (b, map (bookmarkTagTag . E.entityVal) btags)
getUserFeedR :: UserNameP -> Handler RepRss
getUserFeedR unamep@(UserNameP uname) = do
mauthuname <- maybeAuthUsername
@ -125,17 +118,13 @@ getUserFeedR unamep@(UserNameP uname) = do
queryp = "query" :: Text
isowner = maybe False (== uname) mauthuname
mquery <- lookupGetParam queryp
(_, bmarks, alltags) <-
runDB $
do Entity userId user <- getBy404 (UniqueUserName uname)
(_, btmarks) <- runDB $ do
Entity userId user <- getBy404 (UniqueUserName uname)
when (not isowner && userPrivacyLock user)
(redirect (AuthR LoginR))
(cnt, bm) <- bookmarksQuery userId SharedPublic FilterAll [] mquery limit page
tg <- tagsQuery bm
pure (cnt, bm, tg)
bookmarksTagsQuery userId SharedPublic FilterAll [] mquery limit page
let (descr :: Html) = toHtml $ H.text ("Bookmarks saved by " <> uname)
entriesWithTags = toBookmarkWithTagsList bmarks alltags
entries = map bookmarkToRssEntry entriesWithTags
entries = map bookmarkToRssEntry btmarks
updated <- case maximumMay (map feedEntryUpdated entries) of
Nothing -> liftIO $ getCurrentTime
Just m -> return m

View file

@ -3,27 +3,25 @@
module Model where
import qualified ClassyPrelude.Yesod as CP
import Control.Monad.Fail (MonadFail)
import qualified Control.Monad.Combinators as PC (between)
import qualified Data.Aeson as A
import qualified Data.Aeson.Types as A (parseFail)
import qualified Data.Attoparsec.Text as P
import qualified Control.Monad.Combinators as PC
import qualified Data.List.NonEmpty as NE
import qualified Data.Time.ISO8601 as TI
import qualified Data.Time.Clock.POSIX as TI
import qualified Database.Esqueleto.Experimental as E
import qualified Database.Esqueleto.Internal.Internal as E (exists, unsafeSqlFunction)
import qualified Data.Time as TI
import ClassyPrelude.Yesod hiding ((||.))
import Control.Monad.Trans.Maybe
import qualified Data.Time as TI (ParseTime)
import qualified Data.Time.Clock.POSIX as TI (posixSecondsToUTCTime, POSIXTime)
import qualified Data.Time.ISO8601 as TI (parseISO8601, formatISO8601Millis)
import ClassyPrelude.Yesod hiding ((==.), (||.), on, Value, groupBy, exists, (>=.), (<=.))
import Control.Monad.Fail (MonadFail)
import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)
import Control.Monad.Writer (tell)
import Data.Char (isSpace)
import Data.Either (fromRight)
import Data.Foldable (foldl, foldl1, sequenceA_)
import Data.List.NonEmpty (NonEmpty(..))
import Database.Esqueleto.Experimental hiding ((==.))
import Pretty
import System.Directory
import Database.Esqueleto.Experimental
import Database.Esqueleto.Internal.Internal (unsafeSqlFunction)
import Pretty ()
import System.Directory (listDirectory)
import Types
import qualified Data.Map.Strict as MS
@ -142,10 +140,10 @@ migrateIndexes =
sqlite_group_concat ::
PersistField a
=> SqlExpr (E.Value a)
-> SqlExpr (E.Value a)
-> SqlExpr (E.Value Text)
sqlite_group_concat expr sep = E.unsafeSqlFunction "GROUP_CONCAT" [expr, sep]
=> SqlExpr (Value a)
-> SqlExpr (Value a)
-> SqlExpr (Value Text)
sqlite_group_concat expr sep = unsafeSqlFunction "GROUP_CONCAT" [expr, sep]
authenticatePassword :: Text -> Text -> DB (Maybe (Entity User))
authenticatePassword username password = do
@ -159,9 +157,10 @@ authenticatePassword username password = do
getUserByName :: UserNameP -> DB (Maybe (Entity User))
getUserByName (UserNameP uname) = do
selectFirst [UserName ==. uname] []
selectFirst [UserName CP.==. uname] []
bookmarksQuery
-- returns a list of pair of bookmark with tags merged into a string
bookmarksTagsQuery
:: Key User
-> SharedP
-> FilterP
@ -169,74 +168,92 @@ bookmarksQuery
-> Maybe Text
-> Limit
-> Page
-> DB (Int, [Entity Bookmark])
bookmarksQuery userId sharedp filterp tags mquery limit' page =
-> DB (Int, [(Entity Bookmark, Maybe Text)])
bookmarksTagsQuery userId sharedp filterp tags mquery limit' page =
(,) -- total count
<$> fmap (sum . fmap E.unValue)
(select $ do
b <- from $ table @Bookmark
<$> fmap (sum . fmap unValue)
(select $ from (table @Bookmark) >>= \b -> do
_whereClause b
pure E.countRows)
pure countRows)
-- paged data
<*> (select $ do
b <- from $ table @Bookmark
<*> (fmap . fmap . fmap) unValue
(select $ from (table @Bookmark) >>= \b -> do
_whereClause b
orderBy [desc (b ^. BookmarkTime)]
limit limit'
offset ((page - 1) * limit')
pure b)
pure (b, subSelect $ from (table @BookmarkTag) >>= \t -> do
where_ (t ^. BookmarkTagBookmarkId ==. b ^. BookmarkId)
groupBy (t ^. BookmarkTagBookmarkId)
orderBy [asc (t ^. BookmarkTagSeq)]
pure $ sqlite_group_concat (t ^. BookmarkTagTag) (val " ")))
where
_whereClause b = do
where_ $
foldl (\expr tag ->
expr &&. (E.exists $ -- each tag becomes an exists constraint
expr &&. (exists $ -- each tag becomes an exists constraint
from (table @BookmarkTag) >>= \t ->
where_ (t ^. BookmarkTagBookmarkId E.==. b ^. BookmarkId &&.
(t ^. BookmarkTagTag `E.like` val tag))))
(b ^. BookmarkUserId E.==. val userId)
where_ (t ^. BookmarkTagBookmarkId ==. b ^. BookmarkId &&.
(t ^. BookmarkTagTag `like` val tag))))
(b ^. BookmarkUserId ==. val userId)
tags
case sharedp of
SharedAll -> pure ()
SharedPublic -> where_ (b ^. BookmarkShared E.==. val True)
SharedPrivate -> where_ (b ^. BookmarkShared E.==. val False)
SharedPublic -> where_ (b ^. BookmarkShared ==. val True)
SharedPrivate -> where_ (b ^. BookmarkShared ==. val False)
case filterp of
FilterAll -> pure ()
FilterUnread -> where_ (b ^. BookmarkToRead E.==. val True)
FilterStarred -> where_ (b ^. BookmarkSelected E.==. val True)
FilterSingle slug -> where_ (b ^. BookmarkSlug E.==. val slug)
FilterUnread -> where_ (b ^. BookmarkToRead ==. val True)
FilterStarred -> where_ (b ^. BookmarkSelected ==. val True)
FilterSingle slug -> where_ (b ^. BookmarkSlug ==. val slug)
FilterUntagged -> where_ $ notExists $ from (table @BookmarkTag) >>= \t -> where_ $
t ^. BookmarkTagBookmarkId E.==. b ^. BookmarkId
t ^. BookmarkTagBookmarkId ==. b ^. BookmarkId
-- search
sequenceA_ (parseSearchQuery (toLikeExpr b) =<< mquery)
toLikeExpr :: E.SqlExpr (Entity Bookmark) -> Text -> E.SqlExpr (E.Value Bool)
toLikeExpr :: SqlExpr (Entity Bookmark) -> Text -> SqlExpr (Value Bool)
toLikeExpr b term = fromRight p_allFields (P.parseOnly p_onefield term)
where
wild s = (E.%) ++. val s ++. (E.%)
toLikeB field s = b ^. field `E.like` wild s
wild s = (%) ++. val s ++. (%)
toLikeB field s = b ^. field `like` wild s
p_allFields =
(toLikeB BookmarkHref term) ||.
(toLikeB BookmarkDescription term) ||.
(toLikeB BookmarkExtended term) ||.
(E.exists $ from (table @BookmarkTag) >>= \t -> where_ $
(t ^. BookmarkTagBookmarkId E.==. b ^. BookmarkId) &&.
(t ^. BookmarkTagTag `E.like` (wild term))
(exists $ from (table @BookmarkTag) >>= \t -> where_ $
(t ^. BookmarkTagBookmarkId ==. b ^. BookmarkId) &&.
(t ^. BookmarkTagTag `like` (wild term))
)
p_onefield = p_url <|> p_title <|> p_description <|> p_tags <|> p_after <|> p_before
where
p_url = "url:" *> fmap (toLikeB BookmarkHref) P.takeText
p_title = "title:" *> fmap (toLikeB BookmarkDescription) P.takeText
p_description = "description:" *> fmap (toLikeB BookmarkExtended) P.takeText
p_tags = "tags:" *> fmap (\term' -> E.exists $ from (table @BookmarkTag) >>= \t -> where_ $
(t ^. BookmarkTagBookmarkId E.==. b ^. BookmarkId) &&.
(t ^. BookmarkTagTag `E.like` wild term')) P.takeText
p_after = "after:" *> fmap ((b ^. BookmarkTime E.>=.) . val) (parseTimeText =<< P.takeText)
p_before = "before:" *> fmap ((b ^. BookmarkTime E.<=.) . val) (parseTimeText =<< P.takeText)
p_tags = "tags:" *> fmap (\term' -> exists $ from (table @BookmarkTag) >>= \t -> where_ $
(t ^. BookmarkTagBookmarkId ==. b ^. BookmarkId) &&.
(t ^. BookmarkTagTag `like` wild term')) P.takeText
p_after = "after:" *> fmap ((b ^. BookmarkTime >=.) . val) (parseTimeText =<< P.takeText)
p_before = "before:" *> fmap ((b ^. BookmarkTime <=.) . val) (parseTimeText =<< P.takeText)
-- returns a list of pair of bookmark with tags merged into a string
allUserBookmarks :: Key User -> DB [(Entity Bookmark, Text)]
allUserBookmarks user =
(fmap . fmap . fmap) (fromMaybe "" . unValue) $
select $ do
b <- from (table @Bookmark)
where_ (b ^. BookmarkUserId ==. val user)
orderBy [asc (b ^. BookmarkTime)]
pure (b, subSelect $ from (table @BookmarkTag) >>= \t -> do
where_ (t ^. BookmarkTagBookmarkId ==. b ^. BookmarkId)
groupBy (t ^. BookmarkTagBookmarkId)
orderBy [asc (t ^. BookmarkTagSeq)]
pure $ sqlite_group_concat (t ^. BookmarkTagTag) (val " "))
parseSearchQuery ::
(Text -> E.SqlExpr (E.Value Bool))
(Text -> SqlExpr (Value Bool))
-> Text
-> Maybe (E.SqlQuery ())
-> Maybe (SqlQuery ())
parseSearchQuery toExpr =
fmap where_ . either (const Nothing) Just . P.parseOnly andE
where
@ -260,32 +277,24 @@ parseTimeText t =
, "%s" -- 1535932800
]
tagsQuery :: [Entity Bookmark] -> DB [Entity BookmarkTag]
tagsQuery bmarks =
select $ do
t <- from (table @BookmarkTag)
where_ (t ^. BookmarkTagBookmarkId `in_` valList (fmap entityKey bmarks))
orderBy [asc (t ^. BookmarkTagSeq)]
pure t
withTags :: Key Bookmark -> DB [Entity BookmarkTag]
withTags key = selectList [BookmarkTagBookmarkId ==. key] [Asc BookmarkTagSeq]
withTags key = selectList [BookmarkTagBookmarkId CP.==. key] [Asc BookmarkTagSeq]
-- Note List Query
getNote :: Key User -> NtSlug -> DB (Maybe (Entity Note))
getNote userKey slug =
selectFirst [NoteUserId ==. userKey, NoteSlug ==. slug] []
selectFirst [NoteUserId CP.==. userKey, NoteSlug CP.==. slug] []
getNoteList :: Key User -> Maybe Text -> SharedP -> Limit -> Page -> DB (Int, [Entity Note])
getNoteList key mquery sharedp limit' page =
(,) -- total count
<$> fmap (sum . fmap E.unValue)
<$> fmap (sum . fmap unValue)
(select $ do
b <- from (table @Note)
_whereClause b
pure $ E.countRows)
pure $ countRows)
<*> (select $ do
b <- from (table @Note)
_whereClause b
@ -295,26 +304,26 @@ getNoteList key mquery sharedp limit' page =
pure b)
where
_whereClause b = do
where_ $ (b ^. NoteUserId E.==. val key)
where_ $ (b ^. NoteUserId ==. val key)
-- search
sequenceA_ (parseSearchQuery (toLikeExpr b) =<< mquery)
case sharedp of
SharedAll -> pure ()
SharedPublic -> where_ (b ^. NoteShared E.==. val True)
SharedPrivate -> where_ (b ^. NoteShared E.==. val False)
SharedPublic -> where_ (b ^. NoteShared ==. val True)
SharedPrivate -> where_ (b ^. NoteShared ==. val False)
toLikeExpr :: E.SqlExpr (Entity Note) -> Text -> E.SqlExpr (E.Value Bool)
toLikeExpr :: SqlExpr (Entity Note) -> Text -> SqlExpr (Value Bool)
toLikeExpr b term = fromRight p_allFields (P.parseOnly p_onefield term)
where
wild s = (E.%) ++. val s ++. (E.%)
toLikeN field s = b ^. field `E.like` wild s
wild s = (%) ++. val s ++. (%)
toLikeN field s = b ^. field `like` wild s
p_allFields = toLikeN NoteTitle term ||. toLikeN NoteText term
p_onefield = p_title <|> p_text <|> p_after <|> p_before
where
p_title = "title:" *> fmap (toLikeN NoteTitle) P.takeText
p_text = "description:" *> fmap (toLikeN NoteText) P.takeText
p_after = "after:" *> fmap ((b ^. NoteCreated E.>=.) . val) (parseTimeText =<< P.takeText)
p_before = "before:" *> fmap ((b ^. NoteCreated E.<=.) . val) (parseTimeText =<< P.takeText)
p_after = "after:" *> fmap ((b ^. NoteCreated >=.) . val) (parseTimeText =<< P.takeText)
p_before = "before:" *> fmap ((b ^. NoteCreated <=.) . val) (parseTimeText =<< P.takeText)
-- Bookmark Files
@ -462,32 +471,6 @@ getFileBookmarks user = do
marks <- allUserBookmarks user
pure $ fmap (\(bm, t) -> bookmarkTofileBookmark (entityVal bm) t) marks
-- returns a list of pair of bookmark with tags merged into a string
allUserBookmarks :: Key User -> DB [(Entity Bookmark, Text)]
allUserBookmarks user = do
bmarks <- bquery
tags <- tquery
let tagmap = MS.fromList tags
pure $ (\bm@(Entity bid _) -> (bm, findWithDefault mempty bid tagmap)) <$> bmarks
where
bquery :: DB [Entity Bookmark]
bquery =
select $ do
b <- from (table @Bookmark)
where_ (b ^. BookmarkUserId E.==. val user)
orderBy [asc (b ^. BookmarkTime)]
pure b
tquery :: DB [(Key Bookmark, Text)]
tquery =
fmap (\(tid, tags) -> (E.unValue tid, E.unValue tags)) <$>
(select $ do
t <- from (table @BookmarkTag)
where_ (t ^. BookmarkTagUserId E.==. val user)
E.groupBy (t ^. BookmarkTagBookmarkId)
let tags = sqlite_group_concat (t ^. BookmarkTagTag) (E.val " ")
pure (t ^. BookmarkTagBookmarkId, tags))
data TagCloudMode
= TagCloudModeTop Bool Int -- { mode: "top", value: 200 }
| TagCloudModeLowerBound Bool Int -- { mode: "lowerBound", value: 20 }
@ -539,46 +522,46 @@ type Tag = Text
tagCountTop :: Key User -> Int -> DB [(Text, Int)]
tagCountTop user top =
sortOn (toLower . fst) .
fmap (\(tname, tcount) -> (E.unValue tname, E.unValue tcount)) <$>
fmap (\(tname, tcount) -> (unValue tname, unValue tcount)) <$>
( select $ do
t <- from (table @BookmarkTag)
where_ (t ^. BookmarkTagUserId E.==. val user)
E.groupBy (E.lower_ $ t ^. BookmarkTagTag)
let countRows' = E.countRows
E.orderBy [E.desc countRows']
E.limit ((fromIntegral . toInteger) top)
where_ (t ^. BookmarkTagUserId ==. val user)
groupBy (lower_ $ t ^. BookmarkTagTag)
let countRows' = countRows
orderBy [desc countRows']
limit ((fromIntegral . toInteger) top)
pure $ (t ^. BookmarkTagTag, countRows')
)
tagCountLowerBound :: Key User -> Int -> DB [(Text, Int)]
tagCountLowerBound user lowerBound =
fmap (\(tname, tcount) -> (E.unValue tname, E.unValue tcount)) <$>
fmap (\(tname, tcount) -> (unValue tname, unValue tcount)) <$>
( select $ do
t <- from (table @BookmarkTag)
where_ (t ^. BookmarkTagUserId E.==. val user)
E.groupBy (E.lower_ $ t ^. BookmarkTagTag)
let countRows' = E.countRows
E.orderBy [E.asc (t ^. BookmarkTagTag)]
E.having (countRows' E.>=. E.val lowerBound)
where_ (t ^. BookmarkTagUserId ==. val user)
groupBy (lower_ $ t ^. BookmarkTagTag)
let countRows' = countRows
orderBy [asc (t ^. BookmarkTagTag)]
having (countRows' >=. val lowerBound)
pure $ (t ^. BookmarkTagTag, countRows')
)
tagCountRelated :: Key User -> [Tag] -> DB [(Text, Int)]
tagCountRelated user tags =
fmap (\(tname, tcount) -> (E.unValue tname, E.unValue tcount)) <$>
fmap (\(tname, tcount) -> (unValue tname, unValue tcount)) <$>
( select $ do
t <- from (table @BookmarkTag)
where_ $
foldl (\expr tag ->
expr &&. (E.exists $ do
expr &&. (exists $ do
u <- from (table @BookmarkTag)
where_ (u ^. BookmarkTagBookmarkId E.==. t ^. BookmarkTagBookmarkId &&.
(u ^. BookmarkTagTag `E.like` val tag))))
(t ^. BookmarkTagUserId E.==. val user)
where_ (u ^. BookmarkTagBookmarkId ==. t ^. BookmarkTagBookmarkId &&.
(u ^. BookmarkTagTag `like` val tag))))
(t ^. BookmarkTagUserId ==. val user)
tags
E.groupBy (E.lower_ $ t ^. BookmarkTagTag)
let countRows' = E.countRows
E.orderBy [E.asc $ E.lower_ $ (t ^. BookmarkTagTag)]
groupBy (lower_ $ t ^. BookmarkTagTag)
let countRows' = countRows
orderBy [asc $ lower_ $ (t ^. BookmarkTagTag)]
pure $ (t ^. BookmarkTagTag, countRows')
)
@ -664,20 +647,20 @@ instance ToJSON BookmarkForm where toJSON = A.genericToJSON gDefaultFormOptions
gDefaultFormOptions :: A.Options
gDefaultFormOptions = A.defaultOptions { A.fieldLabelModifier = drop 1 }
toBookmarkFormList :: [Entity Bookmark] -> [Entity BookmarkTag] -> [BookmarkForm]
toBookmarkFormList bs as = do
b <- bs
let bid = E.entityKey b
let btags = filter ((==) bid . bookmarkTagBookmarkId . E.entityVal) as
pure $ _toBookmarkForm (b, btags)
toBookmarkFormList :: [(Entity Bookmark, Maybe Text)] -> [BookmarkForm]
toBookmarkFormList = fmap _toBookmarkForm'
_toBookmarkForm :: (Entity Bookmark, [Entity BookmarkTag]) -> BookmarkForm
_toBookmarkForm (Entity bid Bookmark {..}, tags) =
_toBookmarkForm (bm, tags) =
_toBookmarkForm' (bm, Just $ unwords $ fmap (bookmarkTagTag . entityVal) tags)
_toBookmarkForm' :: (Entity Bookmark, Maybe Text) -> BookmarkForm
_toBookmarkForm' (Entity bid Bookmark {..}, tags) =
BookmarkForm
{ _url = bookmarkHref
, _title = Just bookmarkDescription
, _description = Just $ Textarea $ bookmarkExtended
, _tags = Just $ unwords $ fmap (bookmarkTagTag . entityVal) tags
, _tags = Just $ fromMaybe "" tags
, _private = Just $ not bookmarkShared
, _toread = Just $ bookmarkToRead
, _bid = Just $ unBookmarkKey $ bid
@ -687,6 +670,7 @@ _toBookmarkForm (Entity bid Bookmark {..}, tags) =
, _archiveUrl = bookmarkArchiveHref
}
_toBookmark :: UserId -> BookmarkForm -> IO Bookmark
_toBookmark userId BookmarkForm {..} = do
time <- liftIO getCurrentTime
@ -739,7 +723,7 @@ upsertBookmark userId mbid bm tags = do
deleteTags bid
pure (Updated, bid)
deleteTags bid =
deleteWhere [BookmarkTagBookmarkId ==. bid]
deleteWhere [BookmarkTagBookmarkId CP.==. bid]
insertTags userId' bid' =
for_ (zip [1 ..] tags) $
\(i, tag) -> void $ insert $ BookmarkTag userId' tag bid' i
@ -747,7 +731,7 @@ upsertBookmark userId mbid bm tags = do
updateBookmarkArchiveUrl :: Key User -> Key Bookmark -> Maybe Text -> DB ()
updateBookmarkArchiveUrl userId bid marchiveUrl = do
updateWhere
[BookmarkUserId ==. userId, BookmarkId ==. bid]
[BookmarkUserId CP.==. userId, BookmarkId CP.==. bid]
[BookmarkArchiveHref CP.=. marchiveUrl]
upsertNote :: Key User -> Maybe (Key Note) -> Note -> DB (UpsertResult, Key Note)