From 480796a1df691f1ed76c43a78dc9f16dfefbd2a8 Mon Sep 17 00:00:00 2001 From: Eli Yukelzon Date: Thu, 1 Apr 2021 14:43:09 +0300 Subject: [PATCH] MM-33708 - Add MentionCountRoot column to ChannelMembers (#17099) * added new column for root-only mentions Co-authored-by: Mattermod --- .circleci/config.yml | 7 +- api4/channel_test.go | 41 ++++++++ api4/user.go | 21 ---- api4/user_test.go | 7 -- app/app_iface.go | 1 - app/channel.go | 6 +- app/channel_test.go | 26 +++-- app/notification.go | 2 +- app/opentracing/opentracing_layer.go | 22 ----- app/post.go | 26 +++-- app/post_test.go | 30 +++--- app/team.go | 21 ++-- app/user.go | 8 -- model/channel_member.go | 57 +++++------ model/client4.go | 14 --- model/team_member.go | 9 +- scripts/mysql-migration-test.sh | 2 +- scripts/psql-migration-test.sh | 2 +- store/opentracinglayer/opentracinglayer.go | 26 +---- store/retrylayer/retrylayer.go | 28 +----- store/sqlstore/channel_store.go | 108 ++++++++++++--------- store/sqlstore/channel_store_test.go | 2 + store/sqlstore/group_store.go | 1 + store/sqlstore/post_store.go | 2 +- store/sqlstore/team_store.go | 4 +- store/sqlstore/thread_store.go | 29 ------ store/sqlstore/upgrade.go | 32 ++++++ store/sqlstore/user_store.go | 1 + store/store.go | 5 +- store/storetest/channel_store.go | 11 ++- store/storetest/mocks/ChannelStore.go | 24 ++--- store/storetest/mocks/ThreadStore.go | 23 ----- store/storetest/thread_store.go | 6 +- store/storetest/user_store.go | 6 +- store/timerlayer/timerlayer.go | 24 +---- 35 files changed, 288 insertions(+), 346 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2d9c6ea35a..70d15b0ac7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -320,6 +320,9 @@ jobs: mattermost/mattermost-build-server:20201119_golang-1.15.5 \ bash -c "ulimit -n 8096; make ARGS='version' run-cli && make MM_SQLSETTINGS_DATASOURCE='postgres://mmuser:mostest@postgres:5432/latest?sslmode=disable&connect_timeout=10' ARGS='version' run-cli" + echo "Ignoring known mismatch: ChannelMembers.MentionCountRoot" + docker-compose --no-ansi exec -T postgres sh -c 'exec echo "ALTER TABLE ChannelMembers DROP COLUMN MentionCountRoot;" | exec psql -U mmuser -d migrated' + docker-compose --no-ansi exec -T postgres sh -c 'exec echo "ALTER TABLE ChannelMembers DROP COLUMN MentionCountRoot;" | exec psql -U mmuser -d latest' echo "Ignoring known mismatch: ChannelMembers.MsgCountRoot and Channels.TotalMsgCountRoot" docker-compose --no-ansi exec -T postgres sh -c 'exec echo "ALTER TABLE ChannelMembers DROP COLUMN MsgCountRoot;" | exec psql -U mmuser -d migrated' docker-compose --no-ansi exec -T postgres sh -c 'exec echo "ALTER TABLE ChannelMembers DROP COLUMN MsgCountRoot;" | exec psql -U mmuser -d latest' @@ -359,7 +362,9 @@ jobs: echo "Ignoring known MySQL mismatch: ChannelMembers.SchemeGuest" docker-compose --no-ansi exec -T mysql mysql -D migrated -uroot -pmostest -e "ALTER TABLE ChannelMembers DROP COLUMN SchemeGuest;" docker-compose --no-ansi exec -T mysql mysql -D latest -uroot -pmostest -e "ALTER TABLE ChannelMembers DROP COLUMN SchemeGuest;" - echo "Ignoring known MySQL mismatch: ChannelMembers.MentionCountRoot" + echo "Ignoring known MySQL mismatch: ChannelMembers.MentionCountRoot and MsgCountRoot" + docker-compose --no-ansi exec -T mysql mysql -D migrated -uroot -pmostest -e "ALTER TABLE ChannelMembers DROP COLUMN MentionCountRoot;" + docker-compose --no-ansi exec -T mysql mysql -D latest -uroot -pmostest -e "ALTER TABLE ChannelMembers DROP COLUMN MentionCountRoot;" docker-compose --no-ansi exec -T mysql mysql -D migrated -uroot -pmostest -e "ALTER TABLE ChannelMembers DROP COLUMN MsgCountRoot;" docker-compose --no-ansi exec -T mysql mysql -D latest -uroot -pmostest -e "ALTER TABLE ChannelMembers DROP COLUMN MsgCountRoot;" echo "Ignoring known MySQL mismatch: Channels.TotalMsgCountRoot" diff --git a/api4/channel_test.go b/api4/channel_test.go index 7729dedc8a..224907d8c1 100644 --- a/api4/channel_test.go +++ b/api4/channel_test.go @@ -2152,6 +2152,7 @@ func TestViewChannel(t *testing.T) { CheckNoError(t, resp) require.Equal(t, channel.TotalMsgCount, member.MsgCount, "should match message counts") require.Equal(t, int64(0), member.MentionCount, "should have no mentions") + require.Equal(t, int64(0), member.MentionCountRoot, "should have no mentions") _, resp = Client.ViewChannel("junk", view) CheckBadRequestStatus(t, resp) @@ -4064,3 +4065,43 @@ func TestMoveChannel(t *testing.T) { require.Equal(t, team2.Id, newChannel.TeamId) }, "Should be able to (force) move private channel by a member that is not member of target team") } + +func TestRootMentionsCount(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + Client := th.Client + user := th.BasicUser + channel := th.BasicChannel + + // initially, MentionCountRoot is 0 in the database + channelMember, err := th.App.Srv().Store.Channel().GetMember(context.Background(), channel.Id, user.Id) + require.NoError(t, err) + require.Equal(t, int64(0), channelMember.MentionCountRoot) + require.Equal(t, int64(0), channelMember.MentionCount) + + // mention the user in a root post + post1, resp := th.SystemAdminClient.CreatePost(&model.Post{ChannelId: channel.Id, Message: "hey @" + user.Username}) + CheckNoError(t, resp) + // mention the user in a reply post + post2 := &model.Post{ChannelId: channel.Id, Message: "reply at @" + user.Username, RootId: post1.Id} + _, resp = th.SystemAdminClient.CreatePost(post2) + CheckNoError(t, resp) + + // this should perform lazy migration and populate the field + channelUnread, resp := Client.GetChannelUnread(channel.Id, user.Id) + CheckNoError(t, resp) + // reply post is not counted, so we should have one root mention + require.EqualValues(t, int64(1), channelUnread.MentionCountRoot) + // regular count stays the same + require.Equal(t, int64(2), channelUnread.MentionCount) + // validate that DB is updated + channelMember, err = th.App.Srv().Store.Channel().GetMember(context.Background(), channel.Id, user.Id) + require.NoError(t, err) + require.EqualValues(t, int64(1), channelMember.MentionCountRoot) + + // validate that Team level counts are calculated + counts, appErr := th.App.GetTeamUnread(channel.TeamId, user.Id) + require.Nil(t, appErr) + require.Equal(t, int64(1), counts.MentionCountRoot) + require.Equal(t, int64(2), counts.MentionCount) +} diff --git a/api4/user.go b/api4/user.go index 831f197329..0d1d603722 100644 --- a/api4/user.go +++ b/api4/user.go @@ -94,7 +94,6 @@ func (api *API) InitUser() { api.BaseRoutes.UserThreads.Handle("", api.ApiSessionRequired(getThreadsForUser)).Methods("GET") api.BaseRoutes.UserThreads.Handle("/read", api.ApiSessionRequired(updateReadStateAllThreadsByUser)).Methods("PUT") - api.BaseRoutes.UserThreads.Handle("/mention_counts", api.ApiSessionRequired(getMentionCountsForAllThreadsByUser)).Methods("GET") api.BaseRoutes.UserThread.Handle("", api.ApiSessionRequired(getThreadForUser)).Methods("GET") api.BaseRoutes.UserThread.Handle("/following", api.ApiSessionRequired(followThreadByUser)).Methods("PUT") @@ -2871,26 +2870,6 @@ func getThreadForUser(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(threads.ToJson())) } -func getMentionCountsForAllThreadsByUser(c *Context, w http.ResponseWriter, r *http.Request) { - c.RequireUserId().RequireTeamId() - if c.Err != nil { - return - } - - if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) { - c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS) - return - } - counts, err := c.App.GetThreadMentionsForUserPerChannel(c.Params.UserId, c.Params.TeamId) - if err != nil { - c.Err = err - return - } - resp, _ := json.Marshal(counts) - - w.Write(resp) -} - func getThreadsForUser(c *Context, w http.ResponseWriter, r *http.Request) { c.RequireUserId().RequireTeamId() if c.Err != nil { diff --git a/api4/user_test.go b/api4/user_test.go index c9f3ba7dc6..5119efb842 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -5840,11 +5840,6 @@ func TestMaintainUnreadMentionsInThread(t *testing.T) { *cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.CollapsedThreads = model.COLLAPSED_THREADS_DEFAULT_ON }) - checkMentionCounts := func(client *model.Client4, userId string, expected map[string]int64) { - actual, resp2 := client.GetThreadMentionsForUserPerChannel(userId, th.BasicTeam.Id) - CheckNoError(t, resp2) - require.EqualValues(t, expected, actual) - } checkThreadList := func(client *model.Client4, userId string, expectedMentions, expectedThreads int) (*model.Threads, *model.Response) { uss, resp := client.GetUserThreads(userId, th.BasicTeam.Id, model.GetUserThreadsOpts{ Deleted: false, @@ -5870,7 +5865,6 @@ func TestMaintainUnreadMentionsInThread(t *testing.T) { // create reply and mention the original poster and another user postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply @" + th.BasicUser.Username + " and @" + th.BasicUser2.Username, RootId: rpost.Id}) - checkMentionCounts(Client, th.BasicUser.Id, map[string]int64{th.BasicChannel.Id: 1}) // basic user 1 was mentioned 1 time checkThreadList(th.Client, th.BasicUser.Id, 1, 1) // basic user 2 was mentioned 1 time @@ -5899,7 +5893,6 @@ func TestMaintainUnreadMentionsInThread(t *testing.T) { postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: dm.Id, Message: "msg2", RootId: dm_root_post.Id}) // expect increment by two mentions checkThreadList(th.Client, th.BasicUser.Id, 3, 2) - checkMentionCounts(Client, th.BasicUser.Id, map[string]int64{th.BasicChannel.Id: 1, dm.Id: 2}) } func TestReadThreads(t *testing.T) { diff --git a/app/app_iface.go b/app/app_iface.go index 5e00409720..85ccb2e746 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -704,7 +704,6 @@ type AppIface interface { GetTermsOfService(id string) (*model.TermsOfService, *model.AppError) GetThreadForUser(userID, teamID, threadId string, extended bool) (*model.ThreadResponse, *model.AppError) GetThreadMembershipsForUser(userID, teamID string) ([]*model.ThreadMembership, error) - GetThreadMentionsForUserPerChannel(userId, teamId string) (map[string]int64, *model.AppError) GetThreadsForUser(userID, teamID string, options model.GetUserThreadsOpts) (*model.Threads, *model.AppError) GetUploadSession(uploadId string) (*model.UploadSession, *model.AppError) GetUploadSessionsForUser(userID string) ([]*model.UploadSession, *model.AppError) diff --git a/app/channel.go b/app/channel.go index 279f4f752c..261be27865 100644 --- a/app/channel.go +++ b/app/channel.go @@ -1873,7 +1873,6 @@ func (a *App) GetChannelUnread(channelID, userID string) (*model.ChannelUnread, channelUnread.MsgCount = 0 channelUnread.MsgCountRoot = 0 } - return channelUnread, nil } @@ -2340,7 +2339,7 @@ func (a *App) MarkChannelAsUnreadFromPost(postID string, userID string) (*model. return nil, err } - unreadMentions, err := a.countMentionsFromPost(user, post) + unreadMentions, unreadMentionsRoot, err := a.countMentionsFromPost(user, post) if err != nil { return nil, err } @@ -2382,7 +2381,7 @@ func (a *App) MarkChannelAsUnreadFromPost(postID string, userID string) (*model. } } - channelUnread, nErr := a.Srv().Store.Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, *a.Config().ServiceSettings.ThreadAutoFollow) + channelUnread, nErr := a.Srv().Store.Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, unreadMentionsRoot, *a.Config().ServiceSettings.ThreadAutoFollow) if nErr != nil { return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, nErr.Error(), http.StatusInternalServerError) } @@ -2390,6 +2389,7 @@ func (a *App) MarkChannelAsUnreadFromPost(postID string, userID string) (*model. message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_UNREAD, channelUnread.TeamId, channelUnread.ChannelId, channelUnread.UserId, nil) message.Add("msg_count", channelUnread.MsgCount) message.Add("mention_count", channelUnread.MentionCount) + message.Add("mention_count_root", channelUnread.MentionCountRoot) message.Add("last_viewed_at", channelUnread.LastViewedAt) message.Add("post_id", postID) a.Publish(message) diff --git a/app/channel_test.go b/app/channel_test.go index 6358e3fde2..af1341f624 100644 --- a/app/channel_test.go +++ b/app/channel_test.go @@ -1343,15 +1343,24 @@ func TestMarkChannelAsUnreadFromPost(t *testing.T) { require.Nil(t, err) th.CreatePost(c2) + th.App.CreatePost(&model.Post{ + UserId: u2.Id, + ChannelId: c2.Id, + RootId: p4.Id, + Message: "@" + u1.Username, + }, c2, false, true) + response, err := th.App.MarkChannelAsUnreadFromPost(p4.Id, u1.Id) assert.Nil(t, err) assert.Equal(t, int64(1), response.MsgCount) - assert.Equal(t, int64(1), response.MentionCount) + assert.Equal(t, int64(2), response.MentionCount) + assert.Equal(t, int64(1), response.MentionCountRoot) unread, err := th.App.GetChannelUnread(c2.Id, u1.Id) require.Nil(t, err) - assert.Equal(t, int64(1), unread.MsgCount) - assert.Equal(t, int64(1), unread.MentionCount) + assert.Equal(t, int64(2), unread.MsgCount) + assert.Equal(t, int64(2), unread.MentionCount) + assert.Equal(t, int64(1), unread.MentionCountRoot) }) t.Run("Unread on a DM channel", func(t *testing.T) { @@ -1361,15 +1370,20 @@ func TestMarkChannelAsUnreadFromPost(t *testing.T) { th.CreatePost(dc) th.CreatePost(dc) + _, err := th.App.CreatePost(&model.Post{ChannelId: dc.Id, UserId: th.BasicUser.Id, Message: "testReply", RootId: dm1.Id}, dc, false, false) + assert.Nil(t, err) + response, err := th.App.MarkChannelAsUnreadFromPost(dm1.Id, u2.Id) assert.Nil(t, err) assert.Equal(t, int64(0), response.MsgCount) - assert.Equal(t, int64(3), response.MentionCount) + assert.Equal(t, int64(4), response.MentionCount) + assert.Equal(t, int64(3), response.MentionCountRoot) unread, err := th.App.GetChannelUnread(dc.Id, u2.Id) require.Nil(t, err) - assert.Equal(t, int64(3), unread.MsgCount) - assert.Equal(t, int64(3), unread.MentionCount) + assert.Equal(t, int64(4), unread.MsgCount) + assert.Equal(t, int64(4), unread.MentionCount) + assert.Equal(t, int64(3), unread.MentionCountRoot) }) t.Run("Can't unread an imaginary post", func(t *testing.T) { diff --git a/app/notification.go b/app/notification.go index f85e9e1eda..98ae79ae79 100644 --- a/app/notification.go +++ b/app/notification.go @@ -201,7 +201,7 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod umc := make(chan *model.AppError, 1) go func(userID string) { defer close(umc) - nErr := a.Srv().Store.Channel().IncrementMentionCount(post.ChannelId, userID, *a.Config().ServiceSettings.ThreadAutoFollow) + nErr := a.Srv().Store.Channel().IncrementMentionCount(post.ChannelId, userID, *a.Config().ServiceSettings.ThreadAutoFollow, post.RootId == "") if nErr != nil { umc <- model.NewAppError("SendNotifications", "app.channel.increment_mention_count.app_error", nil, nErr.Error(), http.StatusInternalServerError) return diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index 0179055d05..c0873aeb05 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -8650,28 +8650,6 @@ func (a *OpenTracingAppLayer) GetThreadMembershipsForUser(userID string, teamID return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) GetThreadMentionsForUserPerChannel(userId string, teamId string) (map[string]int64, *model.AppError) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetThreadMentionsForUserPerChannel") - - a.ctx = newCtx - a.app.Srv().Store.SetContext(newCtx) - defer func() { - a.app.Srv().Store.SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - resultVar0, resultVar1 := a.app.GetThreadMentionsForUserPerChannel(userId, teamId) - - if resultVar1 != nil { - span.LogFields(spanlog.Error(resultVar1)) - ext.Error.Set(span, true) - } - - return resultVar0, resultVar1 -} - func (a *OpenTracingAppLayer) GetThreadsForUser(userID string, teamID string, options model.GetUserThreadsOpts) (*model.Threads, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetThreadsForUser") diff --git a/app/post.go b/app/post.go index ff8756c787..4260e5bb7d 100644 --- a/app/post.go +++ b/app/post.go @@ -1403,25 +1403,25 @@ func (a *App) countThreadMentions(user *model.User, post *model.Post, teamID str // countMentionsFromPost returns the number of posts in the post's channel that mention the user after and including the // given post. -func (a *App) countMentionsFromPost(user *model.User, post *model.Post) (int, *model.AppError) { +func (a *App) countMentionsFromPost(user *model.User, post *model.Post) (int, int, *model.AppError) { channel, err := a.GetChannel(post.ChannelId) if err != nil { - return 0, err + return 0, 0, err } if channel.Type == model.CHANNEL_DIRECT { // In a DM channel, every post made by the other user is a mention - count, _, nErr := a.Srv().Store.Channel().CountPostsAfter(post.ChannelId, post.CreateAt-1, channel.GetOtherUserIdForDM(user.Id)) + count, countRoot, nErr := a.Srv().Store.Channel().CountPostsAfter(post.ChannelId, post.CreateAt-1, channel.GetOtherUserIdForDM(user.Id)) if nErr != nil { - return 0, model.NewAppError("countMentionsFromPost", "app.channel.count_posts_since.app_error", nil, nErr.Error(), http.StatusInternalServerError) + return 0, 0, model.NewAppError("countMentionsFromPost", "app.channel.count_posts_since.app_error", nil, nErr.Error(), http.StatusInternalServerError) } - return count, nil + return count, countRoot, nil } channelMember, err := a.GetChannelMember(context.Background(), channel.Id, user.Id) if err != nil { - return 0, err + return 0, 0, err } keywords := addMentionKeywordsForUser( @@ -1439,13 +1439,16 @@ func (a *App) countMentionsFromPost(user *model.User, post *model.Post) (int, *m thread, err := a.GetPostThread(post.Id, false, false, false, user.Id) if err != nil { - return 0, err + return 0, 0, err } count := 0 - + countRoot := 0 if isPostMention(user, post, keywords, thread.Posts, mentionedByThread, checkForCommentMentions) { count += 1 + if post.RootId == "" { + countRoot += 1 + } } page := 0 @@ -1458,12 +1461,15 @@ func (a *App) countMentionsFromPost(user *model.User, post *model.Post) (int, *m PerPage: perPage, }) if err != nil { - return 0, err + return 0, 0, err } for _, postID := range postList.Order { if isPostMention(user, postList.Posts[postID], keywords, postList.Posts, mentionedByThread, checkForCommentMentions) { count += 1 + if postList.Posts[postID].RootId == "" { + countRoot += 1 + } } } @@ -1474,7 +1480,7 @@ func (a *App) countMentionsFromPost(user *model.User, post *model.Post) (int, *m page += 1 } - return count, nil + return count, countRoot, nil } func isCommentMention(user *model.User, post *model.Post, otherPosts map[string]*model.Post, mentionedByThread map[string]bool) bool { diff --git a/app/post_test.go b/app/post_test.go index bcd1c000b7..775da0a01d 100644 --- a/app/post_test.go +++ b/app/post_test.go @@ -1231,7 +1231,7 @@ func TestCountMentionsFromPost(t *testing.T) { }, channel, false, true) require.Nil(t, err) - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 0, count) @@ -1270,7 +1270,7 @@ func TestCountMentionsFromPost(t *testing.T) { // post1 and post3 should mention the user - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 2, count) @@ -1309,7 +1309,7 @@ func TestCountMentionsFromPost(t *testing.T) { // post2 and post3 should mention the user - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 2, count) @@ -1346,7 +1346,7 @@ func TestCountMentionsFromPost(t *testing.T) { }, channel, false, true) require.Nil(t, err) - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 0, count) @@ -1388,7 +1388,7 @@ func TestCountMentionsFromPost(t *testing.T) { }, channel, false, true) require.Nil(t, err) - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 0, count) @@ -1442,7 +1442,7 @@ func TestCountMentionsFromPost(t *testing.T) { // post2 should mention the user - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 1, count) @@ -1496,7 +1496,7 @@ func TestCountMentionsFromPost(t *testing.T) { // post2 and post5 should mention the user - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 2, count) @@ -1545,7 +1545,7 @@ func TestCountMentionsFromPost(t *testing.T) { // should be mentioned by post2 and post3 - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 2, count) @@ -1575,12 +1575,12 @@ func TestCountMentionsFromPost(t *testing.T) { }, channel, false, true) require.Nil(t, err) - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 2, count) - count, err = th.App.countMentionsFromPost(user1, post1) + count, _, err = th.App.countMentionsFromPost(user1, post1) assert.Nil(t, err) assert.Equal(t, 0, count) @@ -1617,7 +1617,7 @@ func TestCountMentionsFromPost(t *testing.T) { // post1 and post3 should mention the user, but we only count post3 - count, err := th.App.countMentionsFromPost(user2, post2) + count, _, err := th.App.countMentionsFromPost(user2, post2) assert.Nil(t, err) assert.Equal(t, 1, count) @@ -1648,7 +1648,7 @@ func TestCountMentionsFromPost(t *testing.T) { // post2 should mention the user - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 1, count) @@ -1695,7 +1695,7 @@ func TestCountMentionsFromPost(t *testing.T) { // post4 should mention the user - count, err := th.App.countMentionsFromPost(user2, post3) + count, _, err := th.App.countMentionsFromPost(user2, post3) assert.Nil(t, err) assert.Equal(t, 1, count) @@ -1735,7 +1735,7 @@ func TestCountMentionsFromPost(t *testing.T) { // post3 should mention the user - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, 1, count) @@ -1771,7 +1771,7 @@ func TestCountMentionsFromPost(t *testing.T) { // Every post should mention the user - count, err := th.App.countMentionsFromPost(user2, post1) + count, _, err := th.App.countMentionsFromPost(user2, post1) assert.Nil(t, err) assert.Equal(t, numPosts, count) diff --git a/app/team.go b/app/team.go index 42f4bb4c3b..8cba7bd1fb 100644 --- a/app/team.go +++ b/app/team.go @@ -1139,14 +1139,15 @@ func (a *App) GetTeamUnread(teamID, userID string) (*model.TeamUnread, *model.Ap } var teamUnread = &model.TeamUnread{ - MsgCount: 0, - MsgCountRoot: 0, - MentionCount: 0, - TeamId: teamID, + MsgCount: 0, + MentionCount: 0, + MentionCountRoot: 0, + MsgCountRoot: 0, + TeamId: teamID, } - for _, cu := range channelUnreads { teamUnread.MentionCount += cu.MentionCount + teamUnread.MentionCountRoot += cu.MentionCountRoot if cu.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] != model.CHANNEL_MARK_UNREAD_MENTION { teamUnread.MsgCount += cu.MsgCount @@ -1677,6 +1678,7 @@ func (a *App) GetTeamsUnreadForUser(excludeTeamId string, userID string) ([]*mod unreads := func(cu *model.ChannelUnread, tu *model.TeamUnread) *model.TeamUnread { tu.MentionCount += cu.MentionCount + tu.MentionCountRoot += cu.MentionCountRoot if cu.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] != model.CHANNEL_MARK_UNREAD_MENTION { tu.MsgCount += cu.MsgCount @@ -1692,10 +1694,11 @@ func (a *App) GetTeamsUnreadForUser(excludeTeamId string, userID string) ([]*mod membersMap[id] = unreads(data[i], mu) } else { membersMap[id] = unreads(data[i], &model.TeamUnread{ - MsgCount: 0, - MsgCountRoot: 0, - MentionCount: 0, - TeamId: id, + MsgCount: 0, + MentionCount: 0, + MentionCountRoot: 0, + MsgCountRoot: 0, + TeamId: id, }) } } diff --git a/app/user.go b/app/user.go index 29a0916ca3..860fd7f5be 100644 --- a/app/user.go +++ b/app/user.go @@ -2401,14 +2401,6 @@ func (a *App) GetThreadsForUser(userID, teamID string, options model.GetUserThre return threads, nil } -func (a *App) GetThreadMentionsForUserPerChannel(userId, teamId string) (map[string]int64, *model.AppError) { - res, err := a.Srv().Store.Thread().GetThreadMentionsForUserPerChannel(userId, teamId) - if err != nil { - return nil, model.NewAppError("GetThreadMentionsForUserPerChannel", "app.user.get_threads_for_user.app_error", nil, err.Error(), http.StatusInternalServerError) - } - return res, nil -} - func (a *App) GetThreadForUser(userID, teamID, threadId string, extended bool) (*model.ThreadResponse, *model.AppError) { thread, err := a.Srv().Store.Thread().GetThreadForUser(userID, teamID, threadId, extended) if err != nil { diff --git a/model/channel_member.go b/model/channel_member.go index 5e1beee2ac..bc02881e63 100644 --- a/model/channel_member.go +++ b/model/channel_member.go @@ -24,39 +24,42 @@ const ( ) type ChannelUnread struct { - TeamId string `json:"team_id"` - ChannelId string `json:"channel_id"` - MsgCount int64 `json:"msg_count"` - MsgCountRoot int64 `json:"msg_count_root"` - MentionCount int64 `json:"mention_count"` - NotifyProps StringMap `json:"-"` + TeamId string `json:"team_id"` + ChannelId string `json:"channel_id"` + MsgCount int64 `json:"msg_count"` + MentionCount int64 `json:"mention_count"` + MentionCountRoot int64 `json:"mention_count_root"` + MsgCountRoot int64 `json:"msg_count_root"` + NotifyProps StringMap `json:"-"` } type ChannelUnreadAt struct { - TeamId string `json:"team_id"` - UserId string `json:"user_id"` - ChannelId string `json:"channel_id"` - MsgCount int64 `json:"msg_count"` - MsgCountRoot int64 `json:"msg_count_root"` - MentionCount int64 `json:"mention_count"` - LastViewedAt int64 `json:"last_viewed_at"` - NotifyProps StringMap `json:"-"` + TeamId string `json:"team_id"` + UserId string `json:"user_id"` + ChannelId string `json:"channel_id"` + MsgCount int64 `json:"msg_count"` + MentionCount int64 `json:"mention_count"` + MentionCountRoot int64 `json:"mention_count_root"` + MsgCountRoot int64 `json:"msg_count_root"` + LastViewedAt int64 `json:"last_viewed_at"` + NotifyProps StringMap `json:"-"` } type ChannelMember struct { - ChannelId string `json:"channel_id"` - UserId string `json:"user_id"` - Roles string `json:"roles"` - LastViewedAt int64 `json:"last_viewed_at"` - MsgCount int64 `json:"msg_count"` - MentionCount int64 `json:"mention_count"` - NotifyProps StringMap `json:"notify_props"` - LastUpdateAt int64 `json:"last_update_at"` - SchemeGuest bool `json:"scheme_guest"` - SchemeUser bool `json:"scheme_user"` - SchemeAdmin bool `json:"scheme_admin"` - ExplicitRoles string `json:"explicit_roles"` - MsgCountRoot int64 `json:"msg_count_root"` + ChannelId string `json:"channel_id"` + UserId string `json:"user_id"` + Roles string `json:"roles"` + LastViewedAt int64 `json:"last_viewed_at"` + MsgCount int64 `json:"msg_count"` + MentionCount int64 `json:"mention_count"` + MentionCountRoot int64 `json:"mention_count_root"` + MsgCountRoot int64 `json:"msg_count_root"` + NotifyProps StringMap `json:"notify_props"` + LastUpdateAt int64 `json:"last_update_at"` + SchemeGuest bool `json:"scheme_guest"` + SchemeUser bool `json:"scheme_user"` + SchemeAdmin bool `json:"scheme_admin"` + ExplicitRoles string `json:"explicit_roles"` } type ChannelMembers []ChannelMember diff --git a/model/client4.go b/model/client4.go index 809fa8fbb7..e1974ff2f4 100644 --- a/model/client4.go +++ b/model/client4.go @@ -5885,20 +5885,6 @@ func (c *Client4) DownloadExport(name string, wr io.Writer, offset int64) (int64 return n, BuildResponse(r) } -func (c *Client4) GetThreadMentionsForUserPerChannel(userId, teamId string) (map[string]int64, *Response) { - url := c.GetUserThreadsRoute(userId, teamId) - r, appErr := c.DoApiGet(url+"/mention_counts", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var counts map[string]int64 - json.NewDecoder(r.Body).Decode(&counts) - - return counts, BuildResponse(r) -} - func (c *Client4) GetUserThreads(userId, teamId string, options GetUserThreadsOpts) (*Threads, *Response) { v := url.Values{} if options.Since != 0 { diff --git a/model/team_member.go b/model/team_member.go index 86187db2bc..e8a82c3e2b 100644 --- a/model/team_member.go +++ b/model/team_member.go @@ -31,10 +31,11 @@ type TeamMember struct { //msgp:ignore TeamUnread type TeamUnread struct { - TeamId string `json:"team_id"` - MsgCount int64 `json:"msg_count"` - MsgCountRoot int64 `json:"msg_count_root"` - MentionCount int64 `json:"mention_count"` + TeamId string `json:"team_id"` + MsgCount int64 `json:"msg_count"` + MentionCount int64 `json:"mention_count"` + MentionCountRoot int64 `json:"mention_count_root"` + MsgCountRoot int64 `json:"msg_count_root"` } //msgp:ignore TeamMemberForExport diff --git a/scripts/mysql-migration-test.sh b/scripts/mysql-migration-test.sh index 6e15d28ffa..09955c094f 100755 --- a/scripts/mysql-migration-test.sh +++ b/scripts/mysql-migration-test.sh @@ -22,7 +22,7 @@ make ARGS="config set SqlSettings.DataSource 'mmuser:mostest@tcp(localhost:3306) echo "Setting up fresh db" make ARGS="version --config $TMPDIR/config.json" run-cli -for i in "ChannelMembers SchemeGuest" "ChannelMembers MsgCountRoot" "Channels TotalMsgCountRoot"; do +for i in "ChannelMembers SchemeGuest" "ChannelMembers MsgCountRoot" "ChannelMembers MentionCountRoot" "Channels TotalMsgCountRoot"; do a=( $i ); echo "Ignoring known MySQL mismatch: ${a[0]}.${a[1]}" docker exec mattermost-mysql mysql -D migrated -uroot -pmostest -e "ALTER TABLE ${a[0]} DROP COLUMN ${a[1]};" diff --git a/scripts/psql-migration-test.sh b/scripts/psql-migration-test.sh index c5ef977bde..817c67a8c8 100755 --- a/scripts/psql-migration-test.sh +++ b/scripts/psql-migration-test.sh @@ -22,7 +22,7 @@ make ARGS="config set SqlSettings.DataSource 'postgres://mmuser:mostest@localhos echo "Setting up fresh db" make ARGS="version --config $TMPDIR/config.json" run-cli -for i in "ChannelMembers MsgCountRoot" "Channels TotalMsgCountRoot"; do +for i in "ChannelMembers MentionCountRoot" "ChannelMembers MsgCountRoot" "Channels TotalMsgCountRoot"; do a=( $i ); echo "Ignoring known Postgres mismatch: ${a[0]}.${a[1]}" docker exec mattermost-postgres psql -U mmuser -d migrated -c "ALTER TABLE ${a[0]} DROP COLUMN ${a[1]};" diff --git a/store/opentracinglayer/opentracinglayer.go b/store/opentracinglayer/opentracinglayer.go index 01b3ff45cc..5d578fbbc6 100644 --- a/store/opentracinglayer/opentracinglayer.go +++ b/store/opentracinglayer/opentracinglayer.go @@ -1556,7 +1556,7 @@ func (s *OpenTracingLayerChannelStore) GroupSyncedChannelCount() (int64, error) return result, err } -func (s *OpenTracingLayerChannelStore) IncrementMentionCount(channelID string, userId string, updateThreads bool) error { +func (s *OpenTracingLayerChannelStore) IncrementMentionCount(channelID string, userId string, updateThreads bool, isRoot bool) error { origCtx := s.Root.Store.Context() span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.IncrementMentionCount") s.Root.Store.SetContext(newCtx) @@ -1565,7 +1565,7 @@ func (s *OpenTracingLayerChannelStore) IncrementMentionCount(channelID string, u }() defer span.Finish() - err := s.ChannelStore.IncrementMentionCount(channelID, userId, updateThreads) + err := s.ChannelStore.IncrementMentionCount(channelID, userId, updateThreads, isRoot) if err != nil { span.LogFields(spanlog.Error(err)) ext.Error.Set(span, true) @@ -2110,7 +2110,7 @@ func (s *OpenTracingLayerChannelStore) UpdateLastViewedAt(channelIds []string, u return result, err } -func (s *OpenTracingLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, updateThreads bool) (*model.ChannelUnreadAt, error) { +func (s *OpenTracingLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, updateThreads bool) (*model.ChannelUnreadAt, error) { origCtx := s.Root.Store.Context() span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UpdateLastViewedAtPost") s.Root.Store.SetContext(newCtx) @@ -2119,7 +2119,7 @@ func (s *OpenTracingLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model. }() defer span.Finish() - result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, updateThreads) + result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, mentionCountRoot, updateThreads) if err != nil { span.LogFields(spanlog.Error(err)) ext.Error.Set(span, true) @@ -7864,24 +7864,6 @@ func (s *OpenTracingLayerThreadStore) GetThreadForUser(userId string, teamId str return result, err } -func (s *OpenTracingLayerThreadStore) GetThreadMentionsForUserPerChannel(userId string, teamId string) (map[string]int64, error) { - origCtx := s.Root.Store.Context() - span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetThreadMentionsForUserPerChannel") - s.Root.Store.SetContext(newCtx) - defer func() { - s.Root.Store.SetContext(origCtx) - }() - - defer span.Finish() - result, err := s.ThreadStore.GetThreadMentionsForUserPerChannel(userId, teamId) - if err != nil { - span.LogFields(spanlog.Error(err)) - ext.Error.Set(span, true) - } - - return result, err -} - func (s *OpenTracingLayerThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) { origCtx := s.Root.Store.Context() span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetThreadsForUser") diff --git a/store/retrylayer/retrylayer.go b/store/retrylayer/retrylayer.go index 6cc8c1686b..7a358fd5b3 100644 --- a/store/retrylayer/retrylayer.go +++ b/store/retrylayer/retrylayer.go @@ -1690,11 +1690,11 @@ func (s *RetryLayerChannelStore) GroupSyncedChannelCount() (int64, error) { } -func (s *RetryLayerChannelStore) IncrementMentionCount(channelID string, userId string, updateThreads bool) error { +func (s *RetryLayerChannelStore) IncrementMentionCount(channelID string, userId string, updateThreads bool, isRoot bool) error { tries := 0 for { - err := s.ChannelStore.IncrementMentionCount(channelID, userId, updateThreads) + err := s.ChannelStore.IncrementMentionCount(channelID, userId, updateThreads, isRoot) if err == nil { return nil } @@ -2238,11 +2238,11 @@ func (s *RetryLayerChannelStore) UpdateLastViewedAt(channelIds []string, userId } -func (s *RetryLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, updateThreads bool) (*model.ChannelUnreadAt, error) { +func (s *RetryLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, updateThreads bool) (*model.ChannelUnreadAt, error) { tries := 0 for { - result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, updateThreads) + result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, mentionCountRoot, updateThreads) if err == nil { return result, nil } @@ -8538,26 +8538,6 @@ func (s *RetryLayerThreadStore) GetThreadForUser(userId string, teamId string, t } -func (s *RetryLayerThreadStore) GetThreadMentionsForUserPerChannel(userId string, teamId string) (map[string]int64, error) { - - tries := 0 - for { - result, err := s.ThreadStore.GetThreadMentionsForUserPerChannel(userId, teamId) - if err == nil { - return result, nil - } - if !isRepeatableError(err) { - return result, err - } - tries++ - if tries >= 3 { - err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures") - return result, err - } - } - -} - func (s *RetryLayerThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) { tries := 0 diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go index 13237a517a..550ba66c24 100644 --- a/store/sqlstore/channel_store.go +++ b/store/sqlstore/channel_store.go @@ -39,34 +39,36 @@ type SqlChannelStore struct { } type channelMember struct { - ChannelId string - UserId string - Roles string - LastViewedAt int64 - MsgCount int64 - MentionCount int64 - NotifyProps model.StringMap - LastUpdateAt int64 - SchemeUser sql.NullBool - SchemeAdmin sql.NullBool - SchemeGuest sql.NullBool - MsgCountRoot int64 + ChannelId string + UserId string + Roles string + LastViewedAt int64 + MsgCount int64 + MentionCount int64 + NotifyProps model.StringMap + LastUpdateAt int64 + SchemeUser sql.NullBool + SchemeAdmin sql.NullBool + SchemeGuest sql.NullBool + MentionCountRoot int64 + MsgCountRoot int64 } func NewChannelMemberFromModel(cm *model.ChannelMember) *channelMember { return &channelMember{ - ChannelId: cm.ChannelId, - UserId: cm.UserId, - Roles: cm.ExplicitRoles, - LastViewedAt: cm.LastViewedAt, - MsgCount: cm.MsgCount, - MsgCountRoot: cm.MsgCountRoot, - MentionCount: cm.MentionCount, - NotifyProps: cm.NotifyProps, - LastUpdateAt: cm.LastUpdateAt, - SchemeGuest: sql.NullBool{Valid: true, Bool: cm.SchemeGuest}, - SchemeUser: sql.NullBool{Valid: true, Bool: cm.SchemeUser}, - SchemeAdmin: sql.NullBool{Valid: true, Bool: cm.SchemeAdmin}, + ChannelId: cm.ChannelId, + UserId: cm.UserId, + Roles: cm.ExplicitRoles, + LastViewedAt: cm.LastViewedAt, + MsgCount: cm.MsgCount, + MentionCount: cm.MentionCount, + MentionCountRoot: cm.MentionCountRoot, + MsgCountRoot: cm.MsgCountRoot, + NotifyProps: cm.NotifyProps, + LastUpdateAt: cm.LastUpdateAt, + SchemeGuest: sql.NullBool{Valid: true, Bool: cm.SchemeGuest}, + SchemeUser: sql.NullBool{Valid: true, Bool: cm.SchemeUser}, + SchemeAdmin: sql.NullBool{Valid: true, Bool: cm.SchemeAdmin}, } } @@ -77,6 +79,7 @@ type channelMemberWithSchemeRoles struct { LastViewedAt int64 MsgCount int64 MentionCount int64 + MentionCountRoot int64 NotifyProps model.StringMap LastUpdateAt int64 SchemeGuest sql.NullBool @@ -92,7 +95,7 @@ type channelMemberWithSchemeRoles struct { } func channelMemberSliceColumns() []string { - return []string{"ChannelId", "UserId", "Roles", "LastViewedAt", "MsgCount", "MsgCountRoot", "MentionCount", "NotifyProps", "LastUpdateAt", "SchemeUser", "SchemeAdmin", "SchemeGuest"} + return []string{"ChannelId", "UserId", "Roles", "LastViewedAt", "MsgCount", "MsgCountRoot", "MentionCount", "MentionCountRoot", "NotifyProps", "LastUpdateAt", "SchemeUser", "SchemeAdmin", "SchemeGuest"} } func channelMemberToSlice(member *model.ChannelMember) []interface{} { @@ -104,6 +107,7 @@ func channelMemberToSlice(member *model.ChannelMember) []interface{} { resultSlice = append(resultSlice, member.MsgCount) resultSlice = append(resultSlice, member.MsgCountRoot) resultSlice = append(resultSlice, member.MentionCount) + resultSlice = append(resultSlice, member.MentionCountRoot) resultSlice = append(resultSlice, model.MapToJson(member.NotifyProps)) resultSlice = append(resultSlice, member.LastUpdateAt) resultSlice = append(resultSlice, member.SchemeUser) @@ -229,19 +233,20 @@ func (db channelMemberWithSchemeRoles) ToModel() *model.ChannelMember { strings.Fields(db.Roles), ) return &model.ChannelMember{ - ChannelId: db.ChannelId, - UserId: db.UserId, - Roles: strings.Join(rolesResult.roles, " "), - LastViewedAt: db.LastViewedAt, - MsgCount: db.MsgCount, - MsgCountRoot: db.MsgCountRoot, - MentionCount: db.MentionCount, - NotifyProps: db.NotifyProps, - LastUpdateAt: db.LastUpdateAt, - SchemeAdmin: rolesResult.schemeAdmin, - SchemeUser: rolesResult.schemeUser, - SchemeGuest: rolesResult.schemeGuest, - ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "), + ChannelId: db.ChannelId, + UserId: db.UserId, + Roles: strings.Join(rolesResult.roles, " "), + LastViewedAt: db.LastViewedAt, + MsgCount: db.MsgCount, + MsgCountRoot: db.MsgCountRoot, + MentionCount: db.MentionCount, + MentionCountRoot: db.MentionCountRoot, + NotifyProps: db.NotifyProps, + LastUpdateAt: db.LastUpdateAt, + SchemeAdmin: rolesResult.schemeAdmin, + SchemeUser: rolesResult.schemeUser, + SchemeGuest: rolesResult.schemeGuest, + ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "), } } @@ -717,10 +722,7 @@ func (s SqlChannelStore) GetChannelUnread(channelId, userId string) (*model.Chan var unreadChannel model.ChannelUnread err := s.GetReplica().SelectOne(&unreadChannel, `SELECT - Channels.TeamId TeamId, Channels.Id ChannelId, - (Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount, - (Channels.TotalMsgCountRoot - ChannelMembers.MsgCountRoot) MsgCountRoot, - ChannelMembers.MentionCount MentionCount, ChannelMembers.NotifyProps NotifyProps + Channels.TeamId TeamId, Channels.Id ChannelId, (Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount, (Channels.TotalMsgCountRoot - ChannelMembers.MsgCountRoot) MsgCountRoot, ChannelMembers.MentionCount MentionCount, ChannelMembers.MentionCountRoot MentionCountRoot, ChannelMembers.NotifyProps NotifyProps FROM Channels, ChannelMembers WHERE @@ -2086,6 +2088,7 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string, ChannelMembers cm SET MentionCount = 0, + MentionCountRoot = 0, MsgCount = greatest(cm.MsgCount, c.TotalMsgCount), MsgCountRoot = greatest(cm.MsgCountRoot, c.TotalMsgCountRoot), LastViewedAt = greatest(cm.LastViewedAt, c.LastPostAt), @@ -2140,6 +2143,7 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string, ChannelMembers SET MentionCount = 0, + MentionCountRoot = 0, MsgCount = CASE ChannelId ` + msgCountQuery + ` END, MsgCountRoot = CASE ChannelId ` + msgCountQueryRoot + ` END, LastViewedAt = CASE ChannelId ` + lastViewedQuery + ` END, @@ -2196,7 +2200,7 @@ func (s SqlChannelStore) CountPostsAfter(channelId string, timestamp int64, user // UpdateLastViewedAtPost updates a ChannelMember as if the user last read the channel at the time of the given post. // If the provided mentionCount is -1, the given post and all posts after it are considered to be mentions. Returns // an updated model.ChannelUnreadAt that can be returned to the client. -func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, updateThreads bool) (*model.ChannelUnreadAt, error) { +func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount, mentionCountRoot int, updateThreads bool) (*model.ChannelUnreadAt, error) { var threadsToUpdate []string unreadDate := unreadPost.CreateAt - 1 if updateThreads { @@ -2214,6 +2218,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s params := map[string]interface{}{ "mentions": mentionCount, + "mentionsRoot": mentionCountRoot, "unreadCount": unread, "unreadCountRoot": unreadRoot, "lastViewedAt": unreadDate, @@ -2229,6 +2234,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s ChannelMembers SET MentionCount = :mentions, + MentionCountRoot = :mentionsRoot, MsgCount = (SELECT TotalMsgCount FROM Channels WHERE ID = :channelId) - :unreadCount, MsgCountRoot = (SELECT TotalMsgCountRoot FROM Channels WHERE ID = :channelId) - :unreadCountRoot, LastViewedAt = :lastViewedAt, @@ -2250,6 +2256,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s cm.MsgCount MsgCount, cm.MsgCountRoot MsgCountRoot, cm.MentionCount MentionCount, + cm.MentionCountRoot MentionCountRoot, cm.LastViewedAt LastViewedAt, cm.NotifyProps NotifyProps FROM @@ -2271,7 +2278,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s return result, nil } -func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string, updateThreads bool) error { +func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string, updateThreads, isRoot bool) error { now := model.GetMillis() var threadsToUpdate []string if updateThreads { @@ -2281,17 +2288,21 @@ func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string, return err } } - + rootInc := 0 + if isRoot { + rootInc = 1 + } _, err := s.GetMaster().Exec( `UPDATE ChannelMembers SET MentionCount = MentionCount + 1, + MentionCountRoot = MentionCountRoot + :RootInc, LastUpdateAt = :LastUpdateAt WHERE UserId = :UserId - AND ChannelId = :ChannelId`, - map[string]interface{}{"ChannelId": channelId, "UserId": userId, "LastUpdateAt": now}) + AND ChannelId = :ChannelId`, + map[string]interface{}{"ChannelId": channelId, "UserId": userId, "LastUpdateAt": now, "RootInc": rootInc}) if err != nil { return errors.Wrapf(err, "failed to Update ChannelMembers with channelId=%s and userId=%s", channelId, userId) } @@ -3186,6 +3197,7 @@ func (s SqlChannelStore) GetChannelMembersForExport(userId string, teamId string ChannelMembers.LastViewedAt, ChannelMembers.MsgCount, ChannelMembers.MentionCount, + ChannelMembers.MentionCountRoot, ChannelMembers.NotifyProps, ChannelMembers.LastUpdateAt, ChannelMembers.SchemeUser, @@ -3236,7 +3248,7 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s channelIds = append(channelIds, channel.Id) } query = s.getQueryBuilder(). - Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest"). + Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, MentionCountRoot, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest"). From("ChannelMembers cm"). Join("Users u ON ( u.Id = cm.UserId )"). Where(sq.And{ diff --git a/store/sqlstore/channel_store_test.go b/store/sqlstore/channel_store_test.go index 46143fa04c..86660460e2 100644 --- a/store/sqlstore/channel_store_test.go +++ b/store/sqlstore/channel_store_test.go @@ -69,6 +69,7 @@ func testNewChannelMemberFromModel(t *testing.T) { assert.Equal(t, m.LastViewedAt, db.LastViewedAt) assert.Equal(t, m.MsgCount, db.MsgCount) assert.Equal(t, m.MentionCount, db.MentionCount) + assert.Equal(t, int64(0), m.MentionCountRoot) assert.Equal(t, m.NotifyProps, db.NotifyProps) assert.Equal(t, m.LastUpdateAt, db.LastUpdateAt) assert.Equal(t, true, db.SchemeGuest.Valid) @@ -111,6 +112,7 @@ func testChannelMemberWithSchemeRolesToModel(t *testing.T) { assert.Equal(t, db.LastViewedAt, m.LastViewedAt) assert.Equal(t, db.MsgCount, m.MsgCount) assert.Equal(t, db.MentionCount, m.MentionCount) + assert.Equal(t, db.MentionCountRoot, m.MentionCountRoot) assert.Equal(t, db.NotifyProps, m.NotifyProps) assert.Equal(t, db.LastUpdateAt, m.LastUpdateAt) assert.Equal(t, db.SchemeGuest.Bool, m.SchemeGuest) diff --git a/store/sqlstore/group_store.go b/store/sqlstore/group_store.go index 899711556a..519c9bafbc 100644 --- a/store/sqlstore/group_store.go +++ b/store/sqlstore/group_store.go @@ -919,6 +919,7 @@ func (s *SqlGroupStore) ChannelMembersToRemove(channelID *string) ([]*model.Chan "ChannelMembers.MsgCount", "ChannelMembers.MsgCountRoot", "ChannelMembers.MentionCount", + "ChannelMembers.MentionCountRoot", "ChannelMembers.NotifyProps", "ChannelMembers.LastUpdateAt", "ChannelMembers.LastUpdateAt", diff --git a/store/sqlstore/post_store.go b/store/sqlstore/post_store.go index 7b49b2eac3..d105f70fd4 100644 --- a/store/sqlstore/post_store.go +++ b/store/sqlstore/post_store.go @@ -2030,7 +2030,7 @@ func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId str channelIds = append(channelIds, post.ChannelId) } query = s.getQueryBuilder(). - Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest"). + Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, MentionCountRoot, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest"). From("ChannelMembers cm"). Join("Users u ON ( u.Id = cm.UserId )"). Where(sq.Eq{ diff --git a/store/sqlstore/team_store.go b/store/sqlstore/team_store.go index 3f798e776f..bfd9f74a95 100644 --- a/store/sqlstore/team_store.go +++ b/store/sqlstore/team_store.go @@ -1177,7 +1177,7 @@ func (s SqlTeamStore) GetTeamsForUserWithPagination(userId string, page, perPage // for all the channels in all the teams except the excluded ones. func (s SqlTeamStore) GetChannelUnreadsForAllTeams(excludeTeamId, userId string) ([]*model.ChannelUnread, error) { query, args, err := s.getQueryBuilder(). - Select("Channels.TeamId TeamId", "Channels.Id ChannelId", "(Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount", "(Channels.TotalMsgCountRoot - ChannelMembers.MsgCountRoot) MsgCountRoot", "ChannelMembers.MentionCount MentionCount", "ChannelMembers.NotifyProps NotifyProps"). + Select("Channels.TeamId TeamId", "Channels.Id ChannelId", "(Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount", "(Channels.TotalMsgCountRoot - ChannelMembers.MsgCountRoot) MsgCountRoot", "ChannelMembers.MentionCount MentionCount", "ChannelMembers.MentionCountRoot MentionCountRoot", "ChannelMembers.NotifyProps NotifyProps"). From("Channels"). Join("ChannelMembers ON Id = ChannelId"). Where(sq.Eq{"UserId": userId, "DeleteAt": 0}). @@ -1199,7 +1199,7 @@ func (s SqlTeamStore) GetChannelUnreadsForAllTeams(excludeTeamId, userId string) // GetChannelUnreadsForTeam returns unreads msg count, mention counts and notifyProps for all the channels in a single team. func (s SqlTeamStore) GetChannelUnreadsForTeam(teamId, userId string) ([]*model.ChannelUnread, error) { query, args, err := s.getQueryBuilder(). - Select("Channels.TeamId TeamId", "Channels.Id ChannelId", "(Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount", "(Channels.TotalMsgCountRoot - ChannelMembers.MsgCountRoot) MsgCountRoot", "ChannelMembers.MentionCount MentionCount", "ChannelMembers.NotifyProps NotifyProps"). + Select("Channels.TeamId TeamId", "Channels.Id ChannelId", "(Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount", "(Channels.TotalMsgCountRoot - ChannelMembers.MsgCountRoot) MsgCountRoot", "ChannelMembers.MentionCount MentionCount", "ChannelMembers.MentionCountRoot MentionCountRoot", "ChannelMembers.NotifyProps NotifyProps"). From("Channels"). Join("ChannelMembers ON Id = ChannelId"). Where(sq.Eq{"UserId": userId, "TeamId": teamId, "DeleteAt": 0}).ToSql() diff --git a/store/sqlstore/thread_store.go b/store/sqlstore/thread_store.go index 2850329c73..d4098bccdf 100644 --- a/store/sqlstore/thread_store.go +++ b/store/sqlstore/thread_store.go @@ -109,35 +109,6 @@ func (s *SqlThreadStore) Get(id string) (*model.Thread, error) { return &thread, nil } -func (s *SqlThreadStore) GetThreadMentionsForUserPerChannel(userId, teamId string) (map[string]int64, error) { - type Count struct { - UnreadMentions int64 - ChannelId string - } - var counts []Count - - sql, args, _ := s.getQueryBuilder(). - Select("SUM(UnreadMentions) as UnreadMentions", "ChannelId"). - From("ThreadMemberships"). - LeftJoin("Threads ON Threads.PostId = ThreadMemberships.PostId"). - LeftJoin("Channels ON Threads.ChannelId = Channels.Id"). - Where(sq.And{ - sq.Or{sq.Eq{"Channels.TeamId": teamId}, sq.Eq{"Channels.TeamId": ""}}, - sq.Eq{"ThreadMemberships.UserId": userId}, - sq.Eq{"ThreadMemberships.Following": true}, - }). - GroupBy("Threads.ChannelId").ToSql() - - if _, err := s.GetMaster().Select(&counts, sql, args...); err != nil { - return nil, err - } - result := map[string]int64{} - for _, count := range counts { - result[count.ChannelId] = count.UnreadMentions - } - return result, nil -} - func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) { type JoinedThread struct { PostId string diff --git a/store/sqlstore/upgrade.go b/store/sqlstore/upgrade.go index 31a843f7f8..2234168ccc 100644 --- a/store/sqlstore/upgrade.go +++ b/store/sqlstore/upgrade.go @@ -1011,6 +1011,38 @@ func upgradeDatabaseToVersion535(sqlStore *SqlStore) { sqlStore.CreateColumnIfNotExists("SidebarCategories", "Collapsed", "tinyint(1)", "boolean", "0") + // note: setting default 0 on pre-5.0 tables causes test-db-migration script to fail, so this column will be added to ignore list + sqlStore.CreateColumnIfNotExists("ChannelMembers", "MentionCountRoot", "bigint", "bigint", "0") + sqlStore.AlterColumnDefaultIfExists("ChannelMembers", "MentionCountRoot", model.NewString("0"), model.NewString("0")) + + mentionCountRootCTE := ` + SELECT ChannelId, COALESCE(SUM(UnreadMentions), 0) AS UnreadMentions, UserId + FROM ThreadMemberships + LEFT JOIN Threads ON ThreadMemberships.PostId = Threads.PostId + GROUP BY Threads.ChannelId, ThreadMemberships.UserId + ` + updateMentionCountRootQuery := ` + UPDATE ChannelMembers INNER JOIN (` + mentionCountRootCTE + `) AS q ON + q.ChannelId = ChannelMembers.ChannelId AND + q.UserId=ChannelMembers.UserId AND + ChannelMembers.MentionCount > 0 + SET MentionCountRoot = ChannelMembers.MentionCount - q.UnreadMentions + ` + if sqlStore.DriverName() == model.DATABASE_DRIVER_POSTGRES { + updateMentionCountRootQuery = ` + WITH q AS (` + mentionCountRootCTE + `) + UPDATE channelmembers + SET MentionCountRoot = ChannelMembers.MentionCount - q.UnreadMentions + FROM q + WHERE + q.ChannelId = ChannelMembers.ChannelId AND + q.UserId = ChannelMembers.UserId AND + ChannelMembers.MentionCount > 0 + ` + } + if _, err := sqlStore.GetMaster().Exec(updateMentionCountRootQuery); err != nil { + mlog.Error("Error updating ChannelId in Threads table", mlog.Err(err)) + } sqlStore.CreateColumnIfNotExists("Channels", "TotalMsgCountRoot", "bigint", "bigint", "0") sqlStore.CreateColumnIfNotExistsNoDefault("Channels", "LastRootPostAt", "bigint", "bigint") defer sqlStore.RemoveColumnIfExists("Channels", "LastRootPostAt") diff --git a/store/sqlstore/user_store.go b/store/sqlstore/user_store.go index f9c35ccfd1..2110ed45c5 100644 --- a/store/sqlstore/user_store.go +++ b/store/sqlstore/user_store.go @@ -1634,6 +1634,7 @@ func (us SqlUserStore) GetUsersBatchForIndexing(startTime, endTime int64, limit cm.LastViewedAt, cm.MsgCount, cm.MentionCount, + cm.MentionCountRoot, cm.NotifyProps, cm.LastUpdateAt, cm.SchemeUser, diff --git a/store/store.go b/store/store.go index 516819f8c2..56cd322211 100644 --- a/store/store.go +++ b/store/store.go @@ -193,9 +193,9 @@ type ChannelStore interface { PermanentDeleteMembersByUser(userId string) error PermanentDeleteMembersByChannel(channelID string) error UpdateLastViewedAt(channelIds []string, userId string, updateThreads bool) (map[string]int64, error) - UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, updateThreads bool) (*model.ChannelUnreadAt, error) + UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount, mentionCountRoot int, updateThreads bool) (*model.ChannelUnreadAt, error) CountPostsAfter(channelID string, timestamp int64, userId string) (int, int, error) - IncrementMentionCount(channelID string, userId string, updateThreads bool) error + IncrementMentionCount(channelID string, userId string, updateThreads, isRoot bool) error AnalyticsTypeCount(teamID string, channelType string) (int64, error) GetMembersForUser(teamID string, userId string) (*model.ChannelMembers, error) GetMembersForUserWithPagination(teamID, userId string, page, perPage int) (*model.ChannelMembers, error) @@ -257,7 +257,6 @@ type ThreadStore interface { GetThreadForUser(userId, teamId, threadId string, extended bool) (*model.ThreadResponse, error) Delete(postId string) error GetPosts(threadId string, since int64) ([]*model.Post, error) - GetThreadMentionsForUserPerChannel(userId, teamId string) (map[string]int64, error) MarkAllAsRead(userId, teamID string) error MarkAsRead(userId, threadID string, timestamp int64) error diff --git a/store/storetest/channel_store.go b/store/storetest/channel_store.go index 85a64bd9ee..3396fca513 100644 --- a/store/storetest/channel_store.go +++ b/store/storetest/channel_store.go @@ -340,7 +340,7 @@ func testGetChannelUnread(t *testing.T, ss store.Store) { _, nErr = ss.Channel().Save(c2, -1) require.NoError(t, nErr) - cm2 := &model.ChannelMember{ChannelId: c2.Id, UserId: m2.UserId, NotifyProps: notifyPropsModel, MsgCount: 90, MsgCountRoot: 90, MentionCount: 5} + cm2 := &model.ChannelMember{ChannelId: c2.Id, UserId: m2.UserId, NotifyProps: notifyPropsModel, MsgCount: 90, MsgCountRoot: 90, MentionCount: 5, MentionCountRoot: 1} _, err = ss.Channel().SaveMember(cm2) require.NoError(t, err) @@ -361,6 +361,7 @@ func testGetChannelUnread(t *testing.T, ss store.Store) { require.Equal(t, c2.Id, ch2.ChannelId, "Wrong channel id") require.Equal(t, teamId2, ch2.TeamId, "Wrong team id") require.EqualValues(t, 5, ch2.MentionCount, "wrong MentionCount for channel 2") + require.EqualValues(t, 1, ch2.MentionCountRoot, "wrong MentionCountRoot for channel 2") require.EqualValues(t, 10, ch2.MsgCount, "wrong MsgCount for channel 2") } @@ -4200,16 +4201,16 @@ func testChannelStoreIncrementMentionCount(t *testing.T, ss store.Store) { _, err := ss.Channel().SaveMember(&m1) require.NoError(t, err) - err = ss.Channel().IncrementMentionCount(m1.ChannelId, m1.UserId, false) + err = ss.Channel().IncrementMentionCount(m1.ChannelId, m1.UserId, false, false) require.NoError(t, err, "failed to update") - err = ss.Channel().IncrementMentionCount(m1.ChannelId, "missing id", false) + err = ss.Channel().IncrementMentionCount(m1.ChannelId, "missing id", false, false) require.NoError(t, err, "failed to update") - err = ss.Channel().IncrementMentionCount("missing id", m1.UserId, false) + err = ss.Channel().IncrementMentionCount("missing id", m1.UserId, false, false) require.NoError(t, err, "failed to update") - err = ss.Channel().IncrementMentionCount("missing id", "missing id", false) + err = ss.Channel().IncrementMentionCount("missing id", "missing id", false, false) require.NoError(t, err, "failed to update") } diff --git a/store/storetest/mocks/ChannelStore.go b/store/storetest/mocks/ChannelStore.go index 93e661f59e..a5e9533747 100644 --- a/store/storetest/mocks/ChannelStore.go +++ b/store/storetest/mocks/ChannelStore.go @@ -1294,13 +1294,13 @@ func (_m *ChannelStore) GroupSyncedChannelCount() (int64, error) { return r0, r1 } -// IncrementMentionCount provides a mock function with given fields: channelID, userId, updateThreads -func (_m *ChannelStore) IncrementMentionCount(channelID string, userId string, updateThreads bool) error { - ret := _m.Called(channelID, userId, updateThreads) +// IncrementMentionCount provides a mock function with given fields: channelID, userId, updateThreads, isRoot +func (_m *ChannelStore) IncrementMentionCount(channelID string, userId string, updateThreads bool, isRoot bool) error { + ret := _m.Called(channelID, userId, updateThreads, isRoot) var r0 error - if rf, ok := ret.Get(0).(func(string, string, bool) error); ok { - r0 = rf(channelID, userId, updateThreads) + if rf, ok := ret.Get(0).(func(string, string, bool, bool) error); ok { + r0 = rf(channelID, userId, updateThreads, isRoot) } else { r0 = ret.Error(0) } @@ -1817,13 +1817,13 @@ func (_m *ChannelStore) UpdateLastViewedAt(channelIds []string, userId string, u return r0, r1 } -// UpdateLastViewedAtPost provides a mock function with given fields: unreadPost, userID, mentionCount, updateThreads -func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, updateThreads bool) (*model.ChannelUnreadAt, error) { - ret := _m.Called(unreadPost, userID, mentionCount, updateThreads) +// UpdateLastViewedAtPost provides a mock function with given fields: unreadPost, userID, mentionCount, mentionCountRoot, updateThreads +func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, updateThreads bool) (*model.ChannelUnreadAt, error) { + ret := _m.Called(unreadPost, userID, mentionCount, mentionCountRoot, updateThreads) var r0 *model.ChannelUnreadAt - if rf, ok := ret.Get(0).(func(*model.Post, string, int, bool) *model.ChannelUnreadAt); ok { - r0 = rf(unreadPost, userID, mentionCount, updateThreads) + if rf, ok := ret.Get(0).(func(*model.Post, string, int, int, bool) *model.ChannelUnreadAt); ok { + r0 = rf(unreadPost, userID, mentionCount, mentionCountRoot, updateThreads) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*model.ChannelUnreadAt) @@ -1831,8 +1831,8 @@ func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID st } var r1 error - if rf, ok := ret.Get(1).(func(*model.Post, string, int, bool) error); ok { - r1 = rf(unreadPost, userID, mentionCount, updateThreads) + if rf, ok := ret.Get(1).(func(*model.Post, string, int, int, bool) error); ok { + r1 = rf(unreadPost, userID, mentionCount, mentionCountRoot, updateThreads) } else { r1 = ret.Error(1) } diff --git a/store/storetest/mocks/ThreadStore.go b/store/storetest/mocks/ThreadStore.go index 5099684ab4..8a74f97c03 100644 --- a/store/storetest/mocks/ThreadStore.go +++ b/store/storetest/mocks/ThreadStore.go @@ -180,29 +180,6 @@ func (_m *ThreadStore) GetThreadForUser(userId string, teamId string, threadId s return r0, r1 } -// GetThreadMentionsForUserPerChannel provides a mock function with given fields: userId, teamId -func (_m *ThreadStore) GetThreadMentionsForUserPerChannel(userId string, teamId string) (map[string]int64, error) { - ret := _m.Called(userId, teamId) - - var r0 map[string]int64 - if rf, ok := ret.Get(0).(func(string, string) map[string]int64); ok { - r0 = rf(userId, teamId) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]int64) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(userId, teamId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // GetThreadsForUser provides a mock function with given fields: userId, teamId, opts func (_m *ThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) { ret := _m.Called(userId, teamId, opts) diff --git a/store/storetest/thread_store.go b/store/storetest/thread_store.go index a0317bcc4c..44a46c527f 100644 --- a/store/storetest/thread_store.go +++ b/store/storetest/thread_store.go @@ -243,7 +243,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) { _, err := ss.Thread().UpdateMembership(m) require.NoError(t, err) - _, err = ss.Channel().UpdateLastViewedAtPost(newPosts[0], newPosts[0].UserId, 0, true) + _, err = ss.Channel().UpdateLastViewedAtPost(newPosts[0], newPosts[0].UserId, 0, 0, true) require.NoError(t, err) assert.Eventually(t, func() bool { @@ -263,7 +263,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) { _, err := ss.Thread().UpdateMembership(m) require.NoError(t, err) - err = ss.Channel().IncrementMentionCount(newPosts[0].ChannelId, newPosts[0].UserId, true) + err = ss.Channel().IncrementMentionCount(newPosts[0].ChannelId, newPosts[0].UserId, true, false) require.NoError(t, err) assert.Eventually(t, func() bool { @@ -317,7 +317,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) { _, err := ss.Thread().UpdateMembership(m) require.NoError(t, err) - _, err = ss.Channel().UpdateLastViewedAtPost(newPosts[0], newPosts[0].UserId, 0, true) + _, err = ss.Channel().UpdateLastViewedAtPost(newPosts[0], newPosts[0].UserId, 0, 0, true) require.NoError(t, err) assert.Eventually(t, func() bool { diff --git a/store/storetest/user_store.go b/store/storetest/user_store.go index b62e550bb4..94c547e0b3 100644 --- a/store/storetest/user_store.go +++ b/store/storetest/user_store.go @@ -2246,7 +2246,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) { // Post one message with mention to open channel _, nErr = ss.Post().Save(&p1) require.NoError(t, nErr) - nErr = ss.Channel().IncrementMentionCount(c1.Id, u2.Id, false) + nErr = ss.Channel().IncrementMentionCount(c1.Id, u2.Id, false, false) require.NoError(t, nErr) // Post 2 messages without mention to direct channel @@ -2257,7 +2257,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) { _, nErr = ss.Post().Save(&p2) require.NoError(t, nErr) - nErr = ss.Channel().IncrementMentionCount(c2.Id, u2.Id, false) + nErr = ss.Channel().IncrementMentionCount(c2.Id, u2.Id, false, false) require.NoError(t, nErr) p3 := model.Post{} @@ -2267,7 +2267,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) { _, nErr = ss.Post().Save(&p3) require.NoError(t, nErr) - nErr = ss.Channel().IncrementMentionCount(c2.Id, u2.Id, false) + nErr = ss.Channel().IncrementMentionCount(c2.Id, u2.Id, false, false) require.NoError(t, nErr) badge, unreadCountErr := ss.User().GetUnreadCount(u2.Id) diff --git a/store/timerlayer/timerlayer.go b/store/timerlayer/timerlayer.go index c3b70aa101..716434eba3 100644 --- a/store/timerlayer/timerlayer.go +++ b/store/timerlayer/timerlayer.go @@ -1431,10 +1431,10 @@ func (s *TimerLayerChannelStore) GroupSyncedChannelCount() (int64, error) { return result, err } -func (s *TimerLayerChannelStore) IncrementMentionCount(channelID string, userId string, updateThreads bool) error { +func (s *TimerLayerChannelStore) IncrementMentionCount(channelID string, userId string, updateThreads bool, isRoot bool) error { start := timemodule.Now() - err := s.ChannelStore.IncrementMentionCount(channelID, userId, updateThreads) + err := s.ChannelStore.IncrementMentionCount(channelID, userId, updateThreads, isRoot) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) if s.Root.Metrics != nil { @@ -1952,10 +1952,10 @@ func (s *TimerLayerChannelStore) UpdateLastViewedAt(channelIds []string, userId return result, err } -func (s *TimerLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, updateThreads bool) (*model.ChannelUnreadAt, error) { +func (s *TimerLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, updateThreads bool) (*model.ChannelUnreadAt, error) { start := timemodule.Now() - result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, updateThreads) + result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, mentionCountRoot, updateThreads) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) if s.Root.Metrics != nil { @@ -7096,22 +7096,6 @@ func (s *TimerLayerThreadStore) GetThreadForUser(userId string, teamId string, t return result, err } -func (s *TimerLayerThreadStore) GetThreadMentionsForUserPerChannel(userId string, teamId string) (map[string]int64, error) { - start := timemodule.Now() - - result, err := s.ThreadStore.GetThreadMentionsForUserPerChannel(userId, teamId) - - elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) - if s.Root.Metrics != nil { - success := "false" - if err == nil { - success = "true" - } - s.Root.Metrics.ObserveStoreMethodDuration("ThreadStore.GetThreadMentionsForUserPerChannel", success, elapsed) - } - return result, err -} - func (s *TimerLayerThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) { start := timemodule.Now()