core: mentions in history, unread mentions in stats (#5594)

* core: mentions in history, unread mentions in stats

* fix

* update plans
This commit is contained in:
Evgeny 2025-01-30 17:59:21 +00:00 committed by GitHub
parent 4ed67f094f
commit 3bc822a1e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 131 additions and 62 deletions

View file

@ -894,16 +894,16 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
in Just (fInv, fileDescrText)
| otherwise = Nothing
processContentItem :: GroupMember -> ChatItem 'CTGroup d -> MsgContent -> Maybe (FileInvitation, RcvFileDescrText) -> CM [ChatMsgEvent 'Json]
processContentItem sender ChatItem {meta, quotedItem} mc fInvDescr_ =
processContentItem sender ChatItem {formattedText, meta, quotedItem, mentions} mc fInvDescr_ =
if isNothing fInvDescr_ && not (msgContentHasText mc)
then pure []
else do
let CIMeta {itemTs, itemSharedMsgId, itemTimed} = meta
quotedItemId_ = quoteItemId =<< quotedItem
fInv_ = fst <$> fInvDescr_
-- TODO [mentions] history?
-- let (_t, ft_) = msgContentTexts mc
(chatMsgEvent, _) <- withStore $ \db -> prepareGroupMsg db user gInfo mc M.empty quotedItemId_ Nothing fInv_ itemTimed False
(mc', _, mentions') = updatedMentionNames mc formattedText mentions
mentions'' = M.map (\CIMention {memberId} -> MsgMention {memberId}) mentions'
(chatMsgEvent, _) <- withStore $ \db -> prepareGroupMsg db user gInfo mc' mentions'' quotedItemId_ Nothing fInv_ itemTimed False
let senderVRange = memberChatVRange' sender
xMsgNewChatMsg = ChatMessage {chatVRange = senderVRange, msgId = itemSharedMsgId, chatMsgEvent}
fileDescrEvents <- case (snd <$> fInvDescr_, itemSharedMsgId) of

View file

@ -320,6 +320,7 @@ deriving instance Show AChat
data ChatStats = ChatStats
{ unreadCount :: Int, -- returned both in /_get chat initial API and in /_get chats API
unreadMentions :: Int, -- returned both in /_get chat initial API and in /_get chats API
reportsCount :: Int, -- returned both in /_get chat initial API and in /_get chats API
minUnreadItemId :: ChatItemId,
unreadChat :: Bool
@ -327,7 +328,7 @@ data ChatStats = ChatStats
deriving (Show)
emptyChatStats :: ChatStats
emptyChatStats = ChatStats 0 0 0 False
emptyChatStats = ChatStats 0 0 0 0 False
data NavigationInfo = NavigationInfo
{ afterUnread :: Int,

View file

@ -570,7 +570,12 @@ data AChatPreviewData = forall c. ChatTypeI c => ACPD (SChatType c) (ChatPreview
type ChatStatsRow = (Int, Int, ChatItemId, BoolInt)
toChatStats :: ChatStatsRow -> ChatStats
toChatStats (unreadCount, reportsCount, minUnreadItemId, BI unreadChat) = ChatStats {unreadCount, reportsCount, minUnreadItemId, unreadChat}
toChatStats (unreadCount, reportsCount, minUnreadItemId, BI unreadChat) = ChatStats {unreadCount, unreadMentions = 0, reportsCount, minUnreadItemId, unreadChat}
type GroupStatsRow = (Int, Int, Int, ChatItemId, BoolInt)
toGroupStats :: GroupStatsRow -> ChatStats
toGroupStats (unreadCount, unreadMentions, reportsCount, minUnreadItemId, BI unreadChat) = ChatStats {unreadCount, unreadMentions, reportsCount, minUnreadItemId, unreadChat}
findDirectChatPreviews_ :: DB.Connection -> User -> PaginationByTime -> ChatListQuery -> IO [AChatPreviewData]
findDirectChatPreviews_ db User {userId} pagination clq =
@ -674,9 +679,9 @@ findGroupChatPreviews_ :: DB.Connection -> User -> PaginationByTime -> ChatListQ
findGroupChatPreviews_ db User {userId} pagination clq =
map toPreview <$> getPreviews
where
toPreview :: (GroupId, UTCTime, Maybe ChatItemId) :. ChatStatsRow -> AChatPreviewData
toPreview :: (GroupId, UTCTime, Maybe ChatItemId) :. GroupStatsRow -> AChatPreviewData
toPreview ((groupId, ts, lastItemId_) :. statsRow) =
ACPD SCTGroup $ GroupChatPD ts groupId lastItemId_ (toChatStats statsRow)
ACPD SCTGroup $ GroupChatPD ts groupId lastItemId_ (toGroupStats statsRow)
baseQuery =
[sql|
SELECT
@ -690,12 +695,13 @@ findGroupChatPreviews_ db User {userId} pagination clq =
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -761,7 +767,7 @@ findGroupChatPreviews_ db User {userId} pagination clq =
|]
p = baseParams :. (userId, search, search, search, search)
queryWithPagination q p
queryWithPagination :: ToRow p => Query -> p -> IO [(GroupId, UTCTime, Maybe ChatItemId) :. ChatStatsRow]
queryWithPagination :: ToRow p => Query -> p -> IO [(GroupId, UTCTime, Maybe ChatItemId) :. GroupStatsRow]
queryWithPagination query params = case pagination of
PTLast count -> DB.query db (query <> " ORDER BY g.chat_ts DESC LIMIT ?") (params :. Only count)
PTAfter ts count -> DB.query db (query <> " AND g.chat_ts > ? ORDER BY g.chat_ts ASC LIMIT ?") (params :. (ts, count))
@ -1353,19 +1359,19 @@ getGroupChatInitial_ db user g contentFilter count = do
stats <- liftIO $ getStats minUnreadItemId =<< getGroupUnreadCount_ db user g Nothing
getGroupChatAround' db user g contentFilter minUnreadItemId count "" stats
Nothing -> liftIO $ do
stats <- getStats 0 0
stats <- getStats 0 (0, 0)
(,Just $ NavigationInfo 0 0) <$> getGroupChatLast_ db user g contentFilter count "" stats
where
getStats minUnreadItemId unreadCount = do
getStats minUnreadItemId (unreadCount, unreadMentions) = do
reportsCount <- getGroupReportsCount_ db user g False
pure ChatStats {unreadCount, reportsCount, minUnreadItemId, unreadChat = False}
pure ChatStats {unreadCount, unreadMentions, reportsCount, minUnreadItemId, unreadChat = False}
getGroupStats_ :: DB.Connection -> User -> GroupInfo -> IO ChatStats
getGroupStats_ db user g = do
minUnreadItemId <- fromMaybe 0 <$> getGroupMinUnreadId_ db user g Nothing
unreadCount <- getGroupUnreadCount_ db user g Nothing
(unreadCount, unreadMentions) <- getGroupUnreadCount_ db user g Nothing
reportsCount <- getGroupReportsCount_ db user g False
pure ChatStats {unreadCount, reportsCount, minUnreadItemId, unreadChat = False}
pure ChatStats {unreadCount, unreadMentions, reportsCount, minUnreadItemId, unreadChat = False}
getGroupMinUnreadId_ :: DB.Connection -> User -> GroupInfo -> Maybe MsgContentTag -> IO (Maybe ChatItemId)
getGroupMinUnreadId_ db user g contentFilter =
@ -1375,11 +1381,11 @@ getGroupMinUnreadId_ db user g contentFilter =
baseQuery = "SELECT chat_item_id FROM chat_items WHERE user_id = ? AND group_id = ? "
orderLimit = " ORDER BY item_ts ASC, chat_item_id ASC LIMIT 1"
getGroupUnreadCount_ :: DB.Connection -> User -> GroupInfo -> Maybe MsgContentTag -> IO Int
getGroupUnreadCount_ :: DB.Connection -> User -> GroupInfo -> Maybe MsgContentTag -> IO (Int, Int)
getGroupUnreadCount_ db user g contentFilter =
fromOnly . head <$> queryUnreadGroupItems db user g contentFilter baseQuery ""
head <$> queryUnreadGroupItems db user g contentFilter baseQuery ""
where
baseQuery = "SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? "
baseQuery = "SELECT COUNT(1), COALESCE(SUM(user_mention), 0) FROM chat_items WHERE user_id = ? AND group_id = ? "
getGroupReportsCount_ :: DB.Connection -> User -> GroupInfo -> Bool -> IO Int
getGroupReportsCount_ db User {userId} GroupInfo {groupId} archived =
@ -3111,10 +3117,9 @@ getGroupSndStatusCounts db itemId =
(Only itemId)
getGroupHistoryItems :: DB.Connection -> User -> GroupInfo -> GroupMember -> Int -> IO [Either StoreError (CChatItem 'CTGroup)]
getGroupHistoryItems db user@User {userId} GroupInfo {groupId} m count = do
getGroupHistoryItems db user@User {userId} g@GroupInfo {groupId} m count = do
ciIds <- getLastItemIds_
-- use getGroupCIWithReactions to read reactions data
reverse <$> mapM (runExceptT . getGroupChatItem db user groupId) ciIds
reverse <$> mapM (runExceptT . getGroupCIWithReactions db user g) ciIds
where
getLastItemIds_ :: IO [ChatItemId]
getLastItemIds_ =

View file

@ -22,11 +22,15 @@ CREATE INDEX idx_chat_item_mentions_group_id ON chat_item_mentions(group_id);
CREATE INDEX idx_chat_item_mentions_chat_item_id ON chat_item_mentions(chat_item_id);
CREATE UNIQUE INDEX idx_chat_item_mentions_display_name ON chat_item_mentions(chat_item_id, display_name);
CREATE UNIQUE INDEX idx_chat_item_mentions_member_id ON chat_item_mentions(chat_item_id, member_id);
CREATE INDEX idx_chat_items_groups_user_mention ON chat_items(user_id, group_id, item_status, user_mention);
|]
down_m20250126_mentions :: Query
down_m20250126_mentions =
[sql|
DROP INDEX idx_chat_items_groups_user_mention;
DROP INDEX idx_chat_item_mentions_group_id;
DROP INDEX idx_chat_item_mentions_chat_item_id;
DROP INDEX idx_chat_item_mentions_display_name;

View file

@ -157,7 +157,7 @@ Query:
WHERE i.user_id = ? AND i.item_status = ? AND (g.enable_ntfs = 1 OR g.enable_ntfs IS NULL)
Plan:
SEARCH i USING COVERING INDEX idx_chat_items_groups (user_id=?)
SEARCH i USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=?)
SEARCH g USING INTEGER PRIMARY KEY (rowid=?)
Query:
@ -480,7 +480,7 @@ Query:
LIMIT ?
Plan:
SEARCH chat_items USING INDEX idx_chat_items_groups_history (user_id=?)
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=?)
USE TEMP B-TREE FOR ORDER BY
Query:
@ -491,7 +491,7 @@ Query:
LIMIT ?
Plan:
SEARCH chat_items USING INDEX idx_chat_items_groups_history (user_id=?)
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=?)
USE TEMP B-TREE FOR ORDER BY
Query:
@ -985,7 +985,7 @@ Query:
LIMIT 1
Plan:
SEARCH chat_items USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id=?)
USE TEMP B-TREE FOR ORDER BY
Query:
@ -1005,7 +1005,7 @@ Query:
LIMIT ?
Plan:
SEARCH chat_items USING INDEX idx_chat_items_groups_history (user_id=?)
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=?)
USE TEMP B-TREE FOR ORDER BY
Query:
@ -1151,7 +1151,7 @@ Query:
LIMIT 1
Plan:
SEARCH i USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH i USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id=?)
SEARCH m USING INTEGER PRIMARY KEY (rowid=?)
SEARCH c USING INTEGER PRIMARY KEY (rowid=?)
USE TEMP B-TREE FOR ORDER BY
@ -1920,12 +1920,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -1949,7 +1950,7 @@ Query:
ORDER BY g.chat_ts DESC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
@ -1971,12 +1972,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -1995,7 +1997,7 @@ Query:
ORDER BY g.chat_ts DESC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
@ -2016,12 +2018,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -2039,7 +2042,7 @@ Query:
AND g.chat_ts < ? ORDER BY g.chat_ts DESC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts<?)
@ -2060,12 +2063,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -2083,7 +2087,7 @@ Query:
AND g.chat_ts > ? ORDER BY g.chat_ts ASC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts>?)
@ -2104,12 +2108,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -2127,7 +2132,7 @@ Query:
ORDER BY g.chat_ts DESC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
@ -2148,12 +2153,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -2171,7 +2177,7 @@ Query:
AND g.chat_ts < ? ORDER BY g.chat_ts DESC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts<?)
@ -2192,12 +2198,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -2215,7 +2222,7 @@ Query:
AND g.chat_ts > ? ORDER BY g.chat_ts ASC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts>?)
@ -2236,12 +2243,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -2259,7 +2267,7 @@ Query:
ORDER BY g.chat_ts DESC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
@ -2280,12 +2288,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -2300,7 +2309,7 @@ Query:
WHERE g.user_id = ? AND g.chat_ts < ? ORDER BY g.chat_ts DESC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts<?)
@ -2321,12 +2330,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -2341,7 +2351,7 @@ Query:
WHERE g.user_id = ? AND g.chat_ts > ? ORDER BY g.chat_ts ASC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts>?)
@ -2362,12 +2372,13 @@ Query:
LIMIT 1
) AS chat_item_id,
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
LEFT JOIN (
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
SELECT group_id, COUNT(1) AS UnreadCount, SUM(user_mention) as UnreadMentions, MIN(chat_item_id) AS MinUnread
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL AND item_status = ?
GROUP BY group_id
@ -2382,7 +2393,7 @@ Query:
WHERE g.user_id = ? ORDER BY g.chat_ts DESC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id>?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
@ -2930,7 +2941,7 @@ Query:
LIMIT 1
Plan:
SEARCH chat_items USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id=?)
USE TEMP B-TREE FOR ORDER BY
Query:
@ -4080,7 +4091,7 @@ Query:
WHERE user_id = ? AND group_id = ? AND item_status = ? AND timed_ttl IS NOT NULL AND timed_delete_at IS NULL
Plan:
SEARCH chat_items USING INDEX idx_chat_items_groups (user_id=? AND group_id=? AND item_status=?)
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id=? AND item_status=?)
Query:
SELECT group_snd_item_status, COUNT(1)
@ -4171,7 +4182,7 @@ Query:
WHERE user_id = ? AND group_id = ? AND item_status = ?
Plan:
SEARCH chat_items USING INDEX idx_chat_items_groups (user_id=? AND group_id=? AND item_status=?)
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id=? AND item_status=?)
Query:
UPDATE chat_items SET item_status = ?, updated_at = ?
@ -4666,7 +4677,7 @@ Query:
JOIN files f ON f.chat_item_id = i.chat_item_id
WHERE i.user_id = ?
Plan:
SEARCH i USING COVERING INDEX idx_chat_items_groups_history (user_id=?)
SEARCH i USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=?)
SEARCH f USING INDEX idx_files_chat_item_id (chat_item_id=?)
Query:
@ -4693,7 +4704,7 @@ Query:
JOIN files f ON f.chat_item_id = i.chat_item_id
WHERE i.user_id = ? AND i.group_id = ?
Plan:
SEARCH i USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH i USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id=?)
SEARCH f USING INDEX idx_files_chat_item_id (chat_item_id=?)
Query:
@ -5013,7 +5024,7 @@ SEARCH groups USING COVERING INDEX idx_groups_chat_item_id (chat_item_id=?)
Query: DELETE FROM chat_items WHERE user_id = ? AND group_id = ?
Plan:
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id=?)
SEARCH chat_item_mentions USING COVERING INDEX idx_chat_item_mentions_chat_item_id (chat_item_id=?)
SEARCH group_snd_item_statuses USING COVERING INDEX idx_group_snd_item_statuses_chat_item_id (chat_item_id=?)
SEARCH chat_item_versions USING COVERING INDEX idx_chat_item_versions_chat_item_id (chat_item_id=?)
@ -5198,7 +5209,7 @@ SEARCH chat_tags_chats USING COVERING INDEX idx_chat_tags_chats_chat_tag_id_grou
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_group_id (group_id=?)
SEARCH chat_item_reactions USING COVERING INDEX idx_chat_item_reactions_group_id (group_id=?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_fwd_from_group_id (fwd_from_group_id=?)
SCAN chat_items USING COVERING INDEX idx_chat_items_groups_item_ts
SCAN chat_items USING COVERING INDEX idx_chat_items_groups_user_mention
SEARCH messages USING COVERING INDEX idx_messages_group_id (group_id=?)
SEARCH user_contact_links USING COVERING INDEX idx_user_contact_links_group_id (group_id=?)
SEARCH files USING COVERING INDEX idx_files_group_id (group_id=?)
@ -5310,7 +5321,7 @@ SEARCH protocol_servers USING COVERING INDEX idx_smp_servers_user_id (user_id=?)
SEARCH settings USING COVERING INDEX idx_settings_user_id (user_id=?)
SEARCH commands USING COVERING INDEX idx_commands_user_id (user_id=?)
SEARCH calls USING COVERING INDEX idx_calls_user_id (user_id=?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_history (user_id=?)
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=?)
SEARCH contact_requests USING COVERING INDEX sqlite_autoindex_contact_requests_2 (user_id=?)
SEARCH user_contact_links USING COVERING INDEX sqlite_autoindex_user_contact_links_1 (user_id=?)
SEARCH connections USING COVERING INDEX idx_connections_group_member (user_id=?)
@ -5449,10 +5460,6 @@ Query: SELECT COUNT(1) FROM chat_item_versions WHERE chat_item_id = ?
Plan:
SEARCH chat_item_versions USING COVERING INDEX idx_chat_item_versions_chat_item_id (chat_item_id=?)
Query: SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND item_status = ?
Plan:
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups (user_id=? AND group_id=? AND item_status=?)
Query: SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
Plan:
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id=? AND msg_content_tag=? AND item_deleted=? AND item_sent=?)
@ -5465,6 +5472,10 @@ Query: SELECT COUNT(1) FROM groups WHERE user_id = ? AND chat_item_ttl > 0
Plan:
SEARCH groups USING INDEX idx_groups_chat_ts (user_id=?)
Query: SELECT COUNT(1), COALESCE(SUM(user_mention), 0) FROM chat_items WHERE user_id = ? AND group_id = ? AND item_status = ?
Plan:
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id=? AND item_status=?)
Query: SELECT accepted_at FROM operator_usage_conditions WHERE server_operator_id = ? AND conditions_commit = ?
Plan:
SEARCH operator_usage_conditions USING INDEX idx_operator_usage_conditions_conditions_commit (conditions_commit=? AND server_operator_id=?)

View file

@ -1011,3 +1011,9 @@ CREATE UNIQUE INDEX idx_chat_item_mentions_member_id ON chat_item_mentions(
chat_item_id,
member_id
);
CREATE INDEX idx_chat_items_groups_user_mention ON chat_items(
user_id,
group_id,
item_status,
user_mention
);

View file

@ -191,8 +191,9 @@ chatGroupTests = do
describe "group member reports" $ do
it "should send report to group owner, admins and moderators, but not other users" testGroupMemberReports
describe "group member mentions" $ do
it "should send messages with member mentions" testMemberMention
it "should send and edit messages with member mentions" testMemberMention
it "should forward and quote message updating mentioned member name" testForwardQuoteMention
it "should send updated mentions in history" testGroupHistoryWithMentions
describe "uniqueMsgMentions" testUniqueMsgMentions
describe "updatedMentionNames" testUpdatedMentionNames
where
@ -6686,6 +6687,12 @@ testMemberMention =
[ alice <# "#team cath> hello @Alice",
bob <# "#team cath> hello @Alice"
]
cath ##> "! #team hello @alice @bob"
cath <# "#team [edited] hello @alice @bob"
concurrentlyN_
[ alice <# "#team cath> [edited] hello @alice @bob",
bob <# "#team cath> [edited] hello @alice @bob"
]
testForwardQuoteMention :: HasCallStack => TestParams -> IO ()
testForwardQuoteMention =
@ -6753,6 +6760,41 @@ testForwardQuoteMention =
bob <# "alice> -> forwarded"
bob <## " hello @alice @alice_1"
testGroupHistoryWithMentions :: HasCallStack => TestParams -> IO ()
testGroupHistoryWithMentions =
testChat3 aliceProfile bobProfile cathProfile $
\alice bob cath -> do
createGroup2 "team" alice bob
threadDelay 1000000
alice #> "#team hello @bob"
bob <# "#team alice!> hello @bob"
bob ##> "/p robert"
bob <## "user profile is changed to robert (your 1 contacts are notified)"
alice <## "contact bob changed to robert"
alice <## "use @robert <message> to send messages"
alice ##> "/create link #team"
gLink <- getGroupLink alice "team" GRMember True
cath ##> ("/c " <> gLink)
cath <## "connection request sent!"
alice <## "cath (Catherine): accepting request to join group #team..."
concurrentlyN_
[ alice <## "#team: cath joined the group",
cath
<### [ "#team: joining the group...",
"#team: you joined the group",
WithTime "#team alice> hello @robert [>>]",
"#team: member robert is connected"
],
do
bob <## "#team: alice added cath (Catherine) to the group (connecting...)"
bob <## "#team: new member cath is connected"
]
testUniqueMsgMentions :: SpecWith TestParams
testUniqueMsgMentions = do
it "1 correct mention" $ \_ ->