defmodule Subaru do @moduledoc """ The main facade and entry point for the Subaru graph database. """ alias Subaru.Query @doc """ Returns the configured store module. """ def store do Application.get_env(:subaru, :store, Subaru.Store.Mnesia) end @doc """ Runs a query plan and returns the results as a list. """ def run(%Query{} = query) do Subaru.Executor.run(query, store()) end @doc """ Generate a sortable identifier. """ defdelegate gen_id, to: Subaru.Id, as: :generate @doc """ Adds an `:insert` operation for a vertex struct to a batch. """ def insert(batch, %{id: id} = struct) when is_list(batch) and is_binary(id) do [{:insert, struct} | batch] end @doc """ Adds a `:link` operation to a batch. """ def link(batch, type, from_id, to_id, props \\ %{}) when is_list(batch) do [{:link, type, from_id, to_id, props} | batch] end @doc """ Commits a batch of write operations to the store. """ def commit(batch) when is_list(batch) do low_level_ops = batch |> Enum.reverse() |> Enum.map(&to_low_level_op/1) store().write_batch(low_level_ops) end # --- Passthrough functions to store --- @doc """ Delegates to `c:Subaru.Store.Behaviour.backup/1`. """ def backup(path), do: store().backup(path) @doc """ Delegates to `c:Subaru.Store.Behaviour.restore/1`. """ def restore(path), do: store().restore(path) # --- Private Helpers --- defp to_low_level_op({:insert, struct}) do {label, properties} = struct_to_vertex_parts(struct) value = %{label: label, properties: properties} {:put, {:node, struct.id}, value} end defp to_low_level_op({:link, type, from_id, to_id, props}) do id = gen_id() value = %{from: from_id, to: to_id, label: type, properties: props} {:put, {:edge, id}, value} end defp struct_to_vertex_parts(%{__struct__: schema} = struct) do label = schema |> Module.split() |> List.last() |> Macro.underscore() |> String.to_atom() properties = struct |> Map.from_struct() |> Map.drop([:id, :__struct__]) {label, properties} end end