+ results
+ end
+
+ def search_query(query, for_user) do
+ fts_subquery = fts_search_subquery(query)
+ trigram_subquery = trigram_search_subquery(query)
+ union_query = from(s in trigram_subquery, union_all: ^fts_subquery)
+ distinct_query = from(s in subquery(union_query), order_by: s.search_type, distinct: s.id)
+
+ from(s in subquery(boost_search_rank_query(distinct_query, for_user)),
+ order_by: [desc: s.search_rank],
+ limit: 20
+ )
+ end
+
+ defp boost_search_rank_query(query, nil), do: query
+
+ defp boost_search_rank_query(query, for_user) do
+ friends_ids = get_friends_ids(for_user)
+ followers_ids = get_followers_ids(for_user)
+
+ from(u in subquery(query),
+ select_merge: %{
+ search_rank:
+ fragment(
+ """
+ CASE WHEN (?) THEN (?) * 1.3
+ WHEN (?) THEN (?) * 1.2
+ WHEN (?) THEN (?) * 1.1
+ ELSE (?) END
+ """,
+ u.id in ^friends_ids and u.id in ^followers_ids,
+ u.search_rank,
+ u.id in ^friends_ids,
+ u.search_rank,
+ u.id in ^followers_ids,
+ u.search_rank,
+ u.search_rank
+ )
+ }
+ )
+ end
+
+ defp fts_search_subquery(term, query \\ User) do
+ processed_query =
+ term
+ |> String.replace(~r/\W+/, " ")
+ |> String.trim()
+ |> String.split()
+ |> Enum.map(&(&1 <> ":*"))
+ |> Enum.join(" | ")
+
+ from(
+ u in query,
+ select_merge: %{
+ search_type: ^0,
+ 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