Current section

Files

Jump to
bungee lib repository.ex
Raw

lib/repository.ex

defmodule Bungee.Repository do
@moduledoc """
This is your main interface to Bungee. You can add repositories for all your
Elasticsearch types and let Bungee handle the heavy lifting.
```elixir
defmodule MyRepository do
# Mandatory:
#
# module: This is your Elixir struct that you want to allow to be persisted
# type: This is the Elasticsearch type name
#
# Optionals:
#
# otp_app: If you want multiple Bungee's (Different Elasticsearch connection details)
# then you can add them to your Supervisor with their own names.
# The Bungee to use for this Repository should be defined here
# index: Want to override the index that this type belongs to. Set it here!
#
use Bungee.Repository, module: MyModule, type: my_module
end
```
"""
defmacro __using__(options) do
module = Keyword.fetch!(options, :module)
type = Keyword.fetch!(options, :type)
# If no otp_app is provided, we'll assume :bungee
otp_app = Keyword.get(options, :opt_app, :bungee)
# If no index is provided (`nil`), we'll use the index configured for otp_app
index = Keyword.get(options, :index, nil)
quote do
import Bungee.Repository
@spec fetch_key(%unquote(module){}) :: nil | binary()
@spec save(%unquote(module){}, list()) ::
{:ok, :created, %unquote(module){}} | {:ok, :updated, %unquote(module){}}
@spec save(%unquote(module){}, list()) :: %unquote(module){}
@spec delete(binary(), list()) :: {:ok, :deleted, binary()}
@spec fetch(String.t(), list()) :: {:ok, %unquote(module){}} | {:error, :not_found}
@spec fetch_by(atom(), String.t(), list()) ::
{:ok, pos_integer(), list(%unquote(module){})} | {:error, :not_found}
@spec query(map(), list()) ::
{:ok, non_neg_integer(), list(%unquote(module){})} | {:error, binary()}
defp fetch_key(projection) do
case :erlang.function_exported(unquote(module), :key, 1) do
true -> apply(unquote(module), :key, [projection])
false -> nil
end
end
@doc """
Save struct to Elasticsearch
"""
def save(%unquote(module){} = projection, options) when is_list(options) do
GenServer.call(
unquote(otp_app),
{:save, unquote(index), unquote(type), options, fetch_key(projection), projection}
)
end
@doc """
Save struct to Elasticsearch, raising an error when something goes wrong
"""
def save!(%unquote(module){} = projection, options) when is_list(options) do
{:ok, created_or_updated, updated_projection} = save(projection, options)
updated_projection
end
@doc """
Convenience method: Save a map to Elasticsearch, rather than the struct.
This will attempt to convert the map to the struct, so bare in mind any
constraints on the struct.
"""
def save(map, options) when is_map(map) and is_list(options) do
projection = struct(unquote(module), map)
save(projection, options)
end
@doc false
def save(projection, options \\ []) do
save(projection, options)
end
@doc """
Delete, by identifier, from Elasticsearch
"""
def delete(identifier, options \\ []) when is_list(options) do
GenServer.call(
unquote(otp_app),
{:delete, unquote(index), unquote(type), options, identifier, nil}
)
end
@doc """
Fetch a document, by identifier, from Elasticsearch
"""
def fetch(identifier, options \\ []) when is_list(options) do
GenServer.call(
unquote(otp_app),
{:fetch, unquote(index), unquote(type), options, identifier, unquote(module), nil}
)
end
@doc """
Fetch document(s) from Elasticsearch with a simple key -> value match
"""
def fetch_by(key, value, options \\ []) do
GenServer.call(
unquote(otp_app),
{:fetch_by, unquote(index), unquote(type), options, key, value, unquote(module)}
)
end
@doc """
Fetch a document with a custom query
"""
def query(query, options \\ []) when is_list(options) when is_map(query) do
GenServer.call(
unquote(otp_app),
{:query, unquote(index), unquote(type), options, query, unquote(module), nil}
)
end
end
end
end