Last active
January 9, 2019 17:59
-
-
Save WolfDan/7505734b755de33a7e61c7d12230f575 to your computer and use it in GitHub Desktop.
Instruction Parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%{ | |
where: %{ | |
OR: [ | |
%{ | |
description: "description", | |
OR: [ | |
%{ | |
test: "hmmm" | |
}, | |
%{ | |
test: nil | |
} | |
] | |
}, | |
%{ | |
description: nil, | |
date: "2018" | |
} | |
], | |
date: "bla", | |
createdAt: "2016", | |
createdAt_gt: "2017", | |
createdAt_lt: "2019" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%{ | |
instruction: %{ | |
"createdAt" => %{"eq" => "2016", "gt" => "2017", "lt" => "2019"} | |
}, | |
on_fail: :fail, | |
on_sucess: %{ | |
instruction: %{"date" => "bla"}, | |
on_fail: %{ | |
instruction: %{"description" => "description"}, | |
on_fail: %{ | |
instruction: %{"test" => "hmmm"}, | |
on_fail: %{ | |
instruction: %{"test" => nil}, | |
on_fail: %{ | |
instruction: %{"date" => "2018"}, | |
on_fail: :fail, | |
on_sucess: %{ | |
instruction: %{"description" => nil}, | |
on_fail: :fail, | |
on_sucess: :success | |
} | |
}, | |
on_sucess: :success | |
}, | |
on_sucess: :success | |
}, | |
on_sucess: :success | |
}, | |
on_sucess: :success | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Nomure.Schema.Query.Plan.Parser do | |
@moduledoc """ | |
Parse the `where` given query into a behaviour tree like structure | |
""" | |
@instructions [ | |
"_gt", | |
"_lt", | |
"_starts_with", | |
"_ends_with" | |
] | |
def parse(%{where: statement}) do | |
sorted_keys = get_clean_key_names(statement) | |
get_instructions(sorted_keys, statement) | |
end | |
def get_instructions([name | new_keys], statements, fail_instruction \\ :fail) do | |
statements = for {key, val} <- statements, into: %{}, do: {to_string(key), val} | |
similar_instructions = | |
Enum.filter(statements, fn {x, _} -> String.replace(x, @instructions, "") == name end) | |
|> Map.new() | |
|> Map.keys() | |
instructions = | |
Enum.map(similar_instructions, fn | |
^name -> Map.get(statements, name) | |
x -> {x |> String.replace(name <> "_", ""), Map.get(statements, x)} | |
end) | |
instructions = | |
if Enum.any?(instructions, fn x -> is_tuple(x) end) do | |
# if atribute has the property name then is an `eq` instruction | |
instructions | |
|> Enum.map(fn | |
{x, v} -> {x, v} | |
value -> {"eq", value} | |
end) | |
|> Map.new() | |
else | |
instructions | |
|> List.first() | |
end | |
instruc_values = Map.new([{name, instructions}]) | |
case new_keys do | |
# No more instructions, if sucess all right, if fail all operation fails | |
[] -> | |
%{ | |
instruction: instruc_values, | |
on_sucess: :success, | |
on_fail: fail_instruction | |
} | |
# OR instruction, decople list and process | |
["OR"] -> | |
%{ | |
instruction: instruc_values, | |
on_sucess: :success, | |
on_fail: resolve_or_instruction(Map.get(statements, "OR"), fail_instruction) | |
} | |
# more AND instructions, continue the process | |
_ -> | |
%{ | |
instruction: instruc_values, | |
on_sucess: get_instructions(new_keys, statements), | |
on_fail: fail_instruction | |
} | |
end | |
end | |
def resolve_or_instruction([], _) do | |
:fail | |
end | |
def resolve_or_instruction(instruction_list, fail_instruction) do | |
# We reverse the list so the last item is the first to be processed | |
instruction_list = | |
instruction_list | |
|> Enum.reverse() | |
last_instruction = | |
instruction_list | |
|> List.first() | |
last_instruction = | |
get_instructions(get_clean_key_names(last_instruction), last_instruction, fail_instruction) | |
{_, instruction_list} = | |
instruction_list | |
|> List.pop_at(0) | |
Enum.reduce(instruction_list, last_instruction, fn | |
x, acc -> | |
get_instructions(get_clean_key_names(x), x, acc) | |
end) | |
end | |
def get_clean_key_names(map) do | |
map | |
|> get_keys() | |
|> Enum.map(&String.replace(&1, @instructions, "")) | |
# ["createdAt_gt", "createdAt_lt", "OR"] -> ["OR", "createdAt"] | |
|> Enum.uniq() | |
# ["OR", "createdAt"] -> ["createdAt", "OR"] | |
|> Enum.sort_by(&(&1 == "OR")) | |
end | |
# by priority `AND` then `OR` | |
defp get_keys(map) do | |
map |> Map.keys() |> Enum.map(&to_string/1) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment