diff --git a/lib/rdf/description.ex b/lib/rdf/description.ex index be9d7e6..4ce1f22 100644 --- a/lib/rdf/description.ex +++ b/lib/rdf/description.ex @@ -831,6 +831,30 @@ defmodule RDF.Description do } end + @doc """ + Removes all objects from a description which are quoted triples. + """ + @spec without_quoted_triples(t) :: t + def without_quoted_triples(%__MODULE__{} = description) do + %__MODULE__{ + description + | predications: + Enum.reduce(description.predications, description.predications, fn + {predicate, objects}, predications -> + original_object_count = Enum.count(predications) + + filtered_objects = + Enum.reject(objects, &match?({quoted_triple, _} when is_tuple(quoted_triple), &1)) + + case Enum.count(filtered_objects) do + 0 -> Map.delete(predications, predicate) + ^original_object_count -> predications + _ -> Map.put(predications, predicate, Map.new(filtered_objects)) + end + end) + } + end + @doc """ Checks if two `RDF.Description`s are equal. diff --git a/lib/rdf/graph.ex b/lib/rdf/graph.ex index fdcc208..aaa935a 100644 --- a/lib/rdf/graph.ex +++ b/lib/rdf/graph.ex @@ -663,6 +663,26 @@ defmodule RDF.Graph do @spec annotations(t) :: t defdelegate annotations(graph), to: RDF.Star.Graph + @doc """ + Returns the `RDF.Graph` without all annotations. + + Note: This function excludes only triples where the subject is a quoted triple. + If you want to exclude also triples where the object is a quoted triple, + you'll have to use `RDF.Graph.without_quoted_triples/1`. + """ + @spec without_annotations(t) :: t + defdelegate without_annotations(graph), to: RDF.Star.Graph + + @doc """ + Returns the `RDF.Graph` without all statements including quoted triples on subject or object position. + + This function is relatively costly, since it requires a full walk-through of all triples. + In many cases quoted triples are only used on subject position, where you can use + `RDF.Graph.without_annotations/1`. + """ + @spec without_quoted_triples(t) :: t + defdelegate without_quoted_triples(graph), to: RDF.Star.Graph + @doc """ Gets and updates the description of the given subject, in a single pass. diff --git a/lib/rdf/star/graph.ex b/lib/rdf/star/graph.ex index edc7d55..5d0eb04 100644 --- a/lib/rdf/star/graph.ex +++ b/lib/rdf/star/graph.ex @@ -50,6 +50,40 @@ defmodule RDF.Star.Graph do } end + @spec without_annotations(Graph.t()) :: Graph.t() + def without_annotations(%Graph{} = graph) do + %Graph{ + graph + | descriptions: + for( + non_annotation = {subject, _} when not is_tuple(subject) <- graph.descriptions, + into: %{}, + do: non_annotation + ) + } + end + + @spec without_quoted_triples(Graph.t()) :: Graph.t() + def without_quoted_triples(%Graph{} = graph) do + %Graph{ + graph + | descriptions: + Enum.reduce(graph.descriptions, graph.descriptions, fn + {subject, description}, descriptions when not is_tuple(subject) -> + description_without_quoted_triples = Description.without_quoted_triples(description) + + if Enum.empty?(description_without_quoted_triples) do + Map.delete(descriptions, subject) + else + Map.put(descriptions, subject, description_without_quoted_triples) + end + + {subject, _}, descriptions -> + Map.delete(descriptions, subject) + end) + } + end + @spec add_annotations(Graph.t(), Graph.input(), Description.input() | nil) :: Graph.t() def add_annotations(graph, statements, annotations) diff --git a/test/unit/star/description_test.exs b/test/unit/star/description_test.exs index aa4445d..69642cf 100644 --- a/test/unit/star/description_test.exs +++ b/test/unit/star/description_test.exs @@ -199,6 +199,36 @@ defmodule RDF.Star.Description.Test do assert Description.describes?(annotation(), coercible_statement()) end + describe "without_quoted_triples/1" do + test "empty description" do + assert Description.without_quoted_triples(description()) == Description.new(description()) + end + + test "when the description has no quoted triples on object position" do + description = + Description.new(statement(), + init: [ + {EX.ap1(), EX.ao()}, + {EX.ap2(), EX.ao()} + ] + ) + + assert Description.without_quoted_triples(description) == description + end + + test "when the description has quoted triples" do + assert Description.new(statement(), + init: [ + {EX.ap1(), EX.ao()}, + {EX.ap1(), statement()}, + {EX.ap2(), statement()} + ] + ) + |> Description.without_quoted_triples() == + Description.new(statement(), init: {EX.ap1(), EX.ao()}) + end + end + test "values/2" do assert Description.new(statement(), init: {EX.ap(), statement()}) |> Description.values() == %{} diff --git a/test/unit/star/graph_test.exs b/test/unit/star/graph_test.exs index a5620fe..1f8e045 100644 --- a/test/unit/star/graph_test.exs +++ b/test/unit/star/graph_test.exs @@ -1757,7 +1757,8 @@ defmodule RDF.Star.GraphTest do describe "annotations/1" do test "when no annotations exist" do - assert Graph.annotations(graph()) == graph() + assert Graph.annotations(RDF.graph()) == RDF.graph() + assert Graph.annotations(RDF.graph(statement())) == RDF.graph() end test "when annotations exist" do @@ -1769,6 +1770,52 @@ defmodule RDF.Star.GraphTest do end end + describe "without_annotations/1" do + test "when no annotations exist" do + assert Graph.without_annotations(RDF.graph()) == RDF.graph() + assert Graph.without_annotations(RDF.graph(statement())) == RDF.graph(statement()) + end + + test "when annotations exist" do + assert Graph.without_annotations(graph_with_annotation()) == RDF.graph() + + assert graph_with_annotation() + |> Graph.add(statement()) + |> Graph.without_annotations() == RDF.graph(statement()) + end + + test "quoted triples on object position" do + assert Graph.without_annotations(graph_with_annotations()) == RDF.graph(object_annotation()) + + assert graph_with_annotations() + |> Graph.add(statement()) + |> Graph.without_annotations() == RDF.graph([statement(), object_annotation()]) + end + end + + describe "without_quoted_triples/1" do + test "when no annotations exist" do + assert Graph.without_quoted_triples(RDF.graph()) == RDF.graph() + assert Graph.without_quoted_triples(RDF.graph(statement())) == RDF.graph(statement()) + end + + test "when annotations exist" do + assert Graph.without_quoted_triples(graph_with_annotation()) == RDF.graph() + + assert graph_with_annotation() + |> Graph.add(statement()) + |> Graph.without_quoted_triples() == RDF.graph(statement()) + end + + test "quoted triples on object position" do + assert Graph.without_quoted_triples(graph_with_annotations()) == RDF.graph() + + assert graph_with_annotations() + |> Graph.add(statement()) + |> Graph.without_quoted_triples() == RDF.graph(statement()) + end + end + test "include?/3" do assert Graph.include?(graph_with_annotations(), star_statement()) assert Graph.include?(graph_with_annotations(), {EX.As, EX.ap(), statement()})