Skip to content

Commit

Permalink
Implement map-based mappings configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastián González committed Jul 31, 2018
1 parent c378e36 commit 8a9223c
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 55 deletions.
5 changes: 0 additions & 5 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ use Mix.Config
config :tirexs,
:uri, "http://127.0.0.1:9200"

# config :graveyard,
# index: "graveyard",
# type: "graveyard",
# mappings: CustomMappingsForGraveyard

if Mix.env == :test do
import_config "#{Mix.env}.exs"
end
4 changes: 2 additions & 2 deletions lib/graveyard/exceptions.ex
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
defmodule Graveyard.Errors do
defmodule WrongConfigModuleError do
defmodule ConfigModuleError do
defexception message: "Missing function"

def full_message(error) do
"Supplied module has no get_mappings/2 function"
end
end

defmodule NoElasticSearchInstance do
defmodule ElasticSearchInstanceError do
defexception message: "No ElasticSearch found"
end
end
48 changes: 15 additions & 33 deletions lib/graveyard/mappings.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,30 @@ defmodule Graveyard.Mappings do
alias Graveyard.Support
alias Graveyard.Errors
alias Graveyard.Utils
alias Graveyard.Mappings.Basic
alias Graveyard.Mappings.Builder
alias Graveyard.Utils.TirexsUris
import Tirexs.Index.Settings

@doc """
Returns the mappings from the module indicated in `config.exs`
Returns the mappings object processed from the configured mappings module or the configured map
"""
def get_mappings(index_name \\ Support.index(), type_name \\ Support.type()) do
mappings_from_config = cond do
is_nil(Support.mappings_module) and is_nil(Support.mappings) ->
raise Errors.ConfigModuleError, "Any of :mappings or :mappings_module must be set in config"
!is_nil(Support.mappings_module) ->
module = Support.mappings_module
try do
module.get_mappings(index_name, type_name)
rescue
e in UndefinedFunctionError ->
raise Errors.WrongConfigModuleError
end
!is_nil(Support.mappings) ->
[]
Basic.get_mappings(index_name, type_name)
!is_nil(Support.mappings) or Enum.count(Support.mappings) < 1 ->
Builder.get_mappings(index_name, type_name)
true ->
raise Errors.WrongConfigModuleError, "Any of :mappings or :mappings_module must be set in config"
raise Errors.ConfigModuleError, "Any of :mappings or :mappings_module must be set in config"
end

properties_enhanced = mappings_from_config
|> Keyword.fetch!(:mapping)
|> Keyword.fetch!(:properties)
|> Keyword.merge(timestamps())
|> Keyword.merge(Builder.timestamps())
|> Keyword.merge(add_custom_keywords())

Keyword.take(mappings_from_config, [:index, :type]) ++ [mapping: [properties: properties_enhanced]]
Expand Down Expand Up @@ -59,11 +57,15 @@ defmodule Graveyard.Mappings do
|> Tirexs.Mapping.create_resource

case base do
:error -> raise Errors.NoElasticSearchInstance
:error -> raise Errors.ElasticSearchInstanceError
_ -> base
end
end

@doc """
To be used when the mappings have changed. It ppdates the current mappings with the new one,
maintaining all records within the index.
"""
def apply_mappings_change() do
temporal_index = [source: [index: Support.index(), type: Support.type()], dest: [index: "tmp", type: Support.type()]]
original_index = [source: [index: "tmp", type: Support.type()], dest: [index: Support.index(), type: Support.type()]]
Expand All @@ -87,26 +89,6 @@ defmodule Graveyard.Mappings do
IO.inspect TirexsUris.delete_mapping("tmp")
end

defp timestamps() do
Utils.to_keyword_list(%{
created_at: graveyard_to_elastic(:datetime),
updated_at: graveyard_to_elastic(:datetime)
})
end

defp graveyard_to_elastic(type) do
case type do
:string -> %{type: "keyword"}
:category -> %{type: "keyword"}
:list -> %{type: "keyword"}
:text -> %{type: "text", analyzer: "nGram_analyzer"}
:date -> %{type: "date"}
:datetime -> %{type: "date", format: "dd/MM/yyyy HH:mm:ss"}
:integer -> %{type: "integer"}
:number -> %{type: "float"}
end
end

defp add_custom_keywords() do
[]
end
Expand Down
17 changes: 17 additions & 0 deletions lib/graveyard/mappings/basic.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
defmodule Graveyard.Mappings.Basic do
@moduledoc """
Builds a mapping object from a user-supplied module
"""
alias Graveyard.Support
alias Graveyard.Errors

def get_mappings(index_name, type_name) do
module = Support.mappings_module
try do
module.get_mappings(index_name, type_name)
rescue
e in UndefinedFunctionError ->
raise Errors.ConfigModuleError
end
end
end
48 changes: 47 additions & 1 deletion lib/graveyard/mappings/builder.ex
Original file line number Diff line number Diff line change
@@ -1,3 +1,49 @@
defmodule Graveyard.Mappings.Builder do

@moduledoc """
Builds a mapping object from a user-supplied map schema using the Graveyard Mapping DSL
"""

alias Graveyard.Support
alias Graveyard.Errors
alias Graveyard.Utils

def get_mappings(index_name, type_name) do
build_recursively(Support.mappings)
end

defp build_recursively(config) do
properties = Enum.map(config, fn({key, value}) ->
if Map.has_key?(value, "schema") do
{
Utils.to_indifferent_atom(key),
Utils.to_keyword_list(graveyard_to_elastic(value["type"])) ++ build_recursively(value["schema"])
}
else
{Utils.to_indifferent_atom(key), graveyard_to_elastic(value["type"])}
end
end)
[mapping: [properties: properties], index: Support.index(), type: Support.type()]
end

def timestamps() do
Utils.to_keyword_list(%{
created_at: graveyard_to_elastic(:datetime),
updated_at: graveyard_to_elastic(:datetime)
})
end

defp graveyard_to_elastic(type) do
case type do
:string -> %{type: "keyword"}
:category -> %{type: "keyword"}
:list -> %{type: "keyword"}
:text -> %{type: "text", analyzer: "nGram_analyzer"}
:date -> %{type: "date", format: "dd/MM/yyyy"}
:datetime -> %{type: "date", format: "dd/MM/yyyy HH:mm:ss"}
:integer -> %{type: "integer"}
:number -> %{type: "float"}
:object -> %{type: "object"}
:oblist -> %{type: "nested"}
end
end
end
2 changes: 1 addition & 1 deletion lib/graveyard/orm/destroy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule Graveyard.ORM.Destroy do
nil
:error ->
IO.inspect "ERROR"
raise Graveyard.Errors.NoElasticSearchInstance
raise Graveyard.Errors.ElasticSearchInstanceError
{:error, status, error} ->
IO.inspect(status)
IO.inspect(error)
Expand Down
2 changes: 1 addition & 1 deletion lib/graveyard/orm/find.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defmodule Graveyard.ORM.Find do
%{index: index, type: type} = Map.merge(%{index: Support.index(), type: Support.type()}, opts)

case get("#{index}/#{type}/#{id}") do
:error -> raise Graveyard.Errors.NoElasticSearchInstance
:error -> raise Graveyard.Errors.ElasticSearchInstanceError
{:error, status, object} ->
if Map.get(opts, :log, false) do
IO.inspect(status)
Expand Down
94 changes: 83 additions & 11 deletions test/mappings_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,37 @@ defmodule Graveyard.MappingsTest do
end
end

@last_podcast_mappings %{
"episode" => %{"type" => :text},
"number" => %{"type" => :integer},
"hosts" => %{"type" => :list}
}

@advanced_last_podcast_mappings %{
"episode" => %{"type" => :text},
"number" => %{"type" => :integer},
"hosts" => %{"type" => :list},
"topic" => %{"type" => :object, "schema" => %{
"name" => %{"type" => :string},
"followers" => %{"type" => :integer},
"last_time_played" => %{"type" => :date}
}}
}

@oblist_last_podcast_mappings %{
"episode" => %{"type" => :text},
"number" => %{"type" => :integer},
"hosts" => %{"type" => :list},
"topic" => %{"type" => :object, "schema" => %{
"name" => %{"type" => :string},
"followers" => %{"type" => :integer},
"last_time_played" => %{"type" => :date}
}},
"tags" => %{"type" => :oblist, "schema" => %{
"name" => %{"type" => :string}
}}
}

setup do
Application.put_env(:tirexs, :uri, "http://localhost:9200")
Application.put_env(:graveyard, :index, "test_graveyard_index")
Expand All @@ -37,7 +68,7 @@ defmodule Graveyard.MappingsTest do
end

describe "get_mappings/2" do
setup do
setup do
Application.put_env(:graveyard, :type, "test_graveyard_type")
:ok
end
Expand All @@ -55,24 +86,65 @@ defmodule Graveyard.MappingsTest do
assert Keyword.has_key?(properties, :updated_at)
end

test "returns a mappings object with valid index key for map mappings" do
test "raise if custom mapping file has wrong function signature" do
Application.put_env(:graveyard, :mappings_module, WrongSignatureMappings)
assert_raise Errors.ConfigModuleError, fn -> Mappings.get_mappings end
end

test "returns a valid mappings object for flat map mappings" do
Application.put_env(:graveyard, :mappings_module, nil)
Application.put_env(:graveyard, :mappings, %{
Application.put_env(:graveyard, :mappings, @last_podcast_mappings)

})
actual = Mappings.get_mappings()
properties = actual
|> Keyword.fetch!(:mapping)
|> Keyword.fetch!(:properties)

assert nil
end
assert Keyword.fetch!(actual, :index) == Application.get_env(:graveyard, :index)
assert Keyword.fetch!(actual, :type) == Application.get_env(:graveyard, :type)
assert Keyword.has_key?(properties, :created_at)
assert Keyword.has_key?(properties, :updated_at)
end

test "raise if custom mapping file has wrong function signature" do
Application.put_env(:graveyard, :mappings_module, WrongSignatureMappings)
assert_raise Errors.WrongConfigModuleError, fn -> Mappings.get_mappings end
test "returns a valid mappings object for map mappings with object" do
Application.put_env(:graveyard, :mappings_module, nil)
Application.put_env(:graveyard, :mappings, @advanced_last_podcast_mappings)

actual = Mappings.get_mappings()
properties = actual
|> Keyword.fetch!(:mapping)
|> Keyword.fetch!(:properties)

assert Keyword.fetch!(actual, :index) == Application.get_env(:graveyard, :index)
assert Keyword.fetch!(actual, :type) == Application.get_env(:graveyard, :type)
assert Keyword.has_key?(properties, :topic)
topic_properties = Keyword.fetch!(properties, :topic) |> Keyword.fetch!(:mapping) |> Keyword.fetch!(:properties)
assert Keyword.has_key?(topic_properties, :name)
assert Keyword.has_key?(topic_properties, :followers)
assert Keyword.has_key?(topic_properties, :last_time_played)
end

test "returns a valid mappings object for map mappings with oblists" do
Application.put_env(:graveyard, :mappings_module, nil)
Application.put_env(:graveyard, :mappings, @oblist_last_podcast_mappings)

actual = Mappings.get_mappings()
properties = actual
|> Keyword.fetch!(:mapping)
|> Keyword.fetch!(:properties)

assert Keyword.fetch!(actual, :index) == Application.get_env(:graveyard, :index)
assert Keyword.fetch!(actual, :type) == Application.get_env(:graveyard, :type)
assert Keyword.has_key?(properties, :tags)
tags_properties = Keyword.fetch!(properties, :tags) |> Keyword.fetch!(:mapping) |> Keyword.fetch!(:properties)
assert Keyword.has_key?(tags_properties, :name)
end

# Test with both module and map mappings
test "raise if config file has nil for mappings and mappings_module" do
Application.put_env(:graveyard, :mappings_module, nil)
Application.put_env(:graveyard, :mappings, nil)
assert_raise Errors.WrongConfigModuleError, fn -> Mappings.get_mappings end
assert_raise Errors.ConfigModuleError, fn -> Mappings.get_mappings end
end
end

Expand All @@ -91,7 +163,7 @@ defmodule Graveyard.MappingsTest do

test "raise if no ElasticSearch is found" do
Application.put_env(:tirexs, :uri, "http://nowheretobefound")
assert_raise Errors.NoElasticSearchInstance, fn -> Mappings.create_settings_and_mappings() end
assert_raise Errors.ElasticSearchInstanceError, fn -> Mappings.create_settings_and_mappings() end
end
end
end
2 changes: 1 addition & 1 deletion test/orm/destroy_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ defmodule Graveyard.ORM.DestroyTest do

test "raise error if something fishy goes on", %{record: record} do
Application.put_env(:tirexs, :uri, "http://localhost:9201")
assert_raise Graveyard.Errors.NoElasticSearchInstance, fn() -> Record.destroy(record._id) end
assert_raise Graveyard.Errors.ElasticSearchInstanceError, fn() -> Record.destroy(record._id) end
end
end
end

0 comments on commit 8a9223c

Please sign in to comment.