Limit and unify the forms of input on RDF.Graph functions

This commit is contained in:
Marcel Otto 2020-09-28 02:13:20 +02:00
parent 20a69964c7
commit a8c71df20b
6 changed files with 250 additions and 195 deletions

View file

@ -237,8 +237,6 @@ defmodule RDF do
defdelegate graph(), to: Graph, as: :new defdelegate graph(), to: Graph, as: :new
defdelegate graph(arg), to: Graph, as: :new defdelegate graph(arg), to: Graph, as: :new
defdelegate graph(arg1, arg2), to: Graph, as: :new defdelegate graph(arg1, arg2), to: Graph, as: :new
defdelegate graph(arg1, arg2, arg3), to: Graph, as: :new
defdelegate graph(arg1, arg2, arg3, arg4), to: Graph, as: :new
defdelegate dataset(), to: Dataset, as: :new defdelegate dataset(), to: Dataset, as: :new
defdelegate dataset(arg), to: Dataset, as: :new defdelegate dataset(arg), to: Dataset, as: :new

View file

@ -309,7 +309,7 @@ defmodule RDF.Dataset do
new_graphs = new_graphs =
graphs graphs
|> Map.put(graph_context, Graph.put(graph, subject, predications)) |> Map.put(graph_context, Graph.put(graph, {subject, predications}))
%__MODULE__{name: name, graphs: new_graphs} %__MODULE__{name: name, graphs: new_graphs}
end end

View file

@ -14,6 +14,7 @@ defmodule RDF.Graph do
@behaviour Access @behaviour Access
import RDF.Statement import RDF.Statement
import RDF.Utils
alias RDF.{Description, IRI, PrefixMap, Statement} alias RDF.{Description, IRI, PrefixMap, Statement}
@type graph_description :: %{Statement.subject() => Description.t()} @type graph_description :: %{Statement.subject() => Description.t()}
@ -25,8 +26,21 @@ defmodule RDF.Graph do
base_iri: IRI.t() | nil base_iri: IRI.t() | nil
} }
@type input :: Statement.t() | Description.t() | t @type input ::
Statement.coercible_t()
| {
Statement.coercible_subject(),
Description.input()
}
| Description.t()
| t
| %{
Statement.coercible_subject() => %{
Statement.coercible_predicate() =>
Statement.coercible_object() | [Statement.coercible_object()]
}
}
| list(input)
@type update_description_fun :: (Description.t() -> Description.t()) @type update_description_fun :: (Description.t() -> Description.t())
@type get_and_update_description_fun :: (Description.t() -> {Description.t(), input} | :pop) @type get_and_update_description_fun :: (Description.t() -> {Description.t(), input} | :pop)
@ -54,7 +68,7 @@ defmodule RDF.Graph do
RDF.Graph.new(name: EX.GraphName) RDF.Graph.new(name: EX.GraphName)
""" """
@spec new(input | [input] | keyword) :: t @spec new(input | keyword) :: t
def new(data_or_options) def new(data_or_options)
def new(data_or_options) def new(data_or_options)
@ -74,8 +88,8 @@ defmodule RDF.Graph do
The initial RDF triples can be provided The initial RDF triples can be provided
- as a single statement tuple - as a single statement tuple
- an `RDF.Description` - a `RDF.Description`
- an `RDF.Graph` - a `RDF.Graph`
- or a list with any combination of the former - or a list with any combination of the former
Available options: Available options:
@ -97,7 +111,7 @@ defmodule RDF.Graph do
RDF.Graph.new({EX.S, EX.p, EX.O}, name: EX.GraphName, base_iri: EX.base) RDF.Graph.new({EX.S, EX.p, EX.O}, name: EX.GraphName, base_iri: EX.base)
""" """
@spec new(input | [input], keyword) :: t @spec new(input, keyword) :: t
def new(data, options) def new(data, options)
def new(%__MODULE__{} = graph, options) do def new(%__MODULE__{} = graph, options) do
@ -112,26 +126,12 @@ defmodule RDF.Graph do
|> add(data) |> add(data)
end end
@doc """
Creates an `RDF.Graph` with initial triples.
See `new/2` for available arguments.
"""
@spec new(
Statement.coercible_subject(),
Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()],
keyword
) :: t
def new(subject, predicate, objects, options \\ []),
do: new([], options) |> add(subject, predicate, objects)
@doc """ @doc """
Removes all triples from `graph`. Removes all triples from `graph`.
This function is useful for getting an empty graph based on the settings of This function is useful for getting an empty graph based on the settings of
another graph, as this function keeps graph name name, base IRI and default another graph, as this function keeps graph name, base IRI and default prefixes
prefixes as they are and just removes the triples. as they are and just removes the triples.
""" """
@spec clear(t) :: t @spec clear(t) :: t
def clear(%__MODULE__{} = graph) do def clear(%__MODULE__{} = graph) do
@ -140,42 +140,35 @@ defmodule RDF.Graph do
@doc """ @doc """
Adds triples to a `RDF.Graph`. Adds triples to a `RDF.Graph`.
"""
@spec add(
t,
Statement.coercible_subject(),
Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]
) :: t
def add(%__MODULE__{} = graph, subject, predicate, objects),
do: add(graph, {subject, predicate, objects})
@doc """ The `input` can be provided
Adds triples to a `RDF.Graph`.
- as a single statement tuple
- a `RDF.Description`
- a `RDF.Graph`
- or a list with any combination of the former
When the statements to be added are given as another `RDF.Graph`, When the statements to be added are given as another `RDF.Graph`,
the graph name must not match graph name of the graph to which the statements the graph name must not match graph name of the graph to which the statements
are added. As opposed to that `RDF.Data.merge/2` will produce a `RDF.Dataset` are added. As opposed to that, `RDF.Data.merge/2` will produce a `RDF.Dataset`
containing both graphs. containing both graphs.
Also when the statements to be added are given as another `RDF.Graph`, the Also when the statements to be added are given as another `RDF.Graph`, the
prefixes of this graph will be added. In case of conflicting prefix mappings prefixes of this graph will be added. In case of conflicting prefix mappings
the original prefix from `graph` will be kept. the original prefix from `graph` will be kept.
""" """
@spec add(t, input | [input]) :: t @spec add(t, input) :: t
def add(graph, triples) def add(graph, input)
def add(%__MODULE__{} = graph, {subject, _, _} = statement), def add(%__MODULE__{} = graph, {subject, _, _} = triple),
do: do_add(graph, coerce_subject(subject), statement) do: do_add(graph, coerce_subject(subject), triple)
def add(graph, {subject, predicate, object, _}), def add(graph, {subject, predicate, object, _}),
do: add(graph, {subject, predicate, object}) do: add(graph, {subject, predicate, object})
def add(graph, triples) when is_list(triples) do def add(graph, {subject, predications}),
Enum.reduce(triples, graph, fn triple, graph -> do: do_add(graph, coerce_subject(subject), predications)
add(graph, triple)
end)
end
def add(%__MODULE__{} = graph, %Description{subject: subject} = description) do def add(%__MODULE__{} = graph, %Description{subject: subject} = description) do
if Description.count(description) > 0 do if Description.count(description) > 0 do
@ -198,17 +191,23 @@ defmodule RDF.Graph do
end end
end end
def add(graph, statements) when is_list(statements) or is_map(statements) do
Enum.reduce(statements, graph, fn triple, graph ->
add(graph, triple)
end)
end
defp do_add(%__MODULE__{descriptions: descriptions} = graph, subject, statements) do defp do_add(%__MODULE__{descriptions: descriptions} = graph, subject, statements) do
%__MODULE__{ %__MODULE__{
graph graph
| descriptions: | descriptions:
Map.update( lazy_map_update(
descriptions, descriptions,
subject, subject,
Description.new(subject) |> Description.add(statements), # when new: create and initialize description with statements
fn description -> fn -> Description.new(subject, init: statements) end,
Description.add(description, statements) # when update: merge statements description
end fn description -> Description.add(description, statements) end
) )
} }
end end
@ -222,14 +221,17 @@ defmodule RDF.Graph do
## Examples ## Examples
iex> RDF.Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}]) |> iex> RDF.Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}])
...> RDF.Graph.put([{EX.S1, EX.P2, EX.O3}, {EX.S2, EX.P2, EX.O3}]) ...> |> RDF.Graph.put([{EX.S1, EX.P2, EX.O3}, {EX.S2, EX.P2, EX.O3}])
RDF.Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S1, EX.P2, EX.O3}, {EX.S2, EX.P2, EX.O3}]) RDF.Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S1, EX.P2, EX.O3}, {EX.S2, EX.P2, EX.O3}])
""" """
@spec put(t, input | [input]) :: t @spec put(t, input) :: t
def put(graph, statements) def put(graph, statements)
def put(graph, {subject, predications}),
do: do_put(graph, coerce_subject(subject), predications)
def put(%__MODULE__{} = graph, {subject, _, _} = statement), def put(%__MODULE__{} = graph, {subject, _, _} = statement),
do: do_put(graph, coerce_subject(subject), statement) do: do_put(graph, coerce_subject(subject), statement)
@ -254,93 +256,35 @@ defmodule RDF.Graph do
def put(%__MODULE__{} = graph, statements) when is_map(statements) do def put(%__MODULE__{} = graph, statements) when is_map(statements) do
Enum.reduce(statements, graph, fn {subject, predications}, graph -> Enum.reduce(statements, graph, fn {subject, predications}, graph ->
put(graph, subject, predications) put(graph, {subject, predications})
end) end)
end end
def put(%__MODULE__{} = graph, statements) when is_list(statements) do def put(%__MODULE__{} = graph, statements) when is_list(statements) do
put(graph, Enum.group_by(statements, &elem(&1, 0), fn {_, p, o} -> {p, o} end)) put(
graph,
Enum.group_by(statements, &elem(&1, 0), fn
{_, p, o} -> {p, o}
{_, predications} -> predications
end)
)
end end
@doc """ defp do_put(%__MODULE__{descriptions: descriptions} = graph, subject, predications) do
Add statements to a `RDF.Graph`, overwriting all statements with the same subject and predicate.
"""
@spec put(
t,
Statement.coercible_subject(),
Description.statements() | [Description.statements()]
) :: t
def put(graph, subject, predications)
def put(%__MODULE__{descriptions: descriptions} = graph, subject, predications)
when is_list(predications) do
with subject = coerce_subject(subject) do
# TODO: Can we reduce this case also to do_put somehow? Only the initializer of Map.update differs ...
%__MODULE__{
graph
| descriptions:
Map.update(
descriptions,
subject,
Description.new(subject) |> Description.add(predications),
fn current ->
Description.put(current, predications)
end
)
}
end
end
def put(graph, subject, {_predicate, _objects} = predications),
do: put(graph, subject, [predications])
defp do_put(%__MODULE__{descriptions: descriptions} = graph, subject, statements) do
%__MODULE__{ %__MODULE__{
graph graph
| descriptions: | descriptions:
Map.update( lazy_map_update(
descriptions, descriptions,
subject, subject,
Description.new(subject) |> Description.add(statements), # when new
fn current -> fn -> Description.new(subject, init: predications) end,
Description.put(current, statements) # when updating
end fn current -> Description.put(current, predications) end
) )
} }
end end
@doc """
Add statements to a `RDF.Graph`, overwriting all statements with the same subject and predicate.
## Examples
iex> RDF.Graph.new(EX.S, EX.P, EX.O1) |> RDF.Graph.put(EX.S, EX.P, EX.O2)
RDF.Graph.new(EX.S, EX.P, EX.O2)
iex> RDF.Graph.new(EX.S, EX.P1, EX.O1) |> RDF.Graph.put(EX.S, EX.P2, EX.O2)
RDF.Graph.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}])
"""
@spec put(
t,
Statement.coercible_subject(),
Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]
) :: t
def put(%__MODULE__{} = graph, subject, predicate, objects),
do: put(graph, {subject, predicate, objects})
@doc """
Deletes statements from a `RDF.Graph`.
"""
@spec delete(
t,
Statement.coercible_subject(),
Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]
) :: t
def delete(graph, subject, predicate, object),
do: delete(graph, {subject, predicate, object})
@doc """ @doc """
Deletes statements from a `RDF.Graph`. Deletes statements from a `RDF.Graph`.
@ -356,12 +300,15 @@ defmodule RDF.Graph do
def delete(%__MODULE__{} = graph, {subject, _, _} = triple), def delete(%__MODULE__{} = graph, {subject, _, _} = triple),
do: do_delete(graph, coerce_subject(subject), triple) do: do_delete(graph, coerce_subject(subject), triple)
def delete(graph, {subject, predications}),
do: do_delete(graph, coerce_subject(subject), predications)
def delete(graph, {subject, predicate, object, _}), def delete(graph, {subject, predicate, object, _}),
do: delete(graph, {subject, predicate, object}) do: delete(graph, {subject, predicate, object})
def delete(%__MODULE__{} = graph, triples) when is_list(triples) do def delete(%__MODULE__{} = graph, statements) when is_list(statements) or is_map(statements) do
Enum.reduce(triples, graph, fn triple, graph -> Enum.reduce(statements, graph, fn statement, graph ->
delete(graph, triple) delete(graph, statement)
end) end)
end end
@ -375,8 +322,9 @@ defmodule RDF.Graph do
end end
defp do_delete(%__MODULE__{descriptions: descriptions} = graph, subject, statements) do defp do_delete(%__MODULE__{descriptions: descriptions} = graph, subject, statements) do
with description when not is_nil(description) <- descriptions[subject], if description = descriptions[subject] do
new_description = Description.delete(description, statements) do new_description = Description.delete(description, statements)
%__MODULE__{ %__MODULE__{
graph graph
| descriptions: | descriptions:
@ -387,7 +335,7 @@ defmodule RDF.Graph do
end end
} }
else else
nil -> graph graph
end end
end end
@ -448,7 +396,7 @@ defmodule RDF.Graph do
@spec update( @spec update(
t, t,
Statement.coercible_subject(), Statement.coercible_subject(),
Description.statements() | [Description.statements()] | nil, Description.input() | nil,
update_description_fun update_description_fun
) :: t ) :: t
def update(graph = %__MODULE__{}, subject, initial \\ nil, fun) do def update(graph = %__MODULE__{}, subject, initial \\ nil, fun) do
@ -575,11 +523,11 @@ defmodule RDF.Graph do
## Examples ## Examples
iex> RDF.Graph.new({EX.S, EX.P, EX.O}) |> iex> RDF.Graph.new({EX.S, EX.P, EX.O})
...> RDF.Graph.get_and_update(EX.S, fn current_description -> ...> |> RDF.Graph.get_and_update(EX.S, fn current_description ->
...> {current_description, {EX.P, EX.NEW}} ...> {current_description, {EX.P, EX.NEW}}
...> end) ...> end)
{RDF.Description.new({EX.S, EX.P, EX.O}), RDF.Graph.new({EX.S, EX.P, EX.NEW})} {RDF.Description.new(EX.S, init: {EX.P, EX.O}), RDF.Graph.new({EX.S, EX.P, EX.NEW})}
""" """
@impl Access @impl Access
@ -589,7 +537,7 @@ defmodule RDF.Graph do
with subject = coerce_subject(subject) do with subject = coerce_subject(subject) do
case fun.(get(graph, subject)) do case fun.(get(graph, subject)) do
{old_description, new_description} -> {old_description, new_description} ->
{old_description, put(graph, subject, new_description)} {old_description, put(graph, {subject, new_description})}
:pop -> :pop ->
pop(graph, subject) pop(graph, subject)
@ -792,16 +740,34 @@ defmodule RDF.Graph do
@doc """ @doc """
Checks if the given statement exists within a `RDF.Graph`. Checks if the given statement exists within a `RDF.Graph`.
""" """
@spec include?(t, Statement.t()) :: boolean @spec include?(t, input) :: boolean
def include?( def include?(%__MODULE__{} = graph, {subject, _, _} = triple),
%__MODULE__{descriptions: descriptions}, do: do_include?(graph, coerce_subject(subject), triple)
triple = {subject, _, _}
) do def include?(graph, {subject, predicate, object, _}),
with subject = coerce_subject(subject), do: include?(graph, {subject, predicate, object})
%Description{} <- description = descriptions[subject] do
Description.include?(description, triple) def include?(graph, {subject, predications}),
do: do_include?(graph, coerce_subject(subject), predications)
def include?(%__MODULE__{} = graph, %Description{subject: subject} = description),
do: do_include?(graph, subject, description)
def include?(graph, %__MODULE__{} = other_graph) do
other_graph
|> descriptions()
|> Enum.all?(&include?(graph, &1))
end
def include?(graph, statements) when is_list(statements) or is_map(statements) do
Enum.all?(statements, &include?(graph, &1))
end
defp do_include?(%__MODULE__{descriptions: descriptions}, subject, statements) do
if description = descriptions[subject] do
Description.include?(description, statements)
else else
_ -> false false
end end
end end

View file

@ -22,6 +22,7 @@ defmodule RDF.Statement do
@type term_mapping :: (qualified_term -> any | nil) @type term_mapping :: (qualified_term -> any | nil)
@type t :: Triple.t() | Quad.t() @type t :: Triple.t() | Quad.t()
@type coercible_t :: Triple.coercible_t() | Quad.coercible_t()
@doc """ @doc """
Creates a `RDF.Statement` tuple with proper RDF values. Creates a `RDF.Statement` tuple with proper RDF values.

16
lib/rdf/utils/utils.ex Normal file
View file

@ -0,0 +1,16 @@
defmodule RDF.Utils do
@moduledoc false
def lazy_map_update(map, key, init_fun, fun) do
case map do
%{^key => value} ->
Map.put(map, key, fun.(value))
%{} ->
Map.put(map, key, init_fun.())
other ->
:erlang.error({:badmap, other}, [map, key, init_fun, fun])
end
end
end

View file

@ -32,20 +32,12 @@ defmodule RDF.GraphTest do
g = Graph.new({EX.Subject, EX.predicate(), EX.Object}) g = Graph.new({EX.Subject, EX.predicate(), EX.Object})
assert unnamed_graph?(g) assert unnamed_graph?(g)
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object}) assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object})
g = Graph.new(EX.Subject, EX.predicate(), EX.Object)
assert unnamed_graph?(g)
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object})
end end
test "creating a named graph with an initial triple" do test "creating a named graph with an initial triple" do
g = Graph.new({EX.Subject, EX.predicate(), EX.Object}, name: EX.GraphName) g = Graph.new({EX.Subject, EX.predicate(), EX.Object}, name: EX.GraphName)
assert named_graph?(g, iri(EX.GraphName)) assert named_graph?(g, iri(EX.GraphName))
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object}) assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object})
g = Graph.new(EX.Subject, EX.predicate(), EX.Object, name: EX.GraphName)
assert named_graph?(g, iri(EX.GraphName))
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object})
end end
test "creating an unnamed graph with a list of initial triples" do test "creating an unnamed graph with a list of initial triples" do
@ -59,7 +51,7 @@ defmodule RDF.GraphTest do
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1}) assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject2, EX.predicate2(), EX.Object2}) assert graph_includes_statement?(g, {EX.Subject2, EX.predicate2(), EX.Object2})
g = Graph.new(EX.Subject, EX.predicate(), [EX.Object1, EX.Object2]) g = Graph.new({EX.Subject, EX.predicate(), [EX.Object1, EX.Object2]})
assert unnamed_graph?(g) assert unnamed_graph?(g)
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object1}) assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object2}) assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object2})
@ -76,7 +68,7 @@ defmodule RDF.GraphTest do
assert graph_includes_statement?(g, {EX.Subject, EX.predicate1(), EX.Object1}) assert graph_includes_statement?(g, {EX.Subject, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject, EX.predicate2(), EX.Object2}) assert graph_includes_statement?(g, {EX.Subject, EX.predicate2(), EX.Object2})
g = Graph.new(EX.Subject, EX.predicate(), [EX.Object1, EX.Object2], name: EX.GraphName) g = Graph.new({EX.Subject, EX.predicate(), [EX.Object1, EX.Object2]}, name: EX.GraphName)
assert named_graph?(g, iri(EX.GraphName)) assert named_graph?(g, iri(EX.GraphName))
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object1}) assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object2}) assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object2})
@ -107,17 +99,15 @@ defmodule RDF.GraphTest do
test "creating a named graph from another graph" do test "creating a named graph from another graph" do
g = g =
Graph.new(Graph.new({EX.Subject, EX.predicate(), EX.Object}), Graph.new({EX.Subject, EX.predicate(), EX.Object})
name: EX.GraphName |> Graph.new(name: EX.GraphName)
)
assert named_graph?(g, iri(EX.GraphName)) assert named_graph?(g, iri(EX.GraphName))
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object}) assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object})
g = g =
Graph.new(Graph.new({EX.Subject, EX.predicate(), EX.Object}, name: EX.OtherGraphName), Graph.new({EX.Subject, EX.predicate(), EX.Object}, name: EX.OtherGraphName)
name: EX.GraphName |> Graph.new(name: EX.GraphName)
)
assert named_graph?(g, iri(EX.GraphName)) assert named_graph?(g, iri(EX.GraphName))
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object}) assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object})
@ -175,19 +165,13 @@ defmodule RDF.GraphTest do
|> Graph.clear() == Graph.new(opts) |> Graph.clear() == Graph.new(opts)
end end
describe "add" do describe "add/2" do
test "a proper triple" do test "a proper triple" do
assert Graph.add(graph(), iri(EX.Subject), EX.predicate(), iri(EX.Object))
|> graph_includes_statement?({EX.Subject, EX.predicate(), EX.Object})
assert Graph.add(graph(), {iri(EX.Subject), EX.predicate(), iri(EX.Object)}) assert Graph.add(graph(), {iri(EX.Subject), EX.predicate(), iri(EX.Object)})
|> graph_includes_statement?({EX.Subject, EX.predicate(), EX.Object}) |> graph_includes_statement?({EX.Subject, EX.predicate(), EX.Object})
end end
test "a coercible triple" do test "a coerced triple" do
assert Graph.add(graph(), "http://example.com/Subject", EX.predicate(), EX.Object)
|> graph_includes_statement?({EX.Subject, EX.predicate(), EX.Object})
assert Graph.add( assert Graph.add(
graph(), graph(),
{"http://example.com/Subject", EX.predicate(), EX.Object} {"http://example.com/Subject", EX.predicate(), EX.Object}
@ -196,7 +180,7 @@ defmodule RDF.GraphTest do
end end
test "a triple with multiple objects" do test "a triple with multiple objects" do
g = Graph.add(graph(), EX.Subject1, EX.predicate1(), [EX.Object1, EX.Object2]) g = Graph.add(graph(), {EX.Subject1, EX.predicate1(), [EX.Object1, EX.Object2]})
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1}) assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object2}) assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object2})
end end
@ -214,7 +198,23 @@ defmodule RDF.GraphTest do
assert graph_includes_statement?(g, {EX.Subject3, EX.predicate3(), EX.Object3}) assert graph_includes_statement?(g, {EX.Subject3, EX.predicate3(), EX.Object3})
end end
test "a Description" do test "a graph map" do
g =
Graph.add(graph(), %{
EX.Subject1 => [{EX.predicate1(), EX.Object1}, {EX.predicate2(), [EX.Object2]}],
EX.Subject3 => %{EX.predicate3() => EX.Object3}
})
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate2(), EX.Object2})
assert graph_includes_statement?(g, {EX.Subject3, EX.predicate3(), EX.Object3})
end
test "an empty map" do
assert Graph.add(graph(), %{}) == graph()
end
test "a description" do
g = g =
Graph.add( Graph.add(
graph(), graph(),
@ -235,7 +235,7 @@ defmodule RDF.GraphTest do
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate3(), EX.Object3}) assert graph_includes_statement?(g, {EX.Subject1, EX.predicate3(), EX.Object3})
end end
test "a list of Descriptions" do test "a list of descriptions" do
g = g =
Graph.add(graph(), [ Graph.add(graph(), [
Description.new(EX.Subject1, init: {EX.predicate1(), EX.Object1}), Description.new(EX.Subject1, init: {EX.predicate1(), EX.Object1}),
@ -253,7 +253,7 @@ defmodule RDF.GraphTest do
assert Graph.add(g, {EX.Subject, EX.predicate(), EX.Object}) == g assert Graph.add(g, {EX.Subject, EX.predicate(), EX.Object}) == g
end end
test "a Graph" do test "a graph" do
g = g =
Graph.add( Graph.add(
graph(), graph(),
@ -311,7 +311,7 @@ defmodule RDF.GraphTest do
test "preserves the name and prefixes when the data provided is not a graph" do test "preserves the name and prefixes when the data provided is not a graph" do
graph = graph =
Graph.new(name: EX.GraphName, prefixes: %{ex: EX}) Graph.new(name: EX.GraphName, prefixes: %{ex: EX})
|> Graph.add(EX.Subject, EX.predicate(), EX.Object) |> Graph.add({EX.Subject, EX.predicate(), EX.Object})
assert graph.name == RDF.iri(EX.GraphName) assert graph.name == RDF.iri(EX.GraphName)
assert graph.prefixes == PrefixMap.new(ex: EX) assert graph.prefixes == PrefixMap.new(ex: EX)
@ -328,7 +328,7 @@ defmodule RDF.GraphTest do
end end
end end
describe "put" do describe "put/2" do
test "a list of triples" do test "a list of triples" do
g = g =
Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}]) Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}])
@ -347,7 +347,38 @@ defmodule RDF.GraphTest do
assert graph_includes_statement?(g, {EX.S2, EX.P2, EX.O4}) assert graph_includes_statement?(g, {EX.S2, EX.P2, EX.O4})
end end
test "a Description" do test "a list subject-predications pairs" do
g =
Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}])
|> RDF.Graph.put([
{EX.S1,
[
{EX.P1, EX.O3},
%{EX.P2 => [EX.O4]}
]}
])
assert Graph.triple_count(g) == 3
assert graph_includes_statement?(g, {EX.S1, EX.P1, EX.O3})
assert graph_includes_statement?(g, {EX.S1, EX.P2, EX.O4})
assert graph_includes_statement?(g, {EX.S2, EX.P2, EX.O2})
end
test "a map" do
g =
Graph.new([{EX.S1, EX.p1(), EX.O1}, {EX.S2, EX.p2(), EX.O2}])
|> Graph.put(%{
EX.S1 => [{EX.p1(), EX.O2}],
EX.S2 => %{EX.p1() => EX.O2},
EX.S3 => %{EX.p3() => EX.O3}
})
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O2})
assert graph_includes_statement?(g, {EX.S2, EX.p1(), EX.O2})
assert graph_includes_statement?(g, {EX.S3, EX.p3(), EX.O3})
end
test "a description" do
g = g =
Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}, {EX.S1, EX.P3, EX.O3}]) Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}, {EX.S1, EX.P3, EX.O3}])
|> RDF.Graph.put( |> RDF.Graph.put(
@ -362,7 +393,7 @@ defmodule RDF.GraphTest do
assert graph_includes_statement?(g, {EX.S2, EX.P2, EX.O2}) assert graph_includes_statement?(g, {EX.S2, EX.P2, EX.O2})
end end
test "a Graph" do test "a graph" do
g = g =
Graph.new([ Graph.new([
{EX.S1, EX.P1, EX.O1}, {EX.S1, EX.P1, EX.O1},
@ -403,7 +434,7 @@ defmodule RDF.GraphTest do
test "preserves the name, base_iri and prefixes" do test "preserves the name, base_iri and prefixes" do
graph = graph =
Graph.new(name: EX.GraphName, prefixes: %{ex: EX}, base_iri: EX.base()) Graph.new(name: EX.GraphName, prefixes: %{ex: EX}, base_iri: EX.base())
|> Graph.put(EX.Subject, EX.predicate(), EX.Object) |> Graph.put({EX.Subject, EX.predicate(), EX.Object})
assert graph.name == RDF.iri(EX.GraphName) assert graph.name == RDF.iri(EX.GraphName)
assert graph.prefixes == PrefixMap.new(ex: EX) assert graph.prefixes == PrefixMap.new(ex: EX)
@ -411,7 +442,7 @@ defmodule RDF.GraphTest do
end end
end end
describe "delete" do describe "delete/2" do
setup do setup do
{:ok, {:ok,
graph1: Graph.new({EX.S, EX.p(), EX.O}), graph1: Graph.new({EX.S, EX.p(), EX.O}),
@ -424,7 +455,7 @@ defmodule RDF.GraphTest do
])} ])}
end end
test "a single statement as a triple", test "a triple",
%{graph1: graph1, graph2: graph2} do %{graph1: graph1, graph2: graph2} do
assert Graph.delete(Graph.new(), {EX.S, EX.p(), EX.O}) == Graph.new() assert Graph.delete(Graph.new(), {EX.S, EX.p(), EX.O}) == Graph.new()
assert Graph.delete(graph1, {EX.S, EX.p(), EX.O}) == Graph.new() assert Graph.delete(graph1, {EX.S, EX.p(), EX.O}) == Graph.new()
@ -436,15 +467,13 @@ defmodule RDF.GraphTest do
Graph.new({EX.S, EX.p(), EX.O2}, name: EX.Graph) Graph.new({EX.S, EX.p(), EX.O2}, name: EX.Graph)
end end
test "multiple statements with a triple with multiple objects", test "a triple with multiple objects", %{graph1: graph1, graph2: graph2} do
%{graph1: graph1, graph2: graph2} do
assert Graph.delete(Graph.new(), {EX.S, EX.p(), [EX.O1, EX.O2]}) == Graph.new() assert Graph.delete(Graph.new(), {EX.S, EX.p(), [EX.O1, EX.O2]}) == Graph.new()
assert Graph.delete(graph1, {EX.S, EX.p(), [EX.O, EX.O2]}) == Graph.new() assert Graph.delete(graph1, {EX.S, EX.p(), [EX.O, EX.O2]}) == Graph.new()
assert Graph.delete(graph2, {EX.S, EX.p(), [EX.O1, EX.O2]}) == Graph.new(name: EX.Graph) assert Graph.delete(graph2, {EX.S, EX.p(), [EX.O1, EX.O2]}) == Graph.new(name: EX.Graph)
end end
test "multiple statements with a list of triples", test "a list of triples", %{graph1: graph1, graph2: graph2, graph3: graph3} do
%{graph1: graph1, graph2: graph2, graph3: graph3} do
assert Graph.delete(graph1, [{EX.S, EX.p(), EX.O}, {EX.S, EX.p(), EX.O2}]) == Graph.new() assert Graph.delete(graph1, [{EX.S, EX.p(), EX.O}, {EX.S, EX.p(), EX.O2}]) == Graph.new()
assert Graph.delete(graph2, [{EX.S, EX.p(), EX.O1}, {EX.S, EX.p(), EX.O2}]) == assert Graph.delete(graph2, [{EX.S, EX.p(), EX.O1}, {EX.S, EX.p(), EX.O2}]) ==
@ -457,7 +486,23 @@ defmodule RDF.GraphTest do
]) == Graph.new({EX.S3, EX.p3(), ~L"bar"}) ]) == Graph.new({EX.S3, EX.p3(), ~L"bar"})
end end
test "multiple statements with a Description", test "a map", %{graph1: graph1, graph2: graph2, graph3: graph3} do
assert Graph.delete(graph1, %{EX.S => {EX.p(), [EX.O, EX.O2]}}) == Graph.new()
assert Graph.delete(graph2, %{EX.S => {EX.p(), [EX.O1, EX.O2]}}) ==
Graph.new(name: EX.Graph)
assert Graph.delete(
graph3,
%{
EX.S1 => %{EX.p1() => [EX.O1, EX.O2]},
EX.S2 => %{EX.p2() => EX.O3},
EX.S3 => %{EX.p3() => ~B<foo>}
}
) == Graph.new({EX.S3, EX.p3(), ~L"bar"})
end
test "a description",
%{graph1: graph1, graph2: graph2, graph3: graph3} do %{graph1: graph1, graph2: graph2, graph3: graph3} do
assert Graph.delete( assert Graph.delete(
graph1, graph1,
@ -476,7 +521,7 @@ defmodule RDF.GraphTest do
]) ])
end end
test "multiple statements with a Graph", test "a graph",
%{graph1: graph1, graph2: graph2, graph3: graph3} do %{graph1: graph1, graph2: graph2, graph3: graph3} do
assert Graph.delete(graph1, graph2) == graph1 assert Graph.delete(graph1, graph2) == graph1
assert Graph.delete(graph1, graph1) == Graph.new() assert Graph.delete(graph1, graph1) == Graph.new()
@ -499,15 +544,15 @@ defmodule RDF.GraphTest do
test "preserves the name and prefixes" do test "preserves the name and prefixes" do
graph = graph =
Graph.new(EX.Subject, EX.predicate(), EX.Object, name: EX.GraphName, prefixes: %{ex: EX}) Graph.new({EX.Subject, EX.predicate(), EX.Object}, name: EX.GraphName, prefixes: %{ex: EX})
|> Graph.delete(EX.Subject, EX.predicate(), EX.Object) |> Graph.delete({EX.Subject, EX.predicate(), EX.Object})
assert graph.name == RDF.iri(EX.GraphName) assert graph.name == RDF.iri(EX.GraphName)
assert graph.prefixes == PrefixMap.new(ex: EX) assert graph.prefixes == PrefixMap.new(ex: EX)
end end
end end
describe "delete_subjects" do describe "delete_subjects/2" do
setup do setup do
{:ok, {:ok,
graph1: Graph.new({EX.S, EX.p(), [EX.O1, EX.O2]}, name: EX.Graph), graph1: Graph.new({EX.S, EX.p(), [EX.O1, EX.O2]}, name: EX.Graph),
@ -596,7 +641,7 @@ defmodule RDF.GraphTest do
assert Graph.new() assert Graph.new()
|> Graph.update(EX.S, {EX.P, EX.O}, fun) == |> Graph.update(EX.S, {EX.P, EX.O}, fun) ==
Graph.new(EX.S, EX.P, EX.O) Graph.new({EX.S, EX.P, EX.O})
assert Graph.new() assert Graph.new()
|> Graph.update(EX.S, fun) == |> Graph.update(EX.S, fun) ==
@ -626,6 +671,33 @@ defmodule RDF.GraphTest do
assert Enum.count(graph.descriptions) == 1 assert Enum.count(graph.descriptions) == 1
end end
test "include?/2" do
graph =
Graph.new([
{EX.S1, EX.p(), EX.O1},
{EX.S2, EX.p(), EX.O2}
])
assert Graph.include?(graph, {EX.S1, EX.p(), EX.O1})
assert Graph.include?(graph, {EX.S1, EX.p(), EX.O1, EX.Graph})
assert Graph.include?(graph, [{EX.S1, EX.p(), EX.O1}])
assert Graph.include?(graph, [
{EX.S1, EX.p(), EX.O1},
{EX.S2, EX.p(), EX.O2}
])
refute Graph.include?(graph, [
{EX.S1, EX.p(), EX.O1},
{EX.S2, EX.p(), EX.O3}
])
assert Graph.include?(graph, EX.S1 |> EX.p(EX.O1))
assert Graph.include?(graph, Graph.new(EX.S1 |> EX.p(EX.O1)))
assert Graph.include?(graph, graph)
end
test "values/1" do test "values/1" do
assert Graph.new() |> Graph.values() == %{} assert Graph.new() |> Graph.values() == %{}
@ -710,7 +782,8 @@ defmodule RDF.GraphTest do
end end
test "equal/2" do test "equal/2" do
assert Graph.new({EX.S, EX.p(), EX.O}) |> Graph.equal?(Graph.new({EX.S, EX.p(), EX.O})) assert Graph.new({EX.S, EX.p(), EX.O})
|> Graph.equal?(Graph.new({EX.S, EX.p(), EX.O}))
assert Graph.new({EX.S, EX.p(), EX.O}, name: EX.Graph1) assert Graph.new({EX.S, EX.p(), EX.O}, name: EX.Graph1)
|> Graph.equal?(Graph.new({EX.S, EX.p(), EX.O}, name: EX.Graph1)) |> Graph.equal?(Graph.new({EX.S, EX.p(), EX.O}, name: EX.Graph1))
@ -721,7 +794,8 @@ defmodule RDF.GraphTest do
assert Graph.new({EX.S, EX.p(), EX.O}, base_iri: EX.base()) assert Graph.new({EX.S, EX.p(), EX.O}, base_iri: EX.base())
|> Graph.equal?(Graph.new({EX.S, EX.p(), EX.O}, base_iri: EX.other_base())) |> Graph.equal?(Graph.new({EX.S, EX.p(), EX.O}, base_iri: EX.other_base()))
refute Graph.new({EX.S, EX.p(), EX.O}) |> Graph.equal?(Graph.new({EX.S, EX.p(), EX.O2})) refute Graph.new({EX.S, EX.p(), EX.O})
|> Graph.equal?(Graph.new({EX.S, EX.p(), EX.O2}))
refute Graph.new({EX.S, EX.p(), EX.O}, name: EX.Graph1) refute Graph.new({EX.S, EX.p(), EX.O}, name: EX.Graph1)
|> Graph.equal?(Graph.new({EX.S, EX.p(), EX.O}, name: EX.Graph2)) |> Graph.equal?(Graph.new({EX.S, EX.p(), EX.O}, name: EX.Graph2))