RDF.Data (RDF.ex v3.0.0)

Copy Markdown View Source

Functions for working with RDF data structures.

This module provides a rich API for RDF data structures built on top of the minimal RDF.Data.Source protocol, similar to how Elixir's Enum module builds on the Enumerable protocol.

Summary

Functions

Returns the count of primary elements relative to the structure type.

Returns the default graph from the RDF data structure.

Deletes matching statements from the data structure.

Returns true if the data describes the given subject.

Returns the description of a specific subject in the RDF data structure.

Returns the description of a specific subject, or a default value if not found.

Returns all descriptions in the data structure.

Invokes the given function for each statement in the data structure.

Returns true if the given RDF data structure is empty.

Checks equality of two data structures.

Filters statements in the data structure based on a predicate function.

Returns a specific graph from the RDF data structure.

Returns a specific graph from the RDF data structure, or a default value if not found.

Returns the count of graphs in the RDF data structure.

Returns all graph names in the data structure.

Returns all graphs in the data structure.

Checks if data includes the given statements.

Maps a transformation function over all statements in the data structure.

Transforms statements and accumulates a value in a single pass.

Merges a list of data structures and/or statements into a single structure.

Merges two data structures with automatic structural promotion.

Returns all unique resource objects in the data structure.

Returns all unique object terms in the data structure.

Returns all unique object terms in the data structure matching the filter function.

Removes and returns one statement from the RDF data structure.

Returns the count of unique predicates in the data structure.

Returns all unique predicates in the data structure.

Returns all unique predicates in the data structure matching the filter function.

Returns all statements as quads.

Reduces the RDF data structure using the given function without an initial accumulator.

Reduces the RDF data structure using the given function.

Reduces the RDF data structure using the given function with early termination support.

Rejects statements in the data structure based on a predicate function.

Returns all unique resources (non-literal terms) in the data structure.

Returns the count of statements in the RDF data structure.

Returns all statements in the data structure.

Returns the count of unique subjects in the RDF data structure.

Returns all unique subjects in the data structure.

Returns all unique subjects in the data structure matching the filter function.

Takes the first amount statements from the RDF data structure.

Converts the RDF data structure to a dataset.

Converts the RDF data structure to a graph.

Returns all statements as triples.

Functions

count(data)

@spec count(RDF.Data.Source.t()) :: non_neg_integer()

Returns the count of primary elements relative to the structure type.

This provides a consistent "size" metric that is meaningful for each structure level.

  • :description: counts predicates (with non-empty object sets)
  • :graph: counts subjects (descriptions)
  • :dataset: counts graphs

Examples

iex> desc = EX.S |> EX.p(EX.O1) |> EX.q(EX.O2)
iex> RDF.Data.count(desc)
2  # 2 predicates

iex> graph = RDF.Graph.new([{EX.S1, EX.p, EX.O}, {EX.S2, EX.p, EX.O}])
iex> RDF.Data.count(graph)
2  # 2 subjects

iex> dataset = RDF.Dataset.new([{EX.S, EX.p, EX.O, EX.G1}, {EX.S, EX.p, EX.O, EX.G2}])
iex> RDF.Data.count(dataset)
2  # 2 graphs

default_graph(data)

@spec default_graph(RDF.Data.Source.t()) :: RDF.Data.Source.t()

Returns the default graph from the RDF data structure.

Examples

iex> desc = EX.S |> EX.p(EX.O)
iex> RDF.Data.default_graph(desc)
RDF.graph({EX.S, EX.p, EX.O})

iex> dataset = RDF.dataset([{EX.S, EX.p, EX.O, nil}])
iex> RDF.Data.default_graph(dataset)
RDF.graph({EX.S, EX.p, EX.O})

iex> dataset = RDF.dataset([{EX.S, EX.p, EX.O, EX.Graph1}])
iex> RDF.Data.default_graph(dataset)
RDF.graph()

delete(data, statements_to_delete)

Deletes matching statements from the data structure.

Returns a new data structure with the specified statements removed. The resulting structure maintains the same type and metadata (like graph names) as the original.

Examples

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}])
iex> RDF.Data.delete(graph, {EX.S1, EX.p, EX.O1})
RDF.graph({EX.S2, EX.p, EX.O2})

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}])
iex> RDF.Data.delete(graph, [{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}])
RDF.graph()

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}])
iex> RDF.Data.delete(graph, EX.S1 |> EX.p(EX.O1))
RDF.graph({EX.S2, EX.p, EX.O2})

describes?(data, subject)

@spec describes?(RDF.Data.Source.t(), RDF.Resource.coercible()) :: boolean()

Returns true if the data describes the given subject.

Checks if any statements with the given subject exist in the data structure.

Examples

iex> graph = RDF.graph([{EX.Alice, EX.knows, EX.Bob}])
iex> RDF.Data.describes?(graph, EX.Alice)
true

iex> graph = RDF.graph([{EX.Alice, EX.knows, EX.Bob}])
iex> RDF.Data.describes?(graph, EX.Unknown)
false

description(data, subject)

Returns the description of a specific subject in the RDF data structure.

If the subject doesn't exist, an empty description for that subject is returned. Use description/3 to provide a custom default value.

Examples

iex> graph = RDF.graph([{EX.Alice, EX.knows, EX.Bob}])
iex> RDF.Data.description(graph, EX.Alice)
EX.Alice |> EX.knows(EX.Bob)

iex> graph = RDF.graph([{EX.Alice, EX.knows, EX.Bob}])
iex> RDF.Data.description(graph, EX.Charlie)
RDF.description(EX.Charlie)

description(data, subject, default)

@spec description(RDF.Data.Source.t(), RDF.Resource.coercible(), default) ::
  RDF.Data.Source.t() | default
when default: term()

Returns the description of a specific subject, or a default value if not found.

Examples

iex> graph = RDF.graph([{EX.Alice, EX.knows, EX.Bob}])
iex> RDF.Data.description(graph, EX.Alice, nil)
EX.Alice |> EX.knows(EX.Bob)

iex> graph = RDF.graph([{EX.Alice, EX.knows, EX.Bob}])
iex> RDF.Data.description(graph, EX.Charlie, nil)
nil

iex> graph = RDF.graph([{EX.Alice, EX.knows, EX.Bob}])
iex> RDF.Data.description(graph, EX.Charlie, :not_found)
:not_found

descriptions(data)

@spec descriptions(RDF.Data.Source.t()) :: [RDF.Data.Source.t()]

Returns all descriptions in the data structure.

Examples

iex> graph = RDF.graph([{EX.S1, EX.p(), EX.O1}, {EX.S2, EX.p(), EX.O2}])
iex> RDF.Data.descriptions(graph)
[EX.p(EX.S1, EX.O1), EX.p(EX.S2, EX.O2)]

iex> dataset = RDF.dataset([{EX.S1, EX.p(), EX.O1, nil}, {EX.S2, EX.p(), EX.O2, EX.G}, {EX.S3, EX.p(), EX.O3, EX.G}])
iex> RDF.Data.descriptions(dataset)
[EX.p(EX.S1, EX.O1), EX.p(EX.S2, EX.O2), EX.p(EX.S3, EX.O3)]

each(data, fun)

@spec each(RDF.Data.Source.t(), (RDF.Statement.t() -> any())) :: :ok

Invokes the given function for each statement in the data structure.

The function is invoked with each statement as its only argument and always returns :ok. It is useful for executing side effects on the data.

Examples

RDF.Data.each(graph, &IO.inspect/1)
# prints each statement
# => :ok

empty?(data)

@spec empty?(RDF.Data.Source.t()) :: boolean()

Returns true if the given RDF data structure is empty.

Examples

iex> RDF.Data.empty?(RDF.graph())
true

iex> RDF.Data.empty?(RDF.graph([{EX.S, EX.p, EX.O}]))
false

equal?(data1, data2)

@spec equal?(RDF.Data.Source.t(), RDF.Data.Source.t()) :: boolean()

Checks equality of two data structures.

Supports cross-structure comparisons with special rules:

  • description equals graph if graph contains only that description
  • graph equals dataset if dataset contains only that graph
  • otherwise compares statement sets

Examples

iex> desc1 = EX.S |> EX.p(EX.O)
iex> desc2 = EX.S |> EX.p(EX.O)
iex> RDF.Data.equal?(desc1, desc2)
true

iex> desc = EX.S |> EX.p(EX.O)
iex> graph = RDF.graph([{EX.S, EX.p(), EX.O}])
iex> RDF.Data.equal?(desc, graph)
true

iex> graph = RDF.graph([{EX.S, EX.p(), EX.O}])
iex> dataset = RDF.dataset([{EX.S, EX.p(), EX.O, nil}])
iex> RDF.Data.equal?(graph, dataset)
true

filter(data, fun)

Filters statements in the data structure based on a predicate function.

Returns a new data structure of the same type containing only the statements for which the predicate function returns a truthy value.

Examples

iex> graph = RDF.graph([{EX.S1, EX.p1, EX.O1}, {EX.S2, EX.p2, EX.O2}])
iex> RDF.Data.filter(graph, fn {_s, p, _o} -> p == EX.p1 end)
RDF.graph([{EX.S1, EX.p1, EX.O1}])

iex> dataset = RDF.dataset([{EX.S, EX.p1, EX.O, EX.G}, {EX.S, EX.p2, EX.O, nil}])
iex> RDF.Data.filter(dataset, fn {_s, p, _o, _g} -> p == EX.p1 end)
RDF.dataset([{EX.S, EX.p1, EX.O, EX.G}])

graph(data, graph_name)

Returns a specific graph from the RDF data structure.

If the graph doesn't exist, an empty graph is returned. Use graph/3 to provide a custom default value.

Examples

iex> dataset = RDF.dataset([{EX.S, EX.p, EX.O, EX.Graph1}])
iex> RDF.Data.graph(dataset, EX.Graph1)
RDF.graph({EX.S, EX.p, EX.O}, name: EX.Graph1)

iex> dataset = RDF.dataset([{EX.S, EX.p, EX.O, EX.Graph1}])
iex> RDF.Data.graph(dataset, EX.NonExistent)
RDF.graph(name: EX.NonExistent)

graph(data, graph_name, default)

@spec graph(RDF.Data.Source.t(), RDF.Resource.coercible() | nil, default) ::
  RDF.Data.Source.t() | default
when default: term()

Returns a specific graph from the RDF data structure, or a default value if not found.

Examples

iex> dataset = RDF.dataset([{EX.S, EX.p, EX.O, EX.Graph1}])
iex> RDF.Data.graph(dataset, EX.Graph1, nil)
RDF.graph({EX.S, EX.p, EX.O}, name: EX.Graph1)

iex> dataset = RDF.dataset([{EX.S, EX.p, EX.O, EX.Graph1}])
iex> RDF.Data.graph(dataset, EX.NonExistent, nil)
nil

iex> dataset = RDF.dataset([{EX.S, EX.p, EX.O, EX.Graph1}])
iex> RDF.Data.graph(dataset, EX.NonExistent, :not_found)
:not_found

graph_count(data)

@spec graph_count(RDF.Data.Source.t()) :: non_neg_integer()

Returns the count of graphs in the RDF data structure.

Depending on the structure type, the count represents:

  • :description: Always 1 (implicit unnamed graph)
  • :graph: Always 1
  • :dataset: Number of graphs (including default if present)

Examples

iex> desc = EX.S |> EX.p(EX.O)
iex> RDF.Data.graph_count(desc)
1

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O1}])
iex> RDF.Data.graph_count(graph)
1

iex> dataset = RDF.dataset([
...>   {EX.S1, EX.p, EX.O1, nil},
...>   {EX.S2, EX.p, EX.O2, EX.Graph}
...> ])
iex> RDF.Data.graph_count(dataset)
2

iex> RDF.Data.graph_count(RDF.dataset())
0

graph_names(data)

@spec graph_names(RDF.Data.Source.t()) :: [RDF.IRI.t() | nil]

Returns all graph names in the data structure.

Behavior by structure type:

  • :description: Always returns [nil] (implicit unnamed graph)
  • :graph: Returns [name] where name is the graph's name (could be nil)
  • :dataset: Returns all graph names

Examples

iex> desc = EX.S |> EX.p(EX.O)
iex> RDF.Data.graph_names(desc)
[nil]

iex> graph = RDF.Graph.new([{EX.S, EX.p, EX.O}], name: EX.Graph1)
iex> RDF.Data.graph_names(graph)
[~I<http://example.com/Graph1>]

iex> dataset = RDF.Dataset.new([{EX.S, EX.p, EX.O, EX.G1}, {EX.S2, EX.p, EX.O2, nil}])
iex> RDF.Data.graph_names(dataset)
[nil, ~I<http://example.com/G1>]

graphs(data)

@spec graphs(RDF.Data.Source.t()) :: [RDF.Data.Source.t()]

Returns all graphs in the data structure.

Behavior by structure type:

  • :description: returns a single graph containing the description
  • :graph: returns a list containing the graph itself
  • :dataset: returns all graphs (including the default graph if present)

Examples

iex> description = EX.S |> EX.p(EX.O)
iex> RDF.Data.graphs(description)
[RDF.graph(description)]

iex> graph = RDF.graph([{EX.S, EX.p, EX.O}])
iex> RDF.Data.graphs(graph)
[graph]

iex> dataset = RDF.dataset([{EX.S1, EX.p, EX.O1, nil}, {EX.S2, EX.p, EX.O2, EX.graph1}])
iex> RDF.Data.graphs(dataset)
[RDF.graph([{EX.S1, EX.p, EX.O1}]), RDF.graph([{EX.S2, EX.p, EX.O2}], name: EX.graph1)]

include?(data, statements)

@spec include?(
  data :: RDF.Data.Source.t(),
  statements :: RDF.Statement.t() | [RDF.Statement.t()] | RDF.Data.Source.t()
) :: boolean()

Checks if data includes the given statements.

Returns true if all given statements are present in the data structure.

The statements parameter can be:

  • A single triple {s, p, o} or quad {s, p, o, g}
  • A list of triples or quads
  • Another structure implementing RDF.Data.Source

For datasets, triple patterns (without graph) are matched against all graphs. Use a quad {s, p, o, graph_name} to check a specific graph (including nil for the default graph).

Examples

iex> graph = RDF.Graph.new([{EX.S, EX.p, EX.O}])
iex> RDF.Data.include?(graph, {EX.S, EX.p, EX.O})
true

iex> graph = RDF.Graph.new([{EX.S, EX.p, EX.O}])
iex> RDF.Data.include?(graph, [{EX.S, EX.p, EX.O}])
true

iex> graph = RDF.Graph.new([{EX.S, EX.p, EX.O}])
iex> desc = EX.S |> EX.p(EX.O)
iex> RDF.Data.include?(graph, desc)
true

iex> dataset = RDF.Dataset.new([
...>   {EX.S1, EX.p, EX.O1, nil},
...>   {EX.S2, EX.p, EX.O2, EX.Graph}
...> ])
iex> RDF.Data.include?(dataset, {EX.S1, EX.p, EX.O1})
true
iex> RDF.Data.include?(dataset, {EX.S2, EX.p, EX.O2})
true
iex> RDF.Data.include?(dataset, {EX.S2, EX.p, EX.O2, nil})
false
iex> RDF.Data.include?(dataset, {EX.S2, EX.p, EX.O2, EX.Graph})
true

map(data, fun)

Maps a transformation function over all statements in the data structure.

The given fun function can return:

  • a statement tuple (triple or quad) - included in the result
  • a list of statements - flattened and included in the result
  • nil - excluded from the result

Structural promotion

The result structure type depends on the mapped statements:

  • stays the same type when all mapped statements fit the original structure
  • promotes to a graph structure when statements have different subjects
  • promotes to a dataset structure when statements have different graph names

Examples

iex> RDF.Data.map(EX.S |> EX.p(EX.O), fn {s, p, _o} -> {s, p, EX.New} end)
EX.S |> EX.p(EX.New)

iex> RDF.Data.map(RDF.graph({EX.S, EX.p, EX.O}), fn {s, p, _o} -> {s, p, EX.New} end)
RDF.graph({EX.S, EX.p, EX.New})

Description upgrades to graph when mapped statements have different subjects:

iex> desc = EX.S |> EX.p(EX.O1) |> EX.p(EX.O2)
iex> RDF.Data.map(desc, fn {_s, p, o} -> {o, p, EX.S} end)
RDF.graph([{EX.O1, EX.p, EX.S}, {EX.O2, EX.p, EX.S}])

Graph upgrades to dataset when mapped statements have different graph names:

iex> graph = RDF.graph([{EX.S, EX.p, EX.O1}, {EX.S, EX.p, EX.O2}])
iex> RDF.Data.map(graph, fn {s, p, o} -> {s, p, o, o} end)
RDF.dataset([{EX.S, EX.p, EX.O1, EX.O1}, {EX.S, EX.p, EX.O2, EX.O2}])

map_reduce(data, acc, fun)

@spec map_reduce(RDF.Data.Source.t(), acc, (RDF.Statement.t(), acc -> {mapped, acc})) ::
  {RDF.Data.Source.t(), acc}
when acc: term(), mapped: RDF.Statement.t() | [RDF.Statement.t()] | nil

Transforms statements and accumulates a value in a single pass.

Combines the functionality of map/2 with an accumulator, similar to Enum.map_reduce/3. For each statement, the function receives the statement and the current accumulator, and returns a tuple with the mapped result and the new accumulator.

The mapped result can be:

  • a statement (triple or quad) - replaces the original statement
  • nil - filters out the statement
  • a list of statements - expands to multiple statements

Like map/2, structural promotion occurs based on the mapped results.

Examples

iex> graph = RDF.graph([{EX.S1, EX.p1, EX.O1}, {EX.S2, EX.p2, EX.O2}])
iex> RDF.Data.map_reduce(graph, 0, fn {s, p, _o}, count ->
...>   {{s, p, EX.New}, count + 1}
...> end)
{RDF.graph([{EX.S1, EX.p1, EX.New}, {EX.S2, EX.p2, EX.New}]), 2}

merge(list)

Merges a list of data structures and/or statements into a single structure.

Examples

iex> graph = RDF.graph({EX.S1, EX.p1, EX.O1})
iex> RDF.Data.merge([graph, {EX.S2, EX.p2, EX.O2}, EX.S3 |> EX.p3(EX.O3)])
RDF.graph([{EX.S1, EX.p1, EX.O1}, {EX.S2, EX.p2, EX.O2}, {EX.S3, EX.p3, EX.O3}])

iex> RDF.Data.merge([])
RDF.graph()

merge(data1, data2)

Merges two data structures with automatic structural promotion.

Rules:

  • Description + Description with same subject → Description
  • Description + Description with different subjects → Graph
  • Graph + Graph with same name → Graph
  • Graph + Graph with different names → Dataset
  • Otherwise, promotes to the most complex structure required

Examples

iex> desc1 = EX.S |> EX.p1(EX.O1)
iex> desc2 = EX.S |> EX.p2(EX.O2)
iex> RDF.Data.merge(desc1, desc2)
EX.S |> EX.p1(EX.O1) |> EX.p2(EX.O2)

iex> graph1 = RDF.graph([{EX.S1, EX.p, EX.O}], name: EX.G1)
iex> graph2 = RDF.graph([{EX.S2, EX.p, EX.O}], name: EX.G2)
iex> RDF.Data.merge(graph1, graph2)
RDF.dataset([{EX.S1, EX.p, EX.O, EX.G1}, {EX.S2, EX.p, EX.O, EX.G2}])

object_resources(data)

@spec object_resources(RDF.Data.Source.t()) :: [RDF.Resource.t()]

Returns all unique resource objects in the data structure.

Resource objects are IRIs and blank nodes, excluding literals. For all object terms including literals, use objects/1.

Examples

iex> graph = RDF.Graph.new([{EX.S, EX.p, EX.O1}, {EX.S, EX.p2, "literal"}])
iex> RDF.Data.object_resources(graph)
[~I<http://example.com/O1>]

objects(data)

@spec objects(RDF.Data.Source.t()) :: [RDF.Term.t()]

Returns all unique object terms in the data structure.

Object terms include all RDF terms: IRIs, blank nodes, and literals.

Examples

iex> graph = RDF.Graph.new([{EX.S, EX.p, EX.O1}, {EX.S, EX.p2, "literal"}])
iex> RDF.Data.objects(graph)
[~L"literal", ~I<http://example.com/O1>]

objects(data, filter_fun)

@spec objects(RDF.Data.Source.t(), (RDF.Term.t() -> boolean()) | nil) :: [
  RDF.Term.t()
]

Returns all unique object terms in the data structure matching the filter function.

Examples

iex> graph = RDF.graph([{EX.S, EX.p, EX.O}, {EX.S, EX.p2, "literal"}])
iex> RDF.Data.objects(graph, &RDF.resource?/1)
[RDF.iri(EX.O)]

pop(data)

@spec pop(RDF.Data.Source.t()) :: {RDF.Statement.t() | nil, RDF.Data.Source.t()}

Removes and returns one statement from the RDF data structure.

Returns a tuple {statement, remaining_data} where statement is a triple or quad, and remaining_data is the data structure without that statement. For empty data structures, returns {nil, data}.

The specific statement returned is implementation-dependent and should not be relied upon for ordering.

Examples

iex> graph = RDF.graph([{EX.S, EX.p, EX.O}])
iex> {triple, remaining} = RDF.Data.pop(graph)
iex> triple
{~I<http://example.com/S>, EX.p(), ~I<http://example.com/O>}
iex> RDF.Graph.empty?(remaining)
true

iex> RDF.Data.pop(RDF.graph())
{nil, RDF.graph()}

predicate_count(data)

@spec predicate_count(RDF.Data.Source.t()) :: non_neg_integer()

Returns the count of unique predicates in the data structure.

Examples

iex> desc = EX.S |> EX.p(EX.O1) |> EX.q(EX.O2)
iex> RDF.Data.predicate_count(desc)
2

iex> graph = RDF.Graph.new([{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}, {EX.S3, EX.q, EX.O3}])
iex> RDF.Data.predicate_count(graph)
2

predicates(data)

@spec predicates(RDF.Data.Source.t()) :: [RDF.IRI.t()]

Returns all unique predicates in the data structure.

Examples

iex> graph = RDF.Graph.new([{EX.S, EX.p1, EX.O}, {EX.S, EX.p2, EX.O}])
iex> RDF.Data.predicates(graph)
[~I<http://example.com/p1>, ~I<http://example.com/p2>]

predicates(data, filter_fun)

@spec predicates(RDF.Data.Source.t(), (RDF.IRI.t() -> boolean()) | nil) :: [
  RDF.IRI.t()
]

Returns all unique predicates in the data structure matching the filter function.

Examples

iex> graph = RDF.graph([{EX.S, EX.p1, EX.O}, {EX.S, EX.p2, EX.O}])
iex> RDF.Data.predicates(graph, &(&1 == EX.p1()))
[EX.p1()]

quads(data)

@spec quads(RDF.Data.Source.t()) :: [RDF.Quad.t()]

Returns all statements as quads.

Converts all statements to quad format {subject, predicate, object, graph_name}. For descriptions and graphs, adds the graph name (or nil for default graph).

Examples

iex> graph = RDF.graph([{EX.S, EX.p, EX.O}])
iex> RDF.Data.quads(graph)
[{~I<http://example.com/S>, EX.p(), ~I<http://example.com/O>, nil}]

iex> named_graph = RDF.graph([{EX.S, EX.p, EX.O}], name: EX.G)
iex> RDF.Data.quads(named_graph)
[{~I<http://example.com/S>, EX.p(), ~I<http://example.com/O>, ~I<http://example.com/G>}]

reduce(data, fun)

@spec reduce(RDF.Data.Source.t(), (RDF.Statement.t(), acc -> acc)) :: acc
when acc: term()

Reduces the RDF data structure using the given function without an initial accumulator.

The first statement becomes the initial accumulator.

Raises Enum.EmptyError if the data structure is empty.

reduce(data, acc, fun)

@spec reduce(RDF.Data.Source.t(), acc, (RDF.Statement.t(), acc -> acc)) :: acc
when acc: term()

Reduces the RDF data structure using the given function.

Similar to Enum.reduce/3, applies fun to each statement in the data structure to produce a single result. This is the user-facing function that hides the complexity of the protocol's tagged tuple system.

Examples

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}])
iex> RDF.Data.reduce(graph, 0, fn {_s, _p, _o}, acc -> acc + 1 end)
2

reduce_while(data, acc, fun)

@spec reduce_while(
  RDF.Data.Source.t(),
  acc,
  (RDF.Statement.t(), acc -> {:cont, acc} | {:halt, acc})
) :: acc
when acc: term()

Reduces the RDF data structure using the given function with early termination support.

Similar to Enum.reduce_while/3, applies fun to each statement in the data structure with the ability to halt iteration early. The function must return:

  • {:cont, acc} to continue iteration with the new accumulator
  • {:halt, acc} to stop iteration and return the accumulator

Examples

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}, {EX.S3, EX.p, EX.O3}])
iex> RDF.Data.reduce_while(graph, 0, fn _stmt, acc -> {:cont, acc + 1} end)
3

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O1}, {EX.target, EX.p, EX.O2}])
iex> RDF.Data.reduce_while(graph, nil, fn {s, _p, _o}, _acc ->
...>   if s == ~I<http://example.com/target> do
...>     {:halt, s}
...>   else
...>     {:cont, nil}
...>   end
...> end)
~I<http://example.com/target>

reject(data, fun)

Rejects statements in the data structure based on a predicate function.

Returns a new data structure of the same type containing only the statements for which the predicate function returns a falsy value.

This is the complement of filter/2.

Examples

iex> graph = RDF.graph([{EX.S1, EX.p1, EX.O1}, {EX.S2, EX.p2, EX.O2}])
iex> RDF.Data.reject(graph, fn {_s, p, _o} -> p == EX.p1 end)
RDF.graph([{EX.S2, EX.p2, EX.O2}])

iex> dataset = RDF.dataset([{EX.S, EX.p1, EX.O, EX.G}, {EX.S, EX.p2, EX.O, nil}])
iex> RDF.Data.reject(dataset, fn {_s, p, _o, _g} -> p == EX.p1 end)
RDF.dataset([{EX.S, EX.p2, EX.O}])

resources(data, opts_or_filter \\ [])

@spec resources(
  RDF.Data.Source.t(),
  keyword()
  | (RDF.Resource.t() -> boolean())
  | (RDF.Resource.t(), atom() -> boolean())
) :: [RDF.Resource.t()]

Returns all unique resources (non-literal terms) in the data structure.

The second argument can be either a keyword list of options or a filter function.

Options

  • :predicates - when true, includes predicates in the result (default: false)
  • :filter - a filter function (see below)

Filter function

The filter function can be:

  • a 1-arity function receiving only the term
  • a 2-arity function receiving (term, position) where position is :subject, :predicate, or :object

Examples

iex> graph = RDF.Graph.new([{EX.S, EX.p, EX.O}, {EX.S, EX.p2, "literal"}])
iex> RDF.Data.resources(graph)
[~I<http://example.com/O>, ~I<http://example.com/S>]

iex> graph = RDF.Graph.new([{EX.S, EX.p, EX.O}])
iex> RDF.Data.resources(graph, predicates: true)
[~I<http://example.com/O>, ~I<http://example.com/S>, ~I<http://example.com/p>]

iex> graph = RDF.graph([{EX.S, EX.p, EX.O}, {RDF.bnode(), EX.p2, EX.O2}])
iex> RDF.Data.resources(graph, &RDF.iri?/1) |> MapSet.new()
MapSet.new([RDF.iri(EX.S), RDF.iri(EX.O), RDF.iri(EX.O2)])

iex> graph = RDF.graph([{EX.S, EX.p, EX.O}])
iex> RDF.Data.resources(graph, fn _term, pos -> pos == :subject end)
[RDF.iri(EX.S)]

statement_count(data)

@spec statement_count(RDF.Data.Source.t()) :: non_neg_integer()

Returns the count of statements in the RDF data structure.

Examples

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}])
iex> RDF.Data.statement_count(graph)
2

iex> RDF.Data.statement_count(RDF.dataset())
0

statements(data)

@spec statements(RDF.Data.Source.t()) :: [RDF.Statement.t()]

Returns all statements in the data structure.

Extracts all statements as a list. The format depends on the structure type:

  • :description and :graph: Returns triples {subject, predicate, object}
  • :dataset: Returns quads {subject, predicate, object, graph_name}

Examples

iex> graph = RDF.graph([{EX.S, EX.p, EX.O}])
iex> RDF.Data.statements(graph)
[{~I<http://example.com/S>, EX.p(), ~I<http://example.com/O>}]

iex> dataset = RDF.dataset([{EX.S, EX.p, EX.O, EX.G}])
iex> RDF.Data.statements(dataset)
[{~I<http://example.com/S>, EX.p(), ~I<http://example.com/O>, ~I<http://example.com/G>}]

subject_count(data)

@spec subject_count(RDF.Data.Source.t()) :: non_neg_integer()

Returns the count of unique subjects in the RDF data structure.

Examples

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}])
iex> RDF.Data.subject_count(graph)
2

iex> RDF.Data.subject_count(EX.S |> EX.p(EX.O))
1

iex> RDF.Data.subject_count(RDF.graph())
0

subjects(data)

@spec subjects(RDF.Data.Source.t()) :: [RDF.Resource.t()]

Returns all unique subjects in the data structure.

Examples

iex> graph = RDF.Graph.new([{EX.S1, EX.p, EX.O}, {EX.S2, EX.p, EX.O}])
iex> RDF.Data.subjects(graph)
[~I<http://example.com/S1>, ~I<http://example.com/S2>]

subjects(data, filter_fun)

@spec subjects(RDF.Data.Source.t(), (RDF.Resource.t() -> boolean()) | nil) :: [
  RDF.Resource.t()
]

Returns all unique subjects in the data structure matching the filter function.

Examples

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O}, {RDF.bnode(:b), EX.p, EX.O}])
iex> RDF.Data.subjects(graph, &RDF.iri?/1)
[RDF.iri(EX.S1)]

take(data, amount)

Takes the first amount statements from the RDF data structure.

Returns a new data structure of the same type containing at most amount statements. The order of statements is implementation-dependent and should not be relied upon.

For negative amounts, returns an empty data structure.

Examples

iex> graph = RDF.graph([{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}, {EX.S3, EX.p, EX.O3}])
iex> RDF.Data.statement_count(RDF.Data.take(graph, 2))
2

iex> RDF.Data.take(RDF.graph(), 5)
RDF.graph()

to_dataset(data, opts \\ [])

@spec to_dataset(
  RDF.Data.Source.t(),
  keyword()
) :: RDF.Dataset.t() | RDF.Data.Source.t()

Converts the RDF data structure to a dataset.

If the data is already a dataset and no options are provided, returns it unchanged. For graphs, the graph is embedded preserving its name (named or default graph).

Options

  • :native - Forces conversion to native RDF.Dataset (default: false)

Examples

iex> desc = EX.S |> EX.p(EX.O)
iex> RDF.Data.to_dataset(desc)
RDF.dataset({EX.S, EX.p(), EX.O})

iex> graph = RDF.graph([{EX.S, EX.p, EX.O}], name: EX.G)
iex> RDF.Data.to_dataset(graph)
RDF.dataset({EX.S, EX.p(), EX.O, EX.G})

iex> dataset = RDF.dataset([{EX.S, EX.p, EX.O, EX.G}])
iex> RDF.Data.to_dataset(dataset)
dataset

to_graph(data, opts \\ [])

@spec to_graph(
  RDF.Data.Source.t(),
  keyword()
) :: RDF.Graph.t() | RDF.Data.Source.t()

Converts the RDF data structure to a graph.

If the data is already a graph and no options are provided, returns it unchanged. For datasets, all graphs are merged into a single graph (graph names are lost).

Options

  • :native - Forces conversion to native RDF.Graph (default: false)

Examples

iex> desc = EX.S |> EX.p(EX.O)
iex> RDF.Data.to_graph(desc)
RDF.graph({EX.S, EX.p(), EX.O})

iex> graph = RDF.graph([{EX.S, EX.p, EX.O}], name: EX.G)
iex> RDF.Data.to_graph(graph)
graph

iex> dataset = RDF.dataset([{EX.S1, EX.p, EX.O1, EX.G1}, {EX.S2, EX.p, EX.O2, EX.G2}])
iex> RDF.Data.to_graph(dataset)
RDF.graph([{EX.S1, EX.p, EX.O1}, {EX.S2, EX.p, EX.O2}])

triples(data)

@spec triples(RDF.Data.Source.t()) :: [RDF.Triple.t()]

Returns all statements as triples.

Converts all statements to triple format {subject, predicate, object}. For Datasets, this drops the graph component from quads.

Examples

iex> graph = RDF.graph([{EX.S, EX.p, EX.O}])
iex> RDF.Data.triples(graph)
[{~I<http://example.com/S>, EX.p(), ~I<http://example.com/O>}]

iex> dataset = RDF.dataset([{EX.S, EX.p, EX.O, EX.G}])
iex> RDF.Data.triples(dataset)
[{~I<http://example.com/S>, EX.p(), ~I<http://example.com/O>}]