core: RDF.Dataset
This commit is contained in:
parent
a32ca93f4c
commit
e5c8043cc2
8 changed files with 1066 additions and 20 deletions
515
lib/rdf/dataset.ex
Normal file
515
lib/rdf/dataset.ex
Normal file
|
@ -0,0 +1,515 @@
|
|||
defmodule RDF.Dataset do
|
||||
@moduledoc """
|
||||
Defines a RDF Dataset.
|
||||
|
||||
A `RDF.Dataset` represents a set of `RDF.Dataset`s.
|
||||
"""
|
||||
defstruct name: nil, graphs: %{}
|
||||
|
||||
@behaviour Access
|
||||
|
||||
alias RDF.{Quad, Graph, Description}
|
||||
|
||||
@type t :: module
|
||||
|
||||
|
||||
@doc """
|
||||
Creates an empty unnamed `RDF.Dataset`.
|
||||
"""
|
||||
def new,
|
||||
do: %RDF.Dataset{}
|
||||
|
||||
@doc """
|
||||
Creates an unnamed `RDF.Dataset` with an initial statement.
|
||||
"""
|
||||
def new(statement) when is_tuple(statement),
|
||||
do: new() |> add(statement)
|
||||
|
||||
@doc """
|
||||
Creates an unnamed `RDF.Dataset` with initial statements.
|
||||
"""
|
||||
def new(statements) when is_list(statements),
|
||||
do: new() |> add(statements)
|
||||
|
||||
@doc """
|
||||
Creates an unnamed `RDF.Dataset` with a `RDF.Description`.
|
||||
"""
|
||||
def new(%RDF.Description{} = description),
|
||||
do: new() |> add(description)
|
||||
|
||||
@doc """
|
||||
Creates an empty named `RDF.Dataset`.
|
||||
"""
|
||||
def new(name),
|
||||
do: %RDF.Dataset{name: RDF.uri(name)}
|
||||
|
||||
@doc """
|
||||
Creates a named `RDF.Dataset` with an initial statement.
|
||||
"""
|
||||
def new(name, statement) when is_tuple(statement),
|
||||
do: new(name) |> add(statement)
|
||||
|
||||
@doc """
|
||||
Creates a named `RDF.Dataset` with initial statements.
|
||||
"""
|
||||
def new(name, statements) when is_list(statements),
|
||||
do: new(name) |> add(statements)
|
||||
|
||||
@doc """
|
||||
Creates a named `RDF.Dataset` with a `RDF.Description`.
|
||||
"""
|
||||
def new(name, %RDF.Description{} = description),
|
||||
do: new(name) |> add(description)
|
||||
|
||||
|
||||
|
||||
@doc """
|
||||
Adds triples and quads to a `RDF.Dataset`.
|
||||
"""
|
||||
def add(dataset, statements, graph_context \\ nil)
|
||||
|
||||
def add(dataset, statements, graph_context) when is_list(statements) do
|
||||
with graph_context = Quad.convert_graph_context(graph_context) do
|
||||
Enum.reduce statements, dataset, fn (statement, dataset) ->
|
||||
add(dataset, statement, graph_context)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add(dataset, {subject, predicate, objects}, graph_context),
|
||||
do: add(dataset, {subject, predicate, objects, graph_context})
|
||||
|
||||
def add(%RDF.Dataset{name: name, graphs: graphs},
|
||||
{subject, predicate, objects, graph_context}, _) do
|
||||
with graph_context = Quad.convert_graph_context(graph_context) do
|
||||
updated_graphs =
|
||||
Map.update(graphs, graph_context,
|
||||
Graph.new(graph_context, {subject, predicate, objects}),
|
||||
fn graph -> Graph.add(graph, {subject, predicate, objects}) end)
|
||||
%RDF.Dataset{name: name, graphs: updated_graphs}
|
||||
end
|
||||
end
|
||||
|
||||
def add(%RDF.Dataset{name: name, graphs: graphs},
|
||||
%Description{} = description, graph_context) do
|
||||
with graph_context = Quad.convert_graph_context(graph_context) do
|
||||
updated_graph =
|
||||
Map.get(graphs, graph_context, Graph.new(graph_context))
|
||||
|> Graph.add(description)
|
||||
%RDF.Dataset{
|
||||
name: name,
|
||||
graphs: Map.put(graphs, graph_context, updated_graph)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
Adds statements to a `RDF.Dataset` and overwrites all existing statements with the same subjects and predicates in the specified graph context.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> dataset = RDF.Dataset.new({EX.S, EX.P1, EX.O1})
|
||||
...> RDF.Dataset.put(dataset, {EX.S, EX.P1, EX.O2})
|
||||
RDF.Dataset.new({EX.S, EX.P1, EX.O2})
|
||||
iex> RDF.Dataset.put(dataset, {EX.S, EX.P2, EX.O2})
|
||||
RDF.Dataset.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}])
|
||||
iex> RDF.Dataset.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}]) |>
|
||||
...> RDF.Dataset.put([{EX.S1, EX.P2, EX.O3}, {EX.S2, EX.P2, EX.O3}])
|
||||
RDF.Dataset.new([{EX.S1, EX.P1, EX.O1}, {EX.S1, EX.P2, EX.O3}, {EX.S2, EX.P2, EX.O3}])
|
||||
|
||||
Note: When using a map to pass the statements you'll have to take care for yourselve to
|
||||
avoid using subject key clashes due to using inconsistent, semantically equivalent forms.
|
||||
|
||||
iex> RDF.Dataset.put(RDF.Dataset.new, %{
|
||||
...> EX.S => [{EX.P, EX.O1}],
|
||||
...> {EX.S, nil} => [{EX.P, EX.O2}]})
|
||||
RDF.Dataset.new({EX.S, EX.P, EX.O2})
|
||||
|
||||
The last always always wins in these cases. This decision was made to mitigate
|
||||
performance drawbacks. The list form will always take care of this for you:
|
||||
|
||||
iex> RDF.Dataset.put(RDF.Dataset.new, [
|
||||
...> {EX.S, EX.P, EX.O1},
|
||||
...> {EX.S, EX.P, EX.O2, nil}])
|
||||
RDF.Dataset.new({EX.S, EX.P, EX.O1}, {EX.S, EX.P, EX.O2})
|
||||
"""
|
||||
def put(dataset, statements, graph_context \\ nil)
|
||||
|
||||
def put(%RDF.Dataset{} = dataset, {subject, predicate, objects}, graph_context),
|
||||
do: put(dataset, {subject, predicate, objects, graph_context})
|
||||
|
||||
def put(%RDF.Dataset{name: name, graphs: graphs},
|
||||
{subject, predicate, objects, graph_context}, _) do
|
||||
with graph_context = Quad.convert_graph_context(graph_context) do
|
||||
new_graph =
|
||||
case graphs[graph_context] do
|
||||
graph = %Graph{} ->
|
||||
Graph.put(graph, {subject, predicate, objects})
|
||||
nil ->
|
||||
Graph.new(graph_context, {subject, predicate, objects})
|
||||
end
|
||||
%RDF.Dataset{name: name,
|
||||
graphs: Map.put(graphs, graph_context, new_graph)}
|
||||
end
|
||||
end
|
||||
|
||||
def put(%RDF.Dataset{} = dataset, statements, graph_context)
|
||||
when is_list(statements) do
|
||||
with graph_context = Quad.convert_graph_context(graph_context) do
|
||||
put dataset, Enum.group_by(statements,
|
||||
fn
|
||||
{s, _, _, nil} -> s
|
||||
{s, _, _, c} -> {s, c}
|
||||
{s, _, _} when is_nil(graph_context) -> s
|
||||
{s, _, _} -> {s, graph_context}
|
||||
end,
|
||||
fn
|
||||
{_, p, o, _} -> {p, o}
|
||||
{_, p, o} -> {p, o}
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
def put(%RDF.Dataset{name: name, graphs: graphs},
|
||||
%Description{} = description, graph_context) do
|
||||
with graph_context = Quad.convert_graph_context(graph_context) do
|
||||
updated_graph =
|
||||
Map.get(graphs, graph_context, Graph.new(graph_context))
|
||||
|> Graph.put(description)
|
||||
%RDF.Dataset{
|
||||
name: name,
|
||||
graphs: Map.put(graphs, graph_context, updated_graph)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def put(%RDF.Dataset{} = dataset, statements, graph_context)
|
||||
when is_map(statements) do
|
||||
with graph_context = Quad.convert_graph_context(graph_context) do
|
||||
Enum.reduce statements, dataset,
|
||||
fn ({subject_with_context, predications}, dataset) ->
|
||||
put(dataset, subject_with_context, predications, graph_context)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def put(%RDF.Dataset{name: name, graphs: graphs},
|
||||
{subject, graph_context}, predications, default_graph_context)
|
||||
when is_list(predications) do
|
||||
with graph_context = graph_context || default_graph_context,
|
||||
graph_context = Quad.convert_graph_context(graph_context) do
|
||||
graph = Map.get(graphs, graph_context, Graph.new(graph_context))
|
||||
new_graphs = graphs
|
||||
|> Map.put(graph_context, Graph.put(graph, subject, predications))
|
||||
%RDF.Dataset{name: name, graphs: new_graphs}
|
||||
end
|
||||
end
|
||||
|
||||
def put(%RDF.Dataset{} = dataset, subject, predications, graph_context)
|
||||
when is_list(predications),
|
||||
do: put(dataset, {subject, graph_context}, predications, graph_context)
|
||||
|
||||
|
||||
|
||||
@doc """
|
||||
Fetches the `RDF.Graph` with the given name.
|
||||
|
||||
When a graph with the given name can not be found can not be found `:error` is returned.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> dataset = RDF.Dataset.new([{EX.S1, EX.P1, EX.O1, EX.Graph}, {EX.S2, EX.P2, EX.O2}])
|
||||
...> RDF.Dataset.fetch(dataset, EX.Graph)
|
||||
{:ok, RDF.Graph.new(EX.Graph, {EX.S1, EX.P1, EX.O1})}
|
||||
iex> RDF.Dataset.fetch(dataset, nil)
|
||||
{:ok, RDF.Graph.new({EX.S2, EX.P2, EX.O2})}
|
||||
iex> RDF.Dataset.fetch(dataset, EX.Foo)
|
||||
:error
|
||||
"""
|
||||
def fetch(%RDF.Dataset{graphs: graphs}, graph_name) do
|
||||
Access.fetch(graphs, Quad.convert_graph_context(graph_name))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Fetches the `RDF.Graph` with the given name.
|
||||
|
||||
When a graph with the given name can not be found can not be found the optionally
|
||||
given default value or `nil` is returned
|
||||
|
||||
# Examples
|
||||
|
||||
iex> dataset = RDF.Dataset.new([{EX.S1, EX.P1, EX.O1, EX.Graph}, {EX.S2, EX.P2, EX.O2}])
|
||||
...> RDF.Dataset.get(dataset, EX.Graph)
|
||||
RDF.Graph.new(EX.Graph, {EX.S1, EX.P1, EX.O1})
|
||||
iex> RDF.Dataset.get(dataset, nil)
|
||||
RDF.Graph.new({EX.S2, EX.P2, EX.O2})
|
||||
iex> RDF.Dataset.get(dataset, EX.Foo)
|
||||
nil
|
||||
iex> RDF.Dataset.get(dataset, EX.Foo, :bar)
|
||||
:bar
|
||||
"""
|
||||
def get(%RDF.Dataset{} = dataset, graph_name, default \\ nil) do
|
||||
case fetch(dataset, graph_name) do
|
||||
{:ok, value} -> value
|
||||
:error -> default
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
The default graph of a `RDF.Dataset`.
|
||||
"""
|
||||
def default_graph(%RDF.Dataset{graphs: graphs}),
|
||||
do: Map.get(graphs, nil, Graph.new)
|
||||
|
||||
|
||||
@doc """
|
||||
Gets and updates the graph with the given name, in a single pass.
|
||||
|
||||
Invokes the passed function on the `RDF.Graph` with the given name;
|
||||
this function should return either `{graph_to_return, new_graph}` or `:pop`.
|
||||
|
||||
If the passed function returns `{graph_to_return, new_graph}`, the
|
||||
return value of `get_and_update` is `{graph_to_return, new_dataset}` where
|
||||
`new_dataset` is the input `Dataset` updated with `new_graph` for
|
||||
the given name.
|
||||
|
||||
If the passed function returns `:pop` the graph with the given name is
|
||||
removed and a `{removed_graph, new_dataset}` tuple gets returned.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> dataset = RDF.Dataset.new({EX.S, EX.P, EX.O, EX.Graph})
|
||||
...> RDF.Dataset.get_and_update(dataset, EX.Graph, fn current_graph ->
|
||||
...> {current_graph, {EX.S, EX.P, EX.NEW}}
|
||||
...> end)
|
||||
{RDF.Graph.new(EX.Graph, {EX.S, EX.P, EX.O}), RDF.Dataset.new({EX.S, EX.P, EX.NEW, EX.Graph})}
|
||||
"""
|
||||
def get_and_update(%RDF.Dataset{} = dataset, graph_name, fun) do
|
||||
with graph_context = Quad.convert_graph_context(graph_name) do
|
||||
case fun.(get(dataset, graph_context)) do
|
||||
{old_graph, new_graph} ->
|
||||
{old_graph, put(dataset, new_graph, graph_context)}
|
||||
:pop ->
|
||||
pop(dataset, graph_context)
|
||||
other ->
|
||||
raise "the given function must return a two-element tuple or :pop, got: #{inspect(other)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Pops the graph with the given name.
|
||||
|
||||
When a graph with given name can not be found the optionally given default value
|
||||
or `nil` is returned.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> dataset = RDF.Dataset.new([
|
||||
...> {EX.S1, EX.P1, EX.O1, EX.Graph},
|
||||
...> {EX.S2, EX.P2, EX.O2}])
|
||||
...> RDF.Dataset.pop(dataset, EX.Graph)
|
||||
{RDF.Graph.new(EX.Graph, {EX.S1, EX.P1, EX.O1}), RDF.Dataset.new({EX.S2, EX.P2, EX.O2})}
|
||||
iex> RDF.Dataset.pop(dataset, EX.Foo)
|
||||
{nil, dataset}
|
||||
"""
|
||||
def pop(%RDF.Dataset{name: name, graphs: graphs} = dataset, graph_name) do
|
||||
case Access.pop(graphs, Quad.convert_graph_context(graph_name)) do
|
||||
{nil, _} ->
|
||||
{nil, dataset}
|
||||
{graph, new_graphs} ->
|
||||
{graph, %RDF.Dataset{name: name, graphs: new_graphs}}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@doc """
|
||||
The number of statements within a `RDF.Dataset`.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Dataset.new([
|
||||
...> {EX.S1, EX.p1, EX.O1, EX.Graph},
|
||||
...> {EX.S2, EX.p2, EX.O2},
|
||||
...> {EX.S1, EX.p2, EX.O3}]) |>
|
||||
...> RDF.Dataset.statement_count
|
||||
3
|
||||
"""
|
||||
def statement_count(%RDF.Dataset{graphs: graphs}) do
|
||||
Enum.reduce graphs, 0, fn ({_, graph}, count) ->
|
||||
count + Graph.triple_count(graph)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
The set of all subjects used in the statement within all graphs of a `RDF.Dataset`.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Dataset.new([
|
||||
...> {EX.S1, EX.p1, EX.O1, EX.Graph},
|
||||
...> {EX.S2, EX.p2, EX.O2},
|
||||
...> {EX.S1, EX.p2, EX.O3}]) |>
|
||||
...> RDF.Dataset.subjects
|
||||
MapSet.new([RDF.uri(EX.S1), RDF.uri(EX.S2)])
|
||||
"""
|
||||
def subjects(%RDF.Dataset{graphs: graphs}) do
|
||||
Enum.reduce graphs, MapSet.new, fn ({_, graph}, subjects) ->
|
||||
MapSet.union(subjects, Graph.subjects(graph))
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
The set of all properties used in the predicates within all graphs of a `RDF.Dataset`.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Dataset.new([
|
||||
...> {EX.S1, EX.p1, EX.O1, EX.Graph},
|
||||
...> {EX.S2, EX.p2, EX.O2},
|
||||
...> {EX.S1, EX.p2, EX.O3}]) |>
|
||||
...> RDF.Dataset.predicates
|
||||
MapSet.new([EX.p1, EX.p2])
|
||||
"""
|
||||
def predicates(%RDF.Dataset{graphs: graphs}) do
|
||||
Enum.reduce graphs, MapSet.new, fn ({_, graph}, predicates) ->
|
||||
MapSet.union(predicates, Graph.predicates(graph))
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
The set of all resources used in the objects within a `RDF.Dataset`.
|
||||
|
||||
Note: This function does collect only URIs and BlankNodes, not Literals.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Dataset.new([
|
||||
...> {EX.S1, EX.p1, EX.O1, EX.Graph},
|
||||
...> {EX.S2, EX.p2, EX.O2, EX.Graph},
|
||||
...> {EX.S3, EX.p1, EX.O2},
|
||||
...> {EX.S4, EX.p2, RDF.bnode(:bnode)},
|
||||
...> {EX.S5, EX.p3, "foo"}
|
||||
...> ]) |> RDF.Dataset.objects
|
||||
MapSet.new([RDF.uri(EX.O1), RDF.uri(EX.O2), RDF.bnode(:bnode)])
|
||||
"""
|
||||
def objects(%RDF.Dataset{graphs: graphs}) do
|
||||
Enum.reduce graphs, MapSet.new, fn ({_, graph}, objects) ->
|
||||
MapSet.union(objects, Graph.objects(graph))
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
The set of all resources used within a `RDF.Dataset`.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Dataset.new([
|
||||
...> {EX.S1, EX.p1, EX.O1, EX.Graph},
|
||||
...> {EX.S2, EX.p1, EX.O2, EX.Graph},
|
||||
...> {EX.S2, EX.p2, RDF.bnode(:bnode)},
|
||||
...> {EX.S3, EX.p1, "foo"}
|
||||
...> ]) |> RDF.Dataset.resources
|
||||
MapSet.new([RDF.uri(EX.S1), RDF.uri(EX.S2), RDF.uri(EX.S3),
|
||||
RDF.uri(EX.O1), RDF.uri(EX.O2), RDF.bnode(:bnode), EX.p1, EX.p2])
|
||||
"""
|
||||
def resources(%RDF.Dataset{graphs: graphs}) do
|
||||
Enum.reduce graphs, MapSet.new, fn ({_, graph}, resources) ->
|
||||
MapSet.union(resources, Graph.resources(graph))
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
All statements within all graphs of a `RDF.Dataset`.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Dataset.new([
|
||||
...> {EX.S1, EX.p1, EX.O1, EX.Graph},
|
||||
...> {EX.S2, EX.p2, EX.O2},
|
||||
...> {EX.S1, EX.p2, EX.O3}]) |>
|
||||
...> RDF.Dataset.statements
|
||||
[{RDF.uri(EX.S1), RDF.uri(EX.p1), RDF.uri(EX.O1), RDF.uri(EX.Graph)},
|
||||
{RDF.uri(EX.S1), RDF.uri(EX.p2), RDF.uri(EX.O3)},
|
||||
{RDF.uri(EX.S2), RDF.uri(EX.p2), RDF.uri(EX.O2)}]
|
||||
"""
|
||||
def statements(%RDF.Dataset{graphs: graphs}) do
|
||||
Enum.reduce graphs, [], fn ({_, graph}, all_statements) ->
|
||||
statements = Graph.triples(graph)
|
||||
if graph.name do
|
||||
Enum.map statements, fn {s, p, o} -> {s, p, o, graph.name} end
|
||||
else
|
||||
statements
|
||||
end ++ all_statements
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
Returns if a given statement is in a `RDF.Dataset`.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> dataset = RDF.Dataset.new([
|
||||
...> {EX.S1, EX.p1, EX.O1, EX.Graph},
|
||||
...> {EX.S2, EX.p2, EX.O2},
|
||||
...> {EX.S1, EX.p2, EX.O3}])
|
||||
...> RDF.Dataset.include?(dataset, {EX.S1, EX.p1, EX.O1, EX.Graph})
|
||||
true
|
||||
"""
|
||||
def include?(dataset, statement, graph_context \\ nil)
|
||||
|
||||
def include?(%RDF.Dataset{graphs: graphs}, triple = {_, _, _}, graph_context) do
|
||||
with graph_context = Quad.convert_graph_context(graph_context) do
|
||||
if graph = graphs[graph_context] do
|
||||
Graph.include?(graph, triple)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def include?(%RDF.Dataset{} = description,
|
||||
{subject, predicate, object, graph_context}, _),
|
||||
do: include?(description, {subject, predicate, object}, graph_context)
|
||||
|
||||
|
||||
# TODO: Can/should we isolate and move the Enumerable specific part to the Enumerable implementation?
|
||||
|
||||
def reduce(%RDF.Dataset{graphs: graphs}, {:cont, acc}, _fun)
|
||||
when map_size(graphs) == 0, do: {:done, acc}
|
||||
|
||||
def reduce(%RDF.Dataset{} = dataset, {:cont, acc}, fun) do
|
||||
{statement, rest} = RDF.Dataset.pop(dataset)
|
||||
reduce(rest, fun.(statement, acc), fun)
|
||||
end
|
||||
|
||||
def reduce(_, {:halt, acc}, _fun), do: {:halted, acc}
|
||||
def reduce(dataset = %RDF.Dataset{}, {:suspend, acc}, fun) do
|
||||
{:suspended, acc, &reduce(dataset, &1, fun)}
|
||||
end
|
||||
|
||||
|
||||
def pop(%RDF.Dataset{graphs: graphs} = dataset)
|
||||
when graphs == %{}, do: {nil, dataset}
|
||||
|
||||
def pop(%RDF.Dataset{name: name, graphs: graphs}) do
|
||||
# # TODO: Find a faster way ...
|
||||
[{graph_name, graph}] = Enum.take(graphs, 1)
|
||||
{{s, p, o}, popped_graph} = Graph.pop(graph)
|
||||
popped = if Enum.empty?(popped_graph),
|
||||
do: graphs |> Map.delete(graph_name),
|
||||
else: graphs |> Map.put(graph_name, popped_graph)
|
||||
|
||||
{{s, p, o, graph_name}, %RDF.Dataset{name: name, graphs: popped}}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
defimpl Enumerable, for: RDF.Dataset do
|
||||
def reduce(graph, acc, fun), do: RDF.Dataset.reduce(graph, acc, fun)
|
||||
def member?(graph, statement), do: {:ok, RDF.Dataset.include?(graph, statement)}
|
||||
def count(graph), do: {:ok, RDF.Dataset.statement_count(graph)}
|
||||
end
|
|
@ -22,6 +22,14 @@ defmodule RDF.Triple.InvalidPredicateError do
|
|||
end
|
||||
end
|
||||
|
||||
defmodule RDF.Quad.InvalidGraphContextError do
|
||||
defexception [:graph_context]
|
||||
|
||||
def message(%{graph_context: graph_context}) do
|
||||
"'#{inspect(graph_context)}' is not a valid graph context of a RDF.Quad"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
defmodule RDF.Vocabulary.InvalidBaseURIError do
|
||||
defexception [:message]
|
||||
|
|
|
@ -33,11 +33,17 @@ defmodule RDF.Graph do
|
|||
do: new() |> add(triples)
|
||||
|
||||
@doc """
|
||||
Creates an unnamed `RDF.Graph` with an `RDF.Description`.
|
||||
Creates an unnamed `RDF.Graph` with a `RDF.Description`.
|
||||
"""
|
||||
def new(%RDF.Description{} = description),
|
||||
do: new() |> add(description)
|
||||
|
||||
@doc """
|
||||
Creates an empty unnamed `RDF.Graph`.
|
||||
"""
|
||||
def new(nil),
|
||||
do: new()
|
||||
|
||||
@doc """
|
||||
Creates an empty named `RDF.Graph`.
|
||||
"""
|
||||
|
@ -57,7 +63,7 @@ defmodule RDF.Graph do
|
|||
do: new(name) |> add(triples)
|
||||
|
||||
@doc """
|
||||
Creates a named `RDF.Graph` with an `RDF.Description`.
|
||||
Creates a named `RDF.Graph` with a `RDF.Description`.
|
||||
"""
|
||||
def new(name, %RDF.Description{} = description),
|
||||
do: new(name) |> add(description)
|
||||
|
@ -178,11 +184,10 @@ defmodule RDF.Graph do
|
|||
|
||||
def put(%RDF.Graph{name: name, descriptions: descriptions}, subject, predications)
|
||||
when is_list(predications) do
|
||||
with triple_subject = Triple.convert_subject(subject),
|
||||
description = Map.get(descriptions, triple_subject)
|
||||
|| Description.new(triple_subject) do
|
||||
with subject = Triple.convert_subject(subject) do
|
||||
description = Map.get(descriptions, subject, Description.new(subject))
|
||||
new_descriptions = descriptions
|
||||
|> Map.put(triple_subject, Description.put(description, predications))
|
||||
|> Map.put(subject, Description.put(description, predications))
|
||||
%RDF.Graph{name: name, descriptions: new_descriptions}
|
||||
end
|
||||
end
|
||||
|
@ -257,7 +262,10 @@ defmodule RDF.Graph do
|
|||
case fun.(get(graph, triple_subject)) do
|
||||
{old_description, new_description} ->
|
||||
{old_description, put(graph, triple_subject, new_description)}
|
||||
:pop -> pop(graph, triple_subject)
|
||||
:pop ->
|
||||
pop(graph, triple_subject)
|
||||
other ->
|
||||
raise "the given function must return a two-element tuple or :pop, got: #{inspect(other)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -285,8 +293,33 @@ defmodule RDF.Graph do
|
|||
end
|
||||
|
||||
|
||||
def subject_count(graph), do: Enum.count(graph.descriptions)
|
||||
@doc """
|
||||
The number of subjects within a `RDF.Graph`.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Graph.new([
|
||||
...> {EX.S1, EX.p1, EX.O1},
|
||||
...> {EX.S2, EX.p2, EX.O2},
|
||||
...> {EX.S1, EX.p2, EX.O3}]) |>
|
||||
...> RDF.Graph.subject_count
|
||||
2
|
||||
"""
|
||||
def subject_count(%RDF.Graph{descriptions: descriptions}),
|
||||
do: Enum.count(descriptions)
|
||||
|
||||
@doc """
|
||||
The number of statements within a `RDF.Graph`.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Graph.new([
|
||||
...> {EX.S1, EX.p1, EX.O1},
|
||||
...> {EX.S2, EX.p2, EX.O2},
|
||||
...> {EX.S1, EX.p2, EX.O3}]) |>
|
||||
...> RDF.Graph.triple_count
|
||||
3
|
||||
"""
|
||||
def triple_count(%RDF.Graph{descriptions: descriptions}) do
|
||||
Enum.reduce descriptions, 0, fn ({_subject, description}, count) ->
|
||||
count + Description.count(description)
|
||||
|
@ -294,7 +327,7 @@ defmodule RDF.Graph do
|
|||
end
|
||||
|
||||
@doc """
|
||||
The set of all properties used in the predicates within a `RDF.Graph`.
|
||||
The set of all subjects used in the statements within a `RDF.Graph`.
|
||||
|
||||
# Examples
|
||||
|
||||
|
@ -309,7 +342,7 @@ defmodule RDF.Graph do
|
|||
do: descriptions |> Map.keys |> MapSet.new
|
||||
|
||||
@doc """
|
||||
The set of all properties used in the predicates within a `RDF.Graph`.
|
||||
The set of all properties used in the predicates of the statements within a `RDF.Graph`.
|
||||
|
||||
# Examples
|
||||
|
||||
|
@ -374,6 +407,20 @@ defmodule RDF.Graph do
|
|||
end) |> MapSet.union(subjects(graph))
|
||||
end
|
||||
|
||||
@doc """
|
||||
All statements within a `RDF.Graph`.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Graph.new([
|
||||
...> {EX.S1, EX.p1, EX.O1},
|
||||
...> {EX.S2, EX.p2, EX.O2},
|
||||
...> {EX.S1, EX.p2, EX.O3}
|
||||
...> ]) |> RDF.Graph.triples
|
||||
[{RDF.uri(EX.S1), RDF.uri(EX.p1), RDF.uri(EX.O1)},
|
||||
{RDF.uri(EX.S1), RDF.uri(EX.p2), RDF.uri(EX.O3)},
|
||||
{RDF.uri(EX.S2), RDF.uri(EX.p2), RDF.uri(EX.O2)}]
|
||||
"""
|
||||
def triples(graph = %RDF.Graph{}), do: Enum.to_list(graph)
|
||||
|
||||
def include?(%RDF.Graph{descriptions: descriptions},
|
||||
|
|
64
lib/rdf/quad.ex
Normal file
64
lib/rdf/quad.ex
Normal file
|
@ -0,0 +1,64 @@
|
|||
defmodule RDF.Quad do
|
||||
@moduledoc """
|
||||
Defines a RDF Quad.
|
||||
|
||||
A Quad is a plain Elixir tuple consisting of four valid RDF values for
|
||||
subject, predicate, object and a graph context.
|
||||
"""
|
||||
|
||||
alias RDF.{BlankNode, Literal}
|
||||
|
||||
import RDF.Triple, except: [new: 1, new: 3]
|
||||
|
||||
@type graph_context :: URI.t | BlankNode.t
|
||||
@type convertible_graph_context :: graph_context | atom | String.t
|
||||
|
||||
@doc """
|
||||
Creates a `RDF.Quad` with proper RDF values.
|
||||
|
||||
An error is raised when the given elements are not convertible to RDF values.
|
||||
|
||||
Note: The `RDF.quad` function is a shortcut to this function.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Quad.new("http://example.com/S", "http://example.com/p", 42, "http://example.com/Graph")
|
||||
{RDF.uri("http://example.com/S"), RDF.uri("http://example.com/p"), RDF.literal(42), RDF.uri("http://example.com/Graph")}
|
||||
"""
|
||||
def new(subject, predicate, object, graph_context) do
|
||||
{
|
||||
convert_subject(subject),
|
||||
convert_predicate(predicate),
|
||||
convert_object(object),
|
||||
convert_graph_context(graph_context)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a `RDF.Quad` with proper RDF values.
|
||||
|
||||
An error is raised when the given elements are not convertible to RDF values.
|
||||
|
||||
Note: The `RDF.quad` function is a shortcut to this function.
|
||||
|
||||
# Examples
|
||||
|
||||
iex> RDF.Quad.new {"http://example.com/S", "http://example.com/p", 42, "http://example.com/Graph"}
|
||||
{RDF.uri("http://example.com/S"), RDF.uri("http://example.com/p"), RDF.literal(42), RDF.uri("http://example.com/Graph")}
|
||||
"""
|
||||
def new({subject, predicate, object, graph_context}),
|
||||
do: new(subject, predicate, object, graph_context)
|
||||
|
||||
|
||||
@doc false
|
||||
def convert_graph_context(uri)
|
||||
def convert_graph_context(nil), do: nil
|
||||
def convert_graph_context(uri = %URI{}), do: uri
|
||||
def convert_graph_context(bnode = %BlankNode{}), do: bnode
|
||||
def convert_graph_context(uri) when is_atom(uri) or is_binary(uri),
|
||||
do: RDF.uri(uri)
|
||||
def convert_graph_context(arg),
|
||||
do: raise RDF.Quad.InvalidGraphContextError, graph_context: arg
|
||||
|
||||
|
||||
end
|
|
@ -57,10 +57,46 @@ defmodule RDF.Test.Case do
|
|||
def empty_graph?(%Graph{descriptions: descriptions}),
|
||||
do: descriptions == %{}
|
||||
|
||||
def graph_includes_statement?(graph, statement = {subject, _, _}) do
|
||||
def graph_includes_statement?(graph, {subject, _, _} = statement) do
|
||||
graph.descriptions
|
||||
|> Map.get(uri(subject), %{})
|
||||
|> Enum.member?(statement)
|
||||
end
|
||||
|
||||
|
||||
###############################
|
||||
# RDF.Graph
|
||||
|
||||
def dataset, do: unnamed_dataset()
|
||||
|
||||
def unnamed_dataset, do: Dataset.new
|
||||
|
||||
def named_dataset(name \\ EX.GraphName), do: Dataset.new(name)
|
||||
|
||||
def unnamed_dataset?(%Dataset{name: nil}), do: true
|
||||
def unnamed_dataset?(_), do: false
|
||||
|
||||
def named_dataset?(%Dataset{name: %URI{}}), do: true
|
||||
def named_dataset?(_), do: false
|
||||
def named_dataset?(%Dataset{name: name}, name), do: true
|
||||
def named_dataset?(_, _), do: false
|
||||
|
||||
def empty_dataset?(%Dataset{graphs: graphs}), do: graphs == %{}
|
||||
|
||||
def dataset_includes_statement?(dataset, {_, _, _} = statement) do
|
||||
dataset
|
||||
|> Dataset.default_graph
|
||||
|> graph_includes_statement?(statement)
|
||||
end
|
||||
|
||||
def dataset_includes_statement?(dataset, {subject, predicate, objects, nil}),
|
||||
do: dataset_includes_statement?(dataset, {subject, predicate, objects})
|
||||
|
||||
def dataset_includes_statement?(dataset,
|
||||
{subject, predicate, objects, graph_context}) do
|
||||
dataset.graphs
|
||||
|> Map.get(uri(graph_context), named_graph(graph_context))
|
||||
|> graph_includes_statement?({subject, predicate, objects})
|
||||
end
|
||||
|
||||
end
|
||||
|
|
380
test/unit/dataset_test.exs
Normal file
380
test/unit/dataset_test.exs
Normal file
|
@ -0,0 +1,380 @@
|
|||
defmodule RDF.DatasetTest do
|
||||
use RDF.Test.Case
|
||||
|
||||
doctest RDF.Dataset
|
||||
|
||||
|
||||
describe "construction" do
|
||||
test "creating an empty unnamed dataset" do
|
||||
assert unnamed_dataset?(unnamed_dataset())
|
||||
end
|
||||
|
||||
test "creating an empty dataset with a proper dataset name" do
|
||||
refute unnamed_dataset?(named_dataset())
|
||||
assert named_dataset?(named_dataset())
|
||||
end
|
||||
|
||||
test "creating an empty dataset with a convertible dataset name" do
|
||||
assert named_dataset("http://example.com/DatasetName")
|
||||
|> named_dataset?(uri("http://example.com/DatasetName"))
|
||||
assert named_dataset(EX.Foo) |> named_dataset?(uri(EX.Foo))
|
||||
end
|
||||
|
||||
test "creating an unnamed dataset with an initial triple" do
|
||||
ds = Dataset.new({EX.Subject, EX.predicate, EX.Object})
|
||||
assert unnamed_dataset?(ds)
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate, EX.Object})
|
||||
end
|
||||
|
||||
test "creating an unnamed dataset with an initial quad" do
|
||||
ds = Dataset.new({EX.Subject, EX.predicate, EX.Object, EX.GraphName})
|
||||
assert unnamed_dataset?(ds)
|
||||
assert dataset_includes_statement?(ds,
|
||||
{EX.Subject, EX.predicate, EX.Object, EX.GraphName})
|
||||
end
|
||||
|
||||
test "creating a named dataset with an initial triple" do
|
||||
ds = Dataset.new(EX.DatasetName, {EX.Subject, EX.predicate, EX.Object})
|
||||
assert named_dataset?(ds, uri(EX.DatasetName))
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate, EX.Object})
|
||||
end
|
||||
|
||||
test "creating a named dataset with an initial quad" do
|
||||
ds = Dataset.new(EX.DatasetName, {EX.Subject, EX.predicate, EX.Object, EX.GraphName})
|
||||
assert named_dataset?(ds, uri(EX.DatasetName))
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate, EX.Object, EX.GraphName})
|
||||
end
|
||||
|
||||
test "creating an unnamed dataset with a list of initial statements" do
|
||||
ds = Dataset.new([
|
||||
{EX.Subject1, EX.predicate1, EX.Object1},
|
||||
{EX.Subject2, EX.predicate2, EX.Object2, EX.GraphName},
|
||||
{EX.Subject3, EX.predicate3, EX.Object3, nil}
|
||||
])
|
||||
assert unnamed_dataset?(ds)
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1, nil})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject2, EX.predicate2, EX.Object2, EX.GraphName})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject3, EX.predicate3, EX.Object3, nil})
|
||||
end
|
||||
|
||||
test "creating a named dataset with a list of initial statements" do
|
||||
ds = Dataset.new(EX.DatasetName, [
|
||||
{EX.Subject, EX.predicate1, EX.Object1},
|
||||
{EX.Subject, EX.predicate2, EX.Object2, EX.GraphName},
|
||||
{EX.Subject, EX.predicate3, EX.Object3, nil}
|
||||
])
|
||||
assert named_dataset?(ds, uri(EX.DatasetName))
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate1, EX.Object1, nil})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate2, EX.Object2, EX.GraphName})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate3, EX.Object3, nil})
|
||||
end
|
||||
|
||||
test "creating a named dataset with an initial description" do
|
||||
ds = Dataset.new(EX.GraphName, Description.new({EX.Subject, EX.predicate, EX.Object}))
|
||||
assert named_dataset?(ds, uri(EX.GraphName))
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate, EX.Object})
|
||||
end
|
||||
|
||||
test "creating an unnamed dataset with an initial description" do
|
||||
ds = Dataset.new(Description.new({EX.Subject, EX.predicate, EX.Object}))
|
||||
assert unnamed_dataset?(ds)
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate, EX.Object})
|
||||
end
|
||||
end
|
||||
|
||||
describe "adding statements" do
|
||||
test "a proper triple is added to the default graph" do
|
||||
assert Dataset.add(dataset(), {uri(EX.Subject), EX.predicate, uri(EX.Object)})
|
||||
|> dataset_includes_statement?({EX.Subject, EX.predicate, EX.Object})
|
||||
end
|
||||
|
||||
test "a proper quad is added to the specified graph" do
|
||||
ds = Dataset.add(dataset(), {uri(EX.Subject), EX.predicate, uri(EX.Object), uri(EX.Graph)})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate, EX.Object, uri(EX.Graph)})
|
||||
end
|
||||
|
||||
test "a proper quad with nil context is added to the default graph" do
|
||||
ds = Dataset.add(dataset(), {uri(EX.Subject), EX.predicate, uri(EX.Object), nil})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate, EX.Object})
|
||||
end
|
||||
|
||||
|
||||
test "a convertible triple" do
|
||||
assert Dataset.add(dataset(),
|
||||
{"http://example.com/Subject", EX.predicate, EX.Object})
|
||||
|> dataset_includes_statement?({EX.Subject, EX.predicate, EX.Object})
|
||||
end
|
||||
|
||||
test "a convertible quad" do
|
||||
assert Dataset.add(dataset(),
|
||||
{"http://example.com/Subject", EX.predicate, EX.Object, "http://example.com/GraphName"})
|
||||
|> dataset_includes_statement?({EX.Subject, EX.predicate, EX.Object, EX.GraphName})
|
||||
end
|
||||
|
||||
test "statements with multiple objects" do
|
||||
ds = Dataset.add(dataset(), {EX.Subject1, EX.predicate1, [EX.Object1, EX.Object2]})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object2})
|
||||
|
||||
ds = Dataset.add(dataset(), {EX.Subject1, EX.predicate1, [EX.Object1, EX.Object2], EX.GraphName})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1, EX.GraphName})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object2, EX.GraphName})
|
||||
end
|
||||
|
||||
test "a list of triples without specification the default context" do
|
||||
ds = Dataset.add(dataset(), [
|
||||
{EX.Subject1, EX.predicate1, EX.Object1},
|
||||
{EX.Subject1, EX.predicate2, EX.Object2},
|
||||
{EX.Subject3, EX.predicate3, EX.Object3}
|
||||
])
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2, EX.Object2})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject3, EX.predicate3, EX.Object3})
|
||||
end
|
||||
|
||||
test "a list of triples with specification the default context" do
|
||||
ds = Dataset.add(dataset(), [
|
||||
{EX.Subject1, EX.predicate1, EX.Object1},
|
||||
{EX.Subject1, EX.predicate2, EX.Object2},
|
||||
{EX.Subject3, EX.predicate3, EX.Object3, nil}
|
||||
], EX.Graph)
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1, EX.Graph})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2, EX.Object2, EX.Graph})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject3, EX.predicate3, EX.Object3, nil})
|
||||
end
|
||||
|
||||
test "a list of quads" do
|
||||
ds = Dataset.add(dataset(), [
|
||||
{EX.Subject, EX.predicate1, EX.Object1, EX.Graph1},
|
||||
{EX.Subject, EX.predicate2, EX.Object2, nil},
|
||||
{EX.Subject, EX.predicate1, EX.Object1, EX.Graph2}
|
||||
])
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate1, EX.Object1, EX.Graph1})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate2, EX.Object2, nil})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate1, EX.Object1, EX.Graph2})
|
||||
end
|
||||
|
||||
test "a list of mixed triples and quads" do
|
||||
ds = Dataset.add(dataset(), [
|
||||
{EX.Subject1, EX.predicate1, EX.Object1, EX.GraphName},
|
||||
{EX.Subject3, EX.predicate3, EX.Object3}
|
||||
])
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1, EX.GraphName})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject3, EX.predicate3, EX.Object3, nil})
|
||||
end
|
||||
|
||||
test "a Description" do
|
||||
ds = Dataset.add(dataset(), Description.new(EX.Subject1, [
|
||||
{EX.predicate1, EX.Object1},
|
||||
{EX.predicate2, EX.Object2},
|
||||
]))
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2, EX.Object2})
|
||||
|
||||
ds = Dataset.add(ds, Description.new({EX.Subject1, EX.predicate3, EX.Object3}), EX.Graph)
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2, EX.Object2})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate3, EX.Object3, EX.Graph})
|
||||
end
|
||||
|
||||
@tag skip: "TODO"
|
||||
test "an unnamed Graph" do
|
||||
ds = Dataset.add(dataset(), Graph.new(%{EX.Subject1 => [
|
||||
{EX.predicate1, EX.Object1},
|
||||
{EX.predicate2, EX.Object2},
|
||||
]}))
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2, EX.Object2})
|
||||
end
|
||||
|
||||
@tag skip: "TODO"
|
||||
test "a named Graph" do
|
||||
end
|
||||
|
||||
test "a list of Descriptions" do
|
||||
ds = Dataset.add(dataset(), [
|
||||
Description.new({EX.Subject1, EX.predicate1, EX.Object1}),
|
||||
Description.new({EX.Subject2, EX.predicate2, EX.Object2}),
|
||||
Description.new({EX.Subject1, EX.predicate3, EX.Object3})
|
||||
])
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject2, EX.predicate2, EX.Object2})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate3, EX.Object3})
|
||||
|
||||
ds = Dataset.add(ds, [
|
||||
Description.new({EX.Subject1, EX.predicate1, EX.Object1}),
|
||||
Description.new({EX.Subject2, EX.predicate2, EX.Object2}),
|
||||
Description.new({EX.Subject1, EX.predicate3, EX.Object3})
|
||||
], EX.Graph)
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1, EX.Graph})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject2, EX.predicate2, EX.Object2, EX.Graph})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate3, EX.Object3, EX.Graph})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1, EX.Object1})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject2, EX.predicate2, EX.Object2})
|
||||
assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate3, EX.Object3})
|
||||
end
|
||||
|
||||
@tag skip: "TODO"
|
||||
test "a list of Graphs" do
|
||||
end
|
||||
|
||||
test "duplicates are ignored" do
|
||||
ds = Dataset.add(dataset(), {EX.Subject, EX.predicate, EX.Object, EX.GraphName})
|
||||
assert Dataset.add(ds, {EX.Subject, EX.predicate, EX.Object, EX.GraphName}) == ds
|
||||
end
|
||||
|
||||
test "non-convertible statements elements are causing an error" do
|
||||
assert_raise RDF.InvalidURIError, fn ->
|
||||
Dataset.add(dataset(), {"not a URI", EX.predicate, uri(EX.Object), uri(EX.GraphName)})
|
||||
end
|
||||
assert_raise RDF.InvalidLiteralError, fn ->
|
||||
Dataset.add(dataset(), {EX.Subject, EX.prop, self(), nil})
|
||||
end
|
||||
assert_raise RDF.InvalidURIError, fn ->
|
||||
Dataset.add(dataset(), {uri(EX.Subject), EX.predicate, uri(EX.Object), "not a URI"})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "putting triples" do
|
||||
test "a list of triples" do
|
||||
ds = Dataset.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2, EX.Graph}])
|
||||
|> RDF.Dataset.put([
|
||||
{EX.S1, EX.P2, EX.O3, EX.Graph},
|
||||
{EX.S1, EX.P2, bnode(:foo), nil},
|
||||
{EX.S2, EX.P2, EX.O3, EX.Graph},
|
||||
{EX.S2, EX.P2, EX.O4, EX.Graph}])
|
||||
|
||||
assert Dataset.statement_count(ds) == 5
|
||||
assert dataset_includes_statement?(ds, {EX.S1, EX.P1, EX.O1})
|
||||
assert dataset_includes_statement?(ds, {EX.S1, EX.P2, EX.O3, EX.Graph})
|
||||
assert dataset_includes_statement?(ds, {EX.S1, EX.P2, bnode(:foo)})
|
||||
assert dataset_includes_statement?(ds, {EX.S2, EX.P2, EX.O3, EX.Graph})
|
||||
assert dataset_includes_statement?(ds, {EX.S2, EX.P2, EX.O4, EX.Graph})
|
||||
end
|
||||
|
||||
test "a Description" do
|
||||
ds = Dataset.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}, {EX.S1, EX.P3, EX.O3}])
|
||||
|> RDF.Dataset.put(Description.new(EX.S1, [{EX.P3, EX.O4}, {EX.P2, bnode(:foo)}]))
|
||||
|
||||
assert Dataset.statement_count(ds) == 4
|
||||
assert dataset_includes_statement?(ds, {EX.S1, EX.P1, EX.O1})
|
||||
assert dataset_includes_statement?(ds, {EX.S1, EX.P3, EX.O4})
|
||||
assert dataset_includes_statement?(ds, {EX.S1, EX.P2, bnode(:foo)})
|
||||
assert dataset_includes_statement?(ds, {EX.S2, EX.P2, EX.O2})
|
||||
end
|
||||
|
||||
@tag skip: "TODO"
|
||||
test "an unnamed Graph" do
|
||||
end
|
||||
|
||||
@tag skip: "TODO"
|
||||
test "a named Graph" do
|
||||
end
|
||||
|
||||
# @tag skip: "TODO: Requires Dataset.put with a list to differentiate a list of statements, a list of Descriptions and list of Graphs. Do we want to support mixed lists also?"
|
||||
# test "a list of Descriptions" do
|
||||
# ds = Dataset.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}])
|
||||
# |> RDF.Dataset.put([
|
||||
# Description.new(EX.S1, [{EX.P2, EX.O3}, {EX.P2, bnode(:foo)}]),
|
||||
# Description.new(EX.S2, [{EX.P2, EX.O3}, {EX.P2, EX.O4}])
|
||||
# ])
|
||||
#
|
||||
# assert Dataset.triple_count(ds) == 5
|
||||
# assert dataset_includes_statement?(ds, {EX.S1, EX.P1, EX.O1})
|
||||
# assert dataset_includes_statement?(ds, {EX.S1, EX.P2, EX.O3})
|
||||
# assert dataset_includes_statement?(ds, {EX.S1, EX.P2, bnode(:foo)})
|
||||
# assert dataset_includes_statement?(ds, {EX.S2, EX.P2, EX.O3})
|
||||
# assert dataset_includes_statement?(ds, {EX.S2, EX.P2, EX.O4})
|
||||
# end
|
||||
|
||||
test "simultaneous use of the different forms to address the default context" do
|
||||
ds = RDF.Dataset.put(dataset(), [
|
||||
{EX.S, EX.P, EX.O1},
|
||||
{EX.S, EX.P, EX.O2, nil}])
|
||||
assert Dataset.statement_count(ds) == 2
|
||||
assert dataset_includes_statement?(ds, {EX.S, EX.P, EX.O1})
|
||||
assert dataset_includes_statement?(ds, {EX.S, EX.P, EX.O2})
|
||||
|
||||
# TODO: see comment on RDF.Dataset.put on why the following is not supported
|
||||
# ds = RDF.Dataset.put(dataset(), %{
|
||||
# EX.S => [{EX.P, EX.O1}],
|
||||
# {EX.S, nil} => [{EX.P, EX.O2}]
|
||||
# })
|
||||
# assert Dataset.statement_count(ds) == 2
|
||||
# assert dataset_includes_statement?(ds, {EX.S, EX.P, EX.O1})
|
||||
# assert dataset_includes_statement?(ds, {EX.S, EX.P, EX.O2})
|
||||
end
|
||||
end
|
||||
|
||||
test "pop a statement" do
|
||||
assert Dataset.pop(Dataset.new) == {nil, Dataset.new}
|
||||
|
||||
{quad, dataset} = Dataset.new({EX.S, EX.p, EX.O, EX.Graph}) |> Dataset.pop
|
||||
assert quad == {uri(EX.S), uri(EX.p), uri(EX.O), uri(EX.Graph)}
|
||||
assert Enum.count(dataset.graphs) == 0
|
||||
|
||||
{{subject, predicate, object, _}, dataset} =
|
||||
Dataset.new([{EX.S, EX.p, EX.O, EX.Graph}, {EX.S, EX.p, EX.O}])
|
||||
|> Dataset.pop
|
||||
assert {subject, predicate, object} == {uri(EX.S), uri(EX.p), uri(EX.O)}
|
||||
assert Enum.count(dataset.graphs) == 1
|
||||
|
||||
{{subject, _, _, graph_context}, dataset} =
|
||||
Dataset.new([{EX.S, EX.p, EX.O1, EX.Graph}, {EX.S, EX.p, EX.O2, EX.Graph}])
|
||||
|> Dataset.pop
|
||||
assert subject == uri(EX.S)
|
||||
assert graph_context == uri(EX.Graph)
|
||||
assert Enum.count(dataset.graphs) == 1
|
||||
end
|
||||
|
||||
describe "Enumerable protocol" do
|
||||
test "Enum.count" do
|
||||
assert Enum.count(Dataset.new EX.foo) == 0
|
||||
assert Enum.count(Dataset.new {EX.S, EX.p, EX.O, EX.Graph}) == 1
|
||||
assert Enum.count(Dataset.new [{EX.S, EX.p, EX.O1, EX.Graph}, {EX.S, EX.p, EX.O2}]) == 2
|
||||
|
||||
ds = Dataset.add(dataset(), [
|
||||
{EX.Subject1, EX.predicate1, EX.Object1, EX.Graph},
|
||||
{EX.Subject1, EX.predicate2, EX.Object2, EX.Graph},
|
||||
{EX.Subject3, EX.predicate3, EX.Object3}
|
||||
])
|
||||
assert Enum.count(ds) == 3
|
||||
end
|
||||
|
||||
test "Enum.member?" do
|
||||
refute Enum.member?(Dataset.new, {uri(EX.S), EX.p, uri(EX.O), uri(EX.Graph)})
|
||||
assert Enum.member?(Dataset.new({EX.S, EX.p, EX.O, EX.Graph}),
|
||||
{EX.S, EX.p, EX.O, EX.Graph})
|
||||
|
||||
ds = Dataset.add(dataset(), [
|
||||
{EX.Subject1, EX.predicate1, EX.Object1, EX.Graph},
|
||||
{EX.Subject1, EX.predicate2, EX.Object2, EX.Graph},
|
||||
{EX.Subject3, EX.predicate3, EX.Object3}
|
||||
])
|
||||
assert Enum.member?(ds, {EX.Subject1, EX.predicate1, EX.Object1, EX.Graph})
|
||||
assert Enum.member?(ds, {EX.Subject1, EX.predicate2, EX.Object2, EX.Graph})
|
||||
assert Enum.member?(ds, {EX.Subject3, EX.predicate3, EX.Object3})
|
||||
end
|
||||
|
||||
test "Enum.reduce" do
|
||||
ds = Dataset.add(dataset(), [
|
||||
{EX.Subject1, EX.predicate1, EX.Object1, EX.Graph},
|
||||
{EX.Subject1, EX.predicate2, EX.Object2},
|
||||
{EX.Subject3, EX.predicate3, EX.Object3, EX.Graph}
|
||||
])
|
||||
|
||||
assert ds == Enum.reduce(ds, dataset(),
|
||||
fn(statement, acc) -> acc |> Dataset.add(statement) end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Access behaviour" do
|
||||
test "access with the [] operator" do
|
||||
assert Dataset.new[EX.Graph] == nil
|
||||
assert Dataset.new({EX.S, EX.p, EX.O, EX.Graph})[EX.Graph] ==
|
||||
Graph.new(EX.Graph, {EX.S, EX.p, EX.O})
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -195,15 +195,6 @@ defmodule RDF.GraphTest do
|
|||
end
|
||||
end
|
||||
|
||||
test "subject_count" do
|
||||
g = Graph.add(graph(), [
|
||||
{EX.Subject1, EX.predicate1, EX.Object1},
|
||||
{EX.Subject1, EX.predicate2, EX.Object2},
|
||||
{EX.Subject3, EX.predicate3, EX.Object3}
|
||||
])
|
||||
assert Graph.subject_count(g) == 2
|
||||
end
|
||||
|
||||
test "pop a triple" do
|
||||
assert Graph.pop(Graph.new) == {nil, Graph.new}
|
||||
|
||||
|
|
5
test/unit/quad_test.exs
Normal file
5
test/unit/quad_test.exs
Normal file
|
@ -0,0 +1,5 @@
|
|||
defmodule RDF.QuadTest do
|
||||
use ExUnit.Case
|
||||
|
||||
doctest RDF.Quad
|
||||
end
|
Loading…
Reference in a new issue