Current section
Files
Jump to
Current section
Files
lib/mongodb.ex
defmodule Eidetic.EventStore.MongoDB do
@moduledoc """
MongoDB Adapter for Eidetic
"""
use GenServer
@doc false
def start_link(options \\ []) do
date_type = Keyword.get(options, :date_type, :system)
GenServer.start_link(__MODULE__, %{date_type: date_type}, options)
end
def init(args) do
{:ok, args}
end
@doc false
def handle_call({:record, event = %Eidetic.Event{}}, _from, state = %{date_type: :database}) do
{:ok, %Mongo.InsertOneResult{inserted_id: object_identifier}} =
event
|> transform_in
|> insert(state)
{:reply, {:ok, [object_identifier: object_identifier]}, state}
end
@doc false
def handle_call({:record, event = %Eidetic.Event{}}, _from, state) do
{:ok, %Mongo.InsertOneResult{inserted_id: object_identifier}} =
event
|> transform_in
|> insert(state)
{:reply, {:ok, [object_identifier: object_identifier]}, state}
end
@doc false
def handle_call({:fetch, identifier}, _from, state) do
events =
identifier
|> select
|> transform_out(state)
{:reply, {:ok, events}, state}
end
@doc false
def handle_call({:fetch_until, identifier, version}, _from, state) do
events =
identifier
|> select(version)
|> transform_out(state)
{:reply, {:ok, events}, state}
end
defp insert(document, %{date_type: :system}) do
Mongo.insert_one(:mongo, "events", document, pool: DBConnection.Poolboy)
end
defp insert(document, %{date_type: :database}) do
Mongo.insert_one(
:mongo,
"events",
%{document | datetime: %BSON.Timestamp{value: 0, ordinal: 0}},
pool: DBConnection.Poolboy
)
end
defp select(identifier) do
:mongo
|> Mongo.find("events", %{"$query": %{identifier: identifier}}, pool: DBConnection.Poolboy)
|> Enum.to_list()
end
defp select(identifier, version) do
:mongo
|> Mongo.find(
"events",
%{
"identifier" => identifier,
"serial_number" => %{"$lte" => version}
},
sort: %{"serial_number" => 1},
pool: DBConnection.Poolboy
)
|> Enum.to_list()
end
# This function used to handle date transformations, but isn't required atm
defp transform_in(event = %Eidetic.Event{}) do
event
end
defp transform_out(events, %{date_type: date_type}) when is_list(events) do
Enum.reduce(events, [], fn event, list ->
list ++ [transform_to_eidetic_event(event, date_type)]
end)
end
# Move this to Eidetic, and not the adapter
defp transform_to_eidetic_event(event, :system) when is_map(event) do
event =
Map.update!(event, "payload", fn payload ->
Map.new(payload, fn {k, v} ->
{String.to_atom(k), v}
end)
end)
%Eidetic.Event{}
|> Map.merge(
Map.new(event, fn {k, v} ->
{String.to_atom(k), v}
end)
)
|> Map.delete(:_id)
end
defp transform_to_eidetic_event(event, :database) when is_map(event) do
event =
Map.update!(event, "payload", fn payload ->
Map.new(payload, fn {k, v} ->
{String.to_atom(k), v}
end)
end)
datetime =
event["datetime"].value
|> DateTime.from_unix!()
|> Map.put(:microsecond, {0, 3})
%Eidetic.Event{}
|> Map.merge(
Map.new(event, fn {k, v} ->
{String.to_atom(k), v}
end)
)
|> Map.delete(:_id)
|> Map.put(:datetime, datetime)
end
end