diff --git a/CHANGELOG.md b/CHANGELOG.md index 6784239..d235760 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ This project adheres to [Semantic Versioning](http://semver.org/) and - `RDF.Description.take/2` creates a description from another one by limiting its statements to a set of predicates +- `RDF.Graph.take/3` creates a graph from another one by limiting + its statements to a set of subjects and optionally also a set of predicates - Mix formatter configuration for using `defvocab` without parens diff --git a/lib/rdf/description.ex b/lib/rdf/description.ex index cf4904f..22a09bd 100644 --- a/lib/rdf/description.ex +++ b/lib/rdf/description.ex @@ -367,6 +367,15 @@ defmodule RDF.Description do |> List.first end + +# def update(description = %RDF.Description{}, predicate, initial \\ [], fun) do +# triple_predicate = coerce_predicate(predicate) +# description.predicates +# |> Map.update(triple_predicate, initial, fn objects -> +# end) +# end + + @doc """ Gets and updates the objects of the given predicate of a Description, in a single pass. @@ -620,7 +629,13 @@ defmodule RDF.Description do Creates a description from another one by limiting its statements to those using one of the given `predicates`. If `predicates` contains properties that are not used in the `description`, they're simply ignored. + + If `nil` is passed, the description is left untouched. """ + def take(description, predicates) + + def take(%RDF.Description{} = description, nil), do: description + def take(%RDF.Description{predications: predications} = description, predicates) do predicates = Enum.map(predicates, &(coerce_predicate/1)) %RDF.Description{description | predications: Map.take(predications, predicates)} diff --git a/lib/rdf/graph.ex b/lib/rdf/graph.ex index 2410307..6e2436b 100644 --- a/lib/rdf/graph.ex +++ b/lib/rdf/graph.ex @@ -686,6 +686,33 @@ defmodule RDF.Graph do end end + @doc """ + Creates a graph from another one by limiting its statements to those using one of the given `subjects`. + + If `subjects` contains IRIs that are not used in the `graph`, they're simply ignored. + + The optional `properties` argument allows to limit also properties of the subject descriptions. + + If `nil` is passed as the `subjects`, the subjects will not be limited. + """ + def take(graph, subjects, properties \\ nil) + + def take(%RDF.Graph{} = graph, nil, nil), do: graph + + def take(%RDF.Graph{descriptions: descriptions} = graph, subjects, nil) do + subjects = Enum.map(subjects, &(coerce_subject/1)) + %RDF.Graph{graph | descriptions: Map.take(descriptions, subjects)} + end + + def take(%RDF.Graph{} = graph, subjects, properties) do + graph = take(graph, subjects, nil) + %RDF.Graph{graph | + descriptions: Map.new(graph.descriptions, fn {subject, description} -> + {subject, Description.take(description, properties)} + end) + } + end + @doc """ Checks if two `RDF.Graph`s are equal. diff --git a/test/unit/description_test.exs b/test/unit/description_test.exs index eee2ff9..02dc7f1 100644 --- a/test/unit/description_test.exs +++ b/test/unit/description_test.exs @@ -357,10 +357,23 @@ defmodule RDF.DescriptionTest do %{p: ["Foo"]} end - test "take/2" do - assert Description.new([{EX.S, EX.p1, EX.O1}, {EX.S, EX.p2, EX.O2}]) - |> Description.take([EX.p2, EX.p3]) == - Description.new({EX.S, EX.p2, EX.O2}) + describe "take/2" do + test "with a non-empty property list" do + assert Description.new([{EX.S, EX.p1, EX.O1}, {EX.S, EX.p2, EX.O2}]) + |> Description.take([EX.p2, EX.p3]) == + Description.new({EX.S, EX.p2, EX.O2}) + end + + test "with an empty property list" do + assert Description.new([{EX.S, EX.p1, EX.O1}, {EX.S, EX.p2, EX.O2}]) + |> Description.take([]) == Description.new(EX.S) + end + + test "with nil" do + assert Description.new([{EX.S, EX.p1, EX.O1}, {EX.S, EX.p2, EX.O2}]) + |> Description.take(nil) == + Description.new([{EX.S, EX.p1, EX.O1}, {EX.S, EX.p2, EX.O2}]) + end end test "equal/2" do diff --git a/test/unit/graph_test.exs b/test/unit/graph_test.exs index 25b6e8d..6023cd4 100644 --- a/test/unit/graph_test.exs +++ b/test/unit/graph_test.exs @@ -479,6 +479,48 @@ defmodule RDF.GraphTest do } end + describe "take/2" do + test "with a non-empty subject list" do + assert Graph.new([{EX.s1, EX.p, EX.o1}, {EX.s2, EX.p, EX.o2}]) + |> Graph.take([EX.s2, EX.s3]) == + Graph.new([{EX.s2, EX.p, EX.o2}]) + end + + test "with an empty subject list" do + assert Graph.new([{EX.s1, EX.p, EX.o1}, {EX.s2, EX.p, EX.o2}]) + |> Graph.take([]) == Graph.new() + end + + test "with nil" do + assert Graph.new([{EX.s1, EX.p, EX.o1}, {EX.s2, EX.p, EX.o2}]) + |> Graph.take(nil) == + Graph.new([{EX.s1, EX.p, EX.o1}, {EX.s2, EX.p, EX.o2}]) + end + end + + describe "take/3" do + test "with non-empty subject and property lists" do + assert Graph.new([{EX.s1, EX.p1, EX.o1}, {EX.s1, EX.p2, EX.o1}, {EX.s2, EX.p1, EX.o2}]) + |> Graph.take([EX.s1, EX.s3], [EX.p2]) == + Graph.new([{EX.s1, EX.p2, EX.o1}]) + end + + test "with an empty subject list" do + assert Graph.new( + [ + {EX.s1, EX.p1, EX.o1}, + {EX.s1, EX.p2, EX.o1}, + {EX.s2, EX.p1, EX.o2} + ], name: EX.Graph) + |> Graph.take([], [EX.p1]) == Graph.new(name: EX.Graph) + end + + test "with nil" do + assert Graph.new([{EX.s1, EX.p1, EX.o1}, {EX.s1, EX.p2, EX.o1}, {EX.s2, EX.p1, EX.o2}]) + |> Graph.take(nil, [EX.p1]) == + Graph.new([{EX.s1, EX.p1, EX.o1}, {EX.s2, EX.p1, EX.o2}]) + end + end test "equal/2" do assert Graph.new({EX.S, EX.p, EX.O}) |> Graph.equal?(Graph.new({EX.S, EX.p, EX.O}))