From 25a2d963f601c1f67554fdf0acdd1ff99d4f29b9 Mon Sep 17 00:00:00 2001 From: Marcel Otto Date: Sun, 4 Jun 2017 03:58:30 +0200 Subject: [PATCH] core: add RDF.Dataset.delete --- lib/rdf/dataset.ex | 62 ++++++++++++++++++++++ test/unit/dataset_test.exs | 102 +++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) diff --git a/lib/rdf/dataset.ex b/lib/rdf/dataset.ex index b094544..ff9086c 100644 --- a/lib/rdf/dataset.ex +++ b/lib/rdf/dataset.ex @@ -255,6 +255,68 @@ defmodule RDF.Dataset do do: put(dataset, {subject, graph_context}, predications, graph_context) + @doc """ + Deletes statements from a `RDF.Dataset`. + + The optional third argument `graph_context` defaulting to `nil` for the default + graph, specifies the graph to which the statements are added. + Note that this also applies when deleting a named graph. Its name is ignored over + `graph_context` and its default value. + + Note: When the statements to be deleted are given as another `RDF.Dataset`, + the dataset name must not match dataset name of the dataset from which the statements + are deleted. If you want to delete only datasets with matching names, you can + use `RDF.Data.delete/2`. + + """ + def delete(dataset, statements, graph_context \\ nil) + + def delete(%RDF.Dataset{} = dataset, statements, graph_context) when is_list(statements) do + with graph_context = convert_graph_name(graph_context) do + Enum.reduce statements, dataset, fn (statement, dataset) -> + delete(dataset, statement, graph_context) + end + end + end + + def delete(%RDF.Dataset{} = dataset, {_, _, _} = statement, graph_context), + do: do_delete(dataset, graph_context, statement) + + def delete(%RDF.Dataset{} = dataset, {subject, predicate, objects, graph_context}, _), + do: do_delete(dataset, graph_context, {subject, predicate, objects}) + + def delete(%RDF.Dataset{} = dataset, %Description{} = description, graph_context), + do: do_delete(dataset, graph_context, description) + + def delete(%RDF.Dataset{} = dataset, %RDF.Graph{} = graph, graph_context), + do: do_delete(dataset, graph_context, graph) + + def delete(%RDF.Dataset{} = dataset, %RDF.Dataset{graphs: graphs}, _) do + Enum.reduce graphs, dataset, fn ({_, graph}, dataset) -> + delete(dataset, graph) + end + end + + defp do_delete(%RDF.Dataset{name: name, graphs: graphs} = dataset, + graph_context, statements) do + with graph_context = convert_graph_name(graph_context), + graph when not is_nil(graph) <- graphs[graph_context], + new_graph = Graph.delete(graph, statements) + do + %RDF.Dataset{name: name, + graphs: + if Enum.empty?(new_graph) do + Map.delete(graphs, graph_context) + else + Map.put(graphs, graph_context, new_graph) + end + } + else + nil -> dataset + end + end + + @doc """ Deletes the given graph. """ diff --git a/test/unit/dataset_test.exs b/test/unit/dataset_test.exs index b7e0a60..c3169ae 100644 --- a/test/unit/dataset_test.exs +++ b/test/unit/dataset_test.exs @@ -389,6 +389,108 @@ defmodule RDF.DatasetTest do end + describe "delete" do + setup do + {:ok, + dataset1: Dataset.new({EX.S1, EX.p1, EX.O1}), + dataset2: Dataset.new([ + {EX.S1, EX.p1, EX.O1}, + {EX.S2, EX.p2, EX.O2, EX.Graph}, + ]), + dataset3: Dataset.new([ + {EX.S1, EX.p1, EX.O1}, + {EX.S2, EX.p2, [EX.O1, EX.O2], EX.Graph1}, + {EX.S3, EX.p3, [~B, ~L"bar"], EX.Graph2}, + ]), + } + end + + test "a single statement", + %{dataset1: dataset1, dataset2: dataset2, dataset3: dataset3} do + assert Dataset.delete(Dataset.new, {EX.S, EX.p, EX.O}) == Dataset.new + assert Dataset.delete(dataset1, {EX.S1, EX.p1, EX.O1}) == Dataset.new + assert Dataset.delete(dataset2, {EX.S2, EX.p2, EX.O2, EX.Graph}) == dataset1 + assert Dataset.delete(dataset2, {EX.S1, EX.p1, EX.O1}) == + Dataset.new({EX.S2, EX.p2, EX.O2, EX.Graph}) + assert Dataset.delete(dataset3, {EX.S2, EX.p2, EX.O1, EX.Graph1}) == + Dataset.new [ + {EX.S1, EX.p1, EX.O1}, + {EX.S2, EX.p2, EX.O2, EX.Graph1}, + {EX.S3, EX.p3, [~B, ~L"bar"], EX.Graph2}, + ] + assert Dataset.delete(dataset3, {EX.S2, EX.p2, [EX.O1, EX.O2], EX.Graph1}) == + Dataset.new [ + {EX.S1, EX.p1, EX.O1}, + {EX.S3, EX.p3, [~B, ~L"bar"], EX.Graph2}, + ] + assert Dataset.delete(dataset3, {EX.S2, EX.p2, [EX.O1, EX.O2]}, EX.Graph1) == + Dataset.new [ + {EX.S1, EX.p1, EX.O1}, + {EX.S3, EX.p3, [~B, ~L"bar"], EX.Graph2}, + ] + end + + test "multiple statements with a list of triples", + %{dataset1: dataset1, dataset2: dataset2, dataset3: dataset3} do + assert Dataset.delete(dataset1, [{EX.S1, EX.p1, EX.O1}, + {EX.S1, EX.p1, EX.O2}]) == Dataset.new + assert Dataset.delete(dataset2, [{EX.S1, EX.p1, EX.O1}, + {EX.S2, EX.p2, EX.O2, EX.Graph}]) == Dataset.new + assert Dataset.delete(dataset3, [ + {EX.S1, EX.p1, EX.O1}, + {EX.S2, EX.p2, [EX.O1, EX.O2, EX.O3], EX.Graph1}, + {EX.S3, EX.p3, ~B, EX.Graph2}]) == Dataset.new({EX.S3, EX.p3, ~L"bar", EX.Graph2}) + end + + test "multiple statements with a Description", + %{dataset1: dataset1, dataset2: dataset2} do + assert Dataset.delete(dataset1, Description.new(EX.S1, EX.p1, EX.O1)) == Dataset.new + assert Dataset.delete(dataset1, Description.new(EX.S1, EX.p1, EX.O1), EX.Graph) == dataset1 + assert Dataset.delete(dataset2, Description.new(EX.S2, EX.p2, EX.O2), EX.Graph) == dataset1 + assert Dataset.delete(dataset2, Description.new(EX.S1, EX.p1, EX.O1)) == + Dataset.new({EX.S2, EX.p2, EX.O2, EX.Graph}) + end + + test "multiple statements with a Graph", + %{dataset1: dataset1, dataset2: dataset2, dataset3: dataset3} do + assert Dataset.delete(dataset1, Graph.new({EX.S1, EX.p1, EX.O1})) == Dataset.new + assert Dataset.delete(dataset2, Graph.new({EX.S1, EX.p1, EX.O1})) == + Dataset.new({EX.S2, EX.p2, EX.O2, EX.Graph}) + assert Dataset.delete(dataset2, Graph.new({EX.S2, EX.p2, EX.O2}), EX.Graph) == dataset1 + assert Dataset.delete(dataset2, Graph.new({EX.S2, EX.p2, EX.O2}), EX.Graph) == dataset1 + assert Dataset.delete(dataset3, Graph.new([ + {EX.S1, EX.p1, [EX.O1, EX.O2]}, + {EX.S2, EX.p2, EX.O3}, + {EX.S3, EX.p3, ~B}, + ])) == Dataset.new([ + {EX.S2, EX.p2, [EX.O1, EX.O2], EX.Graph1}, + {EX.S3, EX.p3, [~B, ~L"bar"], EX.Graph2}, + ]) + assert Dataset.delete(dataset3, Graph.new({EX.S3, EX.p3, ~B}), EX.Graph2) == + Dataset.new([ + {EX.S1, EX.p1, EX.O1}, + {EX.S2, EX.p2, [EX.O1, EX.O2], EX.Graph1}, + {EX.S3, EX.p3, ~L"bar", EX.Graph2}, + ]) + end + + test "the name of a graph is ignored", + %{dataset1: dataset1, dataset2: dataset2} do + assert Dataset.delete(dataset1, Graph.new(EX.Graph, {EX.S1, EX.p1, EX.O1})) == Dataset.new + assert Dataset.delete(dataset2, Graph.new(EX.Graph, {EX.S1, EX.p1, EX.O1})) == + Dataset.new({EX.S2, EX.p2, EX.O2, EX.Graph}) + assert Dataset.delete(dataset2, Graph.new(EX.Graph, {EX.S2, EX.p2, EX.O2})) == dataset2 + end + + test "multiple statements with a Dataset", + %{dataset1: dataset1, dataset2: dataset2} do + assert Dataset.delete(dataset1, dataset1) == Dataset.new + assert Dataset.delete(dataset1, dataset2) == Dataset.new + assert Dataset.delete(dataset2, dataset1) == Dataset.new({EX.S2, EX.p2, EX.O2, EX.Graph}) + end + end + + describe "delete_graph" do setup do {:ok,