Improve implementation of RDF.Graph.put/3

- fixes bugs with some combinations of possible inputs
- simplify implementation
- add opts
This commit is contained in:
Marcel Otto 2020-10-04 03:04:47 +02:00
parent 4dfc16a55a
commit 42f787836f
2 changed files with 116 additions and 90 deletions

View file

@ -162,7 +162,6 @@ defmodule RDF.Graph do
- a `RDF.Graph`
- or a list with any combination of the former
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
are added. As opposed to that, `RDF.Data.merge/2` will produce a `RDF.Dataset`
@ -172,44 +171,44 @@ defmodule RDF.Graph do
prefixes of this graph will be added. In case of conflicting prefix mappings
the original prefix from `graph` will be kept.
"""
@spec add(t, input) :: t
def add(graph, input)
@spec add(t, input, keyword) :: t
def add(graph, input, opts \\ [])
def add(graph, {subject, predications}),
do: do_add(graph, coerce_subject(subject), predications)
def add(graph, {subject, predications}, opts),
do: do_add(graph, coerce_subject(subject), predications, opts)
def add(%__MODULE__{} = graph, {subject, _, _} = triple),
do: do_add(graph, coerce_subject(subject), triple)
def add(%__MODULE__{} = graph, {subject, _, _} = triple, opts),
do: do_add(graph, coerce_subject(subject), triple, opts)
def add(graph, {subject, predicate, object, _}),
do: add(graph, {subject, predicate, object})
def add(graph, {subject, predicate, object, _}, opts),
do: add(graph, {subject, predicate, object}, opts)
def add(%__MODULE__{} = graph, %Description{subject: subject} = description) do
def add(%__MODULE__{} = graph, %Description{subject: subject} = description, opts) do
if Description.count(description) > 0 do
do_add(graph, subject, description)
do_add(graph, subject, description, opts)
else
graph
end
end
def add(graph, %__MODULE__{descriptions: descriptions, prefixes: prefixes}) do
def add(graph, %__MODULE__{descriptions: descriptions, prefixes: prefixes}, opts) do
graph =
Enum.reduce(descriptions, graph, fn {_, description}, graph ->
add(graph, description)
add(graph, description, opts)
end)
if prefixes do
add_prefixes(graph, prefixes, fn _, ns, _ -> ns end)
add_prefixes(graph, prefixes, :ignore)
else
graph
end
end
def add(graph, input) when is_list(input) or is_map(input) do
Enum.reduce(input, graph, &add(&2, &1))
def add(graph, input, opts) when is_list(input) or is_map(input) do
Enum.reduce(input, graph, &add(&2, &1, opts))
end
defp do_add(%__MODULE__{descriptions: descriptions} = graph, subject, statements) do
defp do_add(%__MODULE__{descriptions: descriptions} = graph, subject, statements, opts) do
%__MODULE__{
graph
| descriptions:
@ -217,9 +216,9 @@ defmodule RDF.Graph do
descriptions,
subject,
# when new: create and initialize description with statements
fn -> Description.new(subject, init: statements) end,
fn -> Description.new(subject, Keyword.put(opts, :init, statements)) end,
# when update: merge statements description
fn description -> Description.add(description, statements) end
fn description -> Description.add(description, statements, opts) end
)
}
end
@ -238,79 +237,36 @@ defmodule RDF.Graph do
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) :: t
def put(graph, input)
@spec put(t, input, keyword) :: t
def put(graph, input, opts \\ [])
def put(graph, {subject, predications}),
do: do_put(graph, coerce_subject(subject), predications)
def put(%__MODULE__{} = graph, {subject, _, _} = triple),
do: do_put(graph, coerce_subject(subject), triple)
def put(graph, {subject, predicate, object, _}),
do: put(graph, {subject, predicate, object})
def put(%__MODULE__{} = graph, %Description{subject: subject} = description) do
if Description.count(description) > 0 do
do_put(graph, subject, description)
else
graph
end
end
def put(graph, %__MODULE__{descriptions: descriptions, prefixes: prefixes}) do
graph =
Enum.reduce(descriptions, graph, fn {_, description}, graph ->
put(graph, description)
end)
if prefixes do
add_prefixes(graph, prefixes, fn _, ns, _ -> ns end)
else
graph
end
end
def put(%__MODULE__{} = graph, input) when is_map(input) do
Enum.reduce(input, graph, fn {subject, predications}, graph ->
put(graph, {subject, predications})
end)
end
def put(%__MODULE__{} = graph, input) when is_list(input) do
put(
graph,
Enum.group_by(
input,
fn
{subject, _} -> subject
{subject, _, _} -> subject
{subject, _, _, _} -> subject
%Description{subject: subject} -> subject
end,
fn
{_, p, o} -> {p, o}
{_, p, o, _} -> {p, o}
{_, predications} -> predications
%Description{} = description -> description
end
)
)
end
defp do_put(%__MODULE__{descriptions: descriptions} = graph, subject, predications) do
%__MODULE__{
def put(%__MODULE__{} = graph, %__MODULE__{} = input, opts) do
graph = %__MODULE__{
graph
| descriptions:
lazy_map_update(
descriptions,
subject,
# when new
fn -> Description.new(subject, init: predications) end,
# when updating
fn current -> Description.put(current, predications) end
Enum.reduce(
input.descriptions,
graph.descriptions,
fn {subject, description}, descriptions ->
Map.update(
descriptions,
subject,
description,
fn current -> Description.put(current, description, opts) end
)
end
)
}
if input.prefixes do
add_prefixes(graph, input.prefixes, :ignore)
else
graph
end
end
def put(%__MODULE__{} = graph, input, opts) do
put(graph, new() |> add(input, opts), opts)
end
@doc """

View file

@ -174,7 +174,7 @@ defmodule RDF.GraphTest do
assert Graph.change_name(named_graph(), nil).name == nil
end
describe "add/2" do
describe "add/3" 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})
@ -207,6 +207,50 @@ defmodule RDF.GraphTest do
assert graph_includes_statement?(g, {EX.Subject3, EX.predicate3(), EX.Object3})
end
test "a list of subject-predications pairs" do
g =
Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}])
|> RDF.Graph.add([
{EX.S1,
[
{EX.P1, EX.O3},
%{EX.P2 => [EX.O4]}
]}
])
assert Graph.triple_count(g) == 4
assert graph_includes_statement?(g, {EX.S1, EX.P1, EX.O1})
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 mixed list" do
g =
Graph.new([{EX.S1, EX.p1(), EX.O1}, {EX.S2, EX.p2(), EX.O2}, {EX.S1, EX.p3(), EX.O3}])
|> RDF.Graph.add([
%{EX.S1 => {EX.p1(), EX.O41}},
%{
EX.S1 => %{EX.p1() => EX.O42},
EX.S2 => %{EX.p2() => EX.O42}
},
{EX.S2, {EX.p2(), EX.O43}},
[{EX.S2, {EX.p2(), EX.O44}}],
EX.p2(EX.S2, EX.O45)
])
assert Graph.triple_count(g) == 9
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O1})
assert graph_includes_statement?(g, {EX.S1, EX.p3(), EX.O3})
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O41})
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O42})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O2})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O42})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O43})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O44})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O45})
end
test "a graph map" do
g =
Graph.add(graph(), %{
@ -342,7 +386,7 @@ defmodule RDF.GraphTest do
end
end
describe "put/2" do
describe "put/3" do
test "a list of triples" do
g =
Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}])
@ -379,7 +423,7 @@ defmodule RDF.GraphTest do
assert graph_includes_statement?(g, {EX.S2, EX.P2, EX.O3})
end
test "a list subject-predications pairs" do
test "a list of subject-predications pairs" do
g =
Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}])
|> RDF.Graph.put([
@ -396,6 +440,30 @@ defmodule RDF.GraphTest do
assert graph_includes_statement?(g, {EX.S2, EX.P2, EX.O2})
end
test "a mixed list" do
g =
Graph.new([{EX.S1, EX.p1(), EX.O1}, {EX.S2, EX.p2(), EX.O2}, {EX.S1, EX.p3(), EX.O3}])
|> RDF.Graph.put([
%{EX.S1 => {EX.p1(), EX.O41}},
%{
EX.S1 => %{EX.p1() => EX.O42},
EX.S2 => %{EX.p2() => EX.O42}
},
{EX.S2, {EX.p2(), EX.O43}},
[{EX.S2, {EX.p2(), EX.O44}}],
EX.p2(EX.S2, EX.O45)
])
assert Graph.triple_count(g) == 7
assert graph_includes_statement?(g, {EX.S1, EX.p3(), EX.O3})
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O41})
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O42})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O42})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O43})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O44})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O45})
end
test "a map" do
g =
Graph.new([{EX.S1, EX.p1(), EX.O1}, {EX.S2, EX.p2(), EX.O2}])
@ -405,7 +473,9 @@ defmodule RDF.GraphTest do
EX.S3 => %{EX.p3() => EX.O3}
})
assert Graph.triple_count(g) == 4
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O2})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), 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