mirror of
https://github.com/discourse/discourse.git
synced 2025-02-11 05:44:59 +00:00
This partially reverts commit ddb458343dc39a7a8c99467dcd809b444514fe2c. Seeing performance degrade on larger sites so back to drawing board on this one. Instead of the DISTINCT LEFT JOIN, we switch back to IN(subquery).
304 lines
9.8 KiB
Ruby
304 lines
9.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class TopicQuery
|
|
module PrivateMessageLists
|
|
def list_private_messages_all(user)
|
|
list = private_messages_for(user, :all)
|
|
list = filter_archived(list, user, archived: false)
|
|
create_list(:private_messages, {}, list)
|
|
end
|
|
|
|
def list_private_messages_all_sent(user)
|
|
list = private_messages_for(user, :all)
|
|
|
|
list = list.where(<<~SQL, user.id)
|
|
EXISTS (
|
|
SELECT 1 FROM posts
|
|
WHERE posts.topic_id = topics.id AND posts.user_id = ?
|
|
)
|
|
SQL
|
|
|
|
list = filter_archived(list, user, archived: false)
|
|
create_list(:private_messages, {}, list)
|
|
end
|
|
|
|
def list_private_messages_all_archive(user)
|
|
list = private_messages_for(user, :all)
|
|
list = filter_archived(list, user, archived: true)
|
|
create_list(:private_messages, {}, list)
|
|
end
|
|
|
|
def list_private_messages_all_new(user)
|
|
list_private_messages_new(user, :all)
|
|
end
|
|
|
|
def list_private_messages_all_unread(user)
|
|
list_private_messages_unread(user, :all)
|
|
end
|
|
|
|
def list_private_messages(user)
|
|
list = private_messages_for(user, :user)
|
|
list = not_archived(list, user)
|
|
create_list(:private_messages, {}, list)
|
|
end
|
|
|
|
def list_private_messages_archive(user)
|
|
list = private_messages_for(user, :user)
|
|
list = list.joins(:user_archived_messages).where('user_archived_messages.user_id = ?', user.id)
|
|
create_list(:private_messages, {}, list)
|
|
end
|
|
|
|
def list_private_messages_sent(user)
|
|
list = private_messages_for(user, :user)
|
|
|
|
list = list.where(<<~SQL, user.id)
|
|
EXISTS (
|
|
SELECT 1 FROM posts
|
|
WHERE posts.topic_id = topics.id AND posts.user_id = ?
|
|
)
|
|
SQL
|
|
|
|
list = not_archived(list, user)
|
|
create_list(:private_messages, {}, list)
|
|
end
|
|
|
|
def list_private_messages_new(user, type = :user)
|
|
list = filter_private_message_new(user, type)
|
|
list = remove_muted_tags(list, user)
|
|
list = remove_dismissed(list, user)
|
|
|
|
create_list(:private_messages, {}, list)
|
|
end
|
|
|
|
def list_private_messages_unread(user, type = :user)
|
|
list = filter_private_messages_unread(user, type)
|
|
create_list(:private_messages, {}, list)
|
|
end
|
|
|
|
def list_private_messages_group(user)
|
|
list = private_messages_for(user, :group)
|
|
|
|
list = list.joins(<<~SQL)
|
|
LEFT JOIN group_archived_messages gm
|
|
ON gm.topic_id = topics.id AND gm.group_id = #{group.id.to_i}
|
|
SQL
|
|
|
|
list = list.where("gm.id IS NULL")
|
|
publish_read_state = !!group.publish_read_state
|
|
list = append_read_state(list, group) if publish_read_state
|
|
create_list(:private_messages, { publish_read_state: publish_read_state }, list)
|
|
end
|
|
|
|
def list_private_messages_group_archive(user)
|
|
list = private_messages_for(user, :group)
|
|
|
|
list = list.joins(<<~SQL)
|
|
INNER JOIN group_archived_messages gm
|
|
ON gm.topic_id = topics.id AND gm.group_id = #{group.id.to_i}
|
|
SQL
|
|
|
|
publish_read_state = !!group.publish_read_state
|
|
list = append_read_state(list, group) if publish_read_state
|
|
create_list(:private_messages, { publish_read_state: publish_read_state }, list)
|
|
end
|
|
|
|
def list_private_messages_group_new(user)
|
|
list = filter_private_message_new(user, :group)
|
|
list = remove_dismissed(list, user)
|
|
publish_read_state = !!group.publish_read_state
|
|
list = append_read_state(list, group) if publish_read_state
|
|
create_list(:private_messages, { publish_read_state: publish_read_state }, list)
|
|
end
|
|
|
|
def list_private_messages_group_unread(user)
|
|
list = filter_private_messages_unread(user, :group)
|
|
publish_read_state = !!group.publish_read_state
|
|
list = append_read_state(list, group) if publish_read_state
|
|
create_list(:private_messages, { publish_read_state: publish_read_state }, list)
|
|
end
|
|
|
|
def list_private_messages_warnings(user)
|
|
list = private_messages_for(user, :user)
|
|
list = list.where('topics.subtype = ?', TopicSubtype.moderator_warning)
|
|
# Exclude official warnings that the user created, instead of received
|
|
list = list.where('topics.user_id <> ?', user.id)
|
|
create_list(:private_messages, {}, list)
|
|
end
|
|
|
|
def private_messages_for(user, type)
|
|
options = @options
|
|
options.reverse_merge!(per_page: per_page_setting)
|
|
|
|
result = Topic.includes(:allowed_users)
|
|
result = result.includes(:tags) if SiteSetting.tagging_enabled
|
|
|
|
if type == :group
|
|
result = result.joins(
|
|
"INNER JOIN topic_allowed_groups tag ON tag.topic_id = topics.id AND tag.group_id IN (SELECT id FROM groups WHERE LOWER(name) = '#{PG::Connection.escape_string(@options[:group_name].downcase)}')"
|
|
)
|
|
|
|
unless user.admin?
|
|
result = result.joins("INNER JOIN group_users gu ON gu.group_id = tag.group_id AND gu.user_id = #{user.id.to_i}")
|
|
end
|
|
elsif type == :user
|
|
result = result.where("topics.id IN (SELECT topic_id FROM topic_allowed_users WHERE user_id = #{user.id.to_i})")
|
|
elsif type == :all
|
|
group_ids = group_with_messages_ids(user)
|
|
|
|
result =
|
|
if group_ids.present?
|
|
result.where(<<~SQL)
|
|
topics.id IN (
|
|
SELECT topic_id
|
|
FROM topic_allowed_users
|
|
WHERE user_id = #{user.id.to_i}
|
|
UNION ALL
|
|
SELECT topic_id FROM topic_allowed_groups
|
|
WHERE group_id IN (#{group_ids.join(",")})
|
|
)
|
|
SQL
|
|
else
|
|
result.joins(<<~SQL)
|
|
INNER JOIN topic_allowed_users tau
|
|
ON tau.topic_id = topics.id
|
|
AND tau.user_id = #{user.id.to_i}
|
|
SQL
|
|
end
|
|
end
|
|
|
|
result = result.joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{user.id.to_i})")
|
|
.order("topics.bumped_at DESC")
|
|
.private_messages
|
|
|
|
result = result.limit(options[:per_page]) unless options[:limit] == false
|
|
result = result.visible if options[:visible] || @user.nil? || @user.regular?
|
|
|
|
if options[:page]
|
|
offset = options[:page].to_i * options[:per_page]
|
|
result = result.offset(offset) if offset > 0
|
|
end
|
|
result
|
|
end
|
|
|
|
def list_private_messages_tag(user)
|
|
list = private_messages_for(user, :all)
|
|
list = list.joins("JOIN topic_tags tt ON tt.topic_id = topics.id
|
|
JOIN tags t ON t.id = tt.tag_id AND t.name = '#{@options[:tags][0]}'")
|
|
create_list(:private_messages, {}, list)
|
|
end
|
|
|
|
def filter_private_messages_unread(user, type)
|
|
list = TopicQuery.unread_filter(
|
|
private_messages_for(user, type),
|
|
staff: user.staff?
|
|
)
|
|
|
|
first_unread_pm_at =
|
|
case type
|
|
when :user
|
|
user_first_unread_pm_at(user)
|
|
when :group
|
|
GroupUser
|
|
.where(user: user, group: group)
|
|
.pluck_first(:first_unread_pm_at)
|
|
else
|
|
user_first_unread_pm_at = user_first_unread_pm_at(user)
|
|
|
|
group_first_unread_pm_at = GroupUser
|
|
.where(user: user)
|
|
.minimum(:first_unread_pm_at)
|
|
|
|
[user_first_unread_pm_at, group_first_unread_pm_at].compact.min
|
|
end
|
|
|
|
if first_unread_pm_at
|
|
list = list.where("topics.updated_at >= ?", first_unread_pm_at)
|
|
end
|
|
|
|
list
|
|
end
|
|
|
|
def filter_private_message_new(user, type)
|
|
TopicQuery.new_filter(
|
|
private_messages_for(user, type),
|
|
treat_as_new_topic_start_date: user.user_option.treat_as_new_topic_start_date
|
|
)
|
|
end
|
|
|
|
private
|
|
|
|
def append_read_state(list, group)
|
|
group_id = group.id
|
|
return list if group_id.nil?
|
|
|
|
selected_values = list.select_values.empty? ? ['topics.*'] : list.select_values
|
|
selected_values << "COALESCE(tg.last_read_post_number, 0) AS last_read_post_number"
|
|
|
|
list
|
|
.joins("LEFT OUTER JOIN topic_groups tg ON topics.id = tg.topic_id AND tg.group_id = #{group_id}")
|
|
.select(*selected_values)
|
|
end
|
|
|
|
def filter_archived(list, user, archived: true)
|
|
# Executing an extra query instead of a sub-query because it is more
|
|
# efficient for the PG planner. Caution should be used when changing the
|
|
# query here as it can easily lead to an inefficient query.
|
|
group_ids = group_with_messages_ids(user)
|
|
|
|
if group_ids.present?
|
|
list = list.joins(<<~SQL)
|
|
LEFT JOIN group_archived_messages gm
|
|
ON gm.topic_id = topics.id
|
|
AND gm.group_id IN (#{group_ids.join(",")})
|
|
LEFT JOIN user_archived_messages um
|
|
ON um.user_id = #{user.id.to_i}
|
|
AND um.topic_id = topics.id
|
|
SQL
|
|
|
|
if archived
|
|
list.where("um.user_id IS NOT NULL OR gm.topic_id IS NOT NULL")
|
|
else
|
|
list.where("um.user_id IS NULL AND gm.topic_id IS NULL")
|
|
end
|
|
else
|
|
list = list.joins(<<~SQL)
|
|
LEFT JOIN user_archived_messages um
|
|
ON um.user_id = #{user.id.to_i}
|
|
AND um.topic_id = topics.id
|
|
SQL
|
|
|
|
list.where("um.user_id IS #{archived ? "NOT NULL" : "NULL"}")
|
|
end
|
|
end
|
|
|
|
def not_archived(list, user)
|
|
list.joins("LEFT JOIN user_archived_messages um
|
|
ON um.user_id = #{user.id.to_i} AND um.topic_id = topics.id")
|
|
.where('um.user_id IS NULL')
|
|
end
|
|
|
|
def group
|
|
@group ||= begin
|
|
Group
|
|
.where('name ilike ?', @options[:group_name])
|
|
.select(:id, :publish_read_state)
|
|
.first
|
|
end
|
|
end
|
|
|
|
def user_first_unread_pm_at(user)
|
|
UserStat.where(user: user).pluck_first(:first_unread_pm_at)
|
|
end
|
|
|
|
def group_with_messages_ids(user)
|
|
@group_with_messages_ids ||= {}
|
|
|
|
if ids = @group_with_messages_ids[user.id]
|
|
return ids
|
|
end
|
|
|
|
@group_with_messages_ids[user.id] = user.groups.where(has_messages: true).pluck(:id)
|
|
end
|
|
end
|
|
end
|