diff --git a/lib/rdf/graph.ex b/lib/rdf/graph.ex index c894595..9faaa50 100644 --- a/lib/rdf/graph.ex +++ b/lib/rdf/graph.ex @@ -280,7 +280,7 @@ defmodule RDF.Graph do def put(graph, input, opts \\ []) def put(%__MODULE__{} = graph, %__MODULE__{} = input, opts) do - graph = %__MODULE__{ + new_graph = %__MODULE__{ graph | descriptions: Enum.reduce( @@ -293,10 +293,11 @@ defmodule RDF.Graph do } if input.prefixes do - add_prefixes(graph, input.prefixes, :ignore) + add_prefixes(new_graph, input.prefixes, :ignore) else - graph + new_graph end + |> handle_annotation_deletions(graph, input, opts) |> handle_annotation_additions(input, opts) end @@ -327,7 +328,7 @@ defmodule RDF.Graph do def put_properties(graph, input, opts \\ []) def put_properties(%__MODULE__{} = graph, %__MODULE__{} = input, opts) do - graph = %__MODULE__{ + new_graph = %__MODULE__{ graph | descriptions: Enum.reduce( @@ -345,10 +346,11 @@ defmodule RDF.Graph do } if input.prefixes do - add_prefixes(graph, input.prefixes, :ignore) + add_prefixes(new_graph, input.prefixes, :ignore) else - graph + new_graph end + |> handle_annotation_deletions(graph, input, opts) |> handle_annotation_additions(input, opts) end @@ -518,6 +520,19 @@ defmodule RDF.Graph do end end + defp handle_annotation_deletions(graph, original_graph, input, opts) do + if delete_annotations = Keyword.get(opts, :delete_annotations, false) do + diff = + original_graph + |> take(Map.keys(input.descriptions)) + |> RDF.Diff.diff(input) + + delete_annotations(graph, diff.deletions, delete_annotations) + else + graph + end + end + defp clear_annotation_opts(opts), do: Keyword.drop(opts, ~w[add_annotations put_annotations put_annotation_properties]a) diff --git a/test/unit/star/graph_test.exs b/test/unit/star/graph_test.exs index 4fff87f..0c1ae9d 100644 --- a/test/unit/star/graph_test.exs +++ b/test/unit/star/graph_test.exs @@ -614,6 +614,64 @@ defmodule RDF.Star.Graph.Test do expected_graph end + describe "put/3 with delete_annotations option" do + test "no annotations of overwritten statements are removed when delete_annotations is false (default)" do + assert graph() + |> Graph.add({EX.S1, EX.P1, EX.O1}, add_annotations: [{EX.AP, EX.AO}]) + |> Graph.put({EX.S1, EX.P2, EX.O2}, delete_annotations: false) == + graph() + |> Graph.add({{EX.S1, EX.P1, EX.O1}, EX.AP, EX.AO}) + |> Graph.add({EX.S1, EX.P2, EX.O2}) + + assert graph() + |> Graph.add({EX.S1, EX.P1, EX.O1}, add_annotations: [{EX.AP, EX.AO}]) + |> Graph.put({EX.S1, EX.P2, EX.O2}) == + graph() + |> Graph.add({{EX.S1, EX.P1, EX.O1}, EX.AP, EX.AO}) + |> Graph.add({EX.S1, EX.P2, EX.O2}) + end + + test "all annotations of overwritten statements are removed when delete_annotations is true" do + assert graph() + |> Graph.add({EX.S1, EX.P1, EX.O1}, add_annotations: [{EX.AP, EX.AO}]) + |> Graph.put({EX.S1, EX.P2, EX.O2}, delete_annotations: true) == + graph() + |> Graph.add({EX.S1, EX.P2, EX.O2}) + + assert graph() + |> Graph.add( + [ + {EX.S1, EX.P1, EX.O1}, + {EX.S2, EX.P2, EX.O2} + ], + add_annotations: [{EX.AP1, EX.AO1}, {EX.AP2, EX.AO2}] + ) + |> Graph.put({EX.S1, EX.P3, EX.O3}, + add_annotations: [{EX.AP3, EX.AO3}], + delete_annotations: true + ) == + graph() + |> Graph.add([ + {EX.S1, EX.P3, EX.O3}, + {{EX.S1, EX.P3, EX.O3}, {EX.AP3, EX.AO3}}, + {EX.S2, EX.P2, EX.O2}, + {{EX.S2, EX.P2, EX.O2}, {EX.AP1, EX.AO1}}, + {{EX.S2, EX.P2, EX.O2}, {EX.AP2, EX.AO2}} + ]) + end + + test "only the specified annotations of overwritten statements are removed" do + assert graph() + |> Graph.add({EX.S1, EX.P1, EX.O1}, + add_annotations: [{EX.AP1, EX.AO1}, {EX.AP2, EX.AO2}] + ) + |> Graph.put({EX.S1, EX.P2, EX.O2}, delete_annotations: EX.AP1) == + graph() + |> Graph.add({{EX.S1, EX.P1, EX.O1}, EX.AP2, EX.AO2}) + |> Graph.add({EX.S1, EX.P2, EX.O2}) + end + end + test "put_properties/3" do graph = graph() @@ -867,6 +925,64 @@ defmodule RDF.Star.Graph.Test do expected_graph end + describe "put_properties/3 with delete_annotations option" do + test "no annotations of overwritten statements are removed when delete_annotations is false (default)" do + assert graph() + |> Graph.add({EX.S1, EX.P1, EX.O1}, add_annotations: [{EX.AP, EX.AO}]) + |> Graph.put_properties({EX.S1, EX.P1, EX.O2}, delete_annotations: false) == + graph() + |> Graph.add({{EX.S1, EX.P1, EX.O1}, EX.AP, EX.AO}) + |> Graph.add({EX.S1, EX.P1, EX.O2}) + + assert graph() + |> Graph.add({EX.S1, EX.P1, EX.O1}, add_annotations: [{EX.AP, EX.AO}]) + |> Graph.put_properties({EX.S1, EX.P1, EX.O2}) == + graph() + |> Graph.add({{EX.S1, EX.P1, EX.O1}, EX.AP, EX.AO}) + |> Graph.add({EX.S1, EX.P1, EX.O2}) + end + + test "all annotations of overwritten statements are removed when delete_annotations is true" do + assert graph() + |> Graph.add({EX.S1, EX.P1, EX.O1}, add_annotations: [{EX.AP, EX.AO}]) + |> Graph.put_properties({EX.S1, EX.P1, EX.O2}, delete_annotations: true) == + graph() + |> Graph.add({EX.S1, EX.P1, EX.O2}) + + assert graph() + |> Graph.add( + [ + {EX.S1, EX.P1, EX.O1}, + {EX.S2, EX.P2, EX.O2} + ], + add_annotations: [{EX.AP1, EX.AO1}, {EX.AP2, EX.AO2}] + ) + |> Graph.put_properties({EX.S1, EX.P1, EX.O3}, + add_annotations: [{EX.AP3, EX.AO3}], + delete_annotations: true + ) == + graph() + |> Graph.add([ + {EX.S1, EX.P1, EX.O3}, + {{EX.S1, EX.P1, EX.O3}, {EX.AP3, EX.AO3}}, + {EX.S2, EX.P2, EX.O2}, + {{EX.S2, EX.P2, EX.O2}, {EX.AP1, EX.AO1}}, + {{EX.S2, EX.P2, EX.O2}, {EX.AP2, EX.AO2}} + ]) + end + + test "only the specified annotations of overwritten statements are removed" do + assert graph() + |> Graph.add({EX.S1, EX.P1, EX.O1}, + add_annotations: [{EX.AP1, EX.AO1}, {EX.AP2, EX.AO2}] + ) + |> Graph.put_properties({EX.S1, EX.P1, EX.O2}, delete_annotations: EX.AP1) == + graph() + |> Graph.add({{EX.S1, EX.P1, EX.O1}, EX.AP2, EX.AO2}) + |> Graph.add({EX.S1, EX.P1, EX.O2}) + end + end + test "delete/3" do assert graph_with_annotation() |> Graph.delete(star_statement()) == graph() end