[MM-42742] Add top reactions endpoint (#19850)

Этот коммит содержится в:
Mylon Suren
2022-04-14 13:09:35 -04:00
коммит произвёл GitHub
родитель a98975b00f
Коммит 99f02fe96e
18 изменённых файлов: 1360 добавлений и 0 удалений

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

@@ -6451,6 +6451,42 @@ func (s *OpenTracingLayerReactionStore) GetForPostSince(postId string, since int
return result, err
}
func (s *OpenTracingLayerReactionStore) GetTopForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ReactionStore.GetTopForTeamSince")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.ReactionStore.GetTopForTeamSince(teamID, userID, since, offset, limit)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerReactionStore) GetTopForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ReactionStore.GetTopForUserSince")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.ReactionStore.GetTopForUserSince(userID, teamID, since, offset, limit)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ReactionStore.PermanentDeleteBatch")

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

@@ -7315,6 +7315,48 @@ func (s *RetryLayerReactionStore) GetForPostSince(postId string, since int64, ex
}
func (s *RetryLayerReactionStore) GetTopForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
tries := 0
for {
result, err := s.ReactionStore.GetTopForTeamSince(teamID, userID, since, offset, limit)
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
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerReactionStore) GetTopForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
tries := 0
for {
result, err := s.ReactionStore.GetTopForUserSince(userID, teamID, since, offset, limit)
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
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, error) {
tries := 0

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

@@ -228,6 +228,121 @@ func (s *SqlReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int
return rowsAffected, nil
}
// GetTopForTeamSince returns the instance counts of the following Reactions sets:
// a) those created by anyone in private channels in the given user's membership graph on the given team, and
// b) those created by anyone in public channels on the given team.
func (s *SqlReactionStore) GetTopForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
var reactions []*model.TopReaction
query := `
SELECT
EmojiName,
sum(EmojiCount) AS Count
FROM ((
SELECT
EmojiName,
count(EmojiName) AS EmojiCount
FROM
ChannelMembers
INNER JOIN Channels ON ChannelMembers.ChannelId = Channels.Id
INNER JOIN Posts ON Channels.Id = Posts.ChannelId
INNER JOIN Reactions ON Posts.Id = Reactions.PostId
WHERE
Reactions.DeleteAt = 0
AND ChannelMembers.UserId = ?
AND Channels.Type = 'P'
AND Channels.TeamId = ?
AND Reactions.CreateAt > ?
GROUP BY
Reactions.EmojiName)
UNION ALL (
SELECT
EmojiName,
count(EmojiName) AS EmojiCount
FROM
Reactions
INNER JOIN Posts ON Reactions.PostId = Posts.Id
INNER JOIN Channels ON Posts.ChannelId = Channels.Id
WHERE
Reactions.DeleteAt = 0
AND Channels.Type = 'O'
AND Channels.TeamId = ?
AND Reactions.CreateAt > ?
GROUP BY
Reactions.EmojiName)) AS A
GROUP BY
EmojiName
ORDER BY
Count DESC,
EmojiName ASC
LIMIT ?
OFFSET ?`
if err := s.GetReplicaX().Select(&reactions, query, userID, teamID, since, teamID, since, limit+1, offset); err != nil {
return nil, errors.Wrap(err, "failed to get top Reactions")
}
return model.GetTopReactionListWithRankAndPagination(reactions, limit, offset), nil
}
// GetTopForUserSince returns the instance counts of the following Reactions sets:
// a) those created by the given user in any channel type on the given team (across the workspace if no team is given), and
// b) those created by the given user in DM or group channels.
func (s *SqlReactionStore) GetTopForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
var reactions []*model.TopReaction
var args []interface{}
var query string
if teamID != "" {
query = `
SELECT
EmojiName,
count(EmojiName) AS Count
FROM
Reactions
INNER JOIN Posts ON Reactions.PostId = Posts.Id
INNER JOIN Channels ON Posts.ChannelId = Channels.Id
WHERE
Reactions.DeleteAt = 0
AND Reactions.UserId = ?
AND (Channels.TeamId = ? OR Channels.Type = 'D' OR Channels.Type = 'G')
AND Reactions.CreateAt > ?
GROUP BY
EmojiName
ORDER BY
Count DESC,
EmojiName ASC
LIMIT ?
OFFSET ?`
args = []interface{}{userID, teamID, since, limit + 1, offset}
} else {
query = `
SELECT
EmojiName,
count(EmojiName) AS Count
FROM
Reactions
WHERE
Reactions.DeleteAt = 0
AND Reactions.UserId = ?
AND Reactions.CreateAt > ?
GROUP BY
Reactions.EmojiName
ORDER BY
Count DESC,
EmojiName ASC
LIMIT ?
OFFSET ?`
args = []interface{}{userID, since, limit + 1, offset}
}
if err := s.GetReplicaX().Select(&reactions, query, args...); err != nil {
return nil, errors.Wrap(err, "failed to get top Reactions")
}
return model.GetTopReactionListWithRankAndPagination(reactions, limit, offset), nil
}
func (s *SqlReactionStore) saveReactionAndUpdatePost(transaction *sqlxTxWrapper, reaction *model.Reaction) error {
reaction.DeleteAt = 0

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

@@ -679,6 +679,8 @@ type ReactionStore interface {
BulkGetForPosts(postIds []string) ([]*model.Reaction, error)
DeleteOrphanedRows(limit int) (int64, error)
PermanentDeleteBatch(endTime int64, limit int64) (int64, error)
GetTopForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopReactionList, error)
GetTopForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopReactionList, error)
}
type JobStore interface {

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

@@ -141,6 +141,52 @@ func (_m *ReactionStore) GetForPostSince(postId string, since int64, excludeRemo
return r0, r1
}
// GetTopForTeamSince provides a mock function with given fields: teamID, userID, since, offset, limit
func (_m *ReactionStore) GetTopForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
ret := _m.Called(teamID, userID, since, offset, limit)
var r0 *model.TopReactionList
if rf, ok := ret.Get(0).(func(string, string, int64, int, int) *model.TopReactionList); ok {
r0 = rf(teamID, userID, since, offset, limit)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.TopReactionList)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string, int64, int, int) error); ok {
r1 = rf(teamID, userID, since, offset, limit)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetTopForUserSince provides a mock function with given fields: userID, teamID, since, offset, limit
func (_m *ReactionStore) GetTopForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
ret := _m.Called(userID, teamID, since, offset, limit)
var r0 *model.TopReactionList
if rf, ok := ret.Get(0).(func(string, string, int64, int, int) *model.TopReactionList); ok {
r0 = rf(userID, teamID, since, offset, limit)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.TopReactionList)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string, int64, int, int) error); ok {
r1 = rf(userID, teamID, since, offset, limit)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// PermanentDeleteBatch provides a mock function with given fields: endTime, limit
func (_m *ReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, error) {
ret := _m.Called(endTime, limit)

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

@@ -5833,6 +5833,38 @@ func (s *TimerLayerReactionStore) GetForPostSince(postId string, since int64, ex
return result, err
}
func (s *TimerLayerReactionStore) GetTopForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
start := timemodule.Now()
result, err := s.ReactionStore.GetTopForTeamSince(teamID, userID, since, offset, limit)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ReactionStore.GetTopForTeamSince", success, elapsed)
}
return result, err
}
func (s *TimerLayerReactionStore) GetTopForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopReactionList, error) {
start := timemodule.Now()
result, err := s.ReactionStore.GetTopForUserSince(userID, teamID, since, offset, limit)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ReactionStore.GetTopForUserSince", success, elapsed)
}
return result, err
}
func (s *TimerLayerReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, error) {
start := timemodule.Now()