defmodule Graveyard.ORM.Insert do @moduledoc """ Implements the `insert` function """ defmacro __using__(_) do quote do @doc """ Stores a map into ElasticsSearch. Returns {:ok, record} if success, {:error, error} otherwise """ def insert(raw, opts \\ %{}) do cond do is_map(raw) -> Graveyard.ORM.Insert.do_save(raw, opts) is_list(raw) and Enum.map(raw, fn(x) -> is_map(x) end) -> Graveyard.ORM.Bulk.Insert.insert(raw, opts) true -> raise Errors.BadArgumentError, message: ":raw must be a map or list of maps." end end end end alias Graveyard.Record alias Graveyard.Mappings.Auxiliar alias Graveyard.ORM.Opts import Graveyard.Utils import Graveyard.ORM.Validation import Tirexs.HTTP def do_save(raw, opts) do opts = opts |> Opts.Insert.options if opts.validate_before_insert do # Transform input to atom-keyed map validated = raw |> to_indifferent_map |> validate case validated.__valid__ do true -> validated = Map.drop(validated, [:__valid__, :__errors__]) attempt_to_insert(validated, opts) false -> {:error, :validation_failure, validated.__errors__} end else attempt_to_insert(raw, opts) end end def add_aux_fields(raw) do if Enum.empty?(Graveyard.Mappings.Auxiliar.find_fields_with_schema()) do raw |> add_timestamps() else raw |> Map.put(:__aux, auxiliar_nested_fields(raw)) |> add_timestamps() end end def auxiliar_nested_fields(raw_data, config \\ Support.mappings) do fields = Auxiliar.find_fields_with_schema(config) collected_values = Enum.reduce(fields, %{}, fn(field, acc) -> traverse_element_looking_nested(raw_data, field, acc) end) end defp traverse_element_looking_nested(body, auxiliar_field, accumulator) do Enum.reduce(body, accumulator, fn({key, val}, acc) -> cond do is_primitive_list(val) or is_boolean(val) or is_binary(val) or is_number(val) -> acc key == auxiliar_field.nested_key -> previous_value_for_nested_key = Map.get(acc, auxiliar_field.nested_key, %{}) merged_value_for_nested_key = Map.merge( previous_value_for_nested_key, %{ auxiliar_field.name => extract_all_values(val, auxiliar_field.name)} ) Map.put(acc, auxiliar_field.nested_key, merged_value_for_nested_key) is_map(val) -> traverse_element_looking_nested(val, auxiliar_field, acc) is_list_of_maps(val) -> Enum.reduce(val, acc, fn(mmap, inner_acc) -> traverse_element_looking_nested(mmap, auxiliar_field, inner_acc) end) true -> acc end end) end defp extract_all_values(lst, key) do Enum.map(lst, fn(x) -> x[key] end) |> Enum.uniq end defp add_timestamps(document) do document |> Map.put(:created_at, now()) |> Map.put(:updated_at, now()) end defp attempt_to_insert(input, opts) do ready_to_insert = add_aux_fields(input) case post("#{opts.index}/#{opts.type}", ready_to_insert) do {:ok, 201, object} -> {:ok, Record.find(object._id, opts)} {:error, status, error} -> IO.inspect(status) IO.inspect(error) {:error, error} end end end