better user name similarity search

This commit is contained in:
Dylan Knutson
2025-02-12 21:34:32 +00:00
parent 51be64abba
commit 0718dd9cea
3 changed files with 50 additions and 32 deletions

View File

@@ -29,36 +29,53 @@ class Domain::UsersController < ApplicationController
def search_by_name
authorize Domain::User
name = params[:name]&.downcase
@users =
Domain::User
.where("name_for_search ilike ?", "%#{name}%")
.includes(:avatar)
.select("*")
# query =
# Domain::UserSearchName
# .where("name ilike ?", "%#{name}%")
# .joins(:user)
# .select("*")
# .select(
# "levenshtein(name, '#{ReduxApplicationRecord.sanitize_sql_like(name)}') as distance",
# )
# .select(
# "(SELECT COUNT(*) FROM domain_user_post_creations dupc WHERE dupc.user_id = domain_users.id) as num_posts",
# )
# .group(:user_id)
# .order(distance: :asc)
# .limit(10)
# puts "sql: #{query.to_sql}"
#
name = ReduxApplicationRecord.sanitize_sql_like(name)
query =
Domain::UserSearchName
.select("domain_user_search_names.*, domain_users.*")
.select("levenshtein(name, '#{name}') as distance")
.select(
"(SELECT COUNT(*) FROM domain_user_post_creations WHERE user_id = domain_users.id) as num_posts",
"(SELECT COUNT(*) FROM domain_user_post_creations dupc WHERE dupc.user_id = domain_users.id) as num_posts",
)
.select(<<-SQL)
(
SELECT
MIN(levenshtein(name_unnested.name, '#{ReduxApplicationRecord.sanitize_sql_like(name)}')) as distance
FROM
unnest(string_to_array(name_for_search, '|')) as name_unnested(name)
LIMIT 1
) as distance
SQL
.select(<<-SQL)
(
SELECT
name_unnested.name
FROM
unnest(string_to_array(name_for_search, '|')) as name_unnested(name)
ORDER BY
levenshtein(name_unnested.name, '#{ReduxApplicationRecord.sanitize_sql_like(name)}') ASC
LIMIT 1
) as matched_name
SQL
.order(distance: :asc)
.joins(:user)
.where(
"(name ilike ?) OR (similarity(dmetaphone(name), dmetaphone(?)) > 0.8)",
"%#{name}%",
name,
)
.where(
"NOT EXISTS (
SELECT 1
FROM domain_user_search_names dns2
WHERE dns2.user_id = domain_user_search_names.user_id
AND levenshtein(dns2.name, ?) < levenshtein(domain_user_search_names.name, ?)
)",
name,
name,
)
.order("distance ASC")
.limit(10)
puts "query: #{query.to_sql}"
@user_search_names = query
end
sig(:final) do

View File

@@ -294,7 +294,7 @@ export default function UserSearchBar({ isServerRendered }: PropTypes) {
]
.filter(Boolean)
.join(' ')}
placeholder="Search FurAffinity Users?!?"
placeholder="Search Users"
defaultValue={state.userName}
onChange={(e) => {
setState((s) => ({ ...s, typingSettled: false }));

View File

@@ -1,10 +1,11 @@
json.users @users do |user|
json.users @user_search_names do |user_search_name|
user = user_search_name.user
json.id user.id
json.name user.name_for_view
json.show_path domain_user_path(user)
json.thumb domain_user_avatar_img_src_path(user.avatar, thumb: "64-avatar")
json.domain_icon site_icon_path_for_user(user)
json.num_posts user.num_posts if user.has_created_posts?
json.matched_name user.matched_name
json.distance user.distance
json.num_posts user_search_name.num_posts if user.has_created_posts?
json.matched_name user_search_name.name
json.distance user_search_name.distance
end