RDF graph query API
This commit is contained in:
parent
8d68e925ad
commit
7d3473abd0
13 changed files with 343 additions and 246 deletions
|
@ -16,11 +16,10 @@ 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 = %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,
|
||||
"take 1 from BGP.Simple" => fn -> BGP.Simple.stream(all_query, test_graph) |> Enum.take(1) end,
|
||||
"take 1 from BGP.Stream" => fn -> BGP.Stream.stream(all_query, test_graph) |> Enum.take(1) end,
|
||||
})
|
||||
|
||||
|
||||
|
@ -30,21 +29,19 @@ approved_query = %BGP{triple_patterns: [
|
|||
{:test_case, MF.name, :name},
|
||||
{:test_case, RDFS.comment, :comment},
|
||||
]}
|
||||
|
||||
# rdft:approval rdft:Proposed - count: 4
|
||||
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,
|
||||
"APPROVED from BGP.Simple" => fn -> BGP.Simple.execute(approved_query, test_graph) end,
|
||||
"PROPOSED from BGP.Simple" => fn -> BGP.Simple.execute(proposed_query, test_graph) end,
|
||||
|
||||
"APPROVED from BGP.Stream (consumed)" => fn -> BGP.Stream.query(test_graph, approved_query) end,
|
||||
"PROPOSED from BGP.Stream (consumed)" => fn -> BGP.Stream.query(test_graph, proposed_query) end,
|
||||
"APPROVED from BGP.Stream (unconsumed)" => fn -> BGP.Stream.query_stream(test_graph, approved_query) end,
|
||||
"PROPOSED from BGP.Stream (unconsumed)" => fn -> BGP.Stream.query_stream(test_graph, proposed_query) end,
|
||||
"APPROVED from BGP.Stream (1 consumed)" => fn -> BGP.Stream.query_stream(test_graph, approved_query) |> Enum.take(1) end
|
||||
"APPROVED from BGP.Stream (consumed)" => fn -> BGP.Stream.execute(approved_query, test_graph) end,
|
||||
"PROPOSED from BGP.Stream (consumed)" => fn -> BGP.Stream.execute(proposed_query, test_graph) end,
|
||||
"APPROVED from BGP.Stream (unconsumed)" => fn -> BGP.Stream.stream(approved_query, test_graph) end,
|
||||
"PROPOSED from BGP.Stream (unconsumed)" => fn -> BGP.Stream.stream(proposed_query, test_graph) end,
|
||||
"APPROVED from BGP.Stream (1 consumed)" => fn -> BGP.Stream.stream(approved_query, test_graph) |> Enum.take(1) end
|
||||
})
|
||||
|
|
|
@ -479,6 +479,14 @@ defmodule RDF.Graph do
|
|||
Access.fetch(descriptions, coerce_subject(subject))
|
||||
end
|
||||
|
||||
def query(graph, query, opts \\ []) do
|
||||
RDF.Query.execute!(query, graph, opts)
|
||||
end
|
||||
|
||||
def query_stream(graph, query, opts \\ []) do
|
||||
RDF.Query.stream!(query, graph, opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the description of the given subject.
|
||||
|
||||
|
|
55
lib/rdf/query.ex
Normal file
55
lib/rdf/query.ex
Normal file
|
@ -0,0 +1,55 @@
|
|||
defmodule RDF.Query do
|
||||
@moduledoc """
|
||||
The RDF Graph query API.
|
||||
"""
|
||||
|
||||
alias RDF.Graph
|
||||
alias RDF.Query.{BGP, Builder}
|
||||
|
||||
@default_matcher RDF.Query.BGP.Stream
|
||||
|
||||
|
||||
def execute(query, graph, opts \\ [])
|
||||
|
||||
def execute(%BGP{} = query, %Graph{} = graph, opts) do
|
||||
matcher = Keyword.get(opts, :matcher, @default_matcher)
|
||||
matcher.execute(query, graph, opts)
|
||||
end
|
||||
|
||||
def execute(query, graph, opts) when is_list(query) or is_tuple(query) do
|
||||
with {:ok, bgp} <- Builder.bgp(query) do
|
||||
execute(bgp, graph, opts)
|
||||
end
|
||||
end
|
||||
|
||||
def execute!(query, graph, opts) do
|
||||
case execute(query, graph, opts) do
|
||||
{:ok, results} -> results
|
||||
{:error, error} -> raise error
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def stream(query, graph, opts \\ [])
|
||||
|
||||
def stream(%BGP{} = query, %Graph{} = graph, opts) do
|
||||
matcher = Keyword.get(opts, :matcher, @default_matcher)
|
||||
matcher.stream(query, graph, opts)
|
||||
end
|
||||
|
||||
def stream(query, graph, opts) when is_list(query) or is_tuple(query) do
|
||||
with {:ok, bgp} <- Builder.bgp(query) do
|
||||
stream(bgp, graph, opts)
|
||||
end
|
||||
end
|
||||
|
||||
def stream!(query, graph, opts) do
|
||||
case execute(query, graph, opts) do
|
||||
{:ok, results} -> results
|
||||
{:error, error} -> raise error
|
||||
end
|
||||
end
|
||||
|
||||
defdelegate bgp(query), to: Builder, as: :bgp!
|
||||
defdelegate path(query, opts \\ []), to: Builder, as: :path!
|
||||
end
|
|
@ -39,7 +39,7 @@ defmodule RDF.Query.BGP.BlankNodeHandler do
|
|||
|
||||
defp bnodes(triple_patterns) when is_list(triple_patterns) do
|
||||
triple_patterns
|
||||
|> Enum.reduce([], fn triple_pattern, vars -> bnodes(triple_pattern) ++ vars end)
|
||||
|> Enum.flat_map(&bnodes/1)
|
||||
|> Enum.uniq()
|
||||
end
|
||||
|
||||
|
|
|
@ -4,13 +4,13 @@ defmodule RDF.Query.BGP.Matcher do
|
|||
"""
|
||||
|
||||
alias RDF.Query.BGP
|
||||
alias RDF.Graph
|
||||
|
||||
@type solution :: map
|
||||
@type solutions :: [solution]
|
||||
|
||||
@callback query(data :: RDF.Graph.t, bgp :: BGP.t, opts :: Keyword.t) :: solutions
|
||||
|
||||
@callback query_stream(data :: RDF.Graph.t, bgp :: BGP.t, opts :: Keyword.t) :: Enumerable.t()
|
||||
@callback execute(BGP.t, Graph.t, opts :: Keyword.t) :: solutions
|
||||
|
||||
@callback stream(BGP.t, Graph.t, opts :: Keyword.t) :: Enumerable.t()
|
||||
|
||||
end
|
||||
|
|
|
@ -6,51 +6,51 @@ defmodule RDF.Query.BGP.Simple do
|
|||
alias RDF.{Graph, Description}
|
||||
|
||||
@impl RDF.Query.BGP.Matcher
|
||||
def query(data, pattern, opts \\ [])
|
||||
def execute(bgp, graph, opts \\ [])
|
||||
|
||||
def query(_, %BGP{triple_patterns: []}, _), do: [%{}] # https://www.w3.org/TR/sparql11-query/#emptyGroupPattern
|
||||
def execute(%BGP{triple_patterns: []}, _, _), do: [%{}] # https://www.w3.org/TR/sparql11-query/#emptyGroupPattern
|
||||
|
||||
def query(data, %BGP{triple_patterns: triple_patterns}, opts) do
|
||||
def execute(%BGP{triple_patterns: triple_patterns}, %Graph{} = graph, opts) do
|
||||
{bnode_state, preprocessed_triple_patterns} =
|
||||
BlankNodeHandler.preprocess(triple_patterns)
|
||||
|
||||
preprocessed_triple_patterns
|
||||
|> QueryPlanner.query_plan()
|
||||
|> do_query(data)
|
||||
|> do_execute(graph)
|
||||
|> BlankNodeHandler.postprocess(triple_patterns, bnode_state, opts)
|
||||
end
|
||||
|
||||
@impl RDF.Query.BGP.Matcher
|
||||
def query_stream(data, bgp, opts \\ []) do
|
||||
query(data, bgp, opts)
|
||||
def stream(bgp, graph, opts \\ []) do
|
||||
execute(bgp, graph, opts)
|
||||
|> Stream.into([])
|
||||
end
|
||||
|
||||
|
||||
defp do_query([triple_pattern | remaining], data) do
|
||||
do_query(remaining, data, match(data, triple_pattern))
|
||||
defp do_execute([triple_pattern | remaining], graph) do
|
||||
do_execute(remaining, graph, match(graph, triple_pattern))
|
||||
end
|
||||
|
||||
defp do_query(triple_patterns, data, solutions)
|
||||
defp do_execute(triple_patterns, graph, solutions)
|
||||
|
||||
defp do_query(_, _, []), do: []
|
||||
defp do_execute(_, _, []), do: []
|
||||
|
||||
defp do_query([], _, solutions), do: solutions
|
||||
defp do_execute([], _, solutions), do: solutions
|
||||
|
||||
defp do_query([triple_pattern | remaining], data, solutions) do
|
||||
do_query(remaining, data, match_with_solutions(data, triple_pattern, solutions))
|
||||
defp do_execute([triple_pattern | remaining], graph, solutions) do
|
||||
do_execute(remaining, graph, match_with_solutions(graph, triple_pattern, solutions))
|
||||
end
|
||||
|
||||
|
||||
defp match_with_solutions(data, {s, p, o} = triple_pattern, existing_solutions)
|
||||
defp match_with_solutions(graph, {s, p, o} = triple_pattern, existing_solutions)
|
||||
when is_tuple(s) or is_tuple(p) or is_tuple(o) do
|
||||
triple_pattern
|
||||
|> apply_solutions(existing_solutions)
|
||||
|> Enum.flat_map(&(merging_match(&1, data)))
|
||||
|> Enum.flat_map(&(merging_match(&1, graph)))
|
||||
end
|
||||
|
||||
defp match_with_solutions(data, triple_pattern, existing_solutions) do
|
||||
data
|
||||
defp match_with_solutions(graph, triple_pattern, existing_solutions) do
|
||||
graph
|
||||
|> match(triple_pattern)
|
||||
|> Enum.flat_map(fn solution ->
|
||||
Enum.map(existing_solutions, &(Map.merge(solution, &1)))
|
||||
|
@ -76,8 +76,8 @@ defmodule RDF.Query.BGP.Simple do
|
|||
end
|
||||
end
|
||||
|
||||
defp merging_match({dependent_solution, triple_pattern}, data) do
|
||||
case match(data, triple_pattern) do
|
||||
defp merging_match({dependent_solution, triple_pattern}, graph) do
|
||||
case match(graph, triple_pattern) do
|
||||
nil -> []
|
||||
solutions ->
|
||||
Enum.map(solutions, fn solution ->
|
||||
|
@ -143,21 +143,22 @@ defmodule RDF.Query.BGP.Simple do
|
|||
{_, predicate, object_or_variable}) do
|
||||
case predications[predicate] do
|
||||
nil -> []
|
||||
objects -> cond do
|
||||
# object_or_variable is a variable
|
||||
is_atom(object_or_variable) ->
|
||||
Enum.map(objects, fn {object, _} ->
|
||||
%{object_or_variable => object}
|
||||
end)
|
||||
objects ->
|
||||
cond do
|
||||
# object_or_variable is a variable
|
||||
is_atom(object_or_variable) ->
|
||||
Enum.map(objects, fn {object, _} ->
|
||||
%{object_or_variable => object}
|
||||
end)
|
||||
|
||||
# object_or_variable is a object
|
||||
Map.has_key?(objects, object_or_variable) ->
|
||||
[%{}]
|
||||
# object_or_variable is a object
|
||||
Map.has_key?(objects, object_or_variable) ->
|
||||
[%{}]
|
||||
|
||||
# else
|
||||
true ->
|
||||
[]
|
||||
end
|
||||
# else
|
||||
true ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,54 +7,54 @@ defmodule RDF.Query.BGP.Stream do
|
|||
|
||||
|
||||
@impl RDF.Query.BGP.Matcher
|
||||
def query_stream(data, pattern, opts \\ [])
|
||||
def stream(bgp, graph, opts \\ [])
|
||||
|
||||
def query_stream(_, %BGP{triple_patterns: []}, _), do: stream([%{}]) # https://www.w3.org/TR/sparql11-query/#emptyGroupPattern
|
||||
def stream(%BGP{triple_patterns: []}, _, _), do: to_stream([%{}]) # https://www.w3.org/TR/sparql11-query/#emptyGroupPattern
|
||||
|
||||
def query_stream(data, %BGP{triple_patterns: triple_patterns}, opts) do
|
||||
def stream(%BGP{triple_patterns: triple_patterns}, %Graph{} = graph, opts) do
|
||||
{bnode_state, preprocessed_triple_patterns} =
|
||||
BlankNodeHandler.preprocess(triple_patterns)
|
||||
|
||||
preprocessed_triple_patterns
|
||||
|> QueryPlanner.query_plan()
|
||||
|> do_query(data)
|
||||
|> do_execute(graph)
|
||||
|> BlankNodeHandler.postprocess(triple_patterns, bnode_state, opts)
|
||||
end
|
||||
|
||||
@impl RDF.Query.BGP.Matcher
|
||||
def query(data, bgp, opts \\ []) do
|
||||
query_stream(data, bgp, opts)
|
||||
def execute(bgp, graph, opts \\ []) do
|
||||
stream(bgp, graph, opts)
|
||||
|> Enum.to_list()
|
||||
end
|
||||
|
||||
defp do_query([triple_pattern | remaining], data) do
|
||||
do_query(remaining, data, match(data, triple_pattern))
|
||||
defp do_execute([triple_pattern | remaining], graph) do
|
||||
do_execute(remaining, graph, match(graph, triple_pattern))
|
||||
end
|
||||
|
||||
# CAUTION: Careful with using Enum.empty?/1 on the solution stream!! The first match must be
|
||||
# searched for every call in the query loop repeatedly then, which can have dramatic effects potentially.
|
||||
# Only use it very close to the data (in the match/1 functions operating on data directly).
|
||||
|
||||
defp do_query(triple_patterns, data, solutions)
|
||||
defp do_execute(triple_patterns, graph, solutions)
|
||||
|
||||
defp do_query(_, _, nil), do: stream([])
|
||||
defp do_execute(_, _, nil), do: to_stream([])
|
||||
|
||||
defp do_query([], _, solutions), do: solutions
|
||||
defp do_execute([], _, solutions), do: solutions
|
||||
|
||||
defp do_query([triple_pattern | remaining], data, solutions) do
|
||||
do_query(remaining, data, match_with_solutions(data, triple_pattern, solutions))
|
||||
defp do_execute([triple_pattern | remaining], graph, solutions) do
|
||||
do_execute(remaining, graph, match_with_solutions(graph, triple_pattern, solutions))
|
||||
end
|
||||
|
||||
|
||||
defp match_with_solutions(data, {s, p, o} = triple_pattern, existing_solutions)
|
||||
defp match_with_solutions(graph, {s, p, o} = triple_pattern, existing_solutions)
|
||||
when is_tuple(s) or is_tuple(p) or is_tuple(o) do
|
||||
triple_pattern
|
||||
|> apply_solutions(existing_solutions)
|
||||
|> Stream.flat_map(&(merging_match(&1, data)))
|
||||
|> Stream.flat_map(&(merging_match(&1, graph)))
|
||||
end
|
||||
|
||||
defp match_with_solutions(data, triple_pattern, existing_solutions) do
|
||||
if solutions = match(data, triple_pattern) do
|
||||
defp match_with_solutions(graph, triple_pattern, existing_solutions) do
|
||||
if solutions = match(graph, triple_pattern) do
|
||||
Stream.flat_map(solutions, fn solution ->
|
||||
Stream.map(existing_solutions, &(Map.merge(solution, &1)))
|
||||
end)
|
||||
|
@ -80,8 +80,8 @@ defmodule RDF.Query.BGP.Stream do
|
|||
end
|
||||
end
|
||||
|
||||
defp merging_match({dependent_solution, triple_pattern}, data) do
|
||||
case match(data, triple_pattern) do
|
||||
defp merging_match({dependent_solution, triple_pattern}, graph) do
|
||||
case match(graph, triple_pattern) do
|
||||
nil -> []
|
||||
solutions ->
|
||||
Stream.map solutions, fn solution ->
|
||||
|
@ -144,22 +144,23 @@ defmodule RDF.Query.BGP.Stream do
|
|||
{_, predicate, object_or_variable}) do
|
||||
case predications[predicate] do
|
||||
nil -> nil
|
||||
objects -> cond do
|
||||
# object_or_variable is a variable
|
||||
is_atom(object_or_variable) ->
|
||||
Stream.map(objects, fn {object, _} ->
|
||||
%{object_or_variable => object}
|
||||
end)
|
||||
objects ->
|
||||
cond do
|
||||
# object_or_variable is a variable
|
||||
is_atom(object_or_variable) ->
|
||||
Stream.map(objects, fn {object, _} ->
|
||||
%{object_or_variable => object}
|
||||
end)
|
||||
|
||||
# object_or_variable is a object
|
||||
Map.has_key?(objects, object_or_variable) ->
|
||||
stream([%{}])
|
||||
# object_or_variable is a object
|
||||
Map.has_key?(objects, object_or_variable) ->
|
||||
to_stream([%{}])
|
||||
|
||||
# else
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
# else
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp solve_variables(var, val, {var, var, var}), do: {val, val, val}
|
||||
|
@ -171,5 +172,5 @@ defmodule RDF.Query.BGP.Stream do
|
|||
defp solve_variables(var, val, {s, p, var}), do: {s, p, val}
|
||||
defp solve_variables(_, _, pattern), do: pattern
|
||||
|
||||
defp stream(enum), do: Stream.into(enum, [])
|
||||
defp to_stream(enum), do: Stream.into(enum, [])
|
||||
end
|
||||
|
|
25
test/support/rdf_query_test_case.ex
Normal file
25
test/support/rdf_query_test_case.ex
Normal file
|
@ -0,0 +1,25 @@
|
|||
defmodule RDF.Query.Test.Case do
|
||||
use ExUnit.CaseTemplate
|
||||
|
||||
using do
|
||||
quote do
|
||||
use RDF.Test.Case
|
||||
|
||||
alias RDF.Query.BGP
|
||||
|
||||
import unquote(__MODULE__)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
alias RDF.Query.BGP
|
||||
|
||||
def bgp_struct(), do: %BGP{triple_patterns: []}
|
||||
def bgp_struct(triple_patterns) when is_list(triple_patterns),
|
||||
do: %BGP{triple_patterns: triple_patterns}
|
||||
def bgp_struct({_, _, _} = triple_pattern),
|
||||
do: %BGP{triple_patterns: [triple_pattern]}
|
||||
|
||||
def ok_bgp_struct(triple_patterns), do: {:ok, bgp_struct(triple_patterns)}
|
||||
|
||||
end
|
|
@ -1,10 +1,9 @@
|
|||
defmodule RDF.Query.BGP.QueryPlannerTest do
|
||||
use RDF.Test.Case
|
||||
use RDF.Query.Test.Case
|
||||
|
||||
alias RDF.Query.BGP.QueryPlanner
|
||||
|
||||
describe "query_plan/1" do
|
||||
|
||||
test "empty" do
|
||||
assert QueryPlanner.query_plan([]) == []
|
||||
end
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
defmodule RDF.Query.BGP.SimpleTest do
|
||||
use RDF.Test.Case
|
||||
use RDF.Query.Test.Case
|
||||
|
||||
alias RDF.Query.BGP
|
||||
import RDF.Query.BGP.Simple, only: [query: 2]
|
||||
import RDF.Query.BGP.Simple, only: [execute: 2]
|
||||
|
||||
@example_graph Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
|
@ -10,18 +9,12 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.s3, EX.p3, EX.o2}
|
||||
])
|
||||
|
||||
defp bgp(), do: %BGP{triple_patterns: []}
|
||||
defp bgp(triple_patterns) when is_list(triple_patterns),
|
||||
do: %BGP{triple_patterns: triple_patterns}
|
||||
defp bgp({_, _, _} = triple_pattern),
|
||||
do: %BGP{triple_patterns: [triple_pattern]}
|
||||
|
||||
test "empty bgp" do
|
||||
assert query(@example_graph, bgp()) == [%{}]
|
||||
assert bgp_struct() |> execute(@example_graph) == [%{}]
|
||||
end
|
||||
|
||||
test "single {s ?p ?o}" do
|
||||
assert query(@example_graph, bgp({EX.s1, :p, :o})) ==
|
||||
assert bgp_struct({EX.s1, :p, :o}) |> execute(@example_graph) ==
|
||||
[
|
||||
%{p: EX.p1, o: EX.o1},
|
||||
%{p: EX.p2, o: EX.o2}
|
||||
|
@ -29,7 +22,7 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
end
|
||||
|
||||
test "single {?s ?p o}" do
|
||||
assert query(@example_graph, bgp({:s, :p, EX.o2})) ==
|
||||
assert bgp_struct({:s, :p, EX.o2}) |> execute(@example_graph) ==
|
||||
[
|
||||
%{s: EX.s3, p: EX.p3},
|
||||
%{s: EX.s1, p: EX.p2}
|
||||
|
@ -37,12 +30,12 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
end
|
||||
|
||||
test "single {?s p ?o}" do
|
||||
assert query(@example_graph, bgp({:s, EX.p3, :o})) ==
|
||||
assert bgp_struct({:s, EX.p3, :o}) |> execute(@example_graph) ==
|
||||
[%{s: EX.s3, o: EX.o2}]
|
||||
end
|
||||
|
||||
test "with no solutions" do
|
||||
assert query(Graph.new(), bgp({:a, :b, :c})) == []
|
||||
assert bgp_struct({:a, :b, :c}) |> execute(Graph.new()) == []
|
||||
end
|
||||
|
||||
test "with solutions on one triple pattern but none on another one" do
|
||||
|
@ -51,10 +44,10 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.y, EX.y, EX.z},
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{:a, EX.p1, ~L"unmatched" },
|
||||
{:a, EX.y, EX.z}
|
||||
]) == []
|
||||
]) |> execute(example_graph) == []
|
||||
end
|
||||
|
||||
test "repeated variable: {?a ?a ?b}" do
|
||||
|
@ -64,7 +57,7 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({:a, :a, :b})) ==
|
||||
assert bgp_struct({:a, :a, :b}) |> execute(example_graph) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
|
@ -75,7 +68,7 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({:a, :b, :a})) ==
|
||||
assert bgp_struct({:a, :b, :a}) |> execute(example_graph) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
|
@ -86,7 +79,7 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({:b, :a, :a})) ==
|
||||
assert bgp_struct({:b, :a, :a}) |> execute(example_graph) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
|
@ -98,23 +91,24 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
{EX.y, EX.y, EX.y},
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({:a, :a, :a})) == [%{a: EX.y}]
|
||||
assert bgp_struct({:a, :a, :a}) |> execute(example_graph) == [%{a: EX.y}]
|
||||
end
|
||||
|
||||
test "two connected triple patterns with a match" do
|
||||
assert query(@example_graph, bgp [
|
||||
assert execute(bgp_struct([
|
||||
{EX.s1, :p, :o},
|
||||
{EX.s3, :p2, :o }
|
||||
]) == [%{
|
||||
]), @example_graph) == [%{
|
||||
p: EX.p2,
|
||||
p2: EX.p3,
|
||||
o: EX.o2
|
||||
}]
|
||||
|
||||
assert query(@example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, :p, :o1},
|
||||
{EX.s1, :p, :o2}
|
||||
]) ==
|
||||
])
|
||||
|> execute(@example_graph) ==
|
||||
[
|
||||
%{
|
||||
p: EX.p1,
|
||||
|
@ -128,31 +122,28 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
},
|
||||
]
|
||||
|
||||
assert query(
|
||||
Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
{EX.s3, EX.p2, EX.o2},
|
||||
{EX.s3, EX.p3, EX.o1}
|
||||
]),
|
||||
bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, EX.p1, :o},
|
||||
{EX.s3, :p, :o}
|
||||
]) == [%{p: EX.p3, o: EX.o1}]
|
||||
])
|
||||
|> execute(Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
{EX.s3, EX.p2, EX.o2},
|
||||
{EX.s3, EX.p3, EX.o1}
|
||||
])) == [%{p: EX.p3, o: EX.o1}]
|
||||
end
|
||||
|
||||
test "a triple pattern with dependent variables from separate triple patterns" do
|
||||
assert query(
|
||||
Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
{EX.s2, EX.p2, EX.o2},
|
||||
{EX.s3, EX.p2, EX.o1}
|
||||
]),
|
||||
bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, EX.p1, :o},
|
||||
{EX.s2, :p, EX.o2},
|
||||
{:s, :p, :o}
|
||||
]
|
||||
) == [
|
||||
])
|
||||
|> execute(Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
{EX.s2, EX.p2, EX.o2},
|
||||
{EX.s3, EX.p2, EX.o1}
|
||||
])) == [
|
||||
%{
|
||||
s: EX.s3,
|
||||
p: EX.p2,
|
||||
|
@ -162,21 +153,22 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
end
|
||||
|
||||
test "when no solutions" do
|
||||
assert query(@example_graph, bgp({EX.s, EX.p, :o})) == []
|
||||
assert bgp_struct({EX.s, EX.p, :o}) |> execute(@example_graph) == []
|
||||
end
|
||||
|
||||
test "multiple triple patterns with a constant unmatched triple has no solutions" do
|
||||
assert query(@example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, :p, :o},
|
||||
{EX.s, EX.p, EX.o}
|
||||
]) == []
|
||||
]) |> execute(@example_graph) == []
|
||||
end
|
||||
|
||||
test "independent triple patterns lead to cross-products" do
|
||||
assert query(@example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, :p1, :o},
|
||||
{:s, :p2, EX.o2}
|
||||
]) == [
|
||||
])
|
||||
|> execute(@example_graph) == [
|
||||
%{
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
|
@ -205,17 +197,19 @@ defmodule RDF.Query.BGP.SimpleTest do
|
|||
end
|
||||
|
||||
test "blank nodes behave like variables, but don't appear in the solution" do
|
||||
assert query(@example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, :p, RDF.bnode("o")},
|
||||
{EX.s3, :p2, RDF.bnode("o")}
|
||||
]) == [%{p: EX.p2, p2: EX.p3}]
|
||||
])
|
||||
|> execute(@example_graph) == [%{p: EX.p2, p2: EX.p3}]
|
||||
end
|
||||
|
||||
test "cross-product with blank nodes" do
|
||||
assert query(@example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, :p1, :o},
|
||||
{RDF.bnode("s"), :p2, EX.o2}
|
||||
]) ==
|
||||
])
|
||||
|> execute(@example_graph) ==
|
||||
[
|
||||
%{
|
||||
p1: EX.p1,
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
defmodule RDF.Query.BGP.StreamTest do
|
||||
use RDF.Test.Case
|
||||
use RDF.Query.Test.Case
|
||||
|
||||
alias RDF.Query.BGP
|
||||
import RDF.Query.BGP.Stream, only: [query: 2]
|
||||
import RDF.Query.BGP.Stream, only: [execute: 2]
|
||||
|
||||
@example_graph Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
|
@ -10,40 +9,34 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.s3, EX.p3, EX.o2}
|
||||
])
|
||||
|
||||
defp bgp(), do: %BGP{triple_patterns: []}
|
||||
defp bgp(triple_patterns) when is_list(triple_patterns),
|
||||
do: %BGP{triple_patterns: triple_patterns}
|
||||
defp bgp({_, _, _} = triple_pattern),
|
||||
do: %BGP{triple_patterns: [triple_pattern]}
|
||||
|
||||
|
||||
test "empty bgp" do
|
||||
assert query(@example_graph, bgp()) == [%{}]
|
||||
assert bgp_struct() |> execute(@example_graph) == [%{}]
|
||||
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 bgp_struct({EX.s1, :p, :o}) |> execute(@example_graph) ==
|
||||
[
|
||||
%{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 bgp_struct({:s, :p, EX.o2}) |> execute(@example_graph) ==
|
||||
[
|
||||
%{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})) ==
|
||||
assert bgp_struct({:s, EX.p3, :o}) |> execute(@example_graph) ==
|
||||
[%{s: EX.s3, o: EX.o2}]
|
||||
end
|
||||
|
||||
test "with no solutions" do
|
||||
assert query(Graph.new(), bgp({:a, :b, :c})) == []
|
||||
assert bgp_struct({:a, :b, :c}) |> execute(Graph.new()) == []
|
||||
end
|
||||
|
||||
test "with solutions on one triple pattern but none on another one" do
|
||||
|
@ -52,10 +45,10 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.y, EX.y, EX.z},
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{:a, EX.p1, ~L"unmatched" },
|
||||
{:a, EX.y, EX.z}
|
||||
]) == []
|
||||
]) |> execute(example_graph) == []
|
||||
end
|
||||
|
||||
test "repeated variable: {?a ?a ?b}" do
|
||||
|
@ -65,7 +58,7 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({:a, :a, :b})) ==
|
||||
assert bgp_struct({:a, :a, :b}) |> execute(example_graph) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
|
@ -76,7 +69,7 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({:a, :b, :a})) ==
|
||||
assert bgp_struct({:a, :b, :a}) |> execute(example_graph) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
|
@ -87,7 +80,7 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.y, EX.x, EX.y}
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({:b, :a, :a})) ==
|
||||
assert bgp_struct({:b, :a, :a}) |> execute(example_graph) ==
|
||||
[%{a: EX.y, b: EX.x}]
|
||||
end
|
||||
|
||||
|
@ -99,61 +92,59 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
{EX.y, EX.y, EX.y},
|
||||
])
|
||||
|
||||
assert query(example_graph, bgp({:a, :a, :a})) == [%{a: EX.y}]
|
||||
assert bgp_struct({:a, :a, :a}) |> execute(example_graph) == [%{a: EX.y}]
|
||||
end
|
||||
|
||||
test "two connected triple patterns with a match" do
|
||||
assert query(@example_graph, bgp [
|
||||
assert execute(bgp_struct([
|
||||
{EX.s1, :p, :o},
|
||||
{EX.s3, :p2, :o }
|
||||
]) == [%{
|
||||
]), @example_graph) == [%{
|
||||
p: EX.p2,
|
||||
p2: EX.p3,
|
||||
o: EX.o2
|
||||
}]
|
||||
|
||||
assert query(@example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{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,
|
||||
},
|
||||
]
|
||||
])
|
||||
|> execute(@example_graph) ==
|
||||
[
|
||||
%{
|
||||
p: EX.p1,
|
||||
o1: EX.o1,
|
||||
o2: EX.o1,
|
||||
},
|
||||
%{
|
||||
p: EX.p2,
|
||||
o1: EX.o2,
|
||||
o2: EX.o2,
|
||||
},
|
||||
]
|
||||
|
||||
assert query(
|
||||
Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
{EX.s3, EX.p2, EX.o2},
|
||||
{EX.s3, EX.p3, EX.o1}
|
||||
]),
|
||||
bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, EX.p1, :o},
|
||||
{EX.s3, :p, :o}
|
||||
]) == [%{p: EX.p3, o: EX.o1}]
|
||||
])
|
||||
|> execute(Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
{EX.s3, EX.p2, EX.o2},
|
||||
{EX.s3, EX.p3, EX.o1}
|
||||
])) == [%{p: EX.p3, o: EX.o1}]
|
||||
end
|
||||
|
||||
test "a triple pattern with dependent variables from separate triple patterns" do
|
||||
assert query(
|
||||
Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
{EX.s2, EX.p2, EX.o2},
|
||||
{EX.s3, EX.p2, EX.o1}
|
||||
]),
|
||||
bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, EX.p1, :o},
|
||||
{EX.s2, :p, EX.o2},
|
||||
{:s, :p, :o}
|
||||
]
|
||||
) == [
|
||||
])
|
||||
|> execute(Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
{EX.s2, EX.p2, EX.o2},
|
||||
{EX.s3, EX.p2, EX.o1}
|
||||
])) == [
|
||||
%{
|
||||
s: EX.s3,
|
||||
p: EX.p2,
|
||||
|
@ -163,24 +154,24 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
end
|
||||
|
||||
test "when no solutions" do
|
||||
assert query(@example_graph, bgp({EX.s, EX.p, :o})) == []
|
||||
assert bgp_struct({EX.s, EX.p, :o}) |> execute(@example_graph) == []
|
||||
end
|
||||
|
||||
test "multiple triple patterns with a constant unmatched triple has no solutions" do
|
||||
assert query(@example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, :p, :o},
|
||||
{EX.s, EX.p, EX.o}
|
||||
]) == []
|
||||
]) |> execute(@example_graph) == []
|
||||
end
|
||||
|
||||
test "independent triple patterns lead to cross-products" do
|
||||
assert MapSet.new(
|
||||
query(@example_graph, bgp [
|
||||
{EX.s1, :p1, :o},
|
||||
{:s, :p2, EX.o2}
|
||||
])
|
||||
) == MapSet.new([
|
||||
%{
|
||||
assert bgp_struct([
|
||||
{EX.s1, :p1, :o},
|
||||
{:s, :p2, EX.o2}
|
||||
])
|
||||
|> execute(@example_graph)
|
||||
|> MapSet.new() == MapSet.new([
|
||||
%{
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
s: EX.s3,
|
||||
|
@ -208,20 +199,21 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
end
|
||||
|
||||
test "blank nodes behave like variables, but don't appear in the solution" do
|
||||
assert query(@example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, :p, RDF.bnode("o")},
|
||||
{EX.s3, :p2, RDF.bnode("o")}
|
||||
]) == [%{p: EX.p2, p2: EX.p3}]
|
||||
])
|
||||
|> execute(@example_graph) == [%{p: EX.p2, p2: EX.p3}]
|
||||
end
|
||||
|
||||
test "cross-product with blank nodes" do
|
||||
assert MapSet.new(
|
||||
query(@example_graph, bgp [
|
||||
assert bgp_struct([
|
||||
{EX.s1, :p1, :o},
|
||||
{RDF.bnode("s"), :p2, EX.o2}
|
||||
])
|
||||
) ==
|
||||
MapSet.new([
|
||||
])
|
||||
|> execute(@example_graph)
|
||||
|> MapSet.new() == MapSet.new(
|
||||
[
|
||||
%{
|
||||
p1: EX.p1,
|
||||
o: EX.o1,
|
||||
|
@ -242,6 +234,7 @@ defmodule RDF.Query.BGP.StreamTest do
|
|||
o: EX.o2,
|
||||
p2: EX.p2,
|
||||
},
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,37 +1,34 @@
|
|||
defmodule RDF.Query.BuilderTest do
|
||||
use RDF.Test.Case
|
||||
use RDF.Query.Test.Case
|
||||
|
||||
alias RDF.Query.{BGP, Builder}
|
||||
|
||||
defp bgp(triple_patterns) when is_list(triple_patterns),
|
||||
do: {:ok, %BGP{triple_patterns: triple_patterns}}
|
||||
alias RDF.Query.Builder
|
||||
|
||||
describe "new/1" do
|
||||
test "empty triple pattern" do
|
||||
assert Builder.bgp([]) == bgp([])
|
||||
assert Builder.bgp([]) == ok_bgp_struct([])
|
||||
end
|
||||
|
||||
test "one triple pattern doesn't require list brackets" do
|
||||
assert Builder.bgp({EX.s, EX.p, EX.o}) ==
|
||||
bgp [{EX.s, EX.p, EX.o}]
|
||||
ok_bgp_struct [{EX.s, EX.p, EX.o}]
|
||||
end
|
||||
|
||||
test "variables" do
|
||||
assert Builder.bgp([{:s?, :p?, :o?}]) == bgp [{:s, :p, :o}]
|
||||
assert Builder.bgp([{:s?, :p?, :o?}]) == ok_bgp_struct [{:s, :p, :o}]
|
||||
end
|
||||
|
||||
test "blank nodes" do
|
||||
assert Builder.bgp([{RDF.bnode("s"), RDF.bnode("p"), RDF.bnode("o")}]) ==
|
||||
bgp [{RDF.bnode("s"), RDF.bnode("p"), RDF.bnode("o")}]
|
||||
ok_bgp_struct [{RDF.bnode("s"), RDF.bnode("p"), RDF.bnode("o")}]
|
||||
end
|
||||
|
||||
test "blank nodes as atoms" do
|
||||
assert Builder.bgp([{:_s, :_p, :_o}]) ==
|
||||
bgp [{RDF.bnode("s"), RDF.bnode("p"), RDF.bnode("o")}]
|
||||
ok_bgp_struct [{RDF.bnode("s"), RDF.bnode("p"), RDF.bnode("o")}]
|
||||
end
|
||||
|
||||
test "variable notation has precedence over blank node notation" do
|
||||
assert Builder.bgp([{:_s?, :_p?, :_o?}]) == bgp [{:_s, :_p, :_o}]
|
||||
assert Builder.bgp([{:_s?, :_p?, :_o?}]) == ok_bgp_struct [{:_s, :_p, :_o}]
|
||||
end
|
||||
|
||||
test "IRIs" do
|
||||
|
@ -39,26 +36,26 @@ defmodule RDF.Query.BuilderTest do
|
|||
RDF.iri("http://example.com/s"),
|
||||
RDF.iri("http://example.com/p"),
|
||||
RDF.iri("http://example.com/o")}]
|
||||
) == bgp [{EX.s, EX.p, EX.o}]
|
||||
) == ok_bgp_struct [{EX.s, EX.p, EX.o}]
|
||||
|
||||
assert Builder.bgp([{
|
||||
~I<http://example.com/s>,
|
||||
~I<http://example.com/p>,
|
||||
~I<http://example.com/o>}]
|
||||
) == bgp [{EX.s, EX.p, EX.o}]
|
||||
) == ok_bgp_struct [{EX.s, EX.p, EX.o}]
|
||||
|
||||
assert Builder.bgp([{EX.s, EX.p, EX.o}]) ==
|
||||
bgp [{EX.s, EX.p, EX.o}]
|
||||
ok_bgp_struct [{EX.s, EX.p, EX.o}]
|
||||
end
|
||||
|
||||
test "vocabulary term atoms" do
|
||||
assert Builder.bgp([{EX.S, EX.P, EX.O}]) ==
|
||||
bgp [{RDF.iri(EX.S), RDF.iri(EX.P), RDF.iri(EX.O)}]
|
||||
ok_bgp_struct [{RDF.iri(EX.S), RDF.iri(EX.P), RDF.iri(EX.O)}]
|
||||
end
|
||||
|
||||
test "special :a atom for rdf:type" do
|
||||
assert Builder.bgp([{EX.S, :a, EX.O}]) ==
|
||||
bgp [{RDF.iri(EX.S), RDF.type, RDF.iri(EX.O)}]
|
||||
ok_bgp_struct [{RDF.iri(EX.S), RDF.type, RDF.iri(EX.O)}]
|
||||
end
|
||||
|
||||
test "URIs" do
|
||||
|
@ -66,21 +63,21 @@ defmodule RDF.Query.BuilderTest do
|
|||
URI.parse("http://example.com/s"),
|
||||
URI.parse("http://example.com/p"),
|
||||
URI.parse("http://example.com/o")}]
|
||||
) == bgp [{EX.s, EX.p, EX.o}]
|
||||
) == ok_bgp_struct [{EX.s, EX.p, EX.o}]
|
||||
end
|
||||
|
||||
test "literals" do
|
||||
assert Builder.bgp([{EX.s, EX.p, ~L"foo"}]) ==
|
||||
bgp [{EX.s, EX.p, ~L"foo"}]
|
||||
ok_bgp_struct [{EX.s, EX.p, ~L"foo"}]
|
||||
end
|
||||
|
||||
test "values coercible to literals" do
|
||||
assert Builder.bgp([{EX.s, EX.p, "foo"}]) ==
|
||||
bgp [{EX.s, EX.p, ~L"foo"}]
|
||||
ok_bgp_struct [{EX.s, EX.p, ~L"foo"}]
|
||||
assert Builder.bgp([{EX.s, EX.p, 42}]) ==
|
||||
bgp [{EX.s, EX.p, RDF.literal(42)}]
|
||||
ok_bgp_struct [{EX.s, EX.p, RDF.literal(42)}]
|
||||
assert Builder.bgp([{EX.s, EX.p, true}]) ==
|
||||
bgp [{EX.s, EX.p, XSD.true}]
|
||||
ok_bgp_struct [{EX.s, EX.p, XSD.true}]
|
||||
end
|
||||
|
||||
test "literals on non-object positions" do
|
||||
|
@ -93,7 +90,7 @@ defmodule RDF.Query.BuilderTest do
|
|||
{EX.S, EX.p, :o?},
|
||||
{:o?, EX.p2, 42}
|
||||
]) ==
|
||||
bgp [
|
||||
ok_bgp_struct [
|
||||
{RDF.iri(EX.S), EX.p, :o},
|
||||
{:o, EX.p2, RDF.literal(42)}
|
||||
]
|
||||
|
@ -101,19 +98,19 @@ defmodule RDF.Query.BuilderTest do
|
|||
|
||||
test "multiple objects to the same subject-predicate" do
|
||||
assert Builder.bgp([{EX.s, EX.p, EX.o1, EX.o2}]) ==
|
||||
bgp [
|
||||
ok_bgp_struct [
|
||||
{EX.s, EX.p, EX.o1},
|
||||
{EX.s, EX.p, EX.o2}
|
||||
]
|
||||
|
||||
assert Builder.bgp({EX.s, EX.p, EX.o1, EX.o2}) ==
|
||||
bgp [
|
||||
ok_bgp_struct [
|
||||
{EX.s, EX.p, EX.o1},
|
||||
{EX.s, EX.p, EX.o2}
|
||||
]
|
||||
|
||||
assert Builder.bgp({EX.s, EX.p, :o?, false, 42, "foo"}) ==
|
||||
bgp [
|
||||
ok_bgp_struct [
|
||||
{EX.s, EX.p, :o},
|
||||
{EX.s, EX.p, XSD.false},
|
||||
{EX.s, EX.p, RDF.literal(42)},
|
||||
|
@ -127,7 +124,7 @@ defmodule RDF.Query.BuilderTest do
|
|||
[EX.p1, EX.o1],
|
||||
[EX.p2, EX.o2],
|
||||
}]) ==
|
||||
bgp [
|
||||
ok_bgp_struct [
|
||||
{EX.s, EX.p1, EX.o1},
|
||||
{EX.s, EX.p2, EX.o2}
|
||||
]
|
||||
|
@ -138,7 +135,7 @@ defmodule RDF.Query.BuilderTest do
|
|||
[EX.p1, 42, 3.14],
|
||||
[EX.p2, "foo", true],
|
||||
}]) ==
|
||||
bgp [
|
||||
ok_bgp_struct [
|
||||
{EX.s, RDF.type, :o},
|
||||
{EX.s, EX.p1, RDF.literal(42)},
|
||||
{EX.s, EX.p1, RDF.literal(3.14)},
|
||||
|
@ -147,26 +144,26 @@ defmodule RDF.Query.BuilderTest do
|
|||
]
|
||||
|
||||
assert Builder.bgp([{EX.s, [EX.p, EX.o]}]) ==
|
||||
bgp [{EX.s, EX.p, EX.o}]
|
||||
ok_bgp_struct [{EX.s, EX.p, EX.o}]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "path/2" do
|
||||
test "element count == 3" do
|
||||
assert Builder.path([EX.s, EX.p, EX.o]) == bgp [{EX.s, EX.p, EX.o}]
|
||||
assert Builder.path([:s?, :p?, :o?]) == bgp [{:s, :p, :o}]
|
||||
assert Builder.path([EX.s, EX.p, EX.o]) == ok_bgp_struct [{EX.s, EX.p, EX.o}]
|
||||
assert Builder.path([:s?, :p?, :o?]) == ok_bgp_struct [{:s, :p, :o}]
|
||||
end
|
||||
|
||||
test "element count > 3" do
|
||||
assert Builder.path([EX.s, EX.p1, EX.p2, EX.o]) ==
|
||||
bgp [
|
||||
ok_bgp_struct [
|
||||
{EX.s, EX.p1, RDF.bnode("0")},
|
||||
{RDF.bnode("0"), EX.p2, EX.o},
|
||||
]
|
||||
|
||||
assert Builder.path([:s?, :p1?, :p2?, :o?]) ==
|
||||
bgp [
|
||||
ok_bgp_struct [
|
||||
{:s, :p1, RDF.bnode("0")},
|
||||
{RDF.bnode("0"), :p2, :o},
|
||||
]
|
||||
|
@ -180,7 +177,7 @@ defmodule RDF.Query.BuilderTest do
|
|||
|
||||
test "with_elements: true" do
|
||||
assert Builder.path([EX.s, EX.p1, EX.p2, :o?], with_elements: true) ==
|
||||
bgp [
|
||||
ok_bgp_struct [
|
||||
{EX.s, EX.p1, :el0},
|
||||
{:el0, EX.p2, :o},
|
||||
]
|
||||
|
|
27
test/unit/query/query_test.exs
Normal file
27
test/unit/query/query_test.exs
Normal file
|
@ -0,0 +1,27 @@
|
|||
defmodule RDF.QueryTest do
|
||||
use RDF.Query.Test.Case
|
||||
|
||||
@example_graph Graph.new([
|
||||
{EX.s1, EX.p1, EX.o1},
|
||||
{EX.s1, EX.p2, EX.o2},
|
||||
{EX.s3, EX.p3, EX.o2}
|
||||
])
|
||||
|
||||
@example_query [{:s?, :p?, EX.o2}]
|
||||
|
||||
test "execute/2" do
|
||||
assert RDF.Query.execute(RDF.Query.bgp(@example_query), @example_graph) ==
|
||||
BGP.Stream.execute(RDF.Query.bgp(@example_query), @example_graph)
|
||||
|
||||
assert RDF.Query.execute(@example_query, @example_graph) ==
|
||||
BGP.Stream.execute(RDF.Query.bgp(@example_query), @example_graph)
|
||||
end
|
||||
|
||||
test "stream/2" do
|
||||
assert RDF.Query.stream(RDF.Query.bgp(@example_query), @example_graph) ==
|
||||
BGP.Stream.stream(RDF.Query.bgp(@example_query), @example_graph)
|
||||
|
||||
assert RDF.Query.stream(@example_query, @example_graph) ==
|
||||
BGP.Stream.stream(RDF.Query.bgp(@example_query), @example_graph)
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue