-
-
Save rodrigues/b12bc68d7b9c8b558a931d28c19d3b92 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule MyApp.Elastic do | |
require Logger | |
@moduledoc """ | |
Provides a minimal API to talk to Elastic | |
""" | |
@typedoc """ | |
A valid http path being used in `http_call/3` | |
""" | |
@type path :: String.t() | atom | [String.t() | atom] | |
@typedoc """ | |
A valid http body being used in `http_call/3` | |
""" | |
@type body :: String.t() | map | |
@typedoc """ | |
A valid http response returned by `http_call/3` | |
""" | |
@type response :: {:ok | :error, map} | |
@headers [ | |
{"Content-Type", "application/json"} | |
] | |
@doc """ | |
Configured Elastic indices | |
""" | |
@spec indices() :: map | |
def indices do | |
Application.get_env(:my_app, :elastic_indices, %{}) | |
end | |
@doc """ | |
Elastic endpoint as configured (e.g. `localhost:9200`) | |
""" | |
@spec endpoint() :: String.t() | |
def endpoint do | |
Application.get_env(:my_app, :elastic_endpoint, "http://localhost:9200") | |
end | |
@doc """ | |
Exposes the ES index name (or alias) for a given logical index | |
""" | |
@spec index_name(atom) :: atom | |
def index_name(index) do | |
get_in(indices(), [index, :index]) | |
end | |
@doc """ | |
Creates an alias with `alias_name` pointing to `index_name`. | |
Ensure previous references of alias are removed. | |
""" | |
@spec index_alias(atom, atom) :: response | |
def index_alias(alias_name, index_name) do | |
removal_statements = | |
alias_name | |
|> alias_indices | |
|> Enum.map(&%{remove: %{index: &1, alias: alias_name}}) | |
add_statement = %{add: %{index: index_name, alias: alias_name}} | |
payload = %{actions: removal_statements ++ [add_statement]} | |
http_call(:post, [:_aliases], payload) | |
end | |
@doc """ | |
Executes a search query in the Elastic index. | |
""" | |
@spec search(atom, map) :: map | |
def search(index, query) do | |
path = [ | |
index_name(index), | |
:_search | |
] | |
:get | |
|> http_call(path, query) | |
|> elem(1) | |
|> Map.get(:body) | |
|> Jason.decode!() | |
|> log_search(query) | |
end | |
@doc """ | |
Counts how many documents are present in the Elastic index. | |
""" | |
@spec count(atom) :: integer | |
def count(index) do | |
path = [ | |
index_name(index), | |
:_count | |
] | |
:get | |
|> http_call(path, "") | |
|> elem(1) | |
|> Map.get(:body) | |
|> Jason.decode!() | |
|> Map.get("count") | |
end | |
@doc """ | |
Sends an HTTP request to Elastic. | |
""" | |
@spec http_call(atom, path, body) :: response | |
def http_call(method, path, body) when is_map(body) do | |
json = Jason.encode!(body) | |
http_call(method, path, json) | |
end | |
def http_call(method, path, body) do | |
url = uri(path) | |
HTTPoison.request(method, url, body, @headers) | |
end | |
@spec uri(path) :: String.t() | |
defp uri(path) when is_list(path) do | |
path | |
|> Enum.map(&to_string/1) | |
|> Enum.join("/") | |
|> uri | |
end | |
defp uri(path) do | |
"#{endpoint()}/#{path}" | |
end | |
@spec alias_indices(atom) :: [String.t()] | |
defp alias_indices(alias_name) do | |
case http_call(:get, [:_alias, alias_name], "") do | |
{:ok, %{status_code: 200, body: body}} -> | |
body |> Jason.decode!() |> Map.keys() | |
_ -> | |
[] | |
end | |
end | |
defp log_search(response, query) do | |
_ = | |
Logger.debug(fn -> | |
{"Performed ES query", response: inspect(response), query: inspect(query)} | |
end) | |
response | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment