[4274] Fix/improve search token-autocomplete

pull/4302/head
Valentin Syrovatskiy 3 years ago
parent 01e553f5fc
commit edec3a1575
  1. 20
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  2. 32
      apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs
  3. 87
      apps/explorer/lib/explorer/chain.ex

@ -94,23 +94,11 @@ defmodule BlockScoutWeb.ChainController do
def search(conn, _), do: not_found(conn) def search(conn, _), do: not_found(conn)
def token_autocomplete(conn, %{"q" => term}) when is_binary(term) do def token_autocomplete(conn, %{"q" => term}) when is_binary(term) do
if term == "" do result_tokens = Chain.search_token(term)
json(conn, "{}") result_contracts = Chain.search_contract(term)
else result = result_tokens ++ result_contracts
result_tokens =
term
|> String.trim()
|> Chain.search_token()
result_contracts =
term
|> String.trim()
|> Chain.search_contract()
result = result_tokens ++ result_contracts json(conn, result)
json(conn, result)
end
end end
def token_autocomplete(conn, _) do def token_autocomplete(conn, _) do

@ -128,6 +128,38 @@ defmodule BlockScoutWeb.ChainControllerTest do
assert Enum.count(json_response(conn, 200)) == 4 assert Enum.count(json_response(conn, 200)) == 4
end end
test "find by several words" do
insert(:token, name: "first Token")
insert(:token, name: "second Token")
conn =
build_conn()
|> get("/token-autocomplete?q=fir+tok")
assert Enum.count(json_response(conn, 200)) == 1
end
test "find by empty query" do
insert(:token, name: "MaGiCt0k3n")
insert(:smart_contract, name: "MagicContract")
conn =
build_conn()
|> get("/token-autocomplete?q=")
assert Enum.count(json_response(conn, 200)) == 0
end
test "find by non-latin characters" do
insert(:token, name: "someToken")
conn =
build_conn()
|> get("/token-autocomplete?q=%E0%B8%B5%E0%B8%AB")
assert Enum.count(json_response(conn, 200)) == 0
end
end end
describe "GET search/2" do describe "GET search/2" do

@ -1083,51 +1083,64 @@ defmodule Explorer.Chain do
end end
end end
@spec search_token(String.t()) :: [Token.t()] defp prepare_search_term(string) do
def search_token(word) do case Regex.scan(~r/[a-zA-Z0-9]+/, string) do
term = [_ | _] = words ->
word term_final =
|> String.replace(~r/[^a-zA-Z0-9]/, " ") words
|> String.replace(~r/ +/, " & ") |> Enum.map(fn [word] -> word <> ":*" end)
|> Enum.join(" & ")
term_final = term <> ":*" {:some, term_final}
query = _ ->
from(token in Token, :none
where: fragment("to_tsvector('english', symbol || ' ' || name ) @@ to_tsquery(?)", ^term_final), end
select: %{ end
contract_address_hash: token.contract_address_hash,
symbol: token.symbol,
name:
fragment(
"'<b>' || coalesce(?, '') || '</b>' || ' (' || coalesce(?, '') || ') ' || '<i>' || coalesce(?::varchar(255), '') || ' holder(s)' || '</i>'",
token.name,
token.symbol,
token.holder_count
)
},
order_by: [desc: token.holder_count]
)
Repo.all(query) @spec search_token(String.t()) :: [Token.t()]
def search_token(string) do
case prepare_search_term(string) do
{:some, term} ->
query =
from(token in Token,
where: fragment("to_tsvector('english', symbol || ' ' || name ) @@ to_tsquery(?)", ^term),
select: %{
contract_address_hash: token.contract_address_hash,
symbol: token.symbol,
name:
fragment(
"'<b>' || coalesce(?, '') || '</b>' || ' (' || coalesce(?, '') || ') ' || '<i>' || coalesce(?::varchar(255), '') || ' holder(s)' || '</i>'",
token.name,
token.symbol,
token.holder_count
)
},
order_by: [desc: token.holder_count]
)
Repo.all(query)
_ ->
[]
end
end end
@spec search_contract(String.t()) :: [SmartContract.t()] @spec search_contract(String.t()) :: [SmartContract.t()]
def search_contract(word) do def search_contract(string) do
term = case prepare_search_term(string) do
word {:some, term} ->
|> String.replace(~r/[^a-zA-Z0-9]/, " ") query =
|> String.replace(~r/ +/, " & ") from(smart_contract in SmartContract,
where: fragment("to_tsvector('english', name ) @@ to_tsquery(?)", ^term),
select: %{contract_address_hash: smart_contract.address_hash, name: smart_contract.name}
)
term_final = term <> ":*" Repo.all(query)
query = _ ->
from(smart_contract in SmartContract, []
where: fragment("to_tsvector('english', name ) @@ to_tsquery(?)", ^term_final), end
select: %{contract_address_hash: smart_contract.address_hash, name: smart_contract.name}
)
Repo.all(query)
end end
@doc """ @doc """

Loading…
Cancel
Save