2016-10-15 16:26:56 +00:00
|
|
|
defmodule RDF.Description do
|
|
|
|
@moduledoc """
|
2017-06-16 20:50:56 +00:00
|
|
|
A set of RDF triples about the same subject.
|
2016-10-15 16:26:56 +00:00
|
|
|
|
2017-06-16 20:50:56 +00:00
|
|
|
`RDF.Description` implements:
|
|
|
|
|
2018-03-19 00:50:05 +00:00
|
|
|
- Elixir's `Access` behaviour
|
|
|
|
- Elixir's `Enumerable` protocol
|
|
|
|
- Elixir's `Inspect` protocol
|
2017-06-16 20:50:56 +00:00
|
|
|
- the `RDF.Data` protocol
|
2016-10-15 16:26:56 +00:00
|
|
|
"""
|
2017-06-16 20:50:56 +00:00
|
|
|
|
2016-11-11 15:40:58 +00:00
|
|
|
@behaviour Access
|
|
|
|
|
2017-04-12 14:48:17 +00:00
|
|
|
import RDF.Statement
|
2020-03-02 01:07:31 +00:00
|
|
|
alias RDF.{Statement, Triple}
|
2016-10-15 16:26:56 +00:00
|
|
|
|
2020-02-28 17:51:48 +00:00
|
|
|
@type predications :: %{Statement.predicate => %{Statement.object => nil}}
|
|
|
|
|
2020-03-02 01:07:31 +00:00
|
|
|
@type statements ::
|
|
|
|
{Statement.coercible_predicate,
|
|
|
|
Statement.coercible_object | [Statement.coercible_predicate]}
|
|
|
|
| Statement.t
|
|
|
|
| predications
|
|
|
|
| t
|
|
|
|
|
2020-02-28 17:51:48 +00:00
|
|
|
@type t :: %__MODULE__{
|
|
|
|
subject: Statement.subject,
|
|
|
|
predications: predications
|
|
|
|
}
|
|
|
|
|
|
|
|
@enforce_keys [:subject]
|
|
|
|
defstruct subject: nil, predications: %{}
|
|
|
|
|
2016-10-15 16:26:56 +00:00
|
|
|
|
|
|
|
@doc """
|
2017-06-16 21:03:44 +00:00
|
|
|
Creates a new `RDF.Description` about the given subject with optional initial statements.
|
2016-10-15 16:26:56 +00:00
|
|
|
|
|
|
|
When given a list of statements, the first one must contain a subject.
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec new(Statement.coercible_subject | statements | [statements]) :: t
|
2016-10-15 16:26:56 +00:00
|
|
|
def new(subject)
|
|
|
|
|
|
|
|
def new({subject, predicate, object}),
|
2016-11-11 15:40:58 +00:00
|
|
|
do: new(subject) |> add(predicate, object)
|
2016-10-15 16:26:56 +00:00
|
|
|
def new([statement | more_statements]),
|
|
|
|
do: new(statement) |> add(more_statements)
|
2020-03-02 01:07:31 +00:00
|
|
|
def new(%__MODULE__{} = description),
|
2017-06-02 14:49:14 +00:00
|
|
|
do: description
|
2016-10-15 16:26:56 +00:00
|
|
|
def new(subject),
|
2020-03-02 01:07:31 +00:00
|
|
|
do: %__MODULE__{subject: coerce_subject(subject)}
|
2017-05-25 13:29:39 +00:00
|
|
|
|
2017-06-16 21:03:44 +00:00
|
|
|
@doc """
|
|
|
|
Creates a new `RDF.Description` about the given subject with optional initial statements.
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec new(Statement.coercible_subject, statements | [statements]) :: t
|
2017-06-15 15:51:55 +00:00
|
|
|
def new(subject, {predicate, objects}),
|
|
|
|
do: new(subject) |> add(predicate, objects)
|
2016-11-26 22:45:41 +00:00
|
|
|
def new(subject, statements) when is_list(statements),
|
|
|
|
do: new(subject) |> add(statements)
|
2017-03-31 14:08:06 +00:00
|
|
|
def new(subject, %RDF.Description{predications: predications}),
|
|
|
|
do: %RDF.Description{new(subject) | predications: predications}
|
2016-11-24 07:44:07 +00:00
|
|
|
def new(subject, predications = %{}),
|
|
|
|
do: new(subject) |> add(predications)
|
2016-11-11 15:40:58 +00:00
|
|
|
|
2017-06-16 21:03:44 +00:00
|
|
|
@doc """
|
|
|
|
Creates a new `RDF.Description` about the given subject with optional initial statements.
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec new(
|
|
|
|
Statement.coercible_subject | statements | [statements],
|
|
|
|
Statement.coercible_predicate,
|
|
|
|
Statement.coercible_object | [Statement.coercible_object]
|
|
|
|
) :: t
|
2017-06-16 21:03:44 +00:00
|
|
|
def new(%RDF.Description{} = description, predicate, objects),
|
2020-03-02 01:07:31 +00:00
|
|
|
do: add(description, predicate, objects)
|
2017-06-15 15:51:55 +00:00
|
|
|
def new(subject, predicate, objects),
|
|
|
|
do: new(subject) |> add(predicate, objects)
|
|
|
|
|
2016-10-15 16:26:56 +00:00
|
|
|
|
|
|
|
@doc """
|
2016-11-11 15:40:58 +00:00
|
|
|
Add objects to a predicate of a `RDF.Description`.
|
|
|
|
|
2017-06-16 22:27:05 +00:00
|
|
|
## Examples
|
2016-11-11 15:40:58 +00:00
|
|
|
|
|
|
|
iex> RDF.Description.add(RDF.Description.new({EX.S, EX.P1, EX.O1}), EX.P2, EX.O2)
|
|
|
|
RDF.Description.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}])
|
|
|
|
iex> RDF.Description.add(RDF.Description.new({EX.S, EX.P, EX.O1}), EX.P, [EX.O2, EX.O3])
|
|
|
|
RDF.Description.new([{EX.S, EX.P, EX.O1}, {EX.S, EX.P, EX.O2}, {EX.S, EX.P, EX.O3}])
|
2016-10-15 16:26:56 +00:00
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec add(
|
|
|
|
t,
|
|
|
|
Statement.coercible_predicate,
|
|
|
|
Statement.coercible_object | [Statement.coercible_object]
|
|
|
|
) :: t
|
2016-11-11 15:40:58 +00:00
|
|
|
def add(description, predicate, objects)
|
2016-10-15 16:26:56 +00:00
|
|
|
|
2016-11-11 15:40:58 +00:00
|
|
|
def add(description, predicate, objects) when is_list(objects) do
|
|
|
|
Enum.reduce objects, description, fn (object, description) ->
|
|
|
|
add(description, predicate, object)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def add(%RDF.Description{subject: subject, predications: predications}, predicate, object) do
|
2017-08-11 20:22:27 +00:00
|
|
|
with triple_predicate = coerce_predicate(predicate),
|
|
|
|
triple_object = coerce_object(object),
|
2016-11-11 15:40:58 +00:00
|
|
|
new_predications = Map.update(predications,
|
2016-10-15 16:26:56 +00:00
|
|
|
triple_predicate, %{triple_object => nil}, fn objects ->
|
2016-11-26 22:45:41 +00:00
|
|
|
Map.put_new(objects, triple_object, nil)
|
|
|
|
end) do
|
2016-11-11 15:40:58 +00:00
|
|
|
%RDF.Description{subject: subject, predications: new_predications}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Adds statements to a `RDF.Description`.
|
2017-06-02 13:12:53 +00:00
|
|
|
|
|
|
|
Note: When the statements to be added are given as another `RDF.Description`,
|
|
|
|
the subject must not match subject of the description to which the statements
|
|
|
|
are added. As opposed to that `RDF.Data.merge/2` will produce a `RDF.Graph`
|
|
|
|
containing both descriptions.
|
2016-11-11 15:40:58 +00:00
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec add(t, statements | [statements]) :: t
|
2016-11-11 15:40:58 +00:00
|
|
|
def add(description, statements)
|
|
|
|
|
|
|
|
def add(description, {predicate, object}),
|
|
|
|
do: add(description, predicate, object)
|
|
|
|
|
|
|
|
def add(description = %RDF.Description{}, {subject, predicate, object}) do
|
2017-08-11 20:22:27 +00:00
|
|
|
if coerce_subject(subject) == description.subject,
|
2016-11-11 15:40:58 +00:00
|
|
|
do: add(description, predicate, object),
|
|
|
|
else: description
|
|
|
|
end
|
|
|
|
|
2017-06-05 13:50:31 +00:00
|
|
|
def add(description, {subject, predicate, object, _}),
|
|
|
|
do: add(description, {subject, predicate, object})
|
|
|
|
|
2016-11-11 15:40:58 +00:00
|
|
|
def add(description, statements) when is_list(statements) do
|
|
|
|
Enum.reduce statements, description, fn (statement, description) ->
|
|
|
|
add(description, statement)
|
2016-10-15 16:26:56 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-11-15 00:26:57 +00:00
|
|
|
def add(%RDF.Description{subject: subject, predications: predications},
|
|
|
|
%RDF.Description{predications: other_predications}) do
|
|
|
|
merged_predications = Map.merge predications, other_predications,
|
|
|
|
fn (_, objects, other_objects) -> Map.merge(objects, other_objects) end
|
|
|
|
%RDF.Description{subject: subject, predications: merged_predications}
|
|
|
|
end
|
2016-11-11 15:40:58 +00:00
|
|
|
|
2016-11-24 07:44:07 +00:00
|
|
|
def add(description = %RDF.Description{}, predications = %{}) do
|
|
|
|
Enum.reduce predications, description, fn ({predicate, objects}, description) ->
|
|
|
|
add(description, predicate, objects)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2016-11-11 15:40:58 +00:00
|
|
|
@doc """
|
|
|
|
Puts objects to a predicate of a `RDF.Description`, overwriting all existing objects.
|
|
|
|
|
2017-06-16 22:27:05 +00:00
|
|
|
## Examples
|
2016-11-11 15:40:58 +00:00
|
|
|
|
|
|
|
iex> RDF.Description.put(RDF.Description.new({EX.S, EX.P, EX.O1}), EX.P, EX.O2)
|
|
|
|
RDF.Description.new([{EX.S, EX.P, EX.O2}])
|
|
|
|
iex> RDF.Description.put(RDF.Description.new({EX.S, EX.P1, EX.O1}), EX.P2, EX.O2)
|
|
|
|
RDF.Description.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}])
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec put(
|
|
|
|
t,
|
|
|
|
Statement.coercible_predicate,
|
|
|
|
Statement.coercible_object | [Statement.coercible_object]
|
|
|
|
) :: t
|
2016-11-11 15:40:58 +00:00
|
|
|
def put(description, predicate, objects)
|
|
|
|
|
|
|
|
def put(%RDF.Description{subject: subject, predications: predications},
|
|
|
|
predicate, objects) when is_list(objects) do
|
2017-08-11 20:22:27 +00:00
|
|
|
with triple_predicate = coerce_predicate(predicate),
|
2016-11-11 15:40:58 +00:00
|
|
|
triple_objects = Enum.reduce(objects, %{}, fn (object, acc) ->
|
2017-08-11 20:22:27 +00:00
|
|
|
Map.put_new(acc, coerce_object(object), nil) end),
|
2016-11-11 15:40:58 +00:00
|
|
|
do: %RDF.Description{subject: subject,
|
|
|
|
predications: Map.put(predications, triple_predicate, triple_objects)}
|
|
|
|
end
|
|
|
|
|
2017-06-05 13:50:31 +00:00
|
|
|
def put(%RDF.Description{} = description, predicate, object),
|
|
|
|
do: put(description, predicate, [object])
|
2016-11-11 15:40:58 +00:00
|
|
|
|
|
|
|
@doc """
|
|
|
|
Adds statements to a `RDF.Description` and overwrites all existing statements with already used predicates.
|
|
|
|
|
2017-06-16 22:27:05 +00:00
|
|
|
## Examples
|
2016-11-11 15:40:58 +00:00
|
|
|
|
|
|
|
iex> RDF.Description.put(RDF.Description.new({EX.S, EX.P, EX.O1}), {EX.P, EX.O2})
|
|
|
|
RDF.Description.new([{EX.S, EX.P, EX.O2}])
|
|
|
|
iex> RDF.Description.new({EX.S, EX.P1, EX.O1}) |>
|
|
|
|
...> RDF.Description.put([{EX.P2, EX.O2}, {EX.S, EX.P2, EX.O3}, {EX.P1, EX.O4}])
|
|
|
|
RDF.Description.new([{EX.S, EX.P1, EX.O4}, {EX.S, EX.P2, EX.O2}, {EX.S, EX.P2, EX.O3}])
|
2016-11-15 00:26:57 +00:00
|
|
|
iex> RDF.Description.new({EX.S, EX.P, EX.O1}) |>
|
|
|
|
...> RDF.Description.put(RDF.Description.new(EX.S, EX.P, [EX.O1, EX.O2]))
|
|
|
|
RDF.Description.new([{EX.S, EX.P, EX.O1}, {EX.S, EX.P, EX.O2}])
|
2016-11-24 07:44:07 +00:00
|
|
|
iex> RDF.Description.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}]) |>
|
|
|
|
...> RDF.Description.put(%{EX.P2 => [EX.O3, EX.O4]})
|
|
|
|
RDF.Description.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O3}, {EX.S, EX.P2, EX.O4}])
|
2016-11-11 15:40:58 +00:00
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec put(t, statements | [statements]) :: t
|
2016-11-11 15:40:58 +00:00
|
|
|
def put(description, statements)
|
|
|
|
|
2017-06-05 13:50:31 +00:00
|
|
|
def put(%RDF.Description{} = description, {predicate, object}),
|
|
|
|
do: put(description, predicate, object)
|
2016-11-11 15:40:58 +00:00
|
|
|
|
2017-06-05 13:50:31 +00:00
|
|
|
def put(%RDF.Description{} = description, {subject, predicate, object}) do
|
2017-08-11 20:22:27 +00:00
|
|
|
if coerce_subject(subject) == description.subject,
|
2017-06-05 13:50:31 +00:00
|
|
|
do: put(description, predicate, object),
|
|
|
|
else: description
|
2016-10-15 16:26:56 +00:00
|
|
|
end
|
|
|
|
|
2017-06-05 13:50:31 +00:00
|
|
|
def put(description, {subject, predicate, object, _}),
|
|
|
|
do: put(description, {subject, predicate, object})
|
|
|
|
|
|
|
|
def put(%RDF.Description{subject: subject} = description, statements) when is_list(statements) do
|
2016-11-11 15:40:58 +00:00
|
|
|
statements
|
|
|
|
|> Stream.map(fn
|
2017-08-11 20:22:27 +00:00
|
|
|
{p, o} -> {coerce_predicate(p), o}
|
|
|
|
{^subject, p, o} -> {coerce_predicate(p), o}
|
2016-11-11 15:40:58 +00:00
|
|
|
{s, p, o} ->
|
2017-08-11 20:22:27 +00:00
|
|
|
if coerce_subject(s) == subject,
|
|
|
|
do: {coerce_predicate(p), o}
|
2016-11-11 15:40:58 +00:00
|
|
|
bad -> raise ArgumentError, "#{inspect bad} is not a valid statement"
|
|
|
|
end)
|
|
|
|
|> Stream.filter(&(&1)) # filter nil values
|
|
|
|
|> Enum.group_by(&(elem(&1, 0)), &(elem(&1, 1)))
|
2017-06-05 13:50:31 +00:00
|
|
|
|> Enum.reduce(description, fn ({predicate, objects}, description) ->
|
|
|
|
put(description, predicate, objects)
|
|
|
|
end)
|
2016-11-11 15:40:58 +00:00
|
|
|
end
|
2016-11-15 00:26:57 +00:00
|
|
|
|
|
|
|
def put(%RDF.Description{subject: subject, predications: predications},
|
|
|
|
%RDF.Description{predications: other_predications}) do
|
|
|
|
merged_predications = Map.merge predications, other_predications,
|
|
|
|
fn (_, _, other_objects) -> other_objects end
|
|
|
|
%RDF.Description{subject: subject, predications: merged_predications}
|
|
|
|
end
|
2016-11-11 15:40:58 +00:00
|
|
|
|
2016-11-24 07:44:07 +00:00
|
|
|
def put(description = %RDF.Description{}, predications = %{}) do
|
|
|
|
Enum.reduce predications, description, fn ({predicate, objects}, description) ->
|
|
|
|
put(description, predicate, objects)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2017-05-23 00:03:41 +00:00
|
|
|
@doc """
|
|
|
|
Deletes statements from a `RDF.Description`.
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec delete(
|
|
|
|
t,
|
|
|
|
Statement.coercible_predicate,
|
|
|
|
Statement.coercible_object | [Statement.coercible_object]
|
|
|
|
) :: t
|
2017-05-23 00:03:41 +00:00
|
|
|
def delete(description, predicate, objects)
|
|
|
|
|
|
|
|
def delete(description, predicate, objects) when is_list(objects) do
|
|
|
|
Enum.reduce objects, description, fn (object, description) ->
|
|
|
|
delete(description, predicate, object)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def delete(%RDF.Description{subject: subject, predications: predications} = descr, predicate, object) do
|
2017-08-11 20:22:27 +00:00
|
|
|
with triple_predicate = coerce_predicate(predicate),
|
|
|
|
triple_object = coerce_object(object) do
|
2017-05-23 00:03:41 +00:00
|
|
|
if (objects = predications[triple_predicate]) && Map.has_key?(objects, triple_object) do
|
|
|
|
%RDF.Description{
|
|
|
|
subject: subject,
|
|
|
|
predications:
|
|
|
|
if map_size(objects) == 1 do
|
|
|
|
Map.delete(predications, triple_predicate)
|
|
|
|
else
|
|
|
|
Map.update!(predications, triple_predicate, fn objects ->
|
|
|
|
Map.delete(objects, triple_object)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
}
|
|
|
|
else
|
|
|
|
descr
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Deletes statements from a `RDF.Description`.
|
2017-06-02 13:12:53 +00:00
|
|
|
|
|
|
|
Note: When the statements to be deleted are given as another `RDF.Description`,
|
|
|
|
the subject must not match subject of the description from which the statements
|
|
|
|
are deleted. If you want to delete only a matching description subject, you can
|
|
|
|
use `RDF.Data.delete/2`.
|
2017-05-23 00:03:41 +00:00
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec delete(t, statements | [statements]) :: t
|
2017-05-23 00:03:41 +00:00
|
|
|
def delete(description, statements)
|
|
|
|
|
|
|
|
def delete(desc = %RDF.Description{}, {predicate, object}),
|
|
|
|
do: delete(desc, predicate, object)
|
|
|
|
|
|
|
|
def delete(description = %RDF.Description{}, {subject, predicate, object}) do
|
2017-08-11 20:22:27 +00:00
|
|
|
if coerce_subject(subject) == description.subject,
|
2017-05-23 00:03:41 +00:00
|
|
|
do: delete(description, predicate, object),
|
|
|
|
else: description
|
|
|
|
end
|
|
|
|
|
2017-06-05 13:50:31 +00:00
|
|
|
def delete(description, {subject, predicate, object, _}),
|
|
|
|
do: delete(description, {subject, predicate, object})
|
|
|
|
|
2017-05-23 00:03:41 +00:00
|
|
|
def delete(description, statements) when is_list(statements) do
|
|
|
|
Enum.reduce statements, description, fn (statement, description) ->
|
|
|
|
delete(description, statement)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-06-02 13:12:53 +00:00
|
|
|
def delete(description = %RDF.Description{}, other_description = %RDF.Description{}) do
|
|
|
|
Enum.reduce other_description, description, fn ({_, predicate, object}, description) ->
|
|
|
|
delete(description, predicate, object)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-05-23 00:03:41 +00:00
|
|
|
def delete(description = %RDF.Description{}, predications = %{}) do
|
|
|
|
Enum.reduce predications, description, fn ({predicate, objects}, description) ->
|
|
|
|
delete(description, predicate, objects)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2017-06-02 16:06:22 +00:00
|
|
|
@doc """
|
|
|
|
Deletes all statements with the given properties.
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec delete_predicates(t, Statement.coercible_predicate | [Statement.coercible_predicate]) :: t
|
2017-06-02 16:06:22 +00:00
|
|
|
def delete_predicates(description, properties)
|
|
|
|
|
|
|
|
def delete_predicates(%RDF.Description{} = description, properties) when is_list(properties) do
|
|
|
|
Enum.reduce properties, description, fn (property, description) ->
|
|
|
|
delete_predicates(description, property)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def delete_predicates(%RDF.Description{subject: subject, predications: predications}, property) do
|
2017-08-11 20:22:27 +00:00
|
|
|
with property = coerce_predicate(property) do
|
2017-06-02 16:06:22 +00:00
|
|
|
%RDF.Description{subject: subject, predications: Map.delete(predications, property)}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2016-11-11 15:40:58 +00:00
|
|
|
@doc """
|
|
|
|
Fetches the objects for the given predicate of a Description.
|
|
|
|
|
|
|
|
When the predicate can not be found `:error` is returned.
|
|
|
|
|
2017-06-16 22:27:05 +00:00
|
|
|
## Examples
|
2016-11-11 15:40:58 +00:00
|
|
|
|
|
|
|
iex> RDF.Description.fetch(RDF.Description.new({EX.S, EX.p, EX.O}), EX.p)
|
2017-08-20 20:35:14 +00:00
|
|
|
{:ok, [RDF.iri(EX.O)]}
|
2016-11-11 15:40:58 +00:00
|
|
|
iex> RDF.Description.fetch(RDF.Description.new([{EX.S, EX.P, EX.O1},
|
|
|
|
...> {EX.S, EX.P, EX.O2}]), EX.P)
|
2017-08-20 20:35:14 +00:00
|
|
|
{:ok, [RDF.iri(EX.O1), RDF.iri(EX.O2)]}
|
2016-11-11 15:40:58 +00:00
|
|
|
iex> RDF.Description.fetch(RDF.Description.new(EX.S), EX.foo)
|
|
|
|
:error
|
|
|
|
"""
|
2018-09-17 00:08:16 +00:00
|
|
|
@impl Access
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec fetch(t, Statement.coercible_predicate) :: {:ok, [Statement.object]} | :error
|
2016-11-11 15:40:58 +00:00
|
|
|
def fetch(%RDF.Description{predications: predications}, predicate) do
|
2017-08-11 20:22:27 +00:00
|
|
|
with {:ok, objects} <- Access.fetch(predications, coerce_predicate(predicate)) do
|
2016-11-11 15:40:58 +00:00
|
|
|
{:ok, Map.keys(objects)}
|
2016-10-15 16:26:56 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
2016-11-11 15:40:58 +00:00
|
|
|
Gets the objects for the given predicate of a Description.
|
|
|
|
|
2017-07-08 18:55:34 +00:00
|
|
|
When the predicate can not be found, the optionally given default value or `nil` is returned.
|
2016-11-11 15:40:58 +00:00
|
|
|
|
2017-06-16 22:27:05 +00:00
|
|
|
## Examples
|
2016-11-11 15:40:58 +00:00
|
|
|
|
|
|
|
iex> RDF.Description.get(RDF.Description.new({EX.S, EX.P, EX.O}), EX.P)
|
2017-08-20 20:35:14 +00:00
|
|
|
[RDF.iri(EX.O)]
|
2016-11-11 15:40:58 +00:00
|
|
|
iex> RDF.Description.get(RDF.Description.new(EX.S), EX.foo)
|
|
|
|
nil
|
|
|
|
iex> RDF.Description.get(RDF.Description.new(EX.S), EX.foo, :bar)
|
|
|
|
:bar
|
2016-10-15 16:26:56 +00:00
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec get(t, Statement.coercible_predicate, any) :: [Statement.object] | any
|
2016-11-11 15:40:58 +00:00
|
|
|
def get(description = %RDF.Description{}, predicate, default \\ nil) do
|
|
|
|
case fetch(description, predicate) do
|
|
|
|
{:ok, value} -> value
|
|
|
|
:error -> default
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-07-08 18:55:34 +00:00
|
|
|
@doc """
|
|
|
|
Gets a single object for the given predicate of a Description.
|
|
|
|
|
|
|
|
When the predicate can not be found, the optionally given default value or `nil` is returned.
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
iex> RDF.Description.first(RDF.Description.new({EX.S, EX.P, EX.O}), EX.P)
|
2017-08-20 20:35:14 +00:00
|
|
|
RDF.iri(EX.O)
|
2017-07-08 18:55:34 +00:00
|
|
|
iex> RDF.Description.first(RDF.Description.new(EX.S), EX.foo)
|
|
|
|
nil
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec first(t, Statement.coercible_predicate) :: Statement.object | nil
|
2017-07-08 18:55:34 +00:00
|
|
|
def first(description = %RDF.Description{}, predicate) do
|
|
|
|
description
|
|
|
|
|> get(predicate, [])
|
|
|
|
|> List.first
|
|
|
|
end
|
|
|
|
|
2019-10-23 15:31:21 +00:00
|
|
|
@doc """
|
|
|
|
Updates the objects of the `predicate` in `description` with the given function.
|
|
|
|
|
|
|
|
If `predicate` is present in `description` with `objects` as value,
|
|
|
|
`fun` is invoked with argument `objects` and its result is used as the new
|
|
|
|
list of objects of `predicate`. If `predicate` is not present in `description`,
|
2019-10-24 20:03:05 +00:00
|
|
|
`initial` is inserted as the objects of `predicate`. The initial value will
|
2019-10-23 15:31:21 +00:00
|
|
|
not be passed through the update function.
|
|
|
|
|
|
|
|
The initial value and the returned objects by the update function will automatically
|
|
|
|
coerced to proper RDF object values before added.
|
|
|
|
|
|
|
|
## Examples
|
2019-10-15 15:29:46 +00:00
|
|
|
|
2019-10-23 15:31:21 +00:00
|
|
|
iex> RDF.Description.new({EX.S, EX.p, EX.O}) |>
|
|
|
|
...> RDF.Description.update(EX.p, fn objects -> [EX.O2 | objects] end)
|
|
|
|
RDF.Description.new([{EX.S, EX.p, EX.O}, {EX.S, EX.p, EX.O2}])
|
|
|
|
iex> RDF.Description.new(EX.S) |>
|
|
|
|
...> RDF.Description.update(EX.p, EX.O, fn _ -> EX.O2 end)
|
|
|
|
RDF.Description.new({EX.S, EX.p, EX.O})
|
|
|
|
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec update(
|
|
|
|
t,
|
|
|
|
Statement.coercible_predicate,
|
|
|
|
Statement.coercible_object | nil,
|
|
|
|
([Statement.Object] -> [Statement.Object])
|
|
|
|
) :: t
|
2019-10-23 15:31:21 +00:00
|
|
|
def update(description = %RDF.Description{}, predicate, initial \\ nil, fun) do
|
|
|
|
predicate = coerce_predicate(predicate)
|
|
|
|
|
|
|
|
case get(description, predicate) do
|
|
|
|
nil ->
|
|
|
|
if initial do
|
|
|
|
put(description, predicate, initial)
|
|
|
|
else
|
|
|
|
description
|
|
|
|
end
|
|
|
|
|
|
|
|
objects ->
|
|
|
|
objects
|
|
|
|
|> fun.()
|
|
|
|
|> List.wrap()
|
|
|
|
|> case do
|
|
|
|
[] -> delete_predicates(description, predicate)
|
|
|
|
objects -> put(description, predicate, objects)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-10-15 15:29:46 +00:00
|
|
|
|
|
|
|
|
2016-11-11 15:40:58 +00:00
|
|
|
@doc """
|
|
|
|
Gets and updates the objects of the given predicate of a Description, in a single pass.
|
|
|
|
|
2016-11-24 20:36:13 +00:00
|
|
|
Invokes the passed function on the objects of the given predicate; this
|
|
|
|
function should return either `{objects_to_return, new_object}` or `:pop`.
|
|
|
|
|
|
|
|
If the passed function returns `{objects_to_return, new_objects}`, the return
|
|
|
|
value of `get_and_update` is `{objects_to_return, new_description}` where
|
|
|
|
`new_description` is the input `Description` updated with `new_objects` for
|
|
|
|
the given predicate.
|
|
|
|
|
|
|
|
If the passed function returns `:pop` the objects for the given predicate are
|
|
|
|
removed and a `{removed_objects, new_description}` tuple gets returned.
|
|
|
|
|
2017-06-16 22:27:05 +00:00
|
|
|
## Examples
|
2016-11-11 15:40:58 +00:00
|
|
|
|
2016-11-24 20:36:13 +00:00
|
|
|
iex> RDF.Description.new({EX.S, EX.P, EX.O}) |>
|
|
|
|
...> RDF.Description.get_and_update(EX.P, fn current_objects ->
|
|
|
|
...> {current_objects, EX.NEW}
|
|
|
|
...> end)
|
2017-08-20 20:35:14 +00:00
|
|
|
{[RDF.iri(EX.O)], RDF.Description.new({EX.S, EX.P, EX.NEW})}
|
2016-11-24 20:36:13 +00:00
|
|
|
iex> RDF.Description.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}]) |>
|
|
|
|
...> RDF.Description.get_and_update(EX.P1, fn _ -> :pop end)
|
2017-08-20 20:35:14 +00:00
|
|
|
{[RDF.iri(EX.O1)], RDF.Description.new({EX.S, EX.P2, EX.O2})}
|
2016-11-11 15:40:58 +00:00
|
|
|
"""
|
2018-09-17 00:08:16 +00:00
|
|
|
@impl Access
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec get_and_update(
|
|
|
|
t,
|
|
|
|
Statement.coercible_predicate,
|
|
|
|
([Statement.Object] -> {[Statement.Object], t} | :pop)
|
|
|
|
) :: {[Statement.Object], t}
|
2016-11-11 15:40:58 +00:00
|
|
|
def get_and_update(description = %RDF.Description{}, predicate, fun) do
|
2017-08-11 20:22:27 +00:00
|
|
|
with triple_predicate = coerce_predicate(predicate) do
|
2016-11-11 15:40:58 +00:00
|
|
|
case fun.(get(description, triple_predicate)) do
|
2016-11-24 20:36:13 +00:00
|
|
|
{objects_to_return, new_objects} ->
|
|
|
|
{objects_to_return, put(description, triple_predicate, new_objects)}
|
2016-11-11 15:40:58 +00:00
|
|
|
:pop -> pop(description, triple_predicate)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2017-06-16 21:03:44 +00:00
|
|
|
@doc """
|
2017-06-16 21:42:03 +00:00
|
|
|
Pops an arbitrary triple from a `RDF.Description`.
|
2017-06-16 21:03:44 +00:00
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec pop(t) :: {Triple.t | [Statement.Object] | nil, t}
|
2017-06-16 21:03:44 +00:00
|
|
|
def pop(description)
|
|
|
|
|
|
|
|
def pop(description = %RDF.Description{predications: predications})
|
|
|
|
when predications == %{}, do: {nil, description}
|
|
|
|
|
|
|
|
def pop(%RDF.Description{subject: subject, predications: predications}) do
|
|
|
|
# TODO: Find a faster way ...
|
|
|
|
predicate = List.first(Map.keys(predications))
|
|
|
|
[{object, _}] = Enum.take(objects = predications[predicate], 1)
|
|
|
|
|
|
|
|
popped = if Enum.count(objects) == 1,
|
|
|
|
do: elem(Map.pop(predications, predicate), 1),
|
|
|
|
else: elem(pop_in(predications, [predicate, object]), 1)
|
|
|
|
|
|
|
|
{{subject, predicate, object},
|
|
|
|
%RDF.Description{subject: subject, predications: popped}}
|
|
|
|
end
|
|
|
|
|
2016-11-11 15:40:58 +00:00
|
|
|
@doc """
|
2016-11-24 20:36:13 +00:00
|
|
|
Pops the objects of the given predicate of a Description.
|
|
|
|
|
|
|
|
When the predicate can not be found the optionally given default value or `nil` is returned.
|
2016-11-11 15:40:58 +00:00
|
|
|
|
2017-06-16 22:27:05 +00:00
|
|
|
## Examples
|
2016-11-11 15:40:58 +00:00
|
|
|
|
|
|
|
iex> RDF.Description.pop(RDF.Description.new({EX.S, EX.P, EX.O}), EX.P)
|
2017-08-20 20:35:14 +00:00
|
|
|
{[RDF.iri(EX.O)], RDF.Description.new(EX.S)}
|
2016-11-11 15:40:58 +00:00
|
|
|
iex> RDF.Description.pop(RDF.Description.new({EX.S, EX.P, EX.O}), EX.Missing)
|
|
|
|
{nil, RDF.Description.new({EX.S, EX.P, EX.O})}
|
|
|
|
"""
|
2018-09-17 00:08:16 +00:00
|
|
|
@impl Access
|
2016-11-11 15:40:58 +00:00
|
|
|
def pop(description = %RDF.Description{subject: subject, predications: predications}, predicate) do
|
2017-08-11 20:22:27 +00:00
|
|
|
case Access.pop(predications, coerce_predicate(predicate)) do
|
2016-11-11 15:40:58 +00:00
|
|
|
{nil, _} ->
|
|
|
|
{nil, description}
|
|
|
|
{objects, new_predications} ->
|
|
|
|
{Map.keys(objects), %RDF.Description{subject: subject, predications: new_predications}}
|
|
|
|
end
|
2016-10-15 16:26:56 +00:00
|
|
|
end
|
|
|
|
|
2016-11-02 02:19:19 +00:00
|
|
|
|
|
|
|
@doc """
|
|
|
|
The set of all properties used in the predicates within a `RDF.Description`.
|
|
|
|
|
2017-06-16 22:27:05 +00:00
|
|
|
## Examples
|
2016-11-02 02:19:19 +00:00
|
|
|
|
|
|
|
iex> RDF.Description.new([
|
|
|
|
...> {EX.S1, EX.p1, EX.O1},
|
|
|
|
...> {EX.p2, EX.O2},
|
|
|
|
...> {EX.p2, EX.O3}]) |>
|
|
|
|
...> RDF.Description.predicates
|
|
|
|
MapSet.new([EX.p1, EX.p2])
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec predicates(t) :: MapSet.t
|
2016-11-02 02:19:19 +00:00
|
|
|
def predicates(%RDF.Description{predications: predications}),
|
|
|
|
do: predications |> Map.keys |> MapSet.new
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
The set of all resources used in the objects within a `RDF.Description`.
|
|
|
|
|
2017-08-20 20:35:14 +00:00
|
|
|
Note: This function does collect only IRIs and BlankNodes, not Literals.
|
2016-11-02 02:19:19 +00:00
|
|
|
|
2017-06-16 22:27:05 +00:00
|
|
|
## Examples
|
2016-11-02 02:19:19 +00:00
|
|
|
|
|
|
|
iex> RDF.Description.new([
|
|
|
|
...> {EX.S1, EX.p1, EX.O1},
|
|
|
|
...> {EX.p2, EX.O2},
|
|
|
|
...> {EX.p3, EX.O2},
|
|
|
|
...> {EX.p4, RDF.bnode(:bnode)},
|
|
|
|
...> {EX.p3, "foo"}
|
|
|
|
...> ]) |> RDF.Description.objects
|
2017-08-20 20:35:14 +00:00
|
|
|
MapSet.new([RDF.iri(EX.O1), RDF.iri(EX.O2), RDF.bnode(:bnode)])
|
2016-11-02 02:19:19 +00:00
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec objects(t) :: MapSet.t
|
2017-07-21 22:18:22 +00:00
|
|
|
def objects(%RDF.Description{} = description),
|
|
|
|
do: objects(description, &RDF.resource?/1)
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
The set of all resources used in the objects within a `RDF.Description` satisfying the given filter criterion.
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec objects(t, (Statement.object -> boolean)) :: MapSet.t
|
2017-07-21 22:18:22 +00:00
|
|
|
def objects(%RDF.Description{predications: predications}, filter_fn) do
|
2016-11-02 02:19:19 +00:00
|
|
|
Enum.reduce predications, MapSet.new, fn ({_, objects}, acc) ->
|
|
|
|
objects
|
|
|
|
|> Map.keys
|
2017-07-21 22:18:22 +00:00
|
|
|
|> Enum.filter(filter_fn)
|
2016-11-02 02:19:19 +00:00
|
|
|
|> MapSet.new
|
|
|
|
|> MapSet.union(acc)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
The set of all resources used within a `RDF.Description`.
|
|
|
|
|
2017-06-16 22:27:05 +00:00
|
|
|
## Examples
|
2016-11-02 02:19:19 +00:00
|
|
|
|
|
|
|
iex> RDF.Description.new([
|
|
|
|
...> {EX.S1, EX.p1, EX.O1},
|
|
|
|
...> {EX.p2, EX.O2},
|
|
|
|
...> {EX.p1, EX.O2},
|
|
|
|
...> {EX.p2, RDF.bnode(:bnode)},
|
|
|
|
...> {EX.p3, "foo"}
|
|
|
|
...> ]) |> RDF.Description.resources
|
2017-08-20 20:35:14 +00:00
|
|
|
MapSet.new([RDF.iri(EX.O1), RDF.iri(EX.O2), RDF.bnode(:bnode), EX.p1, EX.p2, EX.p3])
|
2016-11-02 02:19:19 +00:00
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec resources(t) :: MapSet.t
|
2016-11-02 02:19:19 +00:00
|
|
|
def resources(description) do
|
|
|
|
description
|
|
|
|
|> objects
|
|
|
|
|> MapSet.union(predicates(description))
|
|
|
|
end
|
|
|
|
|
2017-06-16 21:03:44 +00:00
|
|
|
@doc """
|
|
|
|
The list of all triples within a `RDF.Description`.
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec triples(t) :: keyword
|
2016-11-30 23:06:45 +00:00
|
|
|
def triples(description = %RDF.Description{}), do: Enum.to_list(description)
|
|
|
|
|
2017-05-24 21:38:46 +00:00
|
|
|
defdelegate statements(description), to: RDF.Description, as: :triples
|
|
|
|
|
2016-11-02 02:19:19 +00:00
|
|
|
|
2016-11-11 15:40:58 +00:00
|
|
|
@doc """
|
|
|
|
Returns the number of statements of a `RDF.Description`.
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec count(t) :: non_neg_integer
|
2016-11-11 15:40:58 +00:00
|
|
|
def count(%RDF.Description{predications: predications}) do
|
|
|
|
Enum.reduce predications, 0,
|
|
|
|
fn ({_, objects}, count) -> count + Enum.count(objects) end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2016-10-15 16:26:56 +00:00
|
|
|
@doc """
|
2016-11-02 02:19:19 +00:00
|
|
|
Checks if the given statement exists within a `RDF.Description`.
|
2016-10-15 16:26:56 +00:00
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec include?(t, statements) :: boolean
|
2016-10-15 16:26:56 +00:00
|
|
|
def include?(description, statement)
|
|
|
|
|
|
|
|
def include?(%RDF.Description{predications: predications},
|
|
|
|
{predicate, object}) do
|
2017-08-11 20:22:27 +00:00
|
|
|
with triple_predicate = coerce_predicate(predicate),
|
|
|
|
triple_object = coerce_object(object) do
|
2016-10-15 16:26:56 +00:00
|
|
|
predications
|
|
|
|
|> Map.get(triple_predicate, %{})
|
|
|
|
|> Map.has_key?(triple_object)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def include?(desc = %RDF.Description{subject: desc_subject},
|
|
|
|
{subject, predicate, object}) do
|
2017-08-11 20:22:27 +00:00
|
|
|
coerce_subject(subject) == desc_subject &&
|
2016-10-15 16:26:56 +00:00
|
|
|
include?(desc, {predicate, object})
|
|
|
|
end
|
|
|
|
|
|
|
|
def include?(%RDF.Description{}, _), do: false
|
|
|
|
|
|
|
|
|
2017-07-23 21:59:50 +00:00
|
|
|
@doc """
|
|
|
|
Checks if a `RDF.Description` has the given resource as subject.
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
iex> RDF.Description.new(EX.S1, EX.p1, EX.O1) |> RDF.Description.describes?(EX.S1)
|
|
|
|
true
|
|
|
|
iex> RDF.Description.new(EX.S1, EX.p1, EX.O1) |> RDF.Description.describes?(EX.S2)
|
|
|
|
false
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec describes?(t, Statement.subject) :: boolean
|
2017-07-23 21:59:50 +00:00
|
|
|
def describes?(%RDF.Description{subject: subject}, other_subject) do
|
2017-08-11 20:22:27 +00:00
|
|
|
with other_subject = coerce_subject(other_subject) do
|
2017-07-23 21:59:50 +00:00
|
|
|
subject == other_subject
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-27 14:30:19 +00:00
|
|
|
@doc """
|
|
|
|
Returns a map of the native Elixir values of a `RDF.Description`.
|
|
|
|
|
|
|
|
The subject is not part of the result. It can be converted separately with
|
|
|
|
`RDF.Term.value/1`.
|
|
|
|
|
2018-11-04 21:27:25 +00:00
|
|
|
The optional second argument allows to specify a custom mapping with a function
|
|
|
|
which will receive a tuple `{statement_position, rdf_term}` where
|
|
|
|
`statement_position` is one of the atoms `:predicate` or `:object`,
|
|
|
|
while `rdf_term` is the RDF term to be mapped.
|
|
|
|
|
2018-10-27 14:30:19 +00:00
|
|
|
## Examples
|
|
|
|
|
|
|
|
iex> {~I<http://example.com/S>, ~I<http://example.com/p>, ~L"Foo"}
|
|
|
|
...> |> RDF.Description.new()
|
|
|
|
...> |> RDF.Description.values()
|
|
|
|
%{"http://example.com/p" => ["Foo"]}
|
|
|
|
|
2018-11-04 21:27:25 +00:00
|
|
|
iex> {~I<http://example.com/S>, ~I<http://example.com/p>, ~L"Foo"}
|
|
|
|
...> |> RDF.Description.new()
|
|
|
|
...> |> RDF.Description.values(fn
|
|
|
|
...> {:predicate, predicate} ->
|
|
|
|
...> predicate
|
|
|
|
...> |> to_string()
|
|
|
|
...> |> String.split("/")
|
|
|
|
...> |> List.last()
|
|
|
|
...> |> String.to_atom()
|
|
|
|
...> {_, term} ->
|
|
|
|
...> RDF.Term.value(term)
|
|
|
|
...> end)
|
|
|
|
%{p: ["Foo"]}
|
|
|
|
|
2018-10-27 14:30:19 +00:00
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec values(t, Statement.term_mapping) :: map
|
2018-11-04 21:27:25 +00:00
|
|
|
def values(description, mapping \\ &RDF.Statement.default_term_mapping/1)
|
|
|
|
|
|
|
|
def values(%RDF.Description{predications: predications}, mapping) do
|
2018-10-27 14:30:19 +00:00
|
|
|
Map.new predications, fn {predicate, objects} ->
|
|
|
|
{
|
2018-11-04 21:27:25 +00:00
|
|
|
mapping.({:predicate, predicate}),
|
|
|
|
objects |> Map.keys() |> Enum.map(&(mapping.({:object, &1})))
|
2018-10-27 14:30:19 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
2017-07-23 21:59:50 +00:00
|
|
|
|
2019-10-14 07:13:56 +00:00
|
|
|
@doc """
|
|
|
|
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.
|
2019-10-15 15:29:46 +00:00
|
|
|
|
|
|
|
If `nil` is passed, the description is left untouched.
|
2019-10-14 07:13:56 +00:00
|
|
|
"""
|
2020-05-16 01:51:54 +00:00
|
|
|
@spec take(t, [Statement.coercible_predicate] | Enum.t | nil) :: t
|
2019-10-15 15:29:46 +00:00
|
|
|
def take(description, predicates)
|
|
|
|
|
|
|
|
def take(%RDF.Description{} = description, nil), do: description
|
|
|
|
|
2019-10-14 07:13:56 +00:00
|
|
|
def take(%RDF.Description{predications: predications} = description, predicates) do
|
|
|
|
predicates = Enum.map(predicates, &(coerce_predicate/1))
|
|
|
|
%RDF.Description{description | predications: Map.take(predications, predicates)}
|
|
|
|
end
|
2018-11-04 21:27:25 +00:00
|
|
|
|
2019-04-02 22:12:42 +00:00
|
|
|
@doc """
|
|
|
|
Checks if two `RDF.Description`s are equal.
|
|
|
|
|
|
|
|
Two `RDF.Description`s are considered to be equal if they contain the same triples.
|
|
|
|
"""
|
2020-03-02 01:07:31 +00:00
|
|
|
@spec equal?(t, t) :: boolean
|
2019-04-02 22:12:42 +00:00
|
|
|
def equal?(description1, description2)
|
|
|
|
|
|
|
|
def equal?(%RDF.Description{} = description1, %RDF.Description{} = description2) do
|
|
|
|
description1 == description2
|
|
|
|
end
|
|
|
|
|
|
|
|
def equal?(_, _), do: false
|
|
|
|
|
|
|
|
|
2017-06-16 21:03:44 +00:00
|
|
|
defimpl Enumerable do
|
2018-03-09 22:18:08 +00:00
|
|
|
def member?(desc, triple), do: {:ok, RDF.Description.include?(desc, triple)}
|
|
|
|
def count(desc), do: {:ok, RDF.Description.count(desc)}
|
|
|
|
def slice(_desc), do: {:error, __MODULE__}
|
2016-10-15 16:26:56 +00:00
|
|
|
|
2017-06-16 21:03:44 +00:00
|
|
|
def reduce(%RDF.Description{predications: predications}, {:cont, acc}, _fun)
|
|
|
|
when map_size(predications) == 0, do: {:done, acc}
|
2016-10-15 16:26:56 +00:00
|
|
|
|
2017-06-16 21:03:44 +00:00
|
|
|
def reduce(description = %RDF.Description{}, {:cont, acc}, fun) do
|
|
|
|
{triple, rest} = RDF.Description.pop(description)
|
|
|
|
reduce(rest, fun.(triple, acc), fun)
|
|
|
|
end
|
2016-10-15 16:26:56 +00:00
|
|
|
|
2017-06-16 21:03:44 +00:00
|
|
|
def reduce(_, {:halt, acc}, _fun), do: {:halted, acc}
|
|
|
|
def reduce(description = %RDF.Description{}, {:suspend, acc}, fun) do
|
|
|
|
{:suspended, acc, &reduce(description, &1, fun)}
|
|
|
|
end
|
2016-10-15 16:26:56 +00:00
|
|
|
end
|
|
|
|
|
2017-10-30 10:40:44 +00:00
|
|
|
|
|
|
|
defimpl Collectable do
|
|
|
|
def into(original) do
|
|
|
|
collector_fun = fn
|
|
|
|
description, {:cont, list} when is_list(list)
|
|
|
|
-> RDF.Description.add(description, List.to_tuple(list))
|
|
|
|
description, {:cont, elem} -> RDF.Description.add(description, elem)
|
|
|
|
description, :done -> description
|
|
|
|
_description, :halt -> :ok
|
|
|
|
end
|
|
|
|
|
|
|
|
{original, collector_fun}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-10-15 16:26:56 +00:00
|
|
|
end
|