2019-05-02 18:17:27 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-01-19 23:11:52 -05:00
|
|
|
# ViewModel used on Summary tab on User page
|
|
|
|
|
|
|
|
class UserSummary
|
2016-04-13 17:02:51 -04:00
|
|
|
MAX_SUMMARY_RESULTS = 6
|
2016-03-30 12:05:16 -04:00
|
|
|
MAX_BADGES = 6
|
2016-01-19 23:11:52 -05:00
|
|
|
|
|
|
|
alias read_attribute_for_serialization send
|
|
|
|
|
|
|
|
def initialize(user, guardian)
|
|
|
|
@user = user
|
|
|
|
@guardian = guardian
|
|
|
|
end
|
|
|
|
|
|
|
|
def topics
|
|
|
|
Topic
|
|
|
|
.secured(@guardian)
|
|
|
|
.listable_topics
|
2016-01-27 19:12:12 -05:00
|
|
|
.visible
|
2016-01-19 23:11:52 -05:00
|
|
|
.where(user: @user)
|
2019-01-16 17:58:46 -05:00
|
|
|
.order("like_count DESC, created_at DESC")
|
2016-04-13 17:02:51 -04:00
|
|
|
.limit(MAX_SUMMARY_RESULTS)
|
2016-01-19 23:11:52 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def replies
|
2021-11-18 03:12:03 -05:00
|
|
|
post_query
|
2016-01-19 23:11:52 -05:00
|
|
|
.where("post_number > 1")
|
2019-01-16 17:58:46 -05:00
|
|
|
.order("posts.like_count DESC, posts.created_at DESC")
|
2016-04-13 17:02:51 -04:00
|
|
|
.limit(MAX_SUMMARY_RESULTS)
|
|
|
|
end
|
|
|
|
|
|
|
|
def links
|
|
|
|
TopicLink
|
2016-04-19 12:15:53 -04:00
|
|
|
.joins(:topic, :post)
|
2020-07-07 17:56:29 -04:00
|
|
|
.where(posts: { user_id: @user.id })
|
2016-04-20 16:58:30 -04:00
|
|
|
.includes(:topic, :post)
|
2016-05-02 09:26:23 -04:00
|
|
|
.where("posts.post_type IN (?)", Topic.visible_post_types(@guardian && @guardian.user))
|
|
|
|
.merge(Topic.listable_topics.visible.secured(@guardian))
|
2016-04-13 17:02:51 -04:00
|
|
|
.where(user: @user)
|
2016-04-19 12:15:53 -04:00
|
|
|
.where(internal: false, reflection: false, quote: false)
|
2019-01-16 17:58:46 -05:00
|
|
|
.order("clicks DESC, topic_links.created_at DESC")
|
2016-04-13 17:02:51 -04:00
|
|
|
.limit(MAX_SUMMARY_RESULTS)
|
|
|
|
end
|
|
|
|
|
2016-05-04 16:47:48 -04:00
|
|
|
class UserWithCount < OpenStruct
|
2016-04-13 17:02:51 -04:00
|
|
|
include ActiveModel::SerializerSupport
|
|
|
|
end
|
|
|
|
|
|
|
|
def most_liked_by_users
|
2016-04-18 12:03:33 -04:00
|
|
|
likers = {}
|
2016-04-19 12:15:53 -04:00
|
|
|
UserAction
|
|
|
|
.joins(:target_topic, :target_post)
|
2016-05-30 13:48:46 -04:00
|
|
|
.merge(Topic.listable_topics.visible.secured(@guardian))
|
2016-04-17 06:21:27 -04:00
|
|
|
.where(user: @user)
|
2016-04-13 17:02:51 -04:00
|
|
|
.where(action_type: UserAction::WAS_LIKED)
|
|
|
|
.group(:acting_user_id)
|
2016-04-19 12:15:53 -04:00
|
|
|
.order("COUNT(*) DESC")
|
2016-04-13 17:02:51 -04:00
|
|
|
.limit(MAX_SUMMARY_RESULTS)
|
2016-04-19 12:15:53 -04:00
|
|
|
.pluck("acting_user_id, COUNT(*)")
|
2018-10-18 15:49:34 -04:00
|
|
|
.each { |l| likers[l[0]] = l[1] }
|
2016-04-13 17:02:51 -04:00
|
|
|
|
2018-10-18 15:49:34 -04:00
|
|
|
user_counts(likers)
|
2016-05-04 16:47:48 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def most_liked_users
|
|
|
|
liked_users = {}
|
|
|
|
UserAction
|
|
|
|
.joins(:target_topic, :target_post)
|
2016-05-30 13:48:46 -04:00
|
|
|
.merge(Topic.listable_topics.visible.secured(@guardian))
|
2016-05-04 16:47:48 -04:00
|
|
|
.where(action_type: UserAction::WAS_LIKED)
|
|
|
|
.where(acting_user_id: @user.id)
|
|
|
|
.group(:user_id)
|
|
|
|
.order("COUNT(*) DESC")
|
|
|
|
.limit(MAX_SUMMARY_RESULTS)
|
|
|
|
.pluck("user_actions.user_id, COUNT(*)")
|
2018-10-18 15:49:34 -04:00
|
|
|
.each { |l| liked_users[l[0]] = l[1] }
|
2016-05-04 16:47:48 -04:00
|
|
|
|
2018-10-18 15:49:34 -04:00
|
|
|
user_counts(liked_users)
|
2016-05-04 16:47:48 -04:00
|
|
|
end
|
|
|
|
|
2024-10-15 22:09:07 -04:00
|
|
|
REPLY_ACTIONS = [UserAction::RESPONSE, UserAction::QUOTE, UserAction::MENTION]
|
2016-05-04 16:47:48 -04:00
|
|
|
|
|
|
|
def most_replied_to_users
|
|
|
|
replied_users = {}
|
|
|
|
|
2021-11-18 03:12:03 -05:00
|
|
|
post_query
|
2016-05-04 16:47:48 -04:00
|
|
|
.joins(
|
|
|
|
"JOIN posts replies ON posts.topic_id = replies.topic_id AND posts.reply_to_post_number = replies.post_number",
|
|
|
|
)
|
PERF: Improve performance of `most_replied_to_users` (#26373)
This PR improves the performance of the `most_replied_to_users` method on the `UserSummary` model.
### Old Query
```ruby
post_query
.joins(
"JOIN posts replies ON posts.topic_id = replies.topic_id AND posts.reply_to_post_number = replies.post_number",
)
# We are removing replies by @user, but we can simplify this by getting the using the user_id on the posts.
.where("replies.user_id <> ?", @user.id)
.group("replies.user_id")
.order("COUNT(*) DESC")
.limit(MAX_SUMMARY_RESULTS)
.pluck("replies.user_id, COUNT(*)")
.each { |r| replied_users[r[0]] = r[1] }
```
### Old Query with corrections
```ruby
post_query
.joins(
"JOIN posts replies ON posts.topic_id = replies.topic_id AND replies.reply_to_post_number = posts.post_number",
)
# Remove replies by @user but instead look on loaded posts (we do this so we don't count self replies)
.where("replies.user_id <> posts.user_id")
.group("replies.user_id")
.order("COUNT(*) DESC")
.limit(MAX_SUMMARY_RESULTS)
.pluck("replies.user_id, COUNT(*)")
.each { |r| replied_users[r[0]] = r[1] }
```
### New Query
```ruby
post_query
.joins(
"JOIN posts replies ON posts.topic_id = replies.topic_id AND posts.reply_to_post_number = replies.post_number",
)
# Only include regular posts in our joins, this makes sure we don't have the bloat of loading private messages
.joins(
"JOIN topics ON replies.topic_id = topics.id AND topics.archetype <> 'private_message'",
)
# Only include visible post types, so exclude posts like whispers, etc
.joins(
"AND replies.post_type IN (#{Topic.visible_post_types(@user, include_moderator_actions: false).join(",")})",
)
.where("replies.user_id <> posts.user_id")
.group("replies.user_id")
.order("COUNT(*) DESC")
.limit(MAX_SUMMARY_RESULTS)
.pluck("replies.user_id, COUNT(*)")
.each { |r| replied_users[r[0]] = r[1] }
```
# Conclusion
`most_replied_to_users` was untested, so I introduced a test for the logic, and have confirmed that it passes on both the new query **AND** the old query.
Thank you @danielwaterworth for the debugging assistance.
2024-04-03 16:20:54 -04:00
|
|
|
.joins(
|
|
|
|
"JOIN topics ON replies.topic_id = topics.id AND topics.archetype <> 'private_message'",
|
|
|
|
)
|
|
|
|
.joins(
|
|
|
|
"AND replies.post_type IN (#{Topic.visible_post_types(@user, include_moderator_actions: false).join(",")})",
|
|
|
|
)
|
|
|
|
.where("replies.user_id <> posts.user_id")
|
2016-05-04 16:47:48 -04:00
|
|
|
.group("replies.user_id")
|
|
|
|
.order("COUNT(*) DESC")
|
|
|
|
.limit(MAX_SUMMARY_RESULTS)
|
|
|
|
.pluck("replies.user_id, COUNT(*)")
|
2018-10-18 15:49:34 -04:00
|
|
|
.each { |r| replied_users[r[0]] = r[1] }
|
2016-05-04 16:47:48 -04:00
|
|
|
|
2018-10-18 15:49:34 -04:00
|
|
|
user_counts(replied_users)
|
2016-01-19 23:11:52 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def badges
|
2016-03-30 12:05:16 -04:00
|
|
|
@user.featured_user_badges(MAX_BADGES)
|
2016-01-19 23:11:52 -05:00
|
|
|
end
|
|
|
|
|
2016-05-09 09:51:43 -04:00
|
|
|
def user_id
|
|
|
|
@user.id
|
|
|
|
end
|
|
|
|
|
2020-05-14 12:57:35 -04:00
|
|
|
def user
|
|
|
|
@user
|
|
|
|
end
|
|
|
|
|
2016-01-19 23:11:52 -05:00
|
|
|
def user_stat
|
|
|
|
@user.user_stat
|
|
|
|
end
|
|
|
|
|
2016-04-13 17:02:51 -04:00
|
|
|
def bookmark_count
|
2020-04-21 23:44:19 -04:00
|
|
|
Bookmark.where(user: @user).count
|
2016-04-13 17:02:51 -04:00
|
|
|
end
|
|
|
|
|
2017-11-14 16:39:07 -05:00
|
|
|
def recent_time_read
|
|
|
|
@user.recent_time_read
|
|
|
|
end
|
|
|
|
|
2018-07-18 16:37:50 -04:00
|
|
|
class CategoryWithCounts < OpenStruct
|
|
|
|
include ActiveModel::SerializerSupport
|
|
|
|
KEYS = %i[id name color text_color slug read_restricted parent_category_id]
|
|
|
|
end
|
|
|
|
|
|
|
|
def top_categories
|
2021-11-18 03:12:03 -05:00
|
|
|
post_count_query = post_query.group("topics.category_id")
|
2018-07-18 16:37:50 -04:00
|
|
|
|
|
|
|
top_categories = {}
|
|
|
|
|
2021-04-15 06:05:03 -04:00
|
|
|
Category
|
|
|
|
.where(
|
|
|
|
id: post_count_query.order("count(*) DESC").limit(MAX_SUMMARY_RESULTS).pluck("category_id"),
|
|
|
|
)
|
2018-07-18 16:37:50 -04:00
|
|
|
.pluck(:id, :name, :color, :text_color, :slug, :read_restricted, :parent_category_id)
|
|
|
|
.each do |c|
|
|
|
|
top_categories[c[0].to_i] = CategoryWithCounts.new(
|
|
|
|
Hash[CategoryWithCounts::KEYS.zip(c)].merge(topic_count: 0, post_count: 0),
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
post_count_query
|
|
|
|
.where("post_number > 1")
|
|
|
|
.where("topics.category_id in (?)", top_categories.keys)
|
|
|
|
.pluck("category_id, COUNT(*)")
|
|
|
|
.each { |r| top_categories[r[0].to_i].post_count = r[1] }
|
|
|
|
|
|
|
|
Topic
|
|
|
|
.listable_topics
|
|
|
|
.visible
|
|
|
|
.secured(@guardian)
|
|
|
|
.where("topics.category_id in (?)", top_categories.keys)
|
|
|
|
.where(user: @user)
|
|
|
|
.group("topics.category_id")
|
|
|
|
.pluck("category_id, COUNT(*)")
|
|
|
|
.each { |r| top_categories[r[0].to_i].topic_count = r[1] }
|
|
|
|
|
|
|
|
top_categories.values.sort_by { |r| -(r[:post_count] + r[:topic_count]) }
|
|
|
|
end
|
|
|
|
|
2016-03-30 12:05:16 -04:00
|
|
|
delegate :likes_given,
|
|
|
|
:likes_received,
|
|
|
|
:days_visited,
|
2017-11-17 17:53:22 -05:00
|
|
|
:topics_entered,
|
2016-03-30 12:05:16 -04:00
|
|
|
:posts_read_count,
|
|
|
|
:topic_count,
|
|
|
|
:post_count,
|
|
|
|
:time_read,
|
2016-01-19 23:11:52 -05:00
|
|
|
to: :user_stat
|
|
|
|
|
2018-10-18 15:49:34 -04:00
|
|
|
protected
|
|
|
|
|
|
|
|
def user_counts(user_hash)
|
|
|
|
user_ids = user_hash.keys
|
|
|
|
|
2020-07-17 05:48:08 -04:00
|
|
|
lookup = UserLookup.new(user_ids)
|
2018-10-18 15:49:34 -04:00
|
|
|
user_ids
|
|
|
|
.map do |user_id|
|
2018-12-24 14:34:55 -05:00
|
|
|
lookup_hash = lookup[user_id]
|
2023-01-09 07:20:10 -05:00
|
|
|
|
2021-04-27 15:09:32 -04:00
|
|
|
if lookup_hash.present?
|
|
|
|
primary_group = lookup.primary_groups[user_id]
|
2021-07-08 03:46:21 -04:00
|
|
|
flair_group = lookup.flair_groups[user_id]
|
2023-01-09 07:20:10 -05:00
|
|
|
|
2021-04-27 15:09:32 -04:00
|
|
|
UserWithCount.new(
|
2021-07-08 03:46:21 -04:00
|
|
|
lookup_hash.attributes.merge(
|
|
|
|
count: user_hash[user_id],
|
|
|
|
primary_group: primary_group,
|
|
|
|
flair_group: flair_group,
|
2023-01-09 07:20:10 -05:00
|
|
|
),
|
2021-07-08 03:46:21 -04:00
|
|
|
)
|
2023-01-09 07:20:10 -05:00
|
|
|
end
|
2021-04-27 15:09:32 -04:00
|
|
|
end
|
2018-12-24 14:34:55 -05:00
|
|
|
.compact
|
|
|
|
.sort_by { |u| -u[:count] }
|
2018-10-18 15:49:34 -04:00
|
|
|
end
|
|
|
|
|
2021-11-18 03:12:03 -05:00
|
|
|
def post_query
|
|
|
|
Post
|
|
|
|
.joins(:topic)
|
|
|
|
.includes(:topic)
|
|
|
|
.where(
|
|
|
|
"posts.post_type IN (?)",
|
|
|
|
Topic.visible_post_types(@guardian&.user, include_moderator_actions: false),
|
|
|
|
)
|
|
|
|
.merge(Topic.listable_topics.visible.secured(@guardian))
|
|
|
|
.where(user: @user)
|
|
|
|
end
|
2016-01-19 23:11:52 -05:00
|
|
|
end
|