diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
index 38c5207433..338c9096e0 100644
--- a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
+++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
@@ -94,23 +94,11 @@ defmodule BlockScoutWeb.ChainController do
def search(conn, _), do: not_found(conn)
def token_autocomplete(conn, %{"q" => term}) when is_binary(term) do
- if term == "" do
- json(conn, "{}")
- else
- result_tokens =
- term
- |> String.trim()
- |> Chain.search_token()
-
- result_contracts =
- term
- |> String.trim()
- |> Chain.search_contract()
+ result_tokens = Chain.search_token(term)
+ result_contracts = Chain.search_contract(term)
+ result = result_tokens ++ result_contracts
- result = result_tokens ++ result_contracts
-
- json(conn, result)
- end
+ json(conn, result)
end
def token_autocomplete(conn, _) do
diff --git a/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs
index 72796af6cf..1c6985bd81 100644
--- a/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs
+++ b/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs
@@ -128,6 +128,38 @@ defmodule BlockScoutWeb.ChainControllerTest do
assert Enum.count(json_response(conn, 200)) == 4
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
describe "GET search/2" do
diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex
index 780cc9a872..ccc72d88a8 100644
--- a/apps/explorer/lib/explorer/chain.ex
+++ b/apps/explorer/lib/explorer/chain.ex
@@ -1083,51 +1083,64 @@ defmodule Explorer.Chain do
end
end
- @spec search_token(String.t()) :: [Token.t()]
- def search_token(word) do
- term =
- word
- |> String.replace(~r/[^a-zA-Z0-9]/, " ")
- |> String.replace(~r/ +/, " & ")
+ defp prepare_search_term(string) do
+ case Regex.scan(~r/[a-zA-Z0-9]+/, string) do
+ [_ | _] = words ->
+ term_final =
+ words
+ |> Enum.map(fn [word] -> word <> ":*" end)
+ |> Enum.join(" & ")
- term_final = term <> ":*"
+ {:some, term_final}
- query =
- from(token in Token,
- where: fragment("to_tsvector('english', symbol || ' ' || name ) @@ to_tsquery(?)", ^term_final),
- select: %{
- contract_address_hash: token.contract_address_hash,
- symbol: token.symbol,
- name:
- fragment(
- "'' || coalesce(?, '') || '' || ' (' || coalesce(?, '') || ') ' || '' || coalesce(?::varchar(255), '') || ' holder(s)' || ''",
- token.name,
- token.symbol,
- token.holder_count
- )
- },
- order_by: [desc: token.holder_count]
- )
+ _ ->
+ :none
+ end
+ end
- 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(
+ "'' || coalesce(?, '') || '' || ' (' || coalesce(?, '') || ') ' || '' || coalesce(?::varchar(255), '') || ' holder(s)' || ''",
+ token.name,
+ token.symbol,
+ token.holder_count
+ )
+ },
+ order_by: [desc: token.holder_count]
+ )
+
+ Repo.all(query)
+
+ _ ->
+ []
+ end
end
@spec search_contract(String.t()) :: [SmartContract.t()]
- def search_contract(word) do
- term =
- word
- |> String.replace(~r/[^a-zA-Z0-9]/, " ")
- |> String.replace(~r/ +/, " & ")
+ def search_contract(string) do
+ case prepare_search_term(string) do
+ {:some, term} ->
+ query =
+ 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),
- select: %{contract_address_hash: smart_contract.address_hash, name: smart_contract.name}
- )
-
- Repo.all(query)
+ _ ->
+ []
+ end
end
@doc """