[MM-42742] Add top reactions endpoint (#19850)
Этот коммит содержится в:
@@ -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()
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user