Skip to content

Commit

Permalink
Make intersection/2 available on Elixir >= v1.15 only
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelotto committed Mar 14, 2024
1 parent 48961ec commit d4b5bee
Show file tree
Hide file tree
Showing 7 changed files with 328 additions and 309 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ Elixir versions < 1.12 are no longer supported
- `canonical_hash/2` function on `RDF.Dataset`, `RDF.Graph` and `RDF.Description` which
computes a hash value for the data based on the `RDF.Canonicalization` algorithm
- `intersection/2` function on `RDF.Dataset`, `RDF.Graph` and `RDF.Description` which
create respective graph data intersections
create respective graph data intersections. Since this feature relies on `Map.intersect/3`
that was added in Elixir v1.15, it is only available with a respective Elixir version.
- `RDF.Dataset.put_graph/3` adds new graphs overwriting any existing graphs
- `RDF.Dataset.update/4` to update graphs in a `RDF.Dataset`
- `RDF.Graph.delete_predications/3` to delete all statements in a `RDF.Graph` with
Expand Down
65 changes: 34 additions & 31 deletions lib/rdf/model/dataset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -932,41 +932,44 @@ defmodule RDF.Dataset do
|> Enum.map(& &1.name)
end

@doc """
Returns a new dataset that is the intersection of the given `dataset` with the given `data`.
# This function relies on Map.intersect/3 that was added in Elixir v1.15
if Version.match?(System.version(), ">= 1.15.0") do
@doc """
Returns a new dataset that is the intersection of the given `dataset` with the given `data`.
The `data` can be given in any form an `RDF.Dataset` can be created from.
The `data` can be given in any form an `RDF.Dataset` can be created from.
## Examples
## Examples
iex> RDF.Dataset.new([
...> {EX.S1, EX.p(), [EX.O1, EX.O2]},
...> {EX.S2, EX.p(), EX.O3, EX.Graph}
...> ])
...> |> RDF.Dataset.intersection([
...> {EX.S1, EX.p(), EX.O2},
...> {EX.S2, EX.p(), EX.O3}
...> ])
RDF.Dataset.new({EX.S1, EX.p(), EX.O2})
"""
@spec intersection(t(), t() | Graph.t() | Description.t() | input()) :: t()
def intersection(dataset, data)

def intersection(%__MODULE__{} = dataset1, %__MODULE__{} = dataset2) do
intersection =
dataset1.graphs
|> Map.intersect(dataset2.graphs, fn _, g1, g2 ->
graph_intersection = Graph.intersection(g1, g2)
unless Graph.empty?(graph_intersection), do: graph_intersection
end)
|> RDF.Utils.reject_empty_map_values()

%__MODULE__{dataset1 | graphs: intersection}
end
iex> RDF.Dataset.new([
...> {EX.S1, EX.p(), [EX.O1, EX.O2]},
...> {EX.S2, EX.p(), EX.O3, EX.Graph}
...> ])
...> |> RDF.Dataset.intersection([
...> {EX.S1, EX.p(), EX.O2},
...> {EX.S2, EX.p(), EX.O3}
...> ])
RDF.Dataset.new({EX.S1, EX.p(), EX.O2})
"""
@spec intersection(t(), t() | Graph.t() | Description.t() | input()) :: t()
def intersection(dataset, data)

def intersection(%__MODULE__{} = dataset1, %__MODULE__{} = dataset2) do
intersection =
dataset1.graphs
|> Map.intersect(dataset2.graphs, fn _, g1, g2 ->
graph_intersection = Graph.intersection(g1, g2)
unless Graph.empty?(graph_intersection), do: graph_intersection
end)
|> RDF.Utils.reject_empty_map_values()

%__MODULE__{dataset1 | graphs: intersection}
end

def intersection(%__MODULE__{} = dataset, data) do
intersection(dataset, new(data))
def intersection(%__MODULE__{} = dataset, data) do
intersection(dataset, new(data))
end
end

@doc """
Expand Down
107 changes: 55 additions & 52 deletions lib/rdf/model/description.ex
Original file line number Diff line number Diff line change
Expand Up @@ -850,66 +850,69 @@ defmodule RDF.Description do
}
end

@doc """
Returns a new description that is the intersection of the given `description` with the given `data`.
The `data` can be given in any form an `RDF.Graph` can be created from.
When a `RDF.Dataset` is given, the aggregated description of the subject of
`description` is used for the intersection.
## Examples
iex> EX.S
...> |> EX.p(EX.O1, EX.O2)
...> |> RDF.Description.intersection(EX.S |> EX.p(EX.O2, EX.O3))
EX.S |> EX.p(EX.O2)
iex> EX.S
...> |> EX.p(EX.O1, EX.O2)
...> |> RDF.Description.intersection({EX.Other, EX.p, EX.O2, EX.O3})
RDF.Description.new(EX.S)
"""
@spec intersection(t(), t() | Graph.t() | Dataset.t() | Graph.input()) :: t()
def intersection(description, data)

def intersection(
%__MODULE__{subject: subject} = description1,
%__MODULE__{subject: subject} = description2
) do
intersection =
description1.predications
|> Map.intersect(description2.predications, fn _, o1, o2 ->
objects_intersection = Map.intersect(o1, o2)
if objects_intersection != %{}, do: objects_intersection
end)
|> RDF.Utils.reject_empty_map_values()
# This function relies on Map.intersect/3 that was added in Elixir v1.15
if Version.match?(System.version(), ">= 1.15.0") do
@doc """
Returns a new description that is the intersection of the given `description` with the given `data`.
The `data` can be given in any form an `RDF.Graph` can be created from.
When a `RDF.Dataset` is given, the aggregated description of the subject of
`description` is used for the intersection.
## Examples
iex> EX.S
...> |> EX.p(EX.O1, EX.O2)
...> |> RDF.Description.intersection(EX.S |> EX.p(EX.O2, EX.O3))
EX.S |> EX.p(EX.O2)
iex> EX.S
...> |> EX.p(EX.O1, EX.O2)
...> |> RDF.Description.intersection({EX.Other, EX.p, EX.O2, EX.O3})
RDF.Description.new(EX.S)
"""
@spec intersection(t(), t() | Graph.t() | Dataset.t() | Graph.input()) :: t()
def intersection(description, data)

def intersection(
%__MODULE__{subject: subject} = description1,
%__MODULE__{subject: subject} = description2
) do
intersection =
description1.predications
|> Map.intersect(description2.predications, fn _, o1, o2 ->
objects_intersection = Map.intersect(o1, o2)
if objects_intersection != %{}, do: objects_intersection
end)
|> RDF.Utils.reject_empty_map_values()

%__MODULE__{description1 | predications: intersection}
end
%__MODULE__{description1 | predications: intersection}
end

def intersection(%__MODULE__{subject: subject}, %__MODULE__{}), do: new(subject)
def intersection(%__MODULE__{subject: subject}, %__MODULE__{}), do: new(subject)

def intersection(%__MODULE__{subject: subject} = description, %Graph{} = graph) do
if description2 = Graph.get(graph, subject) do
intersection(description, description2)
else
new(subject)
def intersection(%__MODULE__{subject: subject} = description, %Graph{} = graph) do
if description2 = Graph.get(graph, subject) do
intersection(description, description2)
else
new(subject)
end
end
end

def intersection(%__MODULE__{subject: subject} = description, %Dataset{} = dataset) do
description2 = RDF.Data.description(dataset, subject)
def intersection(%__MODULE__{subject: subject} = description, %Dataset{} = dataset) do
description2 = RDF.Data.description(dataset, subject)

if empty?(description2) do
new(subject)
else
intersection(description, description2)
if empty?(description2) do
new(subject)
else
intersection(description, description2)
end
end
end

def intersection(%__MODULE__{} = description, data) do
intersection(description, Graph.new(data))
def intersection(%__MODULE__{} = description, data) do
intersection(description, Graph.new(data))
end
end

@doc """
Expand Down
79 changes: 41 additions & 38 deletions lib/rdf/model/graph.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1142,47 +1142,50 @@ defmodule RDF.Graph do
Map.has_key?(graph.descriptions, RDF.coerce_subject(subject))
end

@doc """
Returns a new graph that is the intersection of the given `graph` with the given `data`.
The `data` can be given in any form an `RDF.Graph` can be created from.
When a `RDF.Dataset` is given, the aggregation of all of its graphs is used
for the intersection.
## Examples
iex> RDF.Graph.new({EX.S1, EX.p(), [EX.O1, EX.O2]})
...> |> RDF.Graph.intersection({EX.S1, EX.p(), [EX.O2, EX.O3]})
RDF.Graph.new({EX.S1, EX.p(), EX.O2})
"""
@spec intersection(t(), t() | Dataset.t() | Description.t() | input()) :: t()
def intersection(graph, data)

def intersection(%__MODULE__{} = graph1, %__MODULE__{} = graph2) do
intersection =
graph1.descriptions
|> Map.intersect(graph2.descriptions, fn _, p1, p2 ->
description_intersection = Description.intersection(p1, p2)
unless Description.empty?(description_intersection), do: description_intersection
end)
|> RDF.Utils.reject_empty_map_values()

%__MODULE__{graph1 | descriptions: intersection}
end
# This function relies on Map.intersect/3 that was added in Elixir v1.15
if Version.match?(System.version(), ">= 1.15.0") do
@doc """
Returns a new graph that is the intersection of the given `graph` with the given `data`.
The `data` can be given in any form an `RDF.Graph` can be created from.
When a `RDF.Dataset` is given, the aggregation of all of its graphs is used
for the intersection.
## Examples
iex> RDF.Graph.new({EX.S1, EX.p(), [EX.O1, EX.O2]})
...> |> RDF.Graph.intersection({EX.S1, EX.p(), [EX.O2, EX.O3]})
RDF.Graph.new({EX.S1, EX.p(), EX.O2})
"""
@spec intersection(t(), t() | Dataset.t() | Description.t() | input()) :: t()
def intersection(graph, data)

def intersection(%__MODULE__{} = graph1, %__MODULE__{} = graph2) do
intersection =
graph1.descriptions
|> Map.intersect(graph2.descriptions, fn _, p1, p2 ->
description_intersection = Description.intersection(p1, p2)
unless Description.empty?(description_intersection), do: description_intersection
end)
|> RDF.Utils.reject_empty_map_values()

%__MODULE__{graph1 | descriptions: intersection}
end

def intersection(%__MODULE__{} = graph, %Description{subject: subject} = description) do
if description2 = get(graph, subject) do
description
|> Description.intersection(description2)
|> new()
else
new()
def intersection(%__MODULE__{} = graph, %Description{subject: subject} = description) do
if description2 = get(graph, subject) do
description
|> Description.intersection(description2)
|> new()
else
new()
end
end
end

def intersection(%__MODULE__{} = graph, data) do
intersection(graph, new(data))
def intersection(%__MODULE__{} = graph, data) do
intersection(graph, new(data))
end
end

@doc """
Expand Down
Loading

0 comments on commit d4b5bee

Please sign in to comment.