FIX: Experimental hashtag search result matching and limit fixes (#19144)

This changes the hashtag search to first do a lookup to find
results where the slug exactly matches the
search term. Now when we search for hashtags, the
exact matches will be found first and put at the top of
the results.

`ChatChannelFetcher` has also been modified here to allow
for more options for performance -- we do not need to
query DM channels for secured IDs when looking up or searching
channels for hashtags, since they should never show in
results there (they have no slugs). Nor do we need to include
the channel archive records.

Also changes the limit of hashtag results to 20 by default
with a hidden site setting, and makes it so the scroll for the
results is overflowed.
This commit is contained in:
Martin Brennan
2022-11-24 10:07:59 +10:00
committed by GitHub
parent 6b7bdc991d
commit 274b21663e
7 changed files with 131 additions and 45 deletions

View File

@ -29,10 +29,8 @@ module Chat::ChatChannelFetcher
SQL
end
def self.generate_allowed_channel_ids_sql(guardian)
<<~SQL
-- secured category chat channels
#{
def self.generate_allowed_channel_ids_sql(guardian, exclude_dm_channels: false)
category_channel_sql =
ChatChannel
.select(:id)
.joins(
@ -43,40 +41,66 @@ module Chat::ChatChannelFetcher
allowed_category_ids: guardian.allowed_category_ids,
)
.to_sql
}
dm_channel_sql = ""
if !exclude_dm_channels
dm_channel_sql = <<~SQL
UNION
-- secured direct message chat channels
#{
ChatChannel
.select(:id)
.joins(
"INNER JOIN direct_message_channels ON direct_message_channels.id = chat_channels.chatable_id
ChatChannel
.select(:id)
.joins(
"INNER JOIN direct_message_channels ON direct_message_channels.id = chat_channels.chatable_id
AND chat_channels.chatable_type = 'DirectMessage'
INNER JOIN direct_message_users ON direct_message_users.direct_message_channel_id = direct_message_channels.id",
)
.where("direct_message_users.user_id = :user_id", user_id: guardian.user.id)
.to_sql
}
)
.where("direct_message_users.user_id = :user_id", user_id: guardian.user.id)
.to_sql
}
SQL
end
<<~SQL
-- secured category chat channels
#{category_channel_sql}
#{dm_channel_sql}
SQL
end
def self.secured_public_channel_slug_lookup(guardian, slugs)
allowed_channel_ids = generate_allowed_channel_ids_sql(guardian, exclude_dm_channels: true)
ChatChannel
.joins(
"LEFT JOIN categories ON categories.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'Category'",
)
.where(chatable_type: ChatChannel.public_channel_chatable_types)
.where("chat_channels.id IN (#{allowed_channel_ids})")
.where("chat_channels.slug IN (:slugs)", slugs: slugs)
.limit(1)
end
def self.secured_public_channel_search(guardian, options = {})
allowed_channel_ids =
generate_allowed_channel_ids_sql(guardian, exclude_dm_channels: options[:exclude_dm_channels])
channels = ChatChannel.includes(chatable: [:topic_only_relative_url])
channels = channels.includes(:chat_channel_archive) if options[:include_archives]
channels =
ChatChannel
.includes(:chat_channel_archive)
.includes(chatable: [:topic_only_relative_url])
channels
.joins(
"LEFT JOIN categories ON categories.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'Category'",
)
.where(chatable_type: ChatChannel.public_channel_chatable_types)
.where("chat_channels.id IN (#{generate_allowed_channel_ids_sql(guardian)})")
.where("chat_channels.id IN (#{allowed_channel_ids})")
channels = channels.where(status: options[:status]) if options[:status].present?
if options[:filter].present?
sql = "chat_channels.name ILIKE :filter OR chat_channels.slug ILIKE :filter OR categories.name ILIKE :filter"
sql =
"chat_channels.name ILIKE :filter OR chat_channels.slug ILIKE :filter OR categories.name ILIKE :filter"
channels =
channels.where(sql, filter: "%#{options[:filter].downcase}%").order(
"chat_channels.name ASC, categories.name ASC",
@ -115,7 +139,11 @@ module Chat::ChatChannelFetcher
end
def self.secured_public_channels(guardian, memberships, options = { following: true })
channels = secured_public_channel_search(guardian, options)
channels =
secured_public_channel_search(
guardian,
options.merge(include_archives: true),
)
decorate_memberships_with_tracking_data(guardian, channels, memberships)
channels = channels.to_a
preload_custom_fields_for(channels)

View File

@ -19,7 +19,7 @@ class Chat::ChatChannelHashtagDataSource
def self.lookup(guardian, slugs)
if SiteSetting.enable_experimental_hashtag_autocomplete
Chat::ChatChannelFetcher
.secured_public_channel_search(guardian, slugs: slugs)
.secured_public_channel_slug_lookup(guardian, slugs)
.map { |channel| channel_to_hashtag_item(guardian, channel) }
else
[]
@ -29,7 +29,12 @@ class Chat::ChatChannelHashtagDataSource
def self.search(guardian, term, limit)
if SiteSetting.enable_experimental_hashtag_autocomplete
Chat::ChatChannelFetcher
.secured_public_channel_search(guardian, filter: term, limit: limit)
.secured_public_channel_search(
guardian,
filter: term,
limit: limit,
exclude_dm_channels: true,
)
.map { |channel| channel_to_hashtag_item(guardian, channel) }
else
[]