[4274] Fix/improve search token-autocomplete

pull/4302/head
Valentin Syrovatskiy 4 years ago
parent 01e553f5fc
commit edec3a1575
  1. 16
      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. 47
      apps/explorer/lib/explorer/chain.ex

@ -94,24 +94,12 @@ 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_tokens =
term
|> String.trim()
|> Chain.search_token()
result_contracts =
term
|> String.trim()
|> Chain.search_contract()
result = result_tokens ++ result_contracts result = result_tokens ++ result_contracts
json(conn, result) json(conn, result)
end end
end
def token_autocomplete(conn, _) do def token_autocomplete(conn, _) do
json(conn, "{}") json(conn, "{}")

@ -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,18 +1083,28 @@ 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(" & ")
{:some, term_final}
term_final = term <> ":*" _ ->
:none
end
end
@spec search_token(String.t()) :: [Token.t()]
def search_token(string) do
case prepare_search_term(string) do
{:some, term} ->
query = query =
from(token in Token, from(token in Token,
where: fragment("to_tsvector('english', symbol || ' ' || name ) @@ to_tsquery(?)", ^term_final), where: fragment("to_tsvector('english', symbol || ' ' || name ) @@ to_tsquery(?)", ^term),
select: %{ select: %{
contract_address_hash: token.contract_address_hash, contract_address_hash: token.contract_address_hash,
symbol: token.symbol, symbol: token.symbol,
@ -1110,24 +1120,27 @@ defmodule Explorer.Chain do
) )
Repo.all(query) 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]/, " ")
|> String.replace(~r/ +/, " & ")
term_final = term <> ":*"
query = query =
from(smart_contract in SmartContract, from(smart_contract in SmartContract,
where: fragment("to_tsvector('english', name ) @@ to_tsquery(?)", ^term_final), where: fragment("to_tsvector('english', name ) @@ to_tsquery(?)", ^term),
select: %{contract_address_hash: smart_contract.address_hash, name: smart_contract.name} select: %{contract_address_hash: smart_contract.address_hash, name: smart_contract.name}
) )
Repo.all(query) Repo.all(query)
_ ->
[]
end
end end
@doc """ @doc """

Loading…
Cancel
Save