+ defp select_top_users(query, top_user_ids) do
+ from(u in query,
+ or_where: u.id in ^top_user_ids
+ )
+ end
+
+ defp fts_search(query, query_string) do
+ query_string = to_tsquery(query_string)
+
+ from(
+ u in query,
+ where:
+ fragment(
+ # The fragment must _exactly_ match `users_fts_index`, otherwise the index won't work
+ """
+ (
+ setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
+ setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B')
+ ) @@ to_tsquery('simple', ?)
+ """,
+ u.nickname,
+ u.name,
+ ^query_string
+ )
+ )
+ end
+
+ defp to_tsquery(query_string) do
+ String.trim_trailing(query_string, "@" <> local_domain())
+ |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
+ |> String.trim()
+ |> String.split()
+ |> Enum.map(&(&1 <> ":*"))
+ |> Enum.join(" | ")
+ end
+
+ # Considers nickname match, localized nickname match, name match; preferences nickname match
+ defp trigram_rank(query, query_string) do
+ from(
+ u in query,
+ select_merge: %{
+ search_rank:
+ fragment(
+ """
+ similarity(?, ?) +
+ similarity(?, regexp_replace(?, '@.+', '')) +
+ similarity(?, trim(coalesce(?, '')))
+ """,
+ ^query_string,
+ u.nickname,
+ ^query_string,
+ u.nickname,
+ ^query_string,
+ u.name
+ )
+ }
+ )
+ end
+
+ defp base_query(%User{} = user, true), do: User.get_friends_query(user)
+ defp base_query(_user, _following), do: User
+
+ defp filter_invisible_users(query) do
+ from(q in query, where: q.invisible == false)
+ end
+
+ defp filter_discoverable_users(query) do
+ from(q in query, where: q.is_discoverable == true)
+ end
+
+ defp filter_internal_users(query) do
+ from(q in query, where: q.actor_type != "Application")
+ end