MM-33708 - Add MentionCountRoot column to ChannelMembers (#17099)

* added new column for root-only mentions

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Eli Yukelzon
2021-04-01 14:43:09 +03:00
коммит произвёл GitHub
родитель 3c21eef110
Коммит 480796a1df
35 изменённых файлов: 288 добавлений и 346 удалений

Просмотреть файл

@@ -320,6 +320,9 @@ jobs:
mattermost/mattermost-build-server:20201119_golang-1.15.5 \ 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" 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" 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 migrated'
docker-compose --no-ansi exec -T postgres sh -c 'exec echo "ALTER TABLE ChannelMembers DROP COLUMN MsgCountRoot;" | exec psql -U mmuser -d latest' 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" 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 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;" 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 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;" 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" echo "Ignoring known MySQL mismatch: Channels.TotalMsgCountRoot"

Просмотреть файл

@@ -2152,6 +2152,7 @@ func TestViewChannel(t *testing.T) {
CheckNoError(t, resp) CheckNoError(t, resp)
require.Equal(t, channel.TotalMsgCount, member.MsgCount, "should match message counts") 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.MentionCount, "should have no mentions")
require.Equal(t, int64(0), member.MentionCountRoot, "should have no mentions")
_, resp = Client.ViewChannel("junk", view) _, resp = Client.ViewChannel("junk", view)
CheckBadRequestStatus(t, resp) CheckBadRequestStatus(t, resp)
@@ -4064,3 +4065,43 @@ func TestMoveChannel(t *testing.T) {
require.Equal(t, team2.Id, newChannel.TeamId) 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") }, "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)
}

Просмотреть файл

@@ -94,7 +94,6 @@ func (api *API) InitUser() {
api.BaseRoutes.UserThreads.Handle("", api.ApiSessionRequired(getThreadsForUser)).Methods("GET") api.BaseRoutes.UserThreads.Handle("", api.ApiSessionRequired(getThreadsForUser)).Methods("GET")
api.BaseRoutes.UserThreads.Handle("/read", api.ApiSessionRequired(updateReadStateAllThreadsByUser)).Methods("PUT") 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("", api.ApiSessionRequired(getThreadForUser)).Methods("GET")
api.BaseRoutes.UserThread.Handle("/following", api.ApiSessionRequired(followThreadByUser)).Methods("PUT") 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())) 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) { func getThreadsForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUserId().RequireTeamId() c.RequireUserId().RequireTeamId()
if c.Err != nil { if c.Err != nil {

Просмотреть файл

@@ -5840,11 +5840,6 @@ func TestMaintainUnreadMentionsInThread(t *testing.T) {
*cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.ThreadAutoFollow = true
*cfg.ServiceSettings.CollapsedThreads = model.COLLAPSED_THREADS_DEFAULT_ON *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) { checkThreadList := func(client *model.Client4, userId string, expectedMentions, expectedThreads int) (*model.Threads, *model.Response) {
uss, resp := client.GetUserThreads(userId, th.BasicTeam.Id, model.GetUserThreadsOpts{ uss, resp := client.GetUserThreads(userId, th.BasicTeam.Id, model.GetUserThreadsOpts{
Deleted: false, Deleted: false,
@@ -5870,7 +5865,6 @@ func TestMaintainUnreadMentionsInThread(t *testing.T) {
// create reply and mention the original poster and another user // 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}) 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 // basic user 1 was mentioned 1 time
checkThreadList(th.Client, th.BasicUser.Id, 1, 1) checkThreadList(th.Client, th.BasicUser.Id, 1, 1)
// basic user 2 was mentioned 1 time // 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}) postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: dm.Id, Message: "msg2", RootId: dm_root_post.Id})
// expect increment by two mentions // expect increment by two mentions
checkThreadList(th.Client, th.BasicUser.Id, 3, 2) 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) { func TestReadThreads(t *testing.T) {

Просмотреть файл

@@ -704,7 +704,6 @@ type AppIface interface {
GetTermsOfService(id string) (*model.TermsOfService, *model.AppError) GetTermsOfService(id string) (*model.TermsOfService, *model.AppError)
GetThreadForUser(userID, teamID, threadId string, extended bool) (*model.ThreadResponse, *model.AppError) GetThreadForUser(userID, teamID, threadId string, extended bool) (*model.ThreadResponse, *model.AppError)
GetThreadMembershipsForUser(userID, teamID string) ([]*model.ThreadMembership, error) 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) GetThreadsForUser(userID, teamID string, options model.GetUserThreadsOpts) (*model.Threads, *model.AppError)
GetUploadSession(uploadId string) (*model.UploadSession, *model.AppError) GetUploadSession(uploadId string) (*model.UploadSession, *model.AppError)
GetUploadSessionsForUser(userID string) ([]*model.UploadSession, *model.AppError) GetUploadSessionsForUser(userID string) ([]*model.UploadSession, *model.AppError)

Просмотреть файл

@@ -1873,7 +1873,6 @@ func (a *App) GetChannelUnread(channelID, userID string) (*model.ChannelUnread,
channelUnread.MsgCount = 0 channelUnread.MsgCount = 0
channelUnread.MsgCountRoot = 0 channelUnread.MsgCountRoot = 0
} }
return channelUnread, nil return channelUnread, nil
} }
@@ -2340,7 +2339,7 @@ func (a *App) MarkChannelAsUnreadFromPost(postID string, userID string) (*model.
return nil, err return nil, err
} }
unreadMentions, err := a.countMentionsFromPost(user, post) unreadMentions, unreadMentionsRoot, err := a.countMentionsFromPost(user, post)
if err != nil { if err != nil {
return nil, err 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 { if nErr != nil {
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, nErr.Error(), http.StatusInternalServerError) 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 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_UNREAD, channelUnread.TeamId, channelUnread.ChannelId, channelUnread.UserId, nil)
message.Add("msg_count", channelUnread.MsgCount) message.Add("msg_count", channelUnread.MsgCount)
message.Add("mention_count", channelUnread.MentionCount) message.Add("mention_count", channelUnread.MentionCount)
message.Add("mention_count_root", channelUnread.MentionCountRoot)
message.Add("last_viewed_at", channelUnread.LastViewedAt) message.Add("last_viewed_at", channelUnread.LastViewedAt)
message.Add("post_id", postID) message.Add("post_id", postID)
a.Publish(message) a.Publish(message)

Просмотреть файл

@@ -1343,15 +1343,24 @@ func TestMarkChannelAsUnreadFromPost(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
th.CreatePost(c2) 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) response, err := th.App.MarkChannelAsUnreadFromPost(p4.Id, u1.Id)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(1), response.MsgCount) 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) unread, err := th.App.GetChannelUnread(c2.Id, u1.Id)
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, int64(1), unread.MsgCount) assert.Equal(t, int64(2), unread.MsgCount)
assert.Equal(t, int64(1), unread.MentionCount) 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) { 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)
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) response, err := th.App.MarkChannelAsUnreadFromPost(dm1.Id, u2.Id)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(0), response.MsgCount) 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) unread, err := th.App.GetChannelUnread(dc.Id, u2.Id)
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, int64(3), unread.MsgCount) assert.Equal(t, int64(4), unread.MsgCount)
assert.Equal(t, int64(3), unread.MentionCount) 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) { t.Run("Can't unread an imaginary post", func(t *testing.T) {

Просмотреть файл

@@ -201,7 +201,7 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
umc := make(chan *model.AppError, 1) umc := make(chan *model.AppError, 1)
go func(userID string) { go func(userID string) {
defer close(umc) 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 { if nErr != nil {
umc <- model.NewAppError("SendNotifications", "app.channel.increment_mention_count.app_error", nil, nErr.Error(), http.StatusInternalServerError) umc <- model.NewAppError("SendNotifications", "app.channel.increment_mention_count.app_error", nil, nErr.Error(), http.StatusInternalServerError)
return return

Просмотреть файл

@@ -8650,28 +8650,6 @@ func (a *OpenTracingAppLayer) GetThreadMembershipsForUser(userID string, teamID
return resultVar0, resultVar1 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) { func (a *OpenTracingAppLayer) GetThreadsForUser(userID string, teamID string, options model.GetUserThreadsOpts) (*model.Threads, *model.AppError) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetThreadsForUser") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetThreadsForUser")

Просмотреть файл

@@ -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 // countMentionsFromPost returns the number of posts in the post's channel that mention the user after and including the
// given post. // 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) channel, err := a.GetChannel(post.ChannelId)
if err != nil { if err != nil {
return 0, err return 0, 0, err
} }
if channel.Type == model.CHANNEL_DIRECT { if channel.Type == model.CHANNEL_DIRECT {
// In a DM channel, every post made by the other user is a mention // 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 { 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) channelMember, err := a.GetChannelMember(context.Background(), channel.Id, user.Id)
if err != nil { if err != nil {
return 0, err return 0, 0, err
} }
keywords := addMentionKeywordsForUser( 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) thread, err := a.GetPostThread(post.Id, false, false, false, user.Id)
if err != nil { if err != nil {
return 0, err return 0, 0, err
} }
count := 0 count := 0
countRoot := 0
if isPostMention(user, post, keywords, thread.Posts, mentionedByThread, checkForCommentMentions) { if isPostMention(user, post, keywords, thread.Posts, mentionedByThread, checkForCommentMentions) {
count += 1 count += 1
if post.RootId == "" {
countRoot += 1
}
} }
page := 0 page := 0
@@ -1458,12 +1461,15 @@ func (a *App) countMentionsFromPost(user *model.User, post *model.Post) (int, *m
PerPage: perPage, PerPage: perPage,
}) })
if err != nil { if err != nil {
return 0, err return 0, 0, err
} }
for _, postID := range postList.Order { for _, postID := range postList.Order {
if isPostMention(user, postList.Posts[postID], keywords, postList.Posts, mentionedByThread, checkForCommentMentions) { if isPostMention(user, postList.Posts[postID], keywords, postList.Posts, mentionedByThread, checkForCommentMentions) {
count += 1 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 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 { func isCommentMention(user *model.User, post *model.Post, otherPosts map[string]*model.Post, mentionedByThread map[string]bool) bool {

Просмотреть файл

@@ -1231,7 +1231,7 @@ func TestCountMentionsFromPost(t *testing.T) {
}, channel, false, true) }, channel, false, true)
require.Nil(t, err) require.Nil(t, err)
count, err := th.App.countMentionsFromPost(user2, post1) count, _, err := th.App.countMentionsFromPost(user2, post1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 0, count) assert.Equal(t, 0, count)
@@ -1270,7 +1270,7 @@ func TestCountMentionsFromPost(t *testing.T) {
// post1 and post3 should mention the user // 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.Nil(t, err)
assert.Equal(t, 2, count) assert.Equal(t, 2, count)
@@ -1309,7 +1309,7 @@ func TestCountMentionsFromPost(t *testing.T) {
// post2 and post3 should mention the user // 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.Nil(t, err)
assert.Equal(t, 2, count) assert.Equal(t, 2, count)
@@ -1346,7 +1346,7 @@ func TestCountMentionsFromPost(t *testing.T) {
}, channel, false, true) }, channel, false, true)
require.Nil(t, err) require.Nil(t, err)
count, err := th.App.countMentionsFromPost(user2, post1) count, _, err := th.App.countMentionsFromPost(user2, post1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 0, count) assert.Equal(t, 0, count)
@@ -1388,7 +1388,7 @@ func TestCountMentionsFromPost(t *testing.T) {
}, channel, false, true) }, channel, false, true)
require.Nil(t, err) require.Nil(t, err)
count, err := th.App.countMentionsFromPost(user2, post1) count, _, err := th.App.countMentionsFromPost(user2, post1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 0, count) assert.Equal(t, 0, count)
@@ -1442,7 +1442,7 @@ func TestCountMentionsFromPost(t *testing.T) {
// post2 should mention the user // post2 should mention the user
count, err := th.App.countMentionsFromPost(user2, post1) count, _, err := th.App.countMentionsFromPost(user2, post1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 1, count) assert.Equal(t, 1, count)
@@ -1496,7 +1496,7 @@ func TestCountMentionsFromPost(t *testing.T) {
// post2 and post5 should mention the user // 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.Nil(t, err)
assert.Equal(t, 2, count) assert.Equal(t, 2, count)
@@ -1545,7 +1545,7 @@ func TestCountMentionsFromPost(t *testing.T) {
// should be mentioned by post2 and post3 // 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.Nil(t, err)
assert.Equal(t, 2, count) assert.Equal(t, 2, count)
@@ -1575,12 +1575,12 @@ func TestCountMentionsFromPost(t *testing.T) {
}, channel, false, true) }, channel, false, true)
require.Nil(t, err) require.Nil(t, err)
count, err := th.App.countMentionsFromPost(user2, post1) count, _, err := th.App.countMentionsFromPost(user2, post1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, count) assert.Equal(t, 2, count)
count, err = th.App.countMentionsFromPost(user1, post1) count, _, err = th.App.countMentionsFromPost(user1, post1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 0, count) 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 // 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.Nil(t, err)
assert.Equal(t, 1, count) assert.Equal(t, 1, count)
@@ -1648,7 +1648,7 @@ func TestCountMentionsFromPost(t *testing.T) {
// post2 should mention the user // post2 should mention the user
count, err := th.App.countMentionsFromPost(user2, post1) count, _, err := th.App.countMentionsFromPost(user2, post1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 1, count) assert.Equal(t, 1, count)
@@ -1695,7 +1695,7 @@ func TestCountMentionsFromPost(t *testing.T) {
// post4 should mention the user // post4 should mention the user
count, err := th.App.countMentionsFromPost(user2, post3) count, _, err := th.App.countMentionsFromPost(user2, post3)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 1, count) assert.Equal(t, 1, count)
@@ -1735,7 +1735,7 @@ func TestCountMentionsFromPost(t *testing.T) {
// post3 should mention the user // post3 should mention the user
count, err := th.App.countMentionsFromPost(user2, post1) count, _, err := th.App.countMentionsFromPost(user2, post1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 1, count) assert.Equal(t, 1, count)
@@ -1771,7 +1771,7 @@ func TestCountMentionsFromPost(t *testing.T) {
// Every post should mention the user // 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.Nil(t, err)
assert.Equal(t, numPosts, count) assert.Equal(t, numPosts, count)

Просмотреть файл

@@ -1139,14 +1139,15 @@ func (a *App) GetTeamUnread(teamID, userID string) (*model.TeamUnread, *model.Ap
} }
var teamUnread = &model.TeamUnread{ var teamUnread = &model.TeamUnread{
MsgCount: 0, MsgCount: 0,
MsgCountRoot: 0, MentionCount: 0,
MentionCount: 0, MentionCountRoot: 0,
TeamId: teamID, MsgCountRoot: 0,
TeamId: teamID,
} }
for _, cu := range channelUnreads { for _, cu := range channelUnreads {
teamUnread.MentionCount += cu.MentionCount teamUnread.MentionCount += cu.MentionCount
teamUnread.MentionCountRoot += cu.MentionCountRoot
if cu.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] != model.CHANNEL_MARK_UNREAD_MENTION { if cu.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] != model.CHANNEL_MARK_UNREAD_MENTION {
teamUnread.MsgCount += cu.MsgCount 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 { unreads := func(cu *model.ChannelUnread, tu *model.TeamUnread) *model.TeamUnread {
tu.MentionCount += cu.MentionCount tu.MentionCount += cu.MentionCount
tu.MentionCountRoot += cu.MentionCountRoot
if cu.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] != model.CHANNEL_MARK_UNREAD_MENTION { if cu.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] != model.CHANNEL_MARK_UNREAD_MENTION {
tu.MsgCount += cu.MsgCount tu.MsgCount += cu.MsgCount
@@ -1692,10 +1694,11 @@ func (a *App) GetTeamsUnreadForUser(excludeTeamId string, userID string) ([]*mod
membersMap[id] = unreads(data[i], mu) membersMap[id] = unreads(data[i], mu)
} else { } else {
membersMap[id] = unreads(data[i], &model.TeamUnread{ membersMap[id] = unreads(data[i], &model.TeamUnread{
MsgCount: 0, MsgCount: 0,
MsgCountRoot: 0, MentionCount: 0,
MentionCount: 0, MentionCountRoot: 0,
TeamId: id, MsgCountRoot: 0,
TeamId: id,
}) })
} }
} }

Просмотреть файл

@@ -2401,14 +2401,6 @@ func (a *App) GetThreadsForUser(userID, teamID string, options model.GetUserThre
return threads, nil 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) { 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) thread, err := a.Srv().Store.Thread().GetThreadForUser(userID, teamID, threadId, extended)
if err != nil { if err != nil {

Просмотреть файл

@@ -24,39 +24,42 @@ const (
) )
type ChannelUnread struct { type ChannelUnread struct {
TeamId string `json:"team_id"` TeamId string `json:"team_id"`
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
MsgCount int64 `json:"msg_count"` MsgCount int64 `json:"msg_count"`
MsgCountRoot int64 `json:"msg_count_root"` MentionCount int64 `json:"mention_count"`
MentionCount int64 `json:"mention_count"` MentionCountRoot int64 `json:"mention_count_root"`
NotifyProps StringMap `json:"-"` MsgCountRoot int64 `json:"msg_count_root"`
NotifyProps StringMap `json:"-"`
} }
type ChannelUnreadAt struct { type ChannelUnreadAt struct {
TeamId string `json:"team_id"` TeamId string `json:"team_id"`
UserId string `json:"user_id"` UserId string `json:"user_id"`
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
MsgCount int64 `json:"msg_count"` MsgCount int64 `json:"msg_count"`
MsgCountRoot int64 `json:"msg_count_root"` MentionCount int64 `json:"mention_count"`
MentionCount int64 `json:"mention_count"` MentionCountRoot int64 `json:"mention_count_root"`
LastViewedAt int64 `json:"last_viewed_at"` MsgCountRoot int64 `json:"msg_count_root"`
NotifyProps StringMap `json:"-"` LastViewedAt int64 `json:"last_viewed_at"`
NotifyProps StringMap `json:"-"`
} }
type ChannelMember struct { type ChannelMember struct {
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
UserId string `json:"user_id"` UserId string `json:"user_id"`
Roles string `json:"roles"` Roles string `json:"roles"`
LastViewedAt int64 `json:"last_viewed_at"` LastViewedAt int64 `json:"last_viewed_at"`
MsgCount int64 `json:"msg_count"` MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"` MentionCount int64 `json:"mention_count"`
NotifyProps StringMap `json:"notify_props"` MentionCountRoot int64 `json:"mention_count_root"`
LastUpdateAt int64 `json:"last_update_at"` MsgCountRoot int64 `json:"msg_count_root"`
SchemeGuest bool `json:"scheme_guest"` NotifyProps StringMap `json:"notify_props"`
SchemeUser bool `json:"scheme_user"` LastUpdateAt int64 `json:"last_update_at"`
SchemeAdmin bool `json:"scheme_admin"` SchemeGuest bool `json:"scheme_guest"`
ExplicitRoles string `json:"explicit_roles"` SchemeUser bool `json:"scheme_user"`
MsgCountRoot int64 `json:"msg_count_root"` SchemeAdmin bool `json:"scheme_admin"`
ExplicitRoles string `json:"explicit_roles"`
} }
type ChannelMembers []ChannelMember type ChannelMembers []ChannelMember

Просмотреть файл

@@ -5885,20 +5885,6 @@ func (c *Client4) DownloadExport(name string, wr io.Writer, offset int64) (int64
return n, BuildResponse(r) 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) { func (c *Client4) GetUserThreads(userId, teamId string, options GetUserThreadsOpts) (*Threads, *Response) {
v := url.Values{} v := url.Values{}
if options.Since != 0 { if options.Since != 0 {

Просмотреть файл

@@ -31,10 +31,11 @@ type TeamMember struct {
//msgp:ignore TeamUnread //msgp:ignore TeamUnread
type TeamUnread struct { type TeamUnread struct {
TeamId string `json:"team_id"` TeamId string `json:"team_id"`
MsgCount int64 `json:"msg_count"` MsgCount int64 `json:"msg_count"`
MsgCountRoot int64 `json:"msg_count_root"` MentionCount int64 `json:"mention_count"`
MentionCount int64 `json:"mention_count"` MentionCountRoot int64 `json:"mention_count_root"`
MsgCountRoot int64 `json:"msg_count_root"`
} }
//msgp:ignore TeamMemberForExport //msgp:ignore TeamMemberForExport

Просмотреть файл

@@ -22,7 +22,7 @@ make ARGS="config set SqlSettings.DataSource 'mmuser:mostest@tcp(localhost:3306)
echo "Setting up fresh db" echo "Setting up fresh db"
make ARGS="version --config $TMPDIR/config.json" run-cli 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 ); a=( $i );
echo "Ignoring known MySQL mismatch: ${a[0]}.${a[1]}" 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]};" docker exec mattermost-mysql mysql -D migrated -uroot -pmostest -e "ALTER TABLE ${a[0]} DROP COLUMN ${a[1]};"

Просмотреть файл

@@ -22,7 +22,7 @@ make ARGS="config set SqlSettings.DataSource 'postgres://mmuser:mostest@localhos
echo "Setting up fresh db" echo "Setting up fresh db"
make ARGS="version --config $TMPDIR/config.json" run-cli 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 ); a=( $i );
echo "Ignoring known Postgres mismatch: ${a[0]}.${a[1]}" 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]};" docker exec mattermost-postgres psql -U mmuser -d migrated -c "ALTER TABLE ${a[0]} DROP COLUMN ${a[1]};"

Просмотреть файл

@@ -1556,7 +1556,7 @@ func (s *OpenTracingLayerChannelStore) GroupSyncedChannelCount() (int64, error)
return result, err 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() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.IncrementMentionCount") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.IncrementMentionCount")
s.Root.Store.SetContext(newCtx) s.Root.Store.SetContext(newCtx)
@@ -1565,7 +1565,7 @@ func (s *OpenTracingLayerChannelStore) IncrementMentionCount(channelID string, u
}() }()
defer span.Finish() defer span.Finish()
err := s.ChannelStore.IncrementMentionCount(channelID, userId, updateThreads) err := s.ChannelStore.IncrementMentionCount(channelID, userId, updateThreads, isRoot)
if err != nil { if err != nil {
span.LogFields(spanlog.Error(err)) span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true) ext.Error.Set(span, true)
@@ -2110,7 +2110,7 @@ func (s *OpenTracingLayerChannelStore) UpdateLastViewedAt(channelIds []string, u
return result, err 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() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UpdateLastViewedAtPost") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UpdateLastViewedAtPost")
s.Root.Store.SetContext(newCtx) s.Root.Store.SetContext(newCtx)
@@ -2119,7 +2119,7 @@ func (s *OpenTracingLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.
}() }()
defer span.Finish() 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 { if err != nil {
span.LogFields(spanlog.Error(err)) span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true) ext.Error.Set(span, true)
@@ -7864,24 +7864,6 @@ func (s *OpenTracingLayerThreadStore) GetThreadForUser(userId string, teamId str
return result, err 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) { func (s *OpenTracingLayerThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetThreadsForUser") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetThreadsForUser")

Просмотреть файл

@@ -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 tries := 0
for { for {
err := s.ChannelStore.IncrementMentionCount(channelID, userId, updateThreads) err := s.ChannelStore.IncrementMentionCount(channelID, userId, updateThreads, isRoot)
if err == nil { if err == nil {
return 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 tries := 0
for { for {
result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, updateThreads) result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, mentionCountRoot, updateThreads)
if err == nil { if err == nil {
return result, 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) { func (s *RetryLayerThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
tries := 0 tries := 0

Просмотреть файл

@@ -39,34 +39,36 @@ type SqlChannelStore struct {
} }
type channelMember struct { type channelMember struct {
ChannelId string ChannelId string
UserId string UserId string
Roles string Roles string
LastViewedAt int64 LastViewedAt int64
MsgCount int64 MsgCount int64
MentionCount int64 MentionCount int64
NotifyProps model.StringMap NotifyProps model.StringMap
LastUpdateAt int64 LastUpdateAt int64
SchemeUser sql.NullBool SchemeUser sql.NullBool
SchemeAdmin sql.NullBool SchemeAdmin sql.NullBool
SchemeGuest sql.NullBool SchemeGuest sql.NullBool
MsgCountRoot int64 MentionCountRoot int64
MsgCountRoot int64
} }
func NewChannelMemberFromModel(cm *model.ChannelMember) *channelMember { func NewChannelMemberFromModel(cm *model.ChannelMember) *channelMember {
return &channelMember{ return &channelMember{
ChannelId: cm.ChannelId, ChannelId: cm.ChannelId,
UserId: cm.UserId, UserId: cm.UserId,
Roles: cm.ExplicitRoles, Roles: cm.ExplicitRoles,
LastViewedAt: cm.LastViewedAt, LastViewedAt: cm.LastViewedAt,
MsgCount: cm.MsgCount, MsgCount: cm.MsgCount,
MsgCountRoot: cm.MsgCountRoot, MentionCount: cm.MentionCount,
MentionCount: cm.MentionCount, MentionCountRoot: cm.MentionCountRoot,
NotifyProps: cm.NotifyProps, MsgCountRoot: cm.MsgCountRoot,
LastUpdateAt: cm.LastUpdateAt, NotifyProps: cm.NotifyProps,
SchemeGuest: sql.NullBool{Valid: true, Bool: cm.SchemeGuest}, LastUpdateAt: cm.LastUpdateAt,
SchemeUser: sql.NullBool{Valid: true, Bool: cm.SchemeUser}, SchemeGuest: sql.NullBool{Valid: true, Bool: cm.SchemeGuest},
SchemeAdmin: sql.NullBool{Valid: true, Bool: cm.SchemeAdmin}, 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 LastViewedAt int64
MsgCount int64 MsgCount int64
MentionCount int64 MentionCount int64
MentionCountRoot int64
NotifyProps model.StringMap NotifyProps model.StringMap
LastUpdateAt int64 LastUpdateAt int64
SchemeGuest sql.NullBool SchemeGuest sql.NullBool
@@ -92,7 +95,7 @@ type channelMemberWithSchemeRoles struct {
} }
func channelMemberSliceColumns() []string { 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{} { 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.MsgCount)
resultSlice = append(resultSlice, member.MsgCountRoot) resultSlice = append(resultSlice, member.MsgCountRoot)
resultSlice = append(resultSlice, member.MentionCount) resultSlice = append(resultSlice, member.MentionCount)
resultSlice = append(resultSlice, member.MentionCountRoot)
resultSlice = append(resultSlice, model.MapToJson(member.NotifyProps)) resultSlice = append(resultSlice, model.MapToJson(member.NotifyProps))
resultSlice = append(resultSlice, member.LastUpdateAt) resultSlice = append(resultSlice, member.LastUpdateAt)
resultSlice = append(resultSlice, member.SchemeUser) resultSlice = append(resultSlice, member.SchemeUser)
@@ -229,19 +233,20 @@ func (db channelMemberWithSchemeRoles) ToModel() *model.ChannelMember {
strings.Fields(db.Roles), strings.Fields(db.Roles),
) )
return &model.ChannelMember{ return &model.ChannelMember{
ChannelId: db.ChannelId, ChannelId: db.ChannelId,
UserId: db.UserId, UserId: db.UserId,
Roles: strings.Join(rolesResult.roles, " "), Roles: strings.Join(rolesResult.roles, " "),
LastViewedAt: db.LastViewedAt, LastViewedAt: db.LastViewedAt,
MsgCount: db.MsgCount, MsgCount: db.MsgCount,
MsgCountRoot: db.MsgCountRoot, MsgCountRoot: db.MsgCountRoot,
MentionCount: db.MentionCount, MentionCount: db.MentionCount,
NotifyProps: db.NotifyProps, MentionCountRoot: db.MentionCountRoot,
LastUpdateAt: db.LastUpdateAt, NotifyProps: db.NotifyProps,
SchemeAdmin: rolesResult.schemeAdmin, LastUpdateAt: db.LastUpdateAt,
SchemeUser: rolesResult.schemeUser, SchemeAdmin: rolesResult.schemeAdmin,
SchemeGuest: rolesResult.schemeGuest, SchemeUser: rolesResult.schemeUser,
ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "), 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 var unreadChannel model.ChannelUnread
err := s.GetReplica().SelectOne(&unreadChannel, err := s.GetReplica().SelectOne(&unreadChannel,
`SELECT `SELECT
Channels.TeamId TeamId, Channels.Id ChannelId, 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
(Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount,
(Channels.TotalMsgCountRoot - ChannelMembers.MsgCountRoot) MsgCountRoot,
ChannelMembers.MentionCount MentionCount, ChannelMembers.NotifyProps NotifyProps
FROM FROM
Channels, ChannelMembers Channels, ChannelMembers
WHERE WHERE
@@ -2086,6 +2088,7 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string,
ChannelMembers cm ChannelMembers cm
SET SET
MentionCount = 0, MentionCount = 0,
MentionCountRoot = 0,
MsgCount = greatest(cm.MsgCount, c.TotalMsgCount), MsgCount = greatest(cm.MsgCount, c.TotalMsgCount),
MsgCountRoot = greatest(cm.MsgCountRoot, c.TotalMsgCountRoot), MsgCountRoot = greatest(cm.MsgCountRoot, c.TotalMsgCountRoot),
LastViewedAt = greatest(cm.LastViewedAt, c.LastPostAt), LastViewedAt = greatest(cm.LastViewedAt, c.LastPostAt),
@@ -2140,6 +2143,7 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string,
ChannelMembers ChannelMembers
SET SET
MentionCount = 0, MentionCount = 0,
MentionCountRoot = 0,
MsgCount = CASE ChannelId ` + msgCountQuery + ` END, MsgCount = CASE ChannelId ` + msgCountQuery + ` END,
MsgCountRoot = CASE ChannelId ` + msgCountQueryRoot + ` END, MsgCountRoot = CASE ChannelId ` + msgCountQueryRoot + ` END,
LastViewedAt = CASE ChannelId ` + lastViewedQuery + ` 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. // 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 // 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. // 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 var threadsToUpdate []string
unreadDate := unreadPost.CreateAt - 1 unreadDate := unreadPost.CreateAt - 1
if updateThreads { if updateThreads {
@@ -2214,6 +2218,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s
params := map[string]interface{}{ params := map[string]interface{}{
"mentions": mentionCount, "mentions": mentionCount,
"mentionsRoot": mentionCountRoot,
"unreadCount": unread, "unreadCount": unread,
"unreadCountRoot": unreadRoot, "unreadCountRoot": unreadRoot,
"lastViewedAt": unreadDate, "lastViewedAt": unreadDate,
@@ -2229,6 +2234,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s
ChannelMembers ChannelMembers
SET SET
MentionCount = :mentions, MentionCount = :mentions,
MentionCountRoot = :mentionsRoot,
MsgCount = (SELECT TotalMsgCount FROM Channels WHERE ID = :channelId) - :unreadCount, MsgCount = (SELECT TotalMsgCount FROM Channels WHERE ID = :channelId) - :unreadCount,
MsgCountRoot = (SELECT TotalMsgCountRoot FROM Channels WHERE ID = :channelId) - :unreadCountRoot, MsgCountRoot = (SELECT TotalMsgCountRoot FROM Channels WHERE ID = :channelId) - :unreadCountRoot,
LastViewedAt = :lastViewedAt, LastViewedAt = :lastViewedAt,
@@ -2250,6 +2256,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s
cm.MsgCount MsgCount, cm.MsgCount MsgCount,
cm.MsgCountRoot MsgCountRoot, cm.MsgCountRoot MsgCountRoot,
cm.MentionCount MentionCount, cm.MentionCount MentionCount,
cm.MentionCountRoot MentionCountRoot,
cm.LastViewedAt LastViewedAt, cm.LastViewedAt LastViewedAt,
cm.NotifyProps NotifyProps cm.NotifyProps NotifyProps
FROM FROM
@@ -2271,7 +2278,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s
return result, nil 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() now := model.GetMillis()
var threadsToUpdate []string var threadsToUpdate []string
if updateThreads { if updateThreads {
@@ -2281,17 +2288,21 @@ func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string,
return err return err
} }
} }
rootInc := 0
if isRoot {
rootInc = 1
}
_, err := s.GetMaster().Exec( _, err := s.GetMaster().Exec(
`UPDATE `UPDATE
ChannelMembers ChannelMembers
SET SET
MentionCount = MentionCount + 1, MentionCount = MentionCount + 1,
MentionCountRoot = MentionCountRoot + :RootInc,
LastUpdateAt = :LastUpdateAt LastUpdateAt = :LastUpdateAt
WHERE WHERE
UserId = :UserId UserId = :UserId
AND ChannelId = :ChannelId`, AND ChannelId = :ChannelId`,
map[string]interface{}{"ChannelId": channelId, "UserId": userId, "LastUpdateAt": now}) map[string]interface{}{"ChannelId": channelId, "UserId": userId, "LastUpdateAt": now, "RootInc": rootInc})
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to Update ChannelMembers with channelId=%s and userId=%s", channelId, userId) 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.LastViewedAt,
ChannelMembers.MsgCount, ChannelMembers.MsgCount,
ChannelMembers.MentionCount, ChannelMembers.MentionCount,
ChannelMembers.MentionCountRoot,
ChannelMembers.NotifyProps, ChannelMembers.NotifyProps,
ChannelMembers.LastUpdateAt, ChannelMembers.LastUpdateAt,
ChannelMembers.SchemeUser, ChannelMembers.SchemeUser,
@@ -3236,7 +3248,7 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s
channelIds = append(channelIds, channel.Id) channelIds = append(channelIds, channel.Id)
} }
query = s.getQueryBuilder(). 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"). From("ChannelMembers cm").
Join("Users u ON ( u.Id = cm.UserId )"). Join("Users u ON ( u.Id = cm.UserId )").
Where(sq.And{ Where(sq.And{

Просмотреть файл

@@ -69,6 +69,7 @@ func testNewChannelMemberFromModel(t *testing.T) {
assert.Equal(t, m.LastViewedAt, db.LastViewedAt) assert.Equal(t, m.LastViewedAt, db.LastViewedAt)
assert.Equal(t, m.MsgCount, db.MsgCount) assert.Equal(t, m.MsgCount, db.MsgCount)
assert.Equal(t, m.MentionCount, db.MentionCount) 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.NotifyProps, db.NotifyProps)
assert.Equal(t, m.LastUpdateAt, db.LastUpdateAt) assert.Equal(t, m.LastUpdateAt, db.LastUpdateAt)
assert.Equal(t, true, db.SchemeGuest.Valid) 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.LastViewedAt, m.LastViewedAt)
assert.Equal(t, db.MsgCount, m.MsgCount) assert.Equal(t, db.MsgCount, m.MsgCount)
assert.Equal(t, db.MentionCount, m.MentionCount) 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.NotifyProps, m.NotifyProps)
assert.Equal(t, db.LastUpdateAt, m.LastUpdateAt) assert.Equal(t, db.LastUpdateAt, m.LastUpdateAt)
assert.Equal(t, db.SchemeGuest.Bool, m.SchemeGuest) assert.Equal(t, db.SchemeGuest.Bool, m.SchemeGuest)

Просмотреть файл

@@ -919,6 +919,7 @@ func (s *SqlGroupStore) ChannelMembersToRemove(channelID *string) ([]*model.Chan
"ChannelMembers.MsgCount", "ChannelMembers.MsgCount",
"ChannelMembers.MsgCountRoot", "ChannelMembers.MsgCountRoot",
"ChannelMembers.MentionCount", "ChannelMembers.MentionCount",
"ChannelMembers.MentionCountRoot",
"ChannelMembers.NotifyProps", "ChannelMembers.NotifyProps",
"ChannelMembers.LastUpdateAt", "ChannelMembers.LastUpdateAt",
"ChannelMembers.LastUpdateAt", "ChannelMembers.LastUpdateAt",

Просмотреть файл

@@ -2030,7 +2030,7 @@ func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId str
channelIds = append(channelIds, post.ChannelId) channelIds = append(channelIds, post.ChannelId)
} }
query = s.getQueryBuilder(). 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"). From("ChannelMembers cm").
Join("Users u ON ( u.Id = cm.UserId )"). Join("Users u ON ( u.Id = cm.UserId )").
Where(sq.Eq{ Where(sq.Eq{

Просмотреть файл

@@ -1177,7 +1177,7 @@ func (s SqlTeamStore) GetTeamsForUserWithPagination(userId string, page, perPage
// for all the channels in all the teams except the excluded ones. // for all the channels in all the teams except the excluded ones.
func (s SqlTeamStore) GetChannelUnreadsForAllTeams(excludeTeamId, userId string) ([]*model.ChannelUnread, error) { func (s SqlTeamStore) GetChannelUnreadsForAllTeams(excludeTeamId, userId string) ([]*model.ChannelUnread, error) {
query, args, err := s.getQueryBuilder(). 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"). From("Channels").
Join("ChannelMembers ON Id = ChannelId"). Join("ChannelMembers ON Id = ChannelId").
Where(sq.Eq{"UserId": userId, "DeleteAt": 0}). 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. // 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) { func (s SqlTeamStore) GetChannelUnreadsForTeam(teamId, userId string) ([]*model.ChannelUnread, error) {
query, args, err := s.getQueryBuilder(). 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"). From("Channels").
Join("ChannelMembers ON Id = ChannelId"). Join("ChannelMembers ON Id = ChannelId").
Where(sq.Eq{"UserId": userId, "TeamId": teamId, "DeleteAt": 0}).ToSql() Where(sq.Eq{"UserId": userId, "TeamId": teamId, "DeleteAt": 0}).ToSql()

Просмотреть файл

@@ -109,35 +109,6 @@ func (s *SqlThreadStore) Get(id string) (*model.Thread, error) {
return &thread, nil 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) { func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
type JoinedThread struct { type JoinedThread struct {
PostId string PostId string

Просмотреть файл

@@ -1011,6 +1011,38 @@ func upgradeDatabaseToVersion535(sqlStore *SqlStore) {
sqlStore.CreateColumnIfNotExists("SidebarCategories", "Collapsed", "tinyint(1)", "boolean", "0") 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.CreateColumnIfNotExists("Channels", "TotalMsgCountRoot", "bigint", "bigint", "0")
sqlStore.CreateColumnIfNotExistsNoDefault("Channels", "LastRootPostAt", "bigint", "bigint") sqlStore.CreateColumnIfNotExistsNoDefault("Channels", "LastRootPostAt", "bigint", "bigint")
defer sqlStore.RemoveColumnIfExists("Channels", "LastRootPostAt") defer sqlStore.RemoveColumnIfExists("Channels", "LastRootPostAt")

Просмотреть файл

@@ -1634,6 +1634,7 @@ func (us SqlUserStore) GetUsersBatchForIndexing(startTime, endTime int64, limit
cm.LastViewedAt, cm.LastViewedAt,
cm.MsgCount, cm.MsgCount,
cm.MentionCount, cm.MentionCount,
cm.MentionCountRoot,
cm.NotifyProps, cm.NotifyProps,
cm.LastUpdateAt, cm.LastUpdateAt,
cm.SchemeUser, cm.SchemeUser,

Просмотреть файл

@@ -193,9 +193,9 @@ type ChannelStore interface {
PermanentDeleteMembersByUser(userId string) error PermanentDeleteMembersByUser(userId string) error
PermanentDeleteMembersByChannel(channelID string) error PermanentDeleteMembersByChannel(channelID string) error
UpdateLastViewedAt(channelIds []string, userId string, updateThreads bool) (map[string]int64, 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) 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) AnalyticsTypeCount(teamID string, channelType string) (int64, error)
GetMembersForUser(teamID string, userId string) (*model.ChannelMembers, error) GetMembersForUser(teamID string, userId string) (*model.ChannelMembers, error)
GetMembersForUserWithPagination(teamID, userId string, page, perPage int) (*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) GetThreadForUser(userId, teamId, threadId string, extended bool) (*model.ThreadResponse, error)
Delete(postId string) error Delete(postId string) error
GetPosts(threadId string, since int64) ([]*model.Post, error) GetPosts(threadId string, since int64) ([]*model.Post, error)
GetThreadMentionsForUserPerChannel(userId, teamId string) (map[string]int64, error)
MarkAllAsRead(userId, teamID string) error MarkAllAsRead(userId, teamID string) error
MarkAsRead(userId, threadID string, timestamp int64) error MarkAsRead(userId, threadID string, timestamp int64) error

Просмотреть файл

@@ -340,7 +340,7 @@ func testGetChannelUnread(t *testing.T, ss store.Store) {
_, nErr = ss.Channel().Save(c2, -1) _, nErr = ss.Channel().Save(c2, -1)
require.NoError(t, nErr) 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) _, err = ss.Channel().SaveMember(cm2)
require.NoError(t, err) 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, c2.Id, ch2.ChannelId, "Wrong channel id")
require.Equal(t, teamId2, ch2.TeamId, "Wrong team id") require.Equal(t, teamId2, ch2.TeamId, "Wrong team id")
require.EqualValues(t, 5, ch2.MentionCount, "wrong MentionCount for channel 2") 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") 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) _, err := ss.Channel().SaveMember(&m1)
require.NoError(t, err) 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") 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") 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") 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") require.NoError(t, err, "failed to update")
} }

Просмотреть файл

@@ -1294,13 +1294,13 @@ func (_m *ChannelStore) GroupSyncedChannelCount() (int64, error) {
return r0, r1 return r0, r1
} }
// IncrementMentionCount provides a mock function with given fields: 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) error { func (_m *ChannelStore) IncrementMentionCount(channelID string, userId string, updateThreads bool, isRoot bool) error {
ret := _m.Called(channelID, userId, updateThreads) ret := _m.Called(channelID, userId, updateThreads, isRoot)
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(string, string, bool) error); ok { if rf, ok := ret.Get(0).(func(string, string, bool, bool) error); ok {
r0 = rf(channelID, userId, updateThreads) r0 = rf(channelID, userId, updateThreads, isRoot)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
@@ -1817,13 +1817,13 @@ func (_m *ChannelStore) UpdateLastViewedAt(channelIds []string, userId string, u
return r0, r1 return r0, r1
} }
// UpdateLastViewedAtPost provides a mock function with given fields: 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, updateThreads bool) (*model.ChannelUnreadAt, error) { func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, updateThreads bool) (*model.ChannelUnreadAt, error) {
ret := _m.Called(unreadPost, userID, mentionCount, updateThreads) ret := _m.Called(unreadPost, userID, mentionCount, mentionCountRoot, updateThreads)
var r0 *model.ChannelUnreadAt var r0 *model.ChannelUnreadAt
if rf, ok := ret.Get(0).(func(*model.Post, string, int, bool) *model.ChannelUnreadAt); ok { if rf, ok := ret.Get(0).(func(*model.Post, string, int, int, bool) *model.ChannelUnreadAt); ok {
r0 = rf(unreadPost, userID, mentionCount, updateThreads) r0 = rf(unreadPost, userID, mentionCount, mentionCountRoot, updateThreads)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.ChannelUnreadAt) r0 = ret.Get(0).(*model.ChannelUnreadAt)
@@ -1831,8 +1831,8 @@ func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID st
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(*model.Post, string, int, bool) error); ok { if rf, ok := ret.Get(1).(func(*model.Post, string, int, int, bool) error); ok {
r1 = rf(unreadPost, userID, mentionCount, updateThreads) r1 = rf(unreadPost, userID, mentionCount, mentionCountRoot, updateThreads)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }

Просмотреть файл

@@ -180,29 +180,6 @@ func (_m *ThreadStore) GetThreadForUser(userId string, teamId string, threadId s
return r0, r1 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 // 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) { func (_m *ThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
ret := _m.Called(userId, teamId, opts) ret := _m.Called(userId, teamId, opts)

Просмотреть файл

@@ -243,7 +243,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
_, err := ss.Thread().UpdateMembership(m) _, err := ss.Thread().UpdateMembership(m)
require.NoError(t, err) 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) require.NoError(t, err)
assert.Eventually(t, func() bool { assert.Eventually(t, func() bool {
@@ -263,7 +263,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
_, err := ss.Thread().UpdateMembership(m) _, err := ss.Thread().UpdateMembership(m)
require.NoError(t, err) 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) require.NoError(t, err)
assert.Eventually(t, func() bool { assert.Eventually(t, func() bool {
@@ -317,7 +317,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
_, err := ss.Thread().UpdateMembership(m) _, err := ss.Thread().UpdateMembership(m)
require.NoError(t, err) 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) require.NoError(t, err)
assert.Eventually(t, func() bool { assert.Eventually(t, func() bool {

Просмотреть файл

@@ -2246,7 +2246,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
// Post one message with mention to open channel // Post one message with mention to open channel
_, nErr = ss.Post().Save(&p1) _, nErr = ss.Post().Save(&p1)
require.NoError(t, nErr) 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) require.NoError(t, nErr)
// Post 2 messages without mention to direct channel // 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) _, nErr = ss.Post().Save(&p2)
require.NoError(t, nErr) 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) require.NoError(t, nErr)
p3 := model.Post{} p3 := model.Post{}
@@ -2267,7 +2267,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
_, nErr = ss.Post().Save(&p3) _, nErr = ss.Post().Save(&p3)
require.NoError(t, nErr) 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) require.NoError(t, nErr)
badge, unreadCountErr := ss.User().GetUnreadCount(u2.Id) badge, unreadCountErr := ss.User().GetUnreadCount(u2.Id)

Просмотреть файл

@@ -1431,10 +1431,10 @@ func (s *TimerLayerChannelStore) GroupSyncedChannelCount() (int64, error) {
return result, err 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() 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) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil { if s.Root.Metrics != nil {
@@ -1952,10 +1952,10 @@ func (s *TimerLayerChannelStore) UpdateLastViewedAt(channelIds []string, userId
return result, err 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() 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) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil { if s.Root.Metrics != nil {
@@ -7096,22 +7096,6 @@ func (s *TimerLayerThreadStore) GetThreadForUser(userId string, teamId string, t
return result, err 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) { func (s *TimerLayerThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
start := timemodule.Now() start := timemodule.Now()