From 8b723286c5b820a86bfb8037b87acd7fa52ab972 Mon Sep 17 00:00:00 2001 From: Marcel Otto Date: Mon, 28 Sep 2020 17:39:31 +0200 Subject: [PATCH] Limit and unify the forms of input on RDF.Dataset functions --- lib/rdf/dataset.ex | 259 ++++++++++++++++--------------------- lib/rdf/description.ex | 22 ++-- lib/rdf/graph.ex | 54 ++++---- test/unit/dataset_test.exs | 227 +++++++++++++++++++++++++------- 4 files changed, 325 insertions(+), 237 deletions(-) diff --git a/lib/rdf/dataset.ex b/lib/rdf/dataset.ex index 9d54ca0..f66e4f4 100644 --- a/lib/rdf/dataset.ex +++ b/lib/rdf/dataset.ex @@ -13,10 +13,13 @@ defmodule RDF.Dataset do """ + defstruct name: nil, graphs: %{} + @behaviour Access alias RDF.{Description, Graph, IRI, Statement} import RDF.Statement + import RDF.Utils @type graph_name :: IRI.t() | nil @@ -29,8 +32,6 @@ defmodule RDF.Dataset do @type update_graph_fun :: (Graph.t() -> {Graph.t(), input} | :pop) - defstruct name: nil, graphs: %{} - @doc """ Creates an empty unnamed `RDF.Dataset`. """ @@ -47,12 +48,12 @@ defmodule RDF.Dataset do ## Examples - RDF.Graph.new({EX.S, EX.p, EX.O}) + RDF.Dataset.new({EX.S, EX.p, EX.O}) - RDF.Graph.new(name: EX.GraphName) + RDF.Dataset.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) @@ -69,20 +70,14 @@ defmodule RDF.Dataset do @doc """ Creates an `RDF.Dataset` initialized with data. - The initial RDF triples can be provided - - - as a single statement tuple - - an `RDF.Description` - - an `RDF.Graph` - - an `RDF.Dataset` - - or a list with any combination of the former + The initial RDF triples can be provided in any form accepted by `add/3`. Available options: - `name`: the name of the dataset to be created """ - @spec new(input | [input], keyword) :: t + @spec new(input, keyword) :: t def new(data, options) def new(%__MODULE__{} = graph, options) do @@ -95,88 +90,63 @@ defmodule RDF.Dataset do |> add(data) end + defp destination_graph(opts, default \\ nil) do + opts + |> Keyword.get(:graph, default) + |> coerce_graph_name() + end + @doc """ Adds triples and quads to a `RDF.Dataset`. - The optional third `graph_context` argument allows to set a different - destination graph to which the statements are added, ignoring the graph context - of given quads or the name of given graphs. + The triples can be provided in any form accepted by `add/2`. + + - as a single statement tuple + - an `RDF.Description` + - an `RDF.Graph` + - an `RDF.Dataset` + - or a list with any combination of the former + + The `graph` option allows to set a different destination graph to which the + statements should be added, ignoring the graph context of given quads or the + name of given graphs. """ - @spec add(t, input | [input], boolean | nil) :: t - def add(dataset, statements, graph_context \\ false) + @spec add(t, input, keyword) :: t + def add(dataset, statements, opts \\ []) - def add(dataset, statements, graph_context) when is_list(statements) do - with graph_context = graph_context && coerce_graph_name(graph_context) do - Enum.reduce(statements, dataset, fn statement, dataset -> - add(dataset, statement, graph_context) - end) - end + def add(dataset, {_, _, _, graph} = quad, opts), + do: do_add(dataset, destination_graph(opts, graph), quad) + + def add(dataset, %Graph{} = graph, opts), + do: do_add(dataset, destination_graph(opts, graph.name), graph) + + def add(%__MODULE__{} = dataset, %__MODULE__{} = other_dataset, opts) do + other_dataset + |> graphs() + |> Enum.reduce(dataset, &add(&2, &1, opts)) end - def add(dataset, {subject, predicate, objects}, false), - do: add(dataset, {subject, predicate, objects, nil}) - - def add(dataset, {subject, predicate, objects}, graph_context), - do: add(dataset, {subject, predicate, objects, graph_context}) - - def add( - %__MODULE__{name: name, graphs: graphs}, - {subject, predicate, objects, graph_context}, - false - ) do - with graph_context = coerce_graph_name(graph_context) do - updated_graphs = - Map.update( - graphs, - graph_context, - Graph.new({subject, predicate, objects}, name: graph_context), - fn graph -> Graph.add(graph, {subject, predicate, objects}) end - ) - - %__MODULE__{name: name, graphs: updated_graphs} - end + def add(dataset, input, opts) when is_list(input) or is_map(input) do + Enum.reduce(input, dataset, &add(&2, &1, opts)) end - def add(%__MODULE__{} = dataset, {subject, predicate, objects, _}, graph_context), - do: add(dataset, {subject, predicate, objects, graph_context}, false) + def add(dataset, input, opts), do: do_add(dataset, destination_graph(opts), input) - def add(%__MODULE__{} = dataset, %Description{} = description, false), - do: add(dataset, description, nil) - - def add(%__MODULE__{name: name, graphs: graphs}, %Description{} = description, graph_context) do - with graph_context = coerce_graph_name(graph_context) do - updated_graph = - Map.get(graphs, graph_context, Graph.new(name: graph_context)) - |> Graph.add(description) - - %__MODULE__{ - name: name, - graphs: Map.put(graphs, graph_context, updated_graph) - } - end - end - - def add(%__MODULE__{name: name, graphs: graphs}, %Graph{} = graph, false) do + defp do_add(dataset, graph_name, input) do %__MODULE__{ - name: name, - graphs: - Map.update(graphs, graph.name, graph, fn current -> - Graph.add(current, graph) - end) + dataset + | graphs: + lazy_map_update( + dataset.graphs, + graph_name, + # when new: + fn -> Graph.new(input, name: graph_name) end, + # when update: + fn graph -> Graph.add(graph, input) end + ) } end - def add(%__MODULE__{} = dataset, %Graph{} = graph, graph_context), - do: add(dataset, %Graph{graph | name: coerce_graph_name(graph_context)}, false) - - def add(%__MODULE__{} = dataset, %__MODULE__{} = other_dataset, graph_context) do - with graph_context = graph_context && coerce_graph_name(graph_context) do - Enum.reduce(graphs(other_dataset), dataset, fn graph, dataset -> - add(dataset, graph, graph_context) - end) - 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. @@ -191,7 +161,7 @@ defmodule RDF.Dataset do ...> 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}]) """ - @spec put(t, input | [input], Statement.coercible_graph_name() | boolean | nil) :: t + @spec put(t, input, Statement.coercible_graph_name() | boolean | nil) :: t def put(dataset, statements, graph_context \\ false) def put(%__MODULE__{} = dataset, {subject, predicate, objects}, false), @@ -318,71 +288,51 @@ defmodule RDF.Dataset do @doc """ Deletes statements from a `RDF.Dataset`. - The optional third `graph_context` argument allows to set a different - destination graph from which the statements are deleted, ignoring the graph - context of given quads or the name of given graphs. + The `graph` option allows to set a different destination graph from which the + statements should be deleted, ignoring the graph context of given quads or the + name of given graphs. 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`. """ - @spec delete(t, input | [input], Statement.coercible_graph_name() | boolean | nil) :: t - def delete(dataset, statements, graph_context \\ false) + @spec delete(t, input, keyword) :: t + def delete(dataset, statements, opts \\ []) - def delete(%__MODULE__{} = dataset, statements, graph_context) when is_list(statements) do - with graph_context = graph_context && coerce_graph_name(graph_context) do - Enum.reduce(statements, dataset, fn statement, dataset -> - delete(dataset, statement, graph_context) - end) - end + def delete(dataset, {_, _, _, graph} = quad, opts), + do: do_delete(dataset, destination_graph(opts, graph), quad) + + def delete(dataset, %Graph{} = graph, opts), + do: do_delete(dataset, destination_graph(opts, graph.name), graph) + + def delete(%__MODULE__{} = dataset, %__MODULE__{} = other_dataset, opts) do + other_dataset + |> graphs() + |> Enum.reduce(dataset, &delete(&2, &1, opts)) end - def delete(%__MODULE__{} = dataset, {_, _, _} = statement, false), - do: do_delete(dataset, nil, statement) - - def delete(%__MODULE__{} = dataset, {_, _, _} = statement, graph_context), - do: do_delete(dataset, graph_context, statement) - - def delete(%__MODULE__{} = dataset, {subject, predicate, objects, graph_context}, false), - do: do_delete(dataset, graph_context, {subject, predicate, objects}) - - def delete(%__MODULE__{} = dataset, {subject, predicate, objects, _}, graph_context), - do: do_delete(dataset, graph_context, {subject, predicate, objects}) - - def delete(%__MODULE__{} = dataset, %Description{} = description, false), - do: do_delete(dataset, nil, description) - - def delete(%__MODULE__{} = dataset, %Description{} = description, graph_context), - do: do_delete(dataset, graph_context, description) - - def delete(%__MODULE__{} = dataset, %RDF.Graph{name: name} = graph, false), - do: do_delete(dataset, name, graph) - - def delete(%__MODULE__{} = dataset, %RDF.Graph{} = graph, graph_context), - do: do_delete(dataset, graph_context, graph) - - def delete(%__MODULE__{} = dataset, %__MODULE__{graphs: graphs}, graph_context) do - Enum.reduce(graphs, dataset, fn {_, graph}, dataset -> - delete(dataset, graph, graph_context) - end) + def delete(dataset, input, opts) when is_list(input) or is_map(input) do + Enum.reduce(input, dataset, &delete(&2, &1, opts)) end - defp do_delete(%__MODULE__{name: name, graphs: graphs} = dataset, graph_context, statements) do - with graph_context = coerce_graph_name(graph_context), - graph when not is_nil(graph) <- graphs[graph_context], - new_graph = Graph.delete(graph, statements) do + def delete(dataset, input, opts), do: do_delete(dataset, destination_graph(opts), input) + + defp do_delete(dataset, graph_name, input) do + if existing_graph = dataset.graphs[graph_name] do + new_graph = Graph.delete(existing_graph, input) + %__MODULE__{ - name: name, - graphs: - if Enum.empty?(new_graph) do - Map.delete(graphs, graph_context) - else - Map.put(graphs, graph_context, new_graph) - end + dataset + | graphs: + if Enum.empty?(new_graph) do + Map.delete(dataset.graphs, graph_name) + else + Map.put(dataset.graphs, graph_name, new_graph) + end } else - nil -> dataset + dataset end end @@ -695,7 +645,11 @@ defmodule RDF.Dataset do end @doc """ - Returns if a given statement is in a `RDF.Dataset`. + Checks if the given `input` statements exist within `dataset`. + + The `graph` option allows to set a different destination graph in which the + statements should be checked, ignoring the graph context of given quads or the + name of given graphs. ## Examples @@ -706,21 +660,34 @@ defmodule RDF.Dataset do ...> RDF.Dataset.include?(dataset, {EX.S1, EX.p1, EX.O1, EX.Graph}) true """ - @spec include?(t, Statement.t(), Statement.coercible_graph_name() | nil) :: boolean - def include?(dataset, statement, graph_context \\ nil) + @spec include?(t, input, keyword) :: boolean + def include?(dataset, input, opts \\ []) - def include?(%__MODULE__{graphs: graphs}, triple = {_, _, _}, graph_context) do - with graph_context = coerce_graph_name(graph_context) do - if graph = graphs[graph_context] do - Graph.include?(graph, triple) - else - false - end - end + def include?(dataset, {_, _, _, graph} = quad, opts), + do: do_include?(dataset, destination_graph(opts, graph), quad) + + def include?(dataset, %Graph{} = graph, opts), + do: do_include?(dataset, destination_graph(opts, graph.name), graph) + + def include?(%__MODULE__{} = dataset, %__MODULE__{} = other_dataset, opts) do + other_dataset + |> graphs() + |> Enum.all?(&include?(dataset, &1, opts)) end - def include?(%__MODULE__{} = dataset, {subject, predicate, object, graph_context}, _), - do: include?(dataset, {subject, predicate, object}, graph_context) + def include?(dataset, input, opts) when is_list(input) or is_map(input) do + Enum.all?(input, &include?(dataset, &1, opts)) + end + + def include?(dataset, input, opts), do: do_include?(dataset, destination_graph(opts), input) + + defp do_include?(%__MODULE__{graphs: graphs}, graph_name, input) do + if graph = graphs[graph_name] do + Graph.include?(graph, input) + else + false + end + end @doc """ Checks if a graph of a `RDF.Dataset` contains statements about the given resource. diff --git a/lib/rdf/description.ex b/lib/rdf/description.ex index 9a95be2..87c6528 100644 --- a/lib/rdf/description.ex +++ b/lib/rdf/description.ex @@ -10,14 +10,14 @@ defmodule RDF.Description do - the `RDF.Data` protocol """ + @enforce_keys [:subject] + defstruct subject: nil, predications: %{} + @behaviour Access import RDF.Statement alias RDF.{Statement, Triple} - @enforce_keys [:subject] - defstruct subject: nil, predications: %{} - @type t :: %__MODULE__{ subject: Statement.subject(), predications: predications @@ -128,11 +128,8 @@ defmodule RDF.Description do } end - def add(description, predications, opts) - when is_map(predications) or is_list(predications) do - Enum.reduce(predications, description, fn - predications, description -> add(description, predications, opts) - end) + def add(description, input, opts) when is_map(input) or is_list(input) do + Enum.reduce(input, description, &add(&2, &1, opts)) end def add(%__MODULE__{} = description, {subject, predicate, objects}, opts) do @@ -250,11 +247,8 @@ defmodule RDF.Description do } end - def delete(description, predications, opts) - when is_map(predications) or is_list(predications) do - Enum.reduce(predications, description, fn - predications, description -> delete(description, predications, opts) - end) + def delete(description, input, opts) when is_map(input) or is_list(input) do + Enum.reduce(input, description, &delete(&2, &1, opts)) end def delete(%__MODULE__{} = description, {subject, predicate, objects}, opts) do @@ -603,7 +597,7 @@ defmodule RDF.Description do end @doc """ - Checks if the given statement exists within a `RDF.Description`. + Checks if the given `input` statements exist within `description`. """ @spec include?(t, input) :: boolean def include?(description, input) diff --git a/lib/rdf/graph.ex b/lib/rdf/graph.ex index d417244..d6d5c9c 100644 --- a/lib/rdf/graph.ex +++ b/lib/rdf/graph.ex @@ -11,14 +11,14 @@ defmodule RDF.Graph do """ + defstruct name: nil, descriptions: %{}, prefixes: nil, base_iri: nil + @behaviour Access import RDF.Statement import RDF.Utils alias RDF.{Description, IRI, PrefixMap, Statement} - defstruct name: nil, descriptions: %{}, prefixes: nil, base_iri: nil - @type graph_description :: %{Statement.subject() => Description.t()} @type t :: %__MODULE__{ @@ -175,15 +175,15 @@ defmodule RDF.Graph do @spec add(t, input) :: t def add(graph, input) + def add(graph, {subject, predications}), + do: do_add(graph, coerce_subject(subject), predications) + def add(%__MODULE__{} = graph, {subject, _, _} = triple), do: do_add(graph, coerce_subject(subject), triple) def add(graph, {subject, predicate, object, _}), do: add(graph, {subject, predicate, object}) - def add(graph, {subject, predications}), - do: do_add(graph, coerce_subject(subject), predications) - def add(%__MODULE__{} = graph, %Description{subject: subject} = description) do if Description.count(description) > 0 do do_add(graph, subject, description) @@ -205,10 +205,8 @@ defmodule RDF.Graph do 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) + def add(graph, input) when is_list(input) or is_map(input) do + Enum.reduce(input, graph, &add(&2, &1)) end defp do_add(%__MODULE__{descriptions: descriptions} = graph, subject, statements) do @@ -241,13 +239,13 @@ defmodule RDF.Graph do """ @spec put(t, input) :: t - def put(graph, statements) + def put(graph, input) def put(graph, {subject, predications}), do: do_put(graph, coerce_subject(subject), predications) - def put(%__MODULE__{} = graph, {subject, _, _} = statement), - do: do_put(graph, coerce_subject(subject), statement) + 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}) @@ -268,17 +266,17 @@ defmodule RDF.Graph do end end - def put(%__MODULE__{} = graph, statements) when is_map(statements) do - Enum.reduce(statements, graph, fn {subject, predications}, graph -> + 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, statements) when is_list(statements) do + def put(%__MODULE__{} = graph, input) when is_list(input) do put( graph, Enum.group_by( - statements, + input, fn {subject, _} -> subject {subject, _, _} -> subject @@ -318,7 +316,7 @@ defmodule RDF.Graph do use `RDF.Data.delete/2`. """ - @spec delete(t, input | [input]) :: t + @spec delete(t, input) :: t def delete(graph, triples) def delete(%__MODULE__{} = graph, {subject, _, _} = triple), @@ -330,10 +328,8 @@ defmodule RDF.Graph do def delete(graph, {subject, predicate, object, _}), do: delete(graph, {subject, predicate, object}) - def delete(%__MODULE__{} = graph, statements) when is_list(statements) or is_map(statements) do - Enum.reduce(statements, graph, fn statement, graph -> - delete(graph, statement) - end) + def delete(%__MODULE__{} = graph, input) when is_list(input) or is_map(input) do + Enum.reduce(input, graph, &delete(&2, &1)) end def delete(%__MODULE__{} = graph, %Description{subject: subject} = description), @@ -345,9 +341,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, input) do if description = descriptions[subject] do - new_description = Description.delete(description, statements) + new_description = Description.delete(description, input) %__MODULE__{ graph @@ -373,9 +369,7 @@ defmodule RDF.Graph do def delete_subjects(graph, subjects) def delete_subjects(%__MODULE__{} = graph, subjects) when is_list(subjects) do - Enum.reduce(subjects, graph, fn subject, graph -> - delete_subjects(graph, subject) - end) + Enum.reduce(subjects, graph, &delete_subjects(&2, &1)) end def delete_subjects(%__MODULE__{descriptions: descriptions} = graph, subject) do @@ -762,9 +756,11 @@ defmodule RDF.Graph do defdelegate statements(graph), to: RDF.Graph, as: :triples @doc """ - Checks if the given statement exists within a `RDF.Graph`. + Checks if the given `input` statements exist within `graph`. """ @spec include?(t, input) :: boolean + def include?(graph, input) + def include?(%__MODULE__{} = graph, {subject, _, _} = triple), do: do_include?(graph, coerce_subject(subject), triple) @@ -787,9 +783,9 @@ defmodule RDF.Graph do Enum.all?(statements, &include?(graph, &1)) end - defp do_include?(%__MODULE__{descriptions: descriptions}, subject, statements) do + defp do_include?(%__MODULE__{descriptions: descriptions}, subject, input) do if description = descriptions[subject] do - Description.include?(description, statements) + Description.include?(description, input) else false end diff --git a/test/unit/dataset_test.exs b/test/unit/dataset_test.exs index 9c4457f..a64e836 100644 --- a/test/unit/dataset_test.exs +++ b/test/unit/dataset_test.exs @@ -152,7 +152,7 @@ defmodule RDF.DatasetTest do end end - describe "add" do + describe "add/3" do test "a proper triple is added to the default graph" do assert Dataset.add(dataset(), {iri(EX.Subject), EX.predicate(), iri(EX.Object)}) |> dataset_includes_statement?({EX.Subject, EX.predicate(), EX.Object}) @@ -190,11 +190,13 @@ defmodule RDF.DatasetTest do |> dataset_includes_statement?({EX.Subject, EX.predicate(), EX.Object, EX.GraphName}) end - test "a quad and an overwriting graph context " do - assert Dataset.add(dataset(), {EX.Subject, EX.predicate(), EX.Object, EX.Graph}, EX.Other) + test "a quad and an overwriting graph context" do + assert Dataset.add(dataset(), {EX.Subject, EX.predicate(), EX.Object, EX.Graph}, + graph: EX.Other + ) |> dataset_includes_statement?({EX.Subject, EX.predicate(), EX.Object, EX.Other}) - assert Dataset.add(dataset(), {EX.Subject, EX.predicate(), EX.Object, EX.Graph}, nil) + assert Dataset.add(dataset(), {EX.Subject, EX.predicate(), EX.Object, EX.Graph}, graph: nil) |> dataset_includes_statement?({EX.Subject, EX.predicate(), EX.Object}) end @@ -220,7 +222,7 @@ defmodule RDF.DatasetTest do ) end - test "a list of triples without specification of the default context" do + test "a list of triples without an overwriting graph context" do ds = Dataset.add(dataset(), [ {EX.Subject1, EX.predicate1(), EX.Object1}, @@ -233,7 +235,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject3, EX.predicate3(), EX.Object3}) end - test "a list of triples with specification of the default context" do + test "a list of triples with an overwriting graph context" do ds = Dataset.add( dataset(), @@ -242,7 +244,7 @@ defmodule RDF.DatasetTest do {EX.Subject1, EX.predicate2(), EX.Object2}, {EX.Subject3, EX.predicate3(), EX.Object3} ], - EX.Graph + graph: EX.Graph ) assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1(), EX.Object1, EX.Graph}) @@ -257,7 +259,7 @@ defmodule RDF.DatasetTest do {EX.Subject1, EX.predicate2(), EX.Object2}, {EX.Subject3, EX.predicate3(), EX.Object3} ], - nil + graph: nil ) assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1(), EX.Object1, nil}) @@ -265,7 +267,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject3, EX.predicate3(), EX.Object3, nil}) end - test "a list of quads without specification of the default context" do + test "a list of quads without an overwriting graph context" do ds = Dataset.add(dataset(), [ {EX.Subject, EX.predicate1(), EX.Object1, EX.Graph1}, @@ -278,7 +280,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate1(), EX.Object1, EX.Graph2}) end - test "a list of quads with specification of the default context" do + test "a list of quads with an overwriting graph context" do ds = Dataset.add( dataset(), @@ -287,7 +289,7 @@ defmodule RDF.DatasetTest do {EX.Subject, EX.predicate2(), EX.Object2, nil}, {EX.Subject, EX.predicate1(), EX.Object1, EX.Graph2} ], - EX.Graph + graph: EX.Graph ) assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate1(), EX.Object1, EX.Graph}) @@ -302,7 +304,7 @@ defmodule RDF.DatasetTest do {EX.Subject, EX.predicate2(), EX.Object2, nil}, {EX.Subject, EX.predicate1(), EX.Object1, EX.Graph2} ], - nil + graph: nil ) assert dataset_includes_statement?(ds, {EX.Subject, EX.predicate1(), EX.Object1, nil}) @@ -313,19 +315,77 @@ defmodule RDF.DatasetTest do 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} + {EX.S1, EX.p1(), EX.O1, EX.Graph}, + {EX.S3, EX.p3(), EX.O3} ]) - assert dataset_includes_statement?( - ds, - {EX.Subject1, EX.predicate1(), EX.Object1, EX.GraphName} - ) + assert dataset_includes_statement?(ds, {EX.S1, EX.p1(), EX.O1, EX.Graph}) + assert dataset_includes_statement?(ds, {EX.S3, EX.p3(), EX.O3, nil}) + ds = + Dataset.add( + dataset(), + [ + {EX.S1, EX.p1(), EX.O1, EX.Graph}, + {EX.S3, EX.p3(), EX.O3} + ], + graph: EX.Graph2 + ) + + assert dataset_includes_statement?(ds, {EX.S1, EX.p1(), EX.O1, EX.Graph2}) + assert dataset_includes_statement?(ds, {EX.S3, EX.p3(), EX.O3, EX.Graph2}) + end + + test "a map without an overwriting graph context" do + ds = + Dataset.add(dataset(), %{ + EX.Subject1 => %{ + EX.predicate1() => EX.Object1, + 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 map with an overwriting graph context" do + ds = + Dataset.add( + dataset(), + %{ + EX.Subject1 => [ + {EX.predicate2(), EX.Object2}, + {EX.predicate1(), EX.Object1} + ], + EX.Subject3 => %{EX.predicate3() => EX.Object3} + }, + graph: 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, EX.Graph}) + + ds = + Dataset.add( + dataset(), + [ + {EX.Subject1, EX.predicate1(), EX.Object1}, + {EX.Subject1, EX.predicate2(), EX.Object2}, + {EX.Subject3, EX.predicate3(), EX.Object3} + ], + graph: nil + ) + + assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1(), EX.Object1, nil}) + assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2(), EX.Object2, nil}) assert dataset_includes_statement?(ds, {EX.Subject3, EX.predicate3(), EX.Object3, nil}) end - test "a Description without specification of the default context" do + test "a description without an overwriting graph context" do ds = Dataset.add( dataset(), @@ -340,7 +400,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2(), EX.Object2}) end - test "a Description with specification of the default context" do + test "a description with an overwriting graph context" do ds = Dataset.add( dataset(), @@ -350,7 +410,7 @@ defmodule RDF.DatasetTest do {EX.predicate2(), EX.Object2} ] ), - nil + graph: nil ) assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1(), EX.Object1}) @@ -360,7 +420,7 @@ defmodule RDF.DatasetTest do Dataset.add( ds, Description.new(EX.Subject1, init: {EX.predicate3(), EX.Object3}), - EX.Graph + graph: EX.Graph ) assert Enum.count(ds) == 3 @@ -369,7 +429,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate3(), EX.Object3, EX.Graph}) end - test "an unnamed Graph without specification of the default context" do + test "an unnamed graph without an overwriting graph context" do ds = Dataset.add( dataset(), @@ -391,7 +451,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2(), EX.Object3}) end - test "an unnamed Graph with specification of the default context" do + test "an unnamed graph with an overwriting graph context" do ds = Dataset.add( dataset(), @@ -399,21 +459,21 @@ defmodule RDF.DatasetTest do {EX.Subject1, EX.predicate1(), EX.Object1}, {EX.Subject1, EX.predicate2(), EX.Object2} ]), - nil + graph: nil ) assert unnamed_graph?(Dataset.default_graph(ds)) 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, Graph.new({EX.Subject1, EX.predicate2(), EX.Object3}), nil) + ds = Dataset.add(ds, Graph.new({EX.Subject1, EX.predicate2(), EX.Object3}), graph: nil) assert unnamed_graph?(Dataset.default_graph(ds)) assert Enum.count(ds) == 3 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.predicate2(), EX.Object3}) - ds = Dataset.add(ds, Graph.new({EX.Subject1, EX.predicate2(), EX.Object3}), EX.Graph) + ds = Dataset.add(ds, Graph.new({EX.Subject1, EX.predicate2(), EX.Object3}), graph: EX.Graph) assert unnamed_graph?(Dataset.default_graph(ds)) assert named_graph?(Dataset.graph(ds, EX.Graph), iri(EX.Graph)) assert Enum.count(ds) == 4 @@ -423,7 +483,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2(), EX.Object3, EX.Graph}) end - test "a named Graph without specification of the default context" do + test "a named graph without an overwriting graph context" do ds = Dataset.add( dataset(), @@ -472,7 +532,7 @@ defmodule RDF.DatasetTest do ) end - test "a named Graph with specification of the default context" do + test "a named graph with an overwriting graph context" do ds = Dataset.add( dataset(), @@ -483,7 +543,7 @@ defmodule RDF.DatasetTest do ], name: EX.Graph1 ), - nil + graph: nil ) refute Dataset.graph(ds, EX.Graph1) @@ -495,7 +555,7 @@ defmodule RDF.DatasetTest do Dataset.add( ds, Graph.new({EX.Subject1, EX.predicate2(), EX.Object3}, name: EX.Graph2), - nil + graph: nil ) refute Dataset.graph(ds, EX.Graph2) @@ -509,7 +569,7 @@ defmodule RDF.DatasetTest do Dataset.add( ds, Graph.new({EX.Subject1, EX.predicate2(), EX.Object3}, name: EX.Graph3), - EX.Graph + graph: EX.Graph ) assert named_graph?(Dataset.graph(ds, EX.Graph), iri(EX.Graph)) @@ -520,7 +580,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2(), EX.Object3, EX.Graph}) end - test "an unnamed Dataset" do + test "an unnamed dataset" do ds = Dataset.add( dataset(), @@ -536,7 +596,10 @@ defmodule RDF.DatasetTest do ds = Dataset.add(ds, Dataset.new({EX.Subject1, EX.predicate2(), EX.Object3})) ds = Dataset.add(ds, Dataset.new({EX.Subject1, EX.predicate2(), EX.Object3, EX.Graph})) - ds = Dataset.add(ds, Dataset.new({EX.Subject1, EX.predicate2(), EX.Object4}), EX.Graph) + + ds = + Dataset.add(ds, Dataset.new({EX.Subject1, EX.predicate2(), EX.Object4}), graph: EX.Graph) + assert ds.name == nil assert Enum.count(ds) == 5 assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1(), EX.Object1}) @@ -546,7 +609,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2(), EX.Object4, EX.Graph}) end - test "a named Dataset" do + test "a named dataset" do ds = Dataset.add( named_dataset(), @@ -575,7 +638,7 @@ defmodule RDF.DatasetTest do Dataset.add( ds, Dataset.new({EX.Subject1, EX.predicate2(), EX.Object4}, name: EX.DS2), - EX.Graph + graph: EX.Graph ) assert ds.name == iri(EX.DatasetName) @@ -587,7 +650,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate2(), EX.Object4, EX.Graph}) end - test "a list of Descriptions" do + test "a list of descriptions" do ds = Dataset.add(dataset(), [ Description.new(EX.Subject1, init: {EX.predicate1(), EX.Object1}), @@ -607,7 +670,7 @@ defmodule RDF.DatasetTest do Description.new(EX.Subject2, init: {EX.predicate2(), EX.Object2}), Description.new(EX.Subject1, init: {EX.predicate3(), EX.Object3}) ], - EX.Graph + graph: EX.Graph ) assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate1(), EX.Object1, EX.Graph}) @@ -618,7 +681,7 @@ defmodule RDF.DatasetTest do assert dataset_includes_statement?(ds, {EX.Subject1, EX.predicate3(), EX.Object3}) end - test "a list of Graphs" do + test "a list of graphs" do ds = Dataset.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}]) |> RDF.Dataset.add([ @@ -653,6 +716,12 @@ defmodule RDF.DatasetTest do assert_raise RDF.IRI.InvalidError, fn -> Dataset.add(dataset(), {iri(EX.Subject), EX.predicate(), iri(EX.Object), "not a IRI"}) end + + assert_raise RDF.IRI.InvalidError, fn -> + Dataset.add(dataset(), {iri(EX.Subject), EX.predicate(), iri(EX.Object)}, + graph: "not a IRI" + ) + end end end @@ -815,7 +884,7 @@ defmodule RDF.DatasetTest do {EX.S3, EX.p3(), [~B, ~L"bar"], EX.Graph2} ]) - assert Dataset.delete(dataset3, {EX.S2, EX.p2(), [EX.O1, EX.O2]}, EX.Graph1) == + assert Dataset.delete(dataset3, {EX.S2, EX.p2(), [EX.O1, EX.O2]}, graph: EX.Graph1) == Dataset.new([ {EX.S1, EX.p1(), EX.O1}, {EX.S3, EX.p3(), [~B, ~L"bar"], EX.Graph2} @@ -837,22 +906,49 @@ defmodule RDF.DatasetTest do ]) == Dataset.new({EX.S3, EX.p3(), ~L"bar", EX.Graph2}) end - test "multiple statements with a Description", + test "multiple statements with a map", + %{dataset1: dataset1, dataset2: dataset2} do + assert Dataset.delete(dataset1, %{EX.S1 => {EX.p1(), EX.O1}}) == + Dataset.new() + + assert Dataset.delete( + dataset1, + %{EX.S1 => {EX.p1(), EX.O1}}, + graph: EX.Graph + ) == + dataset1 + + assert Dataset.delete( + dataset2, + %{EX.S2 => %{EX.p2() => EX.O2}}, + graph: EX.Graph + ) == + dataset1 + + assert Dataset.delete(dataset2, %{EX.S1 => %{EX.p1() => EX.O1}}) == + Dataset.new({EX.S2, EX.p2(), EX.O2, EX.Graph}) + end + + test "multiple statements with a description", %{dataset1: dataset1, dataset2: dataset2} do assert Dataset.delete(dataset1, Description.new(EX.S1, init: {EX.p1(), EX.O1})) == Dataset.new() - assert Dataset.delete(dataset1, Description.new(EX.S1, init: {EX.p1(), EX.O1}), EX.Graph) == + assert Dataset.delete(dataset1, Description.new(EX.S1, init: {EX.p1(), EX.O1}), + graph: EX.Graph + ) == dataset1 - assert Dataset.delete(dataset2, Description.new(EX.S2, init: {EX.p2(), EX.O2}), EX.Graph) == + assert Dataset.delete(dataset2, Description.new(EX.S2, init: {EX.p2(), EX.O2}), + graph: EX.Graph + ) == dataset1 assert Dataset.delete(dataset2, Description.new(EX.S1, init: {EX.p1(), EX.O1})) == Dataset.new({EX.S2, EX.p2(), EX.O2, EX.Graph}) end - test "multiple statements with a Graph", + 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() @@ -865,8 +961,11 @@ defmodule RDF.DatasetTest do assert Dataset.delete(dataset2, Graph.new({EX.S2, EX.p2(), EX.O2}, name: EX.Graph)) == dataset1 - 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(dataset2, Graph.new({EX.S2, EX.p2(), EX.O2}), graph: EX.Graph) == + dataset1 + + assert Dataset.delete(dataset2, Graph.new({EX.S2, EX.p2(), EX.O2}), graph: EX.Graph) == + dataset1 assert Dataset.delete( dataset3, @@ -898,7 +997,7 @@ defmodule RDF.DatasetTest do {EX.S3, EX.p3(), [~L"bar"], EX.Graph2} ]) - assert Dataset.delete(dataset3, Graph.new({EX.S3, EX.p3(), ~B}), EX.Graph2) == + assert Dataset.delete(dataset3, Graph.new({EX.S3, EX.p3(), ~B}), graph: EX.Graph2) == Dataset.new([ {EX.S1, EX.p1(), EX.O1}, {EX.S2, EX.p2(), [EX.O1, EX.O2], EX.Graph1}, @@ -906,7 +1005,7 @@ defmodule RDF.DatasetTest do ]) end - test "multiple statements with a Dataset", + 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() @@ -978,6 +1077,38 @@ defmodule RDF.DatasetTest do assert Enum.count(dataset.graphs) == 1 end + test "include?/3" do + dataset = + Dataset.new([ + {EX.S1, EX.p(), EX.O1}, + {EX.S2, EX.p(), EX.O2, EX.Graph} + ]) + + assert Dataset.include?(dataset, {EX.S1, EX.p(), EX.O1}) + refute Dataset.include?(dataset, {EX.S2, EX.p(), EX.O2}) + assert Dataset.include?(dataset, {EX.S2, EX.p(), EX.O2, EX.Graph}) + assert Dataset.include?(dataset, {EX.S2, EX.p(), EX.O2}, graph: EX.Graph) + + assert Dataset.include?(dataset, [{EX.S1, EX.p(), EX.O1}]) + assert Dataset.include?(dataset, [{EX.S2, EX.p(), EX.O2}], graph: EX.Graph) + + assert Dataset.include?(dataset, [ + {EX.S1, EX.p(), EX.O1}, + {EX.S2, EX.p(), EX.O2, EX.Graph} + ]) + + refute Dataset.include?(dataset, [ + {EX.S1, EX.p(), EX.O1}, + {EX.S2, EX.p(), EX.O2} + ]) + + assert Dataset.include?(dataset, EX.S1 |> EX.p(EX.O1)) + refute Dataset.include?(dataset, EX.S2 |> EX.p(EX.O2)) + assert Dataset.include?(dataset, EX.p(EX.S2, EX.O2), graph: EX.Graph) + assert Dataset.include?(dataset, Graph.new(EX.S1 |> EX.p(EX.O1))) + assert Dataset.include?(dataset, dataset) + end + test "values/1" do assert Dataset.new() |> Dataset.values() == %{}