2019-05-03 06:17:27 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2013-10-31 03:45:13 +08:00
|
|
|
# Searches for a user by username or full text or name (if enabled in SiteSettings)
|
2017-01-03 15:33:57 +08:00
|
|
|
require_dependency 'search'
|
|
|
|
|
2013-02-07 17:34:49 +08:00
|
|
|
class UserSearch
|
2013-02-07 18:54:55 +08:00
|
|
|
|
2017-07-28 09:20:09 +08:00
|
|
|
def initialize(term, opts = {})
|
2013-10-31 03:45:13 +08:00
|
|
|
@term = term
|
2015-11-30 14:03:47 +08:00
|
|
|
@term_like = "#{term.downcase.gsub("_", "\\_")}%"
|
2014-05-13 23:44:06 +08:00
|
|
|
@topic_id = opts[:topic_id]
|
2019-08-06 15:57:45 +08:00
|
|
|
@category_id = opts[:category_id]
|
2015-04-13 23:03:13 +08:00
|
|
|
@topic_allowed_users = opts[:topic_allowed_users]
|
2014-05-13 23:44:06 +08:00
|
|
|
@searching_user = opts[:searching_user]
|
2017-11-19 08:17:31 +08:00
|
|
|
@include_staged_users = opts[:include_staged_users] || false
|
2014-05-15 10:22:35 +08:00
|
|
|
@limit = opts[:limit] || 20
|
2019-05-10 23:35:36 +08:00
|
|
|
@groups = opts[:groups]
|
2017-02-10 07:45:39 +08:00
|
|
|
@guardian = Guardian.new(@searching_user)
|
2019-05-30 06:40:43 +08:00
|
|
|
@guardian.ensure_can_see_groups!(@groups) if @groups
|
2013-10-31 03:45:13 +08:00
|
|
|
end
|
2013-02-26 00:42:20 +08:00
|
|
|
|
2015-05-14 12:38:47 +08:00
|
|
|
def scoped_users
|
2017-11-19 08:17:31 +08:00
|
|
|
users = User.where(active: true)
|
|
|
|
users = users.where(staged: false) unless @include_staged_users
|
2015-05-14 12:38:47 +08:00
|
|
|
|
2019-05-10 23:35:36 +08:00
|
|
|
if @groups
|
2019-05-29 08:51:30 +08:00
|
|
|
users = users.joins("INNER JOIN group_users ON group_users.user_id = users.id")
|
2019-05-10 23:35:36 +08:00
|
|
|
.where("group_users.group_id IN (?)", @groups.map(&:id))
|
2017-02-10 07:45:39 +08:00
|
|
|
end
|
|
|
|
|
2015-05-14 12:38:47 +08:00
|
|
|
unless @searching_user && @searching_user.staff?
|
|
|
|
users = users.not_suspended
|
|
|
|
end
|
|
|
|
|
|
|
|
# Only show users who have access to private topic
|
|
|
|
if @topic_id && @topic_allowed_users == "true"
|
|
|
|
topic = Topic.find_by(id: @topic_id)
|
|
|
|
|
|
|
|
if topic.category && topic.category.read_restricted
|
|
|
|
users = users.includes(:secure_categories)
|
2017-07-28 09:20:09 +08:00
|
|
|
.where("users.admin = TRUE OR categories.id = ?", topic.category.id)
|
|
|
|
.references(:categories)
|
2015-05-14 12:38:47 +08:00
|
|
|
end
|
|
|
|
end
|
2013-02-07 17:34:49 +08:00
|
|
|
|
2015-05-14 12:38:47 +08:00
|
|
|
users.limit(@limit)
|
|
|
|
end
|
|
|
|
|
|
|
|
def filtered_by_term_users
|
|
|
|
users = scoped_users
|
2014-08-14 01:30:25 +08:00
|
|
|
|
2013-10-31 03:45:13 +08:00
|
|
|
if @term.present?
|
2015-11-30 14:03:47 +08:00
|
|
|
if SiteSetting.enable_names? && @term !~ /[_\.-]/
|
2018-02-20 11:41:00 +08:00
|
|
|
query = Search.ts_query(term: @term, ts_config: "simple")
|
2015-11-30 14:03:47 +08:00
|
|
|
|
2014-07-05 07:11:41 +08:00
|
|
|
users = users.includes(:user_search_data)
|
2017-07-28 09:20:09 +08:00
|
|
|
.references(:user_search_data)
|
|
|
|
.where("user_search_data.search_data @@ #{query}")
|
2018-06-20 15:48:02 +08:00
|
|
|
.order(DB.sql_fragment("CASE WHEN username_lower LIKE ? THEN 0 ELSE 1 END ASC", @term_like))
|
2015-11-30 14:03:47 +08:00
|
|
|
|
2013-10-31 03:45:13 +08:00
|
|
|
else
|
|
|
|
users = users.where("username_lower LIKE :term_like", term_like: @term_like)
|
|
|
|
end
|
2013-02-07 17:34:49 +08:00
|
|
|
end
|
2013-02-26 00:42:20 +08:00
|
|
|
|
2015-05-14 12:38:47 +08:00
|
|
|
users
|
|
|
|
end
|
|
|
|
|
|
|
|
def search_ids
|
|
|
|
users = Set.new
|
|
|
|
|
|
|
|
# 1. exact username matches
|
|
|
|
if @term.present?
|
|
|
|
scoped_users.where(username_lower: @term.downcase)
|
2017-07-28 09:20:09 +08:00
|
|
|
.limit(@limit)
|
|
|
|
.pluck(:id)
|
|
|
|
.each { |id| users << id }
|
2013-02-07 17:34:49 +08:00
|
|
|
|
2014-05-13 23:44:06 +08:00
|
|
|
end
|
|
|
|
|
2015-11-05 06:04:37 +08:00
|
|
|
return users.to_a if users.length >= @limit
|
2015-04-13 23:03:13 +08:00
|
|
|
|
2015-05-14 12:38:47 +08:00
|
|
|
# 2. in topic
|
|
|
|
if @topic_id
|
2019-08-06 15:57:45 +08:00
|
|
|
in_topic = filtered_by_term_users
|
|
|
|
.where('users.id IN (SELECT p.user_id FROM posts p WHERE topic_id = ?)', @topic_id)
|
2019-02-20 10:33:56 +08:00
|
|
|
|
|
|
|
if @searching_user.present?
|
|
|
|
in_topic = in_topic.where('users.id <> ?', @searching_user.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
in_topic
|
2017-07-28 09:20:09 +08:00
|
|
|
.order('last_seen_at DESC')
|
|
|
|
.limit(@limit - users.length)
|
|
|
|
.pluck(:id)
|
|
|
|
.each { |id| users << id }
|
2015-04-13 23:03:13 +08:00
|
|
|
end
|
|
|
|
|
2015-11-05 06:04:37 +08:00
|
|
|
return users.to_a if users.length >= @limit
|
2015-05-14 12:38:47 +08:00
|
|
|
|
2019-08-06 15:57:45 +08:00
|
|
|
secure_category_id = nil
|
|
|
|
|
|
|
|
if @category_id
|
|
|
|
secure_category_id = DB.query_single(<<~SQL, @category_id).first
|
|
|
|
SELECT id FROM categories
|
|
|
|
WHERE read_restricted AND id = ?
|
|
|
|
SQL
|
|
|
|
elsif @topic_id
|
|
|
|
secure_category_id = DB.query_single(<<~SQL, @topic_id).first
|
|
|
|
SELECT id FROM categories
|
|
|
|
WHERE read_restricted AND id IN (
|
|
|
|
SELECT category_id FROM topics
|
|
|
|
WHERE id = ?
|
|
|
|
)
|
|
|
|
SQL
|
|
|
|
end
|
|
|
|
|
|
|
|
# 3. category matches
|
|
|
|
# 10,11,12: trust level groups (tl0/1/2) explicitly bypassed
|
|
|
|
# may amend this in future to allow them if count in the group
|
|
|
|
# is small enough
|
|
|
|
if secure_category_id
|
|
|
|
in_category = filtered_by_term_users
|
|
|
|
.where(<<~SQL, secure_category_id)
|
|
|
|
users.id IN (
|
|
|
|
SELECT gu.user_id
|
|
|
|
FROM group_users gu
|
|
|
|
WHERE group_id IN (
|
|
|
|
SELECT group_id FROM category_groups
|
|
|
|
WHERE category_id = ?
|
|
|
|
) AND group_id NOT IN (10,11,12)
|
|
|
|
LIMIT 200
|
|
|
|
)
|
|
|
|
SQL
|
|
|
|
|
|
|
|
if @searching_user.present?
|
|
|
|
in_category = in_category.where('users.id <> ?', @searching_user.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
in_category
|
|
|
|
.order('last_seen_at DESC')
|
|
|
|
.limit(@limit - users.length)
|
|
|
|
.pluck(:id)
|
|
|
|
.each { |id| users << id }
|
|
|
|
end
|
|
|
|
|
|
|
|
return users.to_a if users.length >= @limit
|
|
|
|
# 4. global matches
|
|
|
|
if @term.present?
|
2019-02-20 10:33:56 +08:00
|
|
|
filtered_by_term_users.order('last_seen_at DESC')
|
|
|
|
.limit(@limit - users.length)
|
|
|
|
.pluck(:id)
|
|
|
|
.each { |id| users << id }
|
|
|
|
end
|
2015-05-14 12:38:47 +08:00
|
|
|
|
|
|
|
users.to_a
|
|
|
|
end
|
|
|
|
|
|
|
|
def search
|
|
|
|
ids = search_ids
|
|
|
|
return User.where("0=1") if ids.empty?
|
|
|
|
|
|
|
|
User.joins("JOIN (SELECT unnest uid, row_number() OVER () AS rn
|
|
|
|
FROM unnest('{#{ids.join(",")}}'::int[])
|
|
|
|
) x on uid = users.id")
|
2017-07-28 09:20:09 +08:00
|
|
|
.order("rn")
|
2013-02-07 17:34:49 +08:00
|
|
|
end
|
2013-10-31 03:45:13 +08:00
|
|
|
|
2013-02-10 20:37:24 +08:00
|
|
|
end
|