diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/pleroma/user.ex | 143 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 6 | ||||
| -rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api_controller.ex | 2 | 
3 files changed, 123 insertions, 28 deletions
| diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 1db1c53cb..06084b117 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -35,7 +35,7 @@ defmodule Pleroma.User do      field(:avatar, :map)      field(:local, :boolean, default: true)      field(:follower_address, :string) -    field(:search_distance, :float, virtual: true) +    field(:search_rank, :float, virtual: true)      field(:tags, {:array, :string}, default: [])      field(:last_refreshed_at, :naive_datetime)      has_many(:notifications, Notification) @@ -510,6 +510,12 @@ defmodule Pleroma.User do      {:ok, Repo.all(q)}    end +  def get_followers_ids(user, page \\ nil) do +    q = get_followers_query(user, page) + +    Repo.all(from(u in q, select: u.id)) +  end +    def get_friends_query(%User{id: id, following: following}, nil) do      from(        u in User, @@ -534,6 +540,12 @@ defmodule Pleroma.User do      {:ok, Repo.all(q)}    end +  def get_friends_ids(user, page \\ nil) do +    q = get_friends_query(user, page) + +    Repo.all(from(u in q, select: u.id)) +  end +    def get_follow_requests_query(%User{} = user) do      from(        a in Activity, @@ -665,37 +677,120 @@ defmodule Pleroma.User do      Repo.all(query)    end -  def search(query, resolve \\ false) do -    # strip the beginning @ off if there is a query +  def search(query, resolve \\ false, for_user \\ nil) do +    # Strip the beginning @ off if there is a query      query = String.trim_leading(query, "@") -    if resolve do -      User.get_or_fetch_by_nickname(query) -    end +    if resolve, do: User.get_or_fetch_by_nickname(query) -    inner = -      from( -        u in User, -        select_merge: %{ -          search_distance: -            fragment( -              "? <-> (? || coalesce(?, ''))", -              ^query, -              u.nickname, -              u.name -            ) -        }, -        where: not is_nil(u.nickname) -      ) +    fts_results = do_search(fts_search_subquery(query), for_user) + +    {:ok, trigram_results} = +      Repo.transaction(fn -> +        Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", []) +        do_search(trigram_search_subquery(query), for_user) +      end) + +    Enum.uniq_by(fts_results ++ trigram_results, & &1.id) +  end +  defp do_search(subquery, for_user, options \\ []) do      q =        from( -        s in subquery(inner), -        order_by: s.search_distance, -        limit: 20 +        s in subquery(subquery), +        order_by: [desc: s.search_rank], +        limit: ^(options[:limit] || 20)        ) -    Repo.all(q) +    results = +      q +      |> Repo.all() +      |> Enum.filter(&(&1.search_rank > 0)) + +    boost_search_results(results, for_user) +  end + +  defp fts_search_subquery(query) do +    processed_query = +      query +      |> String.replace(~r/\W+/, " ") +      |> String.trim() +      |> String.split() +      |> Enum.map(&(&1 <> ":*")) +      |> Enum.join(" | ") + +    from( +      u in User, +      select_merge: %{ +        search_rank: +          fragment( +            """ +            ts_rank_cd( +              setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') || +              setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'), +              to_tsquery('simple', ?), +              32 +            ) +            """, +            u.nickname, +            u.name, +            ^processed_query +          ) +      }, +      where: +        fragment( +          """ +            (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, +          ^processed_query +        ) +    ) +  end + +  defp trigram_search_subquery(query) do +    from( +      u in User, +      select_merge: %{ +        search_rank: +          fragment( +            "similarity(?, trim(? || ' ' || coalesce(?, '')))", +            ^query, +            u.nickname, +            u.name +          ) +      }, +      where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^query) +    ) +  end + +  defp boost_search_results(results, nil), do: results + +  defp boost_search_results(results, for_user) do +    friends_ids = get_friends_ids(for_user) +    followers_ids = get_followers_ids(for_user) + +    Enum.map( +      results, +      fn u -> +        search_rank_coef = +          cond do +            u.id in friends_ids -> +              1.2 + +            u.id in followers_ids -> +              1.1 + +            true -> +              1 +          end + +        Map.put(u, :search_rank, u.search_rank * search_rank_coef) +      end +    ) +    |> Enum.sort_by(&(-&1.search_rank))    end    def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index daad89185..882d336be 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -771,7 +771,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    end    def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do -    accounts = User.search(query, params["resolve"] == "true") +    accounts = User.search(query, params["resolve"] == "true", user)      statuses = status_search(user, query) @@ -795,7 +795,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    end    def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do -    accounts = User.search(query, params["resolve"] == "true") +    accounts = User.search(query, params["resolve"] == "true", user)      statuses = status_search(user, query) @@ -816,7 +816,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    end    def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do -    accounts = User.search(query, params["resolve"] == "true") +    accounts = User.search(query, params["resolve"] == "true", user)      res = AccountView.render("accounts.json", users: accounts, for: user, as: :user) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 1c728166c..ede079963 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -675,7 +675,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do    end    def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do -    users = User.search(query, true) +    users = User.search(query, true, user)      conn      |> put_view(UserView) | 
