Change BGP matching query results to return variable keys as atoms
This commit is contained in:
parent
9cd4478574
commit
f9e451f006
9 changed files with 265 additions and 240 deletions
|
@ -17,7 +17,7 @@ alias RDF.Query.BGP
|
|||
test_graph = RDF.Turtle.read_file!("test/data/TURTLE-TESTS/manifest.ttl", base: "http://www.w3.org/2013/TurtleTests/")
|
||||
|
||||
|
||||
all_query = [{"s", "p", "o"}]
|
||||
all_query = %BGP{triple_patterns: [{:s, :p, :o}]}
|
||||
Benchee.run(%{
|
||||
"take 1 from BGP.Simple" => fn -> BGP.Simple.query_stream(test_graph, all_query) |> Enum.take(1) end,
|
||||
"take 1 from BGP.Stream" => fn -> BGP.Stream.query_stream(test_graph, all_query) |> Enum.take(1) end,
|
||||
|
@ -25,18 +25,19 @@ Benchee.run(%{
|
|||
|
||||
|
||||
# rdft:approval rdft:Approved - count: 287
|
||||
approved_query = [
|
||||
{"test_case", RDFT.approval, RDF.iri(RDFT.Approved)},
|
||||
{"test_case", MF.name, "name"},
|
||||
{"test_case", RDFS.comment, "comment"},
|
||||
]
|
||||
approved_query = %BGP{triple_patterns: [
|
||||
{:test_case, RDFT.approval, RDF.iri(RDFT.Approved)},
|
||||
{:test_case, MF.name, :name},
|
||||
{:test_case, RDFS.comment, :comment},
|
||||
]}
|
||||
|
||||
# rdft:approval rdft:Proposed - count: 4
|
||||
proposed_query = [
|
||||
{"test_case", RDFT.approval, RDF.iri(RDFT.Proposed)},
|
||||
{"test_case", MF.name, "name"},
|
||||
{"test_case", RDFS.comment, "comment"},
|
||||
]
|
||||
proposed_query = %BGP{triple_patterns: [
|
||||
{:test_case, RDFT.approval, RDF.iri(RDFT.Proposed)},
|
||||
{:test_case, MF.name, :name},
|
||||
{:test_case, RDFS.comment, :comment},
|
||||
]}
|
||||
|
||||
Benchee.run(%{
|
||||
"APPROVED from BGP.Simple" => fn -> BGP.Simple.query(test_graph, approved_query) end,
|
||||
"PROPOSED from BGP.Simple" => fn -> BGP.Simple.query(test_graph, proposed_query) end,
|
||||
|
|
|
@ -11,4 +11,23 @@ defmodule RDF.Query.BGP do
|
|||
@type triple_patterns :: list(triple_pattern)
|
||||
|
||||
@type t :: %__MODULE__{triple_patterns: triple_patterns}
|
||||
|
||||
|
||||
def variables(%__MODULE__{triple_patterns: triple_patterns}), do: variables(triple_patterns)
|
||||
|
||||
def variables(triple_patterns) when is_list(triple_patterns) do
|
||||
triple_patterns
|
||||
|> Enum.reduce([], fn triple_pattern, vars -> variables(triple_pattern) ++ vars end)
|
||||
|> Enum.uniq()
|
||||
end
|
||||
|
||||
def variables({s, p, o}) when is_atom(s) and is_atom(p) and is_atom(o), do: [s, p, o]
|
||||
def variables({s, p, _}) when is_atom(s) and is_atom(p), do: [s, p]
|
||||
def variables({s, _, o}) when is_atom(s) and is_atom(o), do: [s, o]
|
||||
def variables({_, p, o}) when is_atom(p) and is_atom(o), do: [p, o]
|
||||
def variables({s, _, _}) when is_atom(s), do: [s]
|
||||
def variables({_, p, _}) when is_atom(p), do: [p]
|
||||
def variables({_, _, o}) when is_atom(o), do: [o]
|
||||
|
||||
def variables(_), do: []
|
||||
end
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
defmodule RDF.Query.BGP.BlankNodeHandler do
|
||||
@moduledoc false
|
||||
|
||||
alias RDF.Query.BGP
|
||||
alias RDF.BlankNode
|
||||
|
||||
@blank_node_prefix "_:"
|
||||
@default_remove_bnode_query_variables Application.get_env(:rdf, :default_remove_bnode_query_variables, true)
|
||||
|
||||
def preprocess(triple_patterns) do
|
||||
|
@ -14,31 +14,42 @@ defmodule RDF.Query.BGP.BlankNodeHandler do
|
|||
end)
|
||||
end
|
||||
|
||||
defp convert_blank_nodes({%BlankNode{} = s, %BlankNode{} = p, %BlankNode{} = o}), do: {true, {to_string(s), to_string(p), to_string(o)}}
|
||||
defp convert_blank_nodes({s, %BlankNode{} = p, %BlankNode{} = o}), do: {true, {s, to_string(p), to_string(o)}}
|
||||
defp convert_blank_nodes({%BlankNode{} = s, p, %BlankNode{} = o}), do: {true, {to_string(s), p, to_string(o)}}
|
||||
defp convert_blank_nodes({%BlankNode{} = s, %BlankNode{} = p, o}), do: {true, {to_string(s), to_string(p), o}}
|
||||
defp convert_blank_nodes({%BlankNode{} = s, p, o}), do: {true, {to_string(s), p, o}}
|
||||
defp convert_blank_nodes({s, %BlankNode{} = p, o}), do: {true, {s, to_string(p), o}}
|
||||
defp convert_blank_nodes({s, p, %BlankNode{} = o}), do: {true, {s, p, to_string(o)}}
|
||||
defp convert_blank_nodes({%BlankNode{} = s, %BlankNode{} = p, %BlankNode{} = o}), do: {true, {bnode_var(s), bnode_var(p), bnode_var(o)}}
|
||||
defp convert_blank_nodes({s, %BlankNode{} = p, %BlankNode{} = o}), do: {true, {s, bnode_var(p), bnode_var(o)}}
|
||||
defp convert_blank_nodes({%BlankNode{} = s, p, %BlankNode{} = o}), do: {true, {bnode_var(s), p, bnode_var(o)}}
|
||||
defp convert_blank_nodes({%BlankNode{} = s, %BlankNode{} = p, o}), do: {true, {bnode_var(s), bnode_var(p), o}}
|
||||
defp convert_blank_nodes({%BlankNode{} = s, p, o}), do: {true, {bnode_var(s), p, o}}
|
||||
defp convert_blank_nodes({s, %BlankNode{} = p, o}), do: {true, {s, bnode_var(p), o}}
|
||||
defp convert_blank_nodes({s, p, %BlankNode{} = o}), do: {true, {s, p, bnode_var(o)}}
|
||||
defp convert_blank_nodes(triple_pattern), do: {false, triple_pattern}
|
||||
|
||||
defp bnode_var(bnode), do: bnode |> to_string() |> String.to_atom()
|
||||
|
||||
def postprocess(solutions, has_blank_nodes, opts) do
|
||||
def postprocess(solutions, bgp, has_blank_nodes, opts) do
|
||||
if has_blank_nodes and
|
||||
Keyword.get(opts, :remove_bnode_query_variables, @default_remove_bnode_query_variables) do
|
||||
Enum.map(solutions, &remove_blank_nodes/1)
|
||||
bnode_vars = bgp |> bnodes() |> Enum.map(&bnode_var/1)
|
||||
Enum.map(solutions, &(Map.drop(&1, bnode_vars)))
|
||||
else
|
||||
solutions
|
||||
end
|
||||
end
|
||||
|
||||
defp remove_blank_nodes(solution) do
|
||||
solution
|
||||
|> Enum.filter(fn
|
||||
{@blank_node_prefix <> _, _} -> false
|
||||
_ -> true
|
||||
end)
|
||||
|> Map.new
|
||||
defp bnodes(%BGP{triple_patterns: triple_patterns}), do: bnodes(triple_patterns)
|
||||
|
||||
defp bnodes(triple_patterns) when is_list(triple_patterns) do
|
||||
triple_patterns
|
||||
|> Enum.reduce([], fn triple_pattern, vars -> bnodes(triple_pattern) ++ vars end)
|
||||
|> Enum.uniq()
|
||||
end
|
||||
|
||||
defp bnodes({%BlankNode{} = s, %BlankNode{} = p, %BlankNode{} = o}), do: [s, p, o]
|
||||
defp bnodes({%BlankNode{} = s, %BlankNode{} = p, _}), do: [s, p]
|
||||
defp bnodes({%BlankNode{} = s, _, %BlankNode{} = o}) , do: [s, o]
|
||||
defp bnodes({_, %BlankNode{} = p, %BlankNode{} = o}), do: [p, o]
|
||||
defp bnodes({%BlankNode{} = s, _, _}), do: [s]
|
||||
defp bnodes({_, %BlankNode{} = p, _}), do: [p]
|
||||
defp bnodes({_, _, %BlankNode{} = o}), do: [o]
|
||||
|
||||
defp bnodes(_), do: []
|
||||
end
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
defmodule RDF.Query.BGP.QueryPlanner do
|
||||
@moduledoc false
|
||||
|
||||
def query_plan(triple_patterns, solved \\ MapSet.new, plan \\ [])
|
||||
alias RDF.Query.BGP
|
||||
|
||||
def query_plan(triple_patterns, solved \\ [], plan \\ [])
|
||||
|
||||
def query_plan([], _, plan), do: Enum.reverse(plan)
|
||||
|
||||
def query_plan(triple_patterns, solved, plan) do
|
||||
[next_best | rest] = Enum.sort_by(triple_patterns, &triple_priority/1)
|
||||
new_solved = MapSet.union(solved, variables(next_best))
|
||||
new_solved = Enum.uniq(BGP.variables(next_best) ++ solved)
|
||||
|
||||
query_plan(
|
||||
mark_solved_variables(rest, new_solved),
|
||||
|
@ -15,33 +17,24 @@ defmodule RDF.Query.BGP.QueryPlanner do
|
|||
[next_best | plan])
|
||||
end
|
||||
|
||||
defp variables({v1, v2, v3}) when is_binary(v1) and is_binary(v2) and is_binary(v3), do: MapSet.new([v1, v2, v3])
|
||||
defp variables({_, v2, v3}) when is_binary(v2) and is_binary(v3), do: MapSet.new([v2, v3])
|
||||
defp variables({v1, _, v3}) when is_binary(v1) and is_binary(v3), do: MapSet.new([v1, v3])
|
||||
defp variables({v1, v2, _}) when is_binary(v1) and is_binary(v2), do: MapSet.new([v1, v2])
|
||||
defp variables({v1, _, _}) when is_binary(v1), do: MapSet.new([v1])
|
||||
defp variables({_, v2, _}) when is_binary(v2), do: MapSet.new([v2])
|
||||
defp variables({_, _, v3}) when is_binary(v3), do: MapSet.new([v3])
|
||||
defp variables(_), do: MapSet.new()
|
||||
|
||||
defp triple_priority({v, v, v}), do: triple_priority({v, :p, :o})
|
||||
defp triple_priority({v, v, o}), do: triple_priority({v, :p, o})
|
||||
defp triple_priority({v, p, v}), do: triple_priority({v, p, :o})
|
||||
defp triple_priority({s, v, v}), do: triple_priority({s, v, :o})
|
||||
defp triple_priority({v, v, v}), do: triple_priority({v, "p", "o"})
|
||||
defp triple_priority({v, v, o}), do: triple_priority({v, "p", o})
|
||||
defp triple_priority({v, p, v}), do: triple_priority({v, p, "o"})
|
||||
defp triple_priority({s, v, v}), do: triple_priority({s, v, "o"})
|
||||
defp triple_priority({s, p, o}) do
|
||||
{sp, pp, op} = {value_priority(s), value_priority(p), value_priority(o)}
|
||||
<<(sp + pp + op) :: size(2), sp :: size(1), pp :: size(1), op :: size(1)>>
|
||||
end
|
||||
|
||||
defp value_priority(value) when is_binary(value), do: 1
|
||||
defp value_priority(_), do: 0
|
||||
defp value_priority(value) when is_atom(value), do: 1
|
||||
defp value_priority(_), do: 0
|
||||
|
||||
defp mark_solved_variables(triple_patterns, solved) do
|
||||
Enum.map triple_patterns, fn {s, p, o} ->
|
||||
{
|
||||
(if is_binary(s) and MapSet.member?(solved, s), do: {s}, else: s),
|
||||
(if is_binary(p) and MapSet.member?(solved, p), do: {p}, else: p),
|
||||
(if is_binary(o) and MapSet.member?(solved, o), do: {o}, else: o)
|
||||
(if is_atom(s) and s in solved, do: {s}, else: s),
|
||||
(if is_atom(p) and p in solved, do: {p}, else: p),
|
||||
(if is_atom(o) and o in solved, do: {o}, else: o)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,13 +11,13 @@ defmodule RDF.Query.BGP.Simple do
|
|||
def query(_, %BGP{triple_patterns: []}, _), do: [%{}] # https://www.w3.org/TR/sparql11-query/#emptyGroupPattern
|
||||
|
||||
def query(data, %BGP{triple_patterns: triple_patterns}, opts) do
|
||||
{bnode_state, triple_patterns} =
|
||||
{bnode_state, preprocessed_triple_patterns} =
|
||||
BlankNodeHandler.preprocess(triple_patterns)
|
||||
|
||||
triple_patterns
|
||||
preprocessed_triple_patterns
|
||||
|> QueryPlanner.query_plan()
|
||||
|> do_query(data)
|
||||
|> BlankNodeHandler.postprocess(bnode_state, opts)
|
||||
|> BlankNodeHandler.postprocess(triple_patterns, bnode_state, opts)
|
||||
end
|
||||
|
||||
@impl RDF.Query.BGP.Matcher
|
||||
|
@ -88,7 +88,7 @@ defmodule RDF.Query.BGP.Simple do
|
|||
|
||||
|
||||
defp match(%Graph{descriptions: descriptions}, {subject_variable, _, _} = triple_pattern)
|
||||
when is_binary(subject_variable) do
|
||||
when is_atom(subject_variable) do
|
||||
Enum.reduce(descriptions, [], fn ({subject, description}, acc) ->
|
||||
case match(description, solve_variables(subject_variable, subject, triple_pattern)) do
|
||||
nil -> acc
|
||||
|
@ -108,7 +108,7 @@ defmodule RDF.Query.BGP.Simple do
|
|||
end
|
||||
|
||||
defp match(%Description{predications: predications}, {_, variable, variable})
|
||||
when is_binary(variable) do
|
||||
when is_atom(variable) do
|
||||
Enum.reduce(predications, [], fn ({predicate, objects}, solutions) ->
|
||||
if Map.has_key?(objects, predicate) do
|
||||
[%{variable => predicate} | solutions]
|
||||
|
@ -119,7 +119,7 @@ defmodule RDF.Query.BGP.Simple do
|
|||
end
|
||||
|
||||
defp match(%Description{predications: predications}, {_, predicate_variable, object_variable})
|
||||
when is_binary(predicate_variable) and is_binary(object_variable) do
|
||||
when is_atom(predicate_variable) and is_atom(object_variable) do
|
||||
Enum.reduce(predications, [], fn ({predicate, objects}, solutions) ->
|
||||
solutions ++
|
||||
Enum.map(objects, fn {object, _} ->
|
||||
|
@ -129,7 +129,7 @@ defmodule RDF.Query.BGP.Simple do
|
|||
end
|
||||
|
||||
defp match(%Description{predications: predications},
|
||||
{_, predicate_variable, object}) when is_binary(predicate_variable) do
|
||||
{_, predicate_variable, object}) when is_atom(predicate_variable) do
|
||||
Enum.reduce(predications, [], fn ({predicate, objects}, solutions) ->
|
||||
if Map.has_key?(objects, object) do
|
||||
[%{predicate_variable => predicate} | solutions]
|
||||
|
@ -145,7 +145,7 @@ defmodule RDF.Query.BGP.Simple do
|
|||
nil -> []
|
||||
objects -> cond do
|
||||
# object_or_variable is a variable
|
||||
is_binary(object_or_variable) ->
|
||||
is_atom(object_or_variable) ->
|
||||
Enum.map(objects, fn {object, _} ->
|
||||
%{object_or_variable => object}
|
||||
end)
|
||||
|
|
|
@ -12,13 +12,13 @@ defmodule RDF.Query.BGP.Stream do
|
|||
def query_stream(_, %BGP{triple_patterns: []}, _), do: stream([%{}]) # https://www.w3.org/TR/sparql11-query/#emptyGroupPattern
|
||||
|
||||
def query_stream(data, %BGP{triple_patterns: triple_patterns}, opts) do
|
||||
{bnode_state, triple_patterns} =
|
||||
{bnode_state, preprocessed_triple_patterns} =
|
||||
BlankNodeHandler.preprocess(triple_patterns)
|
||||
|
||||
triple_patterns
|
||||
preprocessed_triple_patterns
|
||||
|> QueryPlanner.query_plan()
|
||||
|> do_query(data)
|
||||
|> BlankNodeHandler.postprocess(bnode_state, opts)
|
||||
|> BlankNodeHandler.postprocess(triple_patterns, bnode_state, opts)
|
||||
end
|
||||
|
||||
@impl RDF.Query.BGP.Matcher
|
||||
|
@ -92,7 +92,7 @@ defmodule RDF.Query.BGP.Stream do
|
|||
|
||||
|
||||
defp match(%Graph{descriptions: descriptions}, {subject_variable, _, _} = triple_pattern)
|
||||
when is_binary(subject_variable) do
|
||||
when is_atom(subject_variable) do
|
||||
Stream.flat_map(descriptions, fn {subject, description} ->
|
||||
case match(description, solve_variables(subject_variable, subject, triple_pattern)) do
|
||||
nil -> []
|
||||
|
@ -112,7 +112,7 @@ defmodule RDF.Query.BGP.Stream do
|
|||
end
|
||||
|
||||
defp match(%Description{predications: predications}, {_, variable, variable})
|
||||
when is_binary(variable) do
|
||||
when is_atom(variable) do
|
||||
matches =
|
||||
Stream.filter(predications, fn {predicate, objects} -> Map.has_key?(objects, predicate) end)
|
||||
|
||||
|
@ -122,7 +122,7 @@ defmodule RDF.Query.BGP.Stream do
|
|||
end
|
||||
|
||||
defp match(%Description{predications: predications}, {_, predicate_variable, object_variable})
|
||||
when is_binary(predicate_variable) and is_binary(object_variable) do
|
||||
when is_atom(predicate_variable) and is_atom(object_variable) do
|
||||
Stream.flat_map(predications, fn {predicate, objects} ->
|
||||
Stream.map(objects, fn {object, _} ->
|
||||
%{predicate_variable => predicate, object_variable => object}
|
||||
|
@ -131,7 +131,7 @@ defmodule RDF.Query.BGP.Stream do
|
|||
end
|
||||
|
||||
defp match(%Description{predications: predications},
|
||||
{_, predicate_variable, object}) when is_binary(predicate_variable) do
|
||||
{_, predicate_variable, object}) when is_atom(predicate_variable) do
|
||||
matches =
|
||||
Stream.filter(predications, fn {_, objects} -> Map.has_key?(objects, object) end)
|
||||
|
||||
|
@ -146,7 +146,7 @@ defmodule RDF.Query.BGP.Stream do
|
|||
nil -> nil
|
||||
objects -> cond do
|
||||
# object_or_variable is a variable
|
||||
is_binary(object_or_variable) ->
|
||||
is_atom(object_or_variable) ->
|
||||
Stream.map(objects, fn {object, _} ->
|
||||
%{object_or_variable => object}
|
||||
end)
|
||||
|
|
|
@ -10,28 +10,28 @@ defmodule RDF.Query.BGP.QueryPlannerTest do
|
|||
end
|
||||
|
||||
test "single" do
|
||||
assert QueryPlanner.query_plan([{"a", "b", "c"}]) == [{"a", "b", "c"}]
|
||||
assert QueryPlanner.query_plan([{:a, :b, :c}]) == [{:a, :b, :c}]
|
||||
end
|
||||
|
||||
test "multiple connected" do
|
||||
assert QueryPlanner.query_plan([
|
||||
{"a", "b", "c"},
|
||||
{"a", "d", ~L"foo"}
|
||||
{:a, :b, :c},
|
||||
{:a, :d, ~L"foo"}
|
||||
]) == [
|
||||
{"a", "d", ~L"foo"},
|
||||
{{"a"}, "b", "c"}
|
||||
{:a, :d, ~L"foo"},
|
||||
{{:a}, :b, :c}
|
||||
]
|
||||
|
||||
assert QueryPlanner.query_plan([
|
||||
{"s", "p", "o"},
|
||||
{"s2", "p2", "o2"},
|
||||
{"s", "p", "o2"},
|
||||
{"s4", "p4", ~L"foo"}
|
||||
{:s, :p, :o},
|
||||
{:s2, :p2, :o2},
|
||||
{:s, :p, :o2},
|
||||
{:s4, :p4, ~L"foo"}
|
||||
]) == [
|
||||
{"s4", "p4", ~L"foo"},
|
||||
{"s", "p", "o"},
|
||||
{{"s"}, {"p"}, "o2"},
|
||||
{"s2", "p2", {"o2"}},
|
||||
{:s4, :p4, ~L"foo"},
|
||||
{:s, :p, :o},
|
||||
{{:s}, {:p}, :o2},
|
||||
{:s2, :p2, {:o2}},
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,28 +21,28 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
end
|
||||
|
||||
test "single {s ?p ?o}" do
|
||||
assert query(@example_graph, bgp({EX.s1, "p", "o"})) ==
|
||||
assert query(@example_graph, bgp({EX.s1, :p, :o})) ==
|
||||
[
|
||||
%{"p" => EX.p1, "o" => EX.o1},
|
||||
%{"p" => EX.p2, "o" => EX.o2}
|
||||
%{p: EX.p1, o: EX.o1},
|
||||
%{p: EX.p2, o: EX.o2}
|
||||
]
|
||||
end
|
||||
|
||||
test "single {?s ?p o}" do
|
||||
assert query(@example_graph, bgp({"s", "p", EX.o2})) ==
|
||||
assert query(@example_graph, bgp({:s, :p, EX.o2})) ==
|
||||
[
|
||||
%{"s" => EX.s3, "p" => EX.p3},
|
||||
%{"s" => EX.s1, "p" => EX.p2}
|
||||
%{s: EX.s3, p: EX.p3},
|
||||
%{s: EX.s1, p: EX.p2}
|
||||
]
|
||||
end
|
||||
|
||||
test "single {?s p ?o}" do
|
||||
assert query(@example_graph, bgp({"s", EX.p3, "o"})) ==
|
||||
[%{"s" => EX.s3, "o" => EX.o2}]
|
||||
assert query(@example_graph, bgp({:s, EX.p3, :o})) ==
|
||||
[%{s: EX.s3, o: EX.o2}]
|
||||
end
|
||||
|
||||
test "with no solutions" do
|
||||
assert query(Graph.new(), bgp({"a", "b", "c"})) == []
|
||||
assert query(Graph.new(), bgp({:a, :b, :c})) == []
|
||||
end
|
||||
|
||||
test "with solutions on one triple pattern but none on another one" do
|
||||
|
@ -52,8 +52,8 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
])
|
||||
|
||||
assert query(example_graph, bgp [
|
||||
{"a", EX.p1, ~L"unmatched" },
|
||||
{"a", EX.y, EX.z}
|
||||
{:a, EX.p1, ~L"unmatched" },
|
||||
{:a, EX.y, EX.z}
|
||||
]) == []
|
||||
end
|
||||
|
||||
|
@ -64,8 +64,8 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({"a", "a", "b"})) ==
|
||||
[%{"a" => EX.y, "b" => EX.x}]
|
||||
assert query(example_graph, bgp({:a, :a, :b})) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
test "repeated variable: {?a ?b ?a}" do
|
||||
|
@ -75,8 +75,8 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({"a", "b", "a"})) ==
|
||||
[%{"a" => EX.y, "b" => EX.x}]
|
||||
assert query(example_graph, bgp({:a, :b, :a})) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
test "repeated variable: {?b ?a ?a}" do
|
||||
|
@ -86,8 +86,8 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({"b", "a", "a"})) ==
|
||||
[%{"a" => EX.y, "b" => EX.x}]
|
||||
assert query(example_graph, bgp({:b, :a, :a})) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
test "repeated variable: {?a ?a ?a}" do
|
||||
|
@ -98,33 +98,33 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.y, EX.y, EX.y},
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({"a", "a", "a"})) == [%{"a" => EX.y}]
|
||||
assert query(example_graph, bgp({:a, :a, :a})) == [%{a: EX.y}]
|
||||
end
|
||||
|
||||
test "two connected triple patterns with a match" do
|
||||
assert query(@example_graph, bgp [
|
||||
{EX.s1, "p", "o"},
|
||||
{EX.s3, "p2", "o" }
|
||||
{EX.s1, :p, :o},
|
||||
{EX.s3, :p2, :o }
|
||||
]) == [%{
|
||||
"p" => EX.p2,
|
||||
"p2" => EX.p3,
|
||||
"o" => EX.o2
|
||||
p: EX.p2,
|
||||
p2: EX.p3,
|
||||
o: EX.o2
|
||||
}]
|
||||
|
||||
assert query(@example_graph, bgp [
|
||||
{EX.s1, "p", "o1"},
|
||||
{EX.s1, "p", "o2"}
|
||||
{EX.s1, :p, :o1},
|
||||
{EX.s1, :p, :o2}
|
||||
]) ==
|
||||
[
|
||||
%{
|
||||
"p" => EX.p1,
|
||||
"o1" => EX.o1,
|
||||
"o2" => EX.o1,
|
||||
p: EX.p1,
|
||||
o1: EX.o1,
|
||||
o2: EX.o1,
|
||||
},
|
||||
%{
|
||||
"p" => EX.p2,
|
||||
"o1" => EX.o2,
|
||||
"o2" => EX.o2,
|
||||
p: EX.p2,
|
||||
o1: EX.o2,
|
||||
o2: EX.o2,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -135,9 +135,9 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.s3, EX.p3, EX.o1}
|
||||
]),
|
||||
bgp [
|
||||
{EX.s1, EX.p1, "o"},
|
||||
{EX.s3, "p", "o"}
|
||||
]) == [%{"p" => EX.p3, "o" => EX.o1}]
|
||||
{EX.s1, EX.p1, :o},
|
||||
{EX.s3, :p, :o}
|
||||
]) == [%{p: EX.p3, o: EX.o1}]
|
||||
end
|
||||
|
||||
test "a triple pattern with dependent variables from separate triple patterns" do
|
||||
|
@ -148,94 +148,94 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.s3, EX.p2, EX.o1}
|
||||
]),
|
||||
bgp [
|
||||
{EX.s1, EX.p1, "o"},
|
||||
{EX.s2, "p", EX.o2},
|
||||
{"s", "p", "o"}
|
||||
{EX.s1, EX.p1, :o},
|
||||
{EX.s2, :p, EX.o2},
|
||||
{:s, :p, :o}
|
||||
]
|
||||
) == [
|
||||
%{
|
||||
"s" => EX.s3,
|
||||
"p" => EX.p2,
|
||||
"o" => EX.o1,
|
||||
s: EX.s3,
|
||||
p: EX.p2,
|
||||
o: EX.o1,
|
||||
},
|
||||
]
|
||||
end
|
||||
|
||||
test "when no solutions" do
|
||||
assert query(@example_graph, bgp({EX.s, EX.p, "o"})) == []
|
||||
assert query(@example_graph, bgp({EX.s, EX.p, :o})) == []
|
||||
end
|
||||
|
||||
test "multiple triple patterns with a constant unmatched triple has no solutions" do
|
||||
assert query(@example_graph, bgp [
|
||||
{EX.s1, "p", "o"},
|
||||
{EX.s1, :p, :o},
|
||||
{EX.s, EX.p, EX.o}
|
||||
]) == []
|
||||
end
|
||||
|
||||
test "independent triple patterns lead to cross-products" do
|
||||
assert query(@example_graph, bgp [
|
||||
{EX.s1, "p1", "o"},
|
||||
{"s", "p2", EX.o2}
|
||||
{EX.s1, :p1, :o},
|
||||
{:s, :p2, EX.o2}
|
||||
]) == [
|
||||
%{
|
||||
"p1" => EX.p1,
|
||||
"o" => EX.o1,
|
||||
"s" => EX.s3,
|
||||
"p2" => EX.p3,
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
s: EX.s3,
|
||||
p2: EX.p3,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p2,
|
||||
"o" => EX.o2,
|
||||
"s" => EX.s3,
|
||||
"p2" => EX.p3,
|
||||
p1: EX.p2,
|
||||
o: EX.o2,
|
||||
s: EX.s3,
|
||||
p2: EX.p3,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p1,
|
||||
"o" => EX.o1,
|
||||
"s" => EX.s1,
|
||||
"p2" => EX.p2,
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
s: EX.s1,
|
||||
p2: EX.p2,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p2,
|
||||
"o" => EX.o2,
|
||||
"s" => EX.s1,
|
||||
"p2" => EX.p2,
|
||||
p1: EX.p2,
|
||||
o: EX.o2,
|
||||
s: EX.s1,
|
||||
p2: EX.p2,
|
||||
},
|
||||
]
|
||||
end
|
||||
|
||||
test "blank nodes behave like variables, but don't appear in the solution" do
|
||||
assert query(@example_graph, bgp [
|
||||
{EX.s1, "p", RDF.bnode("o")},
|
||||
{EX.s3, "p2", RDF.bnode("o")}
|
||||
]) == [%{"p" => EX.p2, "p2" => EX.p3}]
|
||||
{EX.s1, :p, RDF.bnode("o")},
|
||||
{EX.s3, :p2, RDF.bnode("o")}
|
||||
]) == [%{p: EX.p2, p2: EX.p3}]
|
||||
end
|
||||
|
||||
test "cross-product with blank nodes" do
|
||||
assert query(@example_graph, bgp [
|
||||
{EX.s1, "p1", "o"},
|
||||
{RDF.bnode("s"), "p2", EX.o2}
|
||||
{EX.s1, :p1, :o},
|
||||
{RDF.bnode("s"), :p2, EX.o2}
|
||||
]) ==
|
||||
[
|
||||
%{
|
||||
"p1" => EX.p1,
|
||||
"o" => EX.o1,
|
||||
"p2" => EX.p3,
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
p2: EX.p3,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p2,
|
||||
"o" => EX.o2,
|
||||
"p2" => EX.p3,
|
||||
p1: EX.p2,
|
||||
o: EX.o2,
|
||||
p2: EX.p3,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p1,
|
||||
"o" => EX.o1,
|
||||
"p2" => EX.p2,
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
p2: EX.p2,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p2,
|
||||
"o" => EX.o2,
|
||||
"p2" => EX.p2,
|
||||
p1: EX.p2,
|
||||
o: EX.o2,
|
||||
p2: EX.p2,
|
||||
},
|
||||
]
|
||||
end
|
||||
|
|
|
@ -22,27 +22,28 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
end
|
||||
|
||||
test "single {s ?p ?o}" do
|
||||
assert query(@example_graph, bgp({EX.s1, "p", "o"})) ==
|
||||
[
|
||||
%{"p" => EX.p1, "o" => EX.o1},
|
||||
%{"p" => EX.p2, "o" => EX.o2}
|
||||
]
|
||||
assert query(@example_graph, bgp({EX.s1, :p, :o})) ==
|
||||
[
|
||||
%{p: EX.p1, o: EX.o1},
|
||||
%{p: EX.p2, o: EX.o2}
|
||||
]
|
||||
end
|
||||
|
||||
test "single {?s ?p o}" do
|
||||
assert query(@example_graph, bgp({"s", "p", EX.o2})) ==
|
||||
[
|
||||
%{"s" => EX.s1, "p" => EX.p2},
|
||||
%{"s" => EX.s3, "p" => EX.p3},
|
||||
]
|
||||
assert query(@example_graph, bgp({:s, :p, EX.o2})) ==
|
||||
[
|
||||
%{s: EX.s1, p: EX.p2},
|
||||
%{s: EX.s3, p: EX.p3},
|
||||
]
|
||||
end
|
||||
|
||||
test "single {?s p ?o}" do
|
||||
assert query(@example_graph, bgp({"s", EX.p3, "o"})) == [%{"s" => EX.s3, "o" => EX.o2}]
|
||||
assert query(@example_graph, bgp({:s, EX.p3, :o})) ==
|
||||
[%{s: EX.s3, o: EX.o2}]
|
||||
end
|
||||
|
||||
test "with no solutions" do
|
||||
assert query(Graph.new(), bgp({"a", "b", "c"})) == []
|
||||
assert query(Graph.new(), bgp({:a, :b, :c})) == []
|
||||
end
|
||||
|
||||
test "with solutions on one triple pattern but none on another one" do
|
||||
|
@ -52,8 +53,8 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
])
|
||||
|
||||
assert query(example_graph, bgp [
|
||||
{"a", EX.p1, ~L"unmatched" },
|
||||
{"a", EX.y, EX.z}
|
||||
{:a, EX.p1, ~L"unmatched" },
|
||||
{:a, EX.y, EX.z}
|
||||
]) == []
|
||||
end
|
||||
|
||||
|
@ -64,8 +65,8 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({"a", "a", "b"})) ==
|
||||
[%{"a" => EX.y, "b" => EX.x}]
|
||||
assert query(example_graph, bgp({:a, :a, :b})) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
test "repeated variable: {?a ?b ?a}" do
|
||||
|
@ -75,8 +76,8 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({"a", "b", "a"})) ==
|
||||
[%{"a" => EX.y, "b" => EX.x}]
|
||||
assert query(example_graph, bgp({:a, :b, :a})) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
test "repeated variable: {?b ?a ?a}" do
|
||||
|
@ -86,8 +87,8 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({"b", "a", "a"})) ==
|
||||
[%{"a" => EX.y, "b" => EX.x}]
|
||||
assert query(example_graph, bgp({:b, :a, :a})) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
test "repeated variable: {?a ?a ?a}" do
|
||||
|
@ -98,35 +99,35 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.y, EX.y, EX.y},
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({"a", "a", "a"})) == [%{"a" => EX.y}]
|
||||
assert query(example_graph, bgp({:a, :a, :a})) == [%{a: EX.y}]
|
||||
end
|
||||
|
||||
test "two connected triple patterns with a match" do
|
||||
assert query(@example_graph, bgp [
|
||||
{EX.s1, "p", "o"},
|
||||
{EX.s3, "p2", "o" }
|
||||
{EX.s1, :p, :o},
|
||||
{EX.s3, :p2, :o }
|
||||
]) == [%{
|
||||
"p" => EX.p2,
|
||||
"p2" => EX.p3,
|
||||
"o" => EX.o2
|
||||
p: EX.p2,
|
||||
p2: EX.p3,
|
||||
o: EX.o2
|
||||
}]
|
||||
|
||||
assert query(@example_graph, bgp [
|
||||
{EX.s1, "p", "o1"},
|
||||
{EX.s1, "p", "o2"}
|
||||
{EX.s1, :p, :o1},
|
||||
{EX.s1, :p, :o2}
|
||||
]) ==
|
||||
[
|
||||
%{
|
||||
"p" => EX.p1,
|
||||
"o1" => EX.o1,
|
||||
"o2" => EX.o1,
|
||||
},
|
||||
%{
|
||||
"p" => EX.p2,
|
||||
"o1" => EX.o2,
|
||||
"o2" => EX.o2,
|
||||
},
|
||||
]
|
||||
[
|
||||
%{
|
||||
p: EX.p1,
|
||||
o1: EX.o1,
|
||||
o2: EX.o1,
|
||||
},
|
||||
%{
|
||||
p: EX.p2,
|
||||
o1: EX.o2,
|
||||
o2: EX.o2,
|
||||
},
|
||||
]
|
||||
|
||||
assert query(
|
||||
Graph.new([
|
||||
|
@ -135,9 +136,9 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.s3, EX.p3, EX.o1}
|
||||
]),
|
||||
bgp [
|
||||
{EX.s1, EX.p1, "o"},
|
||||
{EX.s3, "p", "o"}
|
||||
]) == [%{"p" => EX.p3, "o" => EX.o1}]
|
||||
{EX.s1, EX.p1, :o},
|
||||
{EX.s3, :p, :o}
|
||||
]) == [%{p: EX.p3, o: EX.o1}]
|
||||
end
|
||||
|
||||
test "a triple pattern with dependent variables from separate triple patterns" do
|
||||
|
@ -148,26 +149,26 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.s3, EX.p2, EX.o1}
|
||||
]),
|
||||
bgp [
|
||||
{EX.s1, EX.p1, "o"},
|
||||
{EX.s2, "p", EX.o2},
|
||||
{"s", "p", "o"}
|
||||
{EX.s1, EX.p1, :o},
|
||||
{EX.s2, :p, EX.o2},
|
||||
{:s, :p, :o}
|
||||
]
|
||||
) == [
|
||||
%{
|
||||
"s" => EX.s3,
|
||||
"p" => EX.p2,
|
||||
"o" => EX.o1,
|
||||
s: EX.s3,
|
||||
p: EX.p2,
|
||||
o: EX.o1,
|
||||
},
|
||||
]
|
||||
end
|
||||
|
||||
test "when no solutions" do
|
||||
assert query(@example_graph, bgp({EX.s, EX.p, "o"})) == []
|
||||
assert query(@example_graph, bgp({EX.s, EX.p, :o})) == []
|
||||
end
|
||||
|
||||
test "multiple triple patterns with a constant unmatched triple has no solutions" do
|
||||
assert query(@example_graph, bgp [
|
||||
{EX.s1, "p", "o"},
|
||||
{EX.s1, :p, :o},
|
||||
{EX.s, EX.p, EX.o}
|
||||
]) == []
|
||||
end
|
||||
|
@ -175,71 +176,71 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
test "independent triple patterns lead to cross-products" do
|
||||
assert MapSet.new(
|
||||
query(@example_graph, bgp [
|
||||
{EX.s1, "p1", "o"},
|
||||
{"s", "p2", EX.o2}
|
||||
{EX.s1, :p1, :o},
|
||||
{:s, :p2, EX.o2}
|
||||
])
|
||||
) == MapSet.new([
|
||||
%{
|
||||
"p1" => EX.p1,
|
||||
"o" => EX.o1,
|
||||
"s" => EX.s3,
|
||||
"p2" => EX.p3,
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
s: EX.s3,
|
||||
p2: EX.p3,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p2,
|
||||
"o" => EX.o2,
|
||||
"s" => EX.s3,
|
||||
"p2" => EX.p3,
|
||||
p1: EX.p2,
|
||||
o: EX.o2,
|
||||
s: EX.s3,
|
||||
p2: EX.p3,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p1,
|
||||
"o" => EX.o1,
|
||||
"s" => EX.s1,
|
||||
"p2" => EX.p2,
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
s: EX.s1,
|
||||
p2: EX.p2,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p2,
|
||||
"o" => EX.o2,
|
||||
"s" => EX.s1,
|
||||
"p2" => EX.p2,
|
||||
p1: EX.p2,
|
||||
o: EX.o2,
|
||||
s: EX.s1,
|
||||
p2: EX.p2,
|
||||
},
|
||||
])
|
||||
end
|
||||
|
||||
test "blank nodes behave like variables, but don't appear in the solution" do
|
||||
assert query(@example_graph, bgp [
|
||||
{EX.s1, "p", RDF.bnode("o")},
|
||||
{EX.s3, "p2", RDF.bnode("o")}
|
||||
]) == [%{"p" => EX.p2, "p2" => EX.p3}]
|
||||
{EX.s1, :p, RDF.bnode("o")},
|
||||
{EX.s3, :p2, RDF.bnode("o")}
|
||||
]) == [%{p: EX.p2, p2: EX.p3}]
|
||||
end
|
||||
|
||||
test "cross-product with blank nodes" do
|
||||
assert MapSet.new(
|
||||
query(@example_graph, bgp [
|
||||
{EX.s1, "p1", "o"},
|
||||
{RDF.bnode("s"), "p2", EX.o2}
|
||||
{EX.s1, :p1, :o},
|
||||
{RDF.bnode("s"), :p2, EX.o2}
|
||||
])
|
||||
) ==
|
||||
MapSet.new([
|
||||
%{
|
||||
"p1" => EX.p1,
|
||||
"o" => EX.o1,
|
||||
"p2" => EX.p3,
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
p2: EX.p3,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p2,
|
||||
"o" => EX.o2,
|
||||
"p2" => EX.p3,
|
||||
p1: EX.p2,
|
||||
o: EX.o2,
|
||||
p2: EX.p3,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p1,
|
||||
"o" => EX.o1,
|
||||
"p2" => EX.p2,
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
p2: EX.p2,
|
||||
},
|
||||
%{
|
||||
"p1" => EX.p2,
|
||||
"o" => EX.o2,
|
||||
"p2" => EX.p2,
|
||||
p1: EX.p2,
|
||||
o: EX.o2,
|
||||
p2: EX.p2,
|
||||
},
|
||||
])
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue