Skip to content

Instantly share code, notes, and snippets.

@rodrigues
Last active July 12, 2019 19:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rodrigues/b12bc68d7b9c8b558a931d28c19d3b92 to your computer and use it in GitHub Desktop.
Save rodrigues/b12bc68d7b9c8b558a931d28c19d3b92 to your computer and use it in GitHub Desktop.
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