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 \
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"

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

@@ -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)
}

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

@@ -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 {

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

@@ -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) {

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

@@ -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)

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

@@ -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)

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

@@ -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) {

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

@@ -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

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

@@ -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")

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

@@ -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 {

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

@@ -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)

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

@@ -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,
})
}
}

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

@@ -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 {

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

@@ -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

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

@@ -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 {

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

@@ -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

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

@@ -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]};"

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

@@ -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]};"

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

@@ -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")

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

@@ -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

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

@@ -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{

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

@@ -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)

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

@@ -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",

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

@@ -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{

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

@@ -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()

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

@@ -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

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

@@ -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")

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

@@ -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,

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

@@ -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

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

@@ -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")
}

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

@@ -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)
}

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

@@ -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)

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

@@ -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 {

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

@@ -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)

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

@@ -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()