[MM-42739] Insights - Top Channels API Endpoint (#19953)
* [MM-42739] Initial setup for top channels for team * [MM-42739] Add initial tests * [MM-42739] Update tests * [MM-42739] Add top channels for user * [MM-42739] Fix query * [MM-42739] Update query * [MM-42739] Improve query performance * [MM-42739] Remove rank * [MM-42739] Fix tests to use new time range today * [MM-42739] Add tests for top channels for user * [MM-42739] Add test for pagination * Remove top channels by time struct * [MM-42739] Update test names * [MM-42739] Remove rank from top reactions * [MM-42739] Return empty array instead of nil when result is empty * [MM-42739] Add additional tests and update permissions check for teams * [MM-42739] Add excluded channel tests for top reactions * [MM-42739] Move insights to api4/insights and keep time range as string until required * [MM-42739] Update queries only check DeleteAt after union * [MM-42739] Improve query performance by using publicchannels table * [MM-42739] Fix broken query after merge
Этот коммит содержится в:
@@ -4097,3 +4097,161 @@ func (s SqlChannelStore) GetTeamForChannel(channelID string) (*model.Team, error
|
||||
}
|
||||
return &team, nil
|
||||
}
|
||||
|
||||
// GetTopChannelsForTeamSince returns the filtered post counts of the following Channels sets:
|
||||
// a) those that are private channels in the given user's membership graph on the given team, and
|
||||
// b) those that are public channels in the given team.
|
||||
func (s SqlChannelStore) GetTopChannelsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
channels := make([]*model.TopChannel, 0)
|
||||
var args []interface{}
|
||||
postgresPropQuery := `AND (Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false')`
|
||||
mySqlPropsQuery := `AND (JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false')`
|
||||
|
||||
query := `
|
||||
SELECT
|
||||
ID,
|
||||
Type,
|
||||
DisplayName,
|
||||
Name,
|
||||
TeamID,
|
||||
MessageCount
|
||||
FROM
|
||||
((SELECT
|
||||
Posts.ChannelId AS ID,
|
||||
'O' AS Type,
|
||||
PublicChannels.DisplayName AS DisplayName,
|
||||
PublicChannels.Name AS Name,
|
||||
PublicChannels.TeamId AS TeamID,
|
||||
count(Posts.Id) AS MessageCount,
|
||||
PublicChannels.DeleteAt AS DeleteAt
|
||||
FROM
|
||||
Posts
|
||||
LEFT JOIN PublicChannels on Posts.ChannelId = PublicChannels.Id
|
||||
WHERE
|
||||
Posts.DeleteAt = 0
|
||||
AND Posts.CreateAt > ?
|
||||
AND Posts.Type = ''`
|
||||
args = []interface{}{since}
|
||||
|
||||
if s.DriverName() == model.DatabaseDriverMysql {
|
||||
query += mySqlPropsQuery
|
||||
} else if s.DriverName() == model.DatabaseDriverPostgres {
|
||||
query += postgresPropQuery
|
||||
}
|
||||
|
||||
query += `
|
||||
AND PublicChannels.TeamId = ?
|
||||
GROUP BY
|
||||
Posts.ChannelId,
|
||||
PublicChannels.DisplayName,
|
||||
PublicChannels.Name,
|
||||
PublicChannels.TeamId,
|
||||
PublicChannels.DeleteAt)
|
||||
UNION ALL
|
||||
(SELECT
|
||||
Posts.ChannelId AS ID,
|
||||
Channels.Type AS Type,
|
||||
Channels.DisplayName AS DisplayName,
|
||||
Channels.Name AS Name,
|
||||
Channels.TeamId AS TeamID,
|
||||
count(Posts.Id) AS MessageCount,
|
||||
Channels.DeleteAt AS DeleteAt
|
||||
FROM
|
||||
Posts
|
||||
LEFT JOIN Channels on Posts.ChannelId = Channels.Id
|
||||
LEFT JOIN ChannelMembers on Posts.ChannelId = ChannelMembers.ChannelId
|
||||
WHERE
|
||||
Posts.DeleteAt = 0
|
||||
AND Posts.CreateAt > ?
|
||||
AND Posts.Type = ''`
|
||||
args = append(args, teamID, since)
|
||||
|
||||
if s.DriverName() == model.DatabaseDriverMysql {
|
||||
query += mySqlPropsQuery
|
||||
} else if s.DriverName() == model.DatabaseDriverPostgres {
|
||||
query += postgresPropQuery
|
||||
}
|
||||
|
||||
query += `
|
||||
AND Channels.TeamId = ?
|
||||
AND Channels.Type = 'P'
|
||||
AND ChannelMembers.UserId = ?
|
||||
GROUP BY
|
||||
Posts.ChannelId,
|
||||
Channels.Type,
|
||||
Channels.DisplayName,
|
||||
Channels.Name,
|
||||
Channels.TeamId,
|
||||
Channels.DeleteAt)) AS A
|
||||
WHERE
|
||||
DeleteAt = 0
|
||||
ORDER BY
|
||||
MessageCount DESC,
|
||||
Name ASC
|
||||
LIMIT ?
|
||||
OFFSET ?`
|
||||
args = append(args, teamID, userID, limit+1, offset)
|
||||
|
||||
if err := s.GetReplicaX().Select(&channels, query, args...); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get top Channels")
|
||||
}
|
||||
|
||||
return model.GetTopChannelListWithPagination(channels, limit), nil
|
||||
}
|
||||
|
||||
// GetTopChannelsForUserSince returns the filtered post counts of channels with with posts created by the user
|
||||
// after the given timestamp within the given team (or across the workspace if no team is given). Excludes DM and GM channels.
|
||||
func (s SqlChannelStore) GetTopChannelsForUserSince(userID string, teamID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||
channels := make([]*model.TopChannel, 0)
|
||||
var args []interface{}
|
||||
var query string
|
||||
|
||||
query = `
|
||||
SELECT
|
||||
Posts.ChannelId AS ID,
|
||||
Channels.Type AS Type,
|
||||
Channels.DisplayName AS DisplayName,
|
||||
Channels.Name AS Name,
|
||||
Channels.TeamId AS TeamID,
|
||||
count(Posts.Id) AS MessageCount
|
||||
FROM
|
||||
Posts
|
||||
LEFT JOIN Channels on Posts.ChannelId = Channels.Id
|
||||
LEFT JOIN ChannelMembers on Posts.ChannelId = ChannelMembers.ChannelId
|
||||
WHERE
|
||||
Posts.DeleteAt = 0
|
||||
AND Posts.CreateAt > ?
|
||||
AND Posts.Type = ''
|
||||
AND Posts.UserID = ?
|
||||
AND Channels.DeleteAt = 0
|
||||
AND (Channels.Type = 'O' OR Channels.Type = 'P')
|
||||
AND ChannelMembers.UserId = ?`
|
||||
|
||||
args = []interface{}{since, userID, userID}
|
||||
|
||||
if teamID != "" {
|
||||
query += `
|
||||
AND Channels.TeamID = ?`
|
||||
args = append(args, teamID)
|
||||
}
|
||||
|
||||
query += `
|
||||
Group By
|
||||
Posts.ChannelId,
|
||||
Channels.Type,
|
||||
Channels.DisplayName,
|
||||
Channels.Name,
|
||||
Channels.TeamId
|
||||
ORDER BY
|
||||
MessageCount DESC,
|
||||
Name ASC
|
||||
LIMIT ?
|
||||
OFFSET ?`
|
||||
args = append(args, limit+1, offset)
|
||||
|
||||
if err := s.GetReplicaX().Select(&channels, query, args...); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get top Channels")
|
||||
}
|
||||
|
||||
return model.GetTopChannelListWithPagination(channels, limit), nil
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ func (s *SqlReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int
|
||||
// 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
|
||||
reactions := make([]*model.TopReaction, 0)
|
||||
|
||||
query := `
|
||||
SELECT
|
||||
@@ -241,34 +241,41 @@ func (s *SqlReactionStore) GetTopForTeamSince(teamID string, userID string, sinc
|
||||
FROM ((
|
||||
SELECT
|
||||
EmojiName,
|
||||
count(EmojiName) AS EmojiCount
|
||||
count(EmojiName) AS EmojiCount,
|
||||
Reactions.DeleteAt AS DeleteAt,
|
||||
Reactions.CreateAt AS CreateAt
|
||||
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 = ?
|
||||
ChannelMembers.UserId = ?
|
||||
AND Channels.Type = 'P'
|
||||
AND Channels.TeamId = ?
|
||||
AND Reactions.CreateAt > ?
|
||||
GROUP BY
|
||||
Reactions.EmojiName)
|
||||
Reactions.EmojiName,
|
||||
Reactions.DeleteAt,
|
||||
Reactions.CreateAt)
|
||||
UNION ALL (
|
||||
SELECT
|
||||
EmojiName,
|
||||
count(EmojiName) AS EmojiCount
|
||||
count(EmojiName) AS EmojiCount,
|
||||
Reactions.DeleteAt AS DeleteAt,
|
||||
Reactions.CreateAt AS CreateAt
|
||||
FROM
|
||||
Reactions
|
||||
INNER JOIN Posts ON Reactions.PostId = Posts.Id
|
||||
INNER JOIN PublicChannels ON Posts.ChannelId = PublicChannels.Id
|
||||
WHERE
|
||||
Reactions.DeleteAt = 0
|
||||
AND PublicChannels.TeamId = ?
|
||||
AND Reactions.CreateAt > ?
|
||||
PublicChannels.TeamId = ?
|
||||
GROUP BY
|
||||
Reactions.EmojiName)) AS A
|
||||
Reactions.EmojiName,
|
||||
Reactions.DeleteAt,
|
||||
Reactions.CreateAt)) AS A
|
||||
WHERE
|
||||
DeleteAt = 0
|
||||
AND CreateAt > ?
|
||||
GROUP BY
|
||||
EmojiName
|
||||
ORDER BY
|
||||
@@ -277,18 +284,18 @@ func (s *SqlReactionStore) GetTopForTeamSince(teamID string, userID string, sinc
|
||||
LIMIT ?
|
||||
OFFSET ?`
|
||||
|
||||
if err := s.GetReplicaX().Select(&reactions, query, userID, teamID, since, teamID, since, limit+1, offset); err != nil {
|
||||
if err := s.GetReplicaX().Select(&reactions, query, userID, teamID, teamID, since, limit+1, offset); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get top Reactions")
|
||||
}
|
||||
|
||||
return model.GetTopReactionListWithRankAndPagination(reactions, limit, offset), nil
|
||||
return model.GetTopReactionListWithPagination(reactions, limit), 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
|
||||
reactions := make([]*model.TopReaction, 0)
|
||||
var args []interface{}
|
||||
var query string
|
||||
|
||||
@@ -339,7 +346,7 @@ func (s *SqlReactionStore) GetTopForUserSince(userID string, teamID string, sinc
|
||||
return nil, errors.Wrap(err, "failed to get top Reactions")
|
||||
}
|
||||
|
||||
return model.GetTopReactionListWithRankAndPagination(reactions, limit, offset), nil
|
||||
return model.GetTopReactionListWithPagination(reactions, limit), nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) saveReactionAndUpdatePost(transaction *sqlxTxWrapper, reaction *model.Reaction) error {
|
||||
|
||||
Ссылка в новой задаче
Block a user