Skip to content

Commit

Permalink
add support for rule wildcard
Browse files Browse the repository at this point in the history
  • Loading branch information
lenileiro committed Jul 27, 2024
1 parent ec0634b commit ed19091
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 1 deletion.
3 changes: 2 additions & 1 deletion lib/ex_datalog.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ defmodule ExDatalog do
matching_rules = get_matching_rules(rules, rule)
initial_facts = MapSet.new(Map.values(facts))

if Enum.empty?(matching_rules) do
if Enum.empty?(matching_rules) and rule != "*" do
{:ok, []}
else
all_facts = apply_rules(initial_facts, matching_rules)
Expand All @@ -46,6 +46,7 @@ defmodule ExDatalog do

def evaluate_query(_, _), do: {:error, :invalid_ExDatalog}

defp get_matching_rules(rules, "*"), do: rules
defp get_matching_rules(rules, nil), do: rules
defp get_matching_rules(rules, rule), do: Enum.filter(rules, &(&1.name == to_string(rule)))

Expand Down
128 changes: 128 additions & 0 deletions test/ex_datalog_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -689,5 +689,133 @@ defmodule ExExDatalogTest do

assert Enum.sort(results) == Enum.sort(expected_results)
end

test "rules with wildcard are evaluated correctly", %{datalog: datalog} do
parent_rule_fn = fn
%Fact{object_id: object_id, subject_id: subject_id, object_relation: "parent"} ->
%Fact{
object_id: object_id,
subject_id: subject_id,
object_relation: "parent"
}
end

parent_rule = %Rule{name: "parent", function: parent_rule_fn}
{:ok, datalog} = ExDatalog.add_rule(datalog, parent_rule)

ancestor_rule_fn_1 = fn
%Fact{object_id: object_id, subject_id: subject_id, object_relation: "ancestor"} ->
%Fact{
object_id: object_id,
subject_id: subject_id,
object_relation: "ancestor"
}
end

ancestor_rule_fn_2 = fn
%Fact{object_id: grandparent, subject_id: parent, object_relation: "parent"},
%Fact{object_id: parent, subject_id: descendant, object_relation: "parent"} ->
%Fact{
object_id: grandparent,
subject_id: descendant,
object_relation: "ancestor"
}
end

ancestor_rule_fn_3 = fn
%Fact{object_id: object_id, subject_id: parent, object_relation: "parent"},
%Fact{object_id: parent, subject_id: subject_id, object_relation: "ancestor"} ->
%Fact{
object_id: object_id,
subject_id: subject_id,
object_relation: "ancestor"
}
end

ancestor_rule_1 = %Rule{name: "ancestor", function: ancestor_rule_fn_1}
ancestor_rule_2 = %Rule{name: "ancestor", function: ancestor_rule_fn_2}
ancestor_rule_3 = %Rule{name: "ancestor", function: ancestor_rule_fn_3}

{:ok, datalog} = ExDatalog.add_rule(datalog, ancestor_rule_1)
{:ok, datalog} = ExDatalog.add_rule(datalog, ancestor_rule_2)
{:ok, datalog} = ExDatalog.add_rule(datalog, ancestor_rule_3)

{:ok, datalog} =
ExDatalog.add_fact(datalog, %Fact{
object_id: "Alice",
subject_id: "Bob",
object_relation: "parent"
})

{:ok, datalog} =
ExDatalog.add_fact(datalog, %Fact{
object_id: "Bob",
subject_id: "Charlie",
object_relation: "parent"
})

{:ok, datalog} =
ExDatalog.add_fact(datalog, %Fact{
object_id: "Charlie",
subject_id: "Daisy",
object_relation: "parent"
})

query_params = %{rule: "*"}
{:ok, results} = ExDatalog.evaluate_query(datalog, query_params)

expected_results = [
%ExDatalog.Fact{
object_id: "Alice",
object_namespace: nil,
object_relation: "ancestor",
subject_id: "Charlie",
subject_namespace: nil,
subject_relation: nil
},
%ExDatalog.Fact{
object_id: "Alice",
object_namespace: nil,
object_relation: "ancestor",
subject_id: "Daisy",
subject_namespace: nil,
subject_relation: nil
},
%ExDatalog.Fact{
object_namespace: nil,
object_id: "Alice",
object_relation: "parent",
subject_namespace: nil,
subject_id: "Bob",
subject_relation: nil
},
%ExDatalog.Fact{
object_id: "Bob",
object_namespace: nil,
object_relation: "ancestor",
subject_id: "Daisy",
subject_namespace: nil,
subject_relation: nil
},
%ExDatalog.Fact{
object_namespace: nil,
object_id: "Bob",
object_relation: "parent",
subject_namespace: nil,
subject_id: "Charlie",
subject_relation: nil
},
%ExDatalog.Fact{
object_namespace: nil,
object_id: "Charlie",
object_relation: "parent",
subject_namespace: nil,
subject_id: "Daisy",
subject_relation: nil
}
]

assert Enum.sort(results) == Enum.sort(expected_results)
end
end
end

0 comments on commit ed19091

Please sign in to comment.