# Basic Graph API
Generally, an RTC.Compound
can be used almost interchangeable with an RDF.Graph
, as it tries to be as API-compatible as possible with respect to the triples it includes.
# Creating compounds from scratch
A new compound with a set of triples can be created with the RTC.Compound.new/3
function.
Besides the triples as the first argument, it expects an optional identifier for the compound and some optional keyword options.
As with all functions that expect triples, the triples can be given in any of the supported input forms of RDF.ex (as described here).
# creating a compound from a single triple
iex> RTC.Compound.new({EX.S, EX.P, EX.O}, EX.Compound)
#RTC.Compound<id: ~I<http://example.com/Compound>
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rtc: <https://w3id.org/rtc#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://example.com/Compound>
rtc:elements << <http://example.com/S> <http://example.com/P> <http://example.com/O> >> .
<http://example.com/S>
<http://example.com/P> <http://example.com/O> .
>
# creating a compound from a RDF.Description
iex> EX.S
...> |> EX.p1(EX.O1)
...> |> EX.p2(EX.O2)
...> |> RTC.Compound.new(EX.Compound, prefixes: [ex: EX])
#RTC.Compound<id: ~I<http://example.com/Compound>
@prefix ex: <http://example.com/> .
@prefix rtc: <https://w3id.org/rtc#> .
ex:Compound
rtc:elements << ex:S ex:p1 ex:O1 >>, << ex:S ex:p2 ex:O2 >> .
ex:S
ex:p1 ex:O1 ;
ex:p2 ex:O2 .
>
# creating a compound from a RDF.Graph
iex> """
...> @prefix ex: <http://example.com/> .
...>
...> ex:S
...> ex:p1 ex:O1 ;
...> ex:p2 ex:O2 .
...> """
...> |> RDF.Turtle.read_string!()
...> |> RTC.Compound.new(EX.Compound)
#RTC.Compound<id: ~I<http://example.com/Compound>
@prefix ex: <http://example.com/> .
@prefix rtc: <https://w3id.org/rtc#> .
ex:Compound
rtc:elements << ex:S ex:p1 ex:O1 >>, << ex:S ex:p2 ex:O2 >> .
ex:S
ex:p1 ex:O1 ;
ex:p2 ex:O2 .
>
WARNING
RTC.Compound.new/3
is the only function whose API is a bit different than the counterpart on RDF.Graph
. The first argument must be the initial triples. If you really want to create an empty compound, you can do so by providing an empty list (or map) as the first argument.
RTC.Compound.new([], EX.Compound)
Another difference is that the identifier of the compound is not provided via the :name
keyword option, but as the second argument. All other options of RDF.Graph.new/2
can be used similarly on RTC.Compound.new/3
, but with a little different semantics as described below.
# Auto-generated ids
The new
function can also be called without specifying a compound id. In this case a BlankNode is automatically created and used as the compound id.
iex> RTC.Compound.new([{EX.S, EX.p(), EX.O}]) |> RTC.Compound.id()
~B<b867>
This identifier creation behavior, however, can be configured and customized via RDF.Resource.Generator
s.
For example, to create UUIDv4 URIs instead, you could use this application configuration in your config.exs
:
config :rtc, :id,
generator: RDF.IRI.UUID.Generator,
uuid_version: 4,
prefix: "http://example.com/ns/"
With this configuration in place the same function call leads to a compound like this:
iex> RTC.Compound.new([{EX.S, EX.p(), EX.O}]) |> RTC.Compound.id()
~I<http://example.com/ns/911f2951-a701-4ec1-a1bb-0b3645c2d45f>
Various other functions which create compounds implicitly use this configured resource generator. See the guide on resource generators for more information and available generators.
# Assertion modes
An RTC.Compound
can contain both asserted and unasserted triples. Unasserted triples are those that occur only as quoted triples, but are not stated as normal triple of a RDF graph.
By default, triples added to a compound are interpreted as asserted. This behavior of the new/3
and add/3
functions, however, can be changed by the following configuration in the application environment.
config :rtc, :assertion_mode, :unasserted
The default behavior can be overridden via the :assertion_mode
option on individual calls of these functions.
iex> {EX.S1, EX.P1, EX.O1}
...> |> RTC.Compound.new(EX.Compound, assertion_mode: :unasserted, prefixes: [ex: EX])
...> |> RTC.Compound.add({EX.S2, EX.P2, EX.O2}, assertion_mode: :unasserted)
...> |> RTC.Compound.add({EX.S3, EX.P3, EX.O3})
#RTC.Compound<id: ~I<http://example.com/Compound>
@prefix ex: <http://example.com/> .
@prefix rtc: <https://w3id.org/rtc#> .
ex:Compound
rtc:elements
<< ex:S1 ex:P1 ex:O1 >>,
<< ex:S2 ex:P2 ex:O2 >>,
<< ex:S3 ex:P3 ex:O3 >> .
ex:S3
ex:P3 ex:O3 .
>
# Accessing the triples in a compound
As we mentioned in the beginning, the RTC.Compound
functions for accessing the triples of a compound are mostly compatible with the RDF.Graph
API. So, many of its functions for querying or updating the triples are available:
RTC.Compound.add/3
RTC.Compound.delete/3
RTC.Compound.delete_descriptions/3
RTC.Compound.descriptions/2
RTC.Compound.description/3
RTC.Compound.get/3
RTC.Compound.fetch/3
RTC.Compound.describes?/3
RTC.Compound.empty?/2
RTC.Compound.include?/3
RTC.Compound.triples/2
RTC.Compound.pop/1
RTC.Compound.pop/3
RTC.Compound.subjects/2
RTC.Compound.predicates/2
RTC.Compound.objects/2
RTC.Compound.resources/2
iex> EX.S
...> |> EX.p(EX.O)
...> |> RTC.Compound.new(EX.Compound, prefixes: [ex: EX])
...> |> RTC.Compound.add({EX.S, EX.p(), EX.O2})
#RTC.Compound<id: ~I<http://example.com/Compound>
@prefix ex: <http://example.com/> .
@prefix rtc: <https://w3id.org/rtc#> .
ex:Compound
rtc:elements << ex:S ex:p ex:O >>, << ex:S ex:p ex:O2 >> .
ex:S
ex:p ex:O, ex:O2 .
>
WARNING
The RTC.Compound.put/2
function ist not supported yet as its specific semantics with respect to sub-compound is not decided yet.
Also, the RDF.Graph
functions to make individual RDF-star triple annotations are not supported yet (as well as the respective options on the compound update functions).
The capability to support RDF-star annotations of individual triples as part of a compound is also not available yet.
All of these functions support the :assertion_mode
option to limit the query or operation to either the asserted triples with the :asserted
value or the unasserted triples with the value :unasserted
. Default is the value :all
with which the query or operation is executed on all triples.
iex> compound =
...> RTC.Compound.new({EX.S, EX.p1(), EX.O1}, EX.Compound, prefixes: [ex: EX])
...> |> RTC.Compound.add({EX.S, EX.p2(), EX.O2}, assertion_mode: :unasserted)
#RTC.Compound<id: ~I<http://example.com/Compound>
@prefix ex: <http://example.com/> .
@prefix rtc: <https://w3id.org/rtc#> .
ex:Compound
rtc:elements << ex:S ex:p1 ex:O1 >>, << ex:S ex:p2 ex:O2 >> .
ex:S
ex:p1 ex:O1 .
>
iex> RTC.Compound.delete_descriptions(compound, EX.S, assertion_mode: :asserted)
#RTC.Compound<id: ~I<http://example.com/Compound>
@prefix ex: <http://example.com/> .
@prefix rtc: <https://w3id.org/rtc#> .
ex:Compound
rtc:elements << ex:S ex:p2 ex:O2 >> .
>
iex> RTC.Compound.triples(compound, assertion_mode: :unasserted)
[
{~I<http://example.com/S>, ~I<http://example.com/p2>, ~I<http://example.com/O2>}
]
Just like for RDF.Graph
, Elixir's Enumerable
protocol is implemented for the RTC.Compound
struct by means of the contained triples, which means all Enum
functions can be used with a RTC.Compound
.
iex> EX.S
...> |> EX.p1(EX.O1)
...> |> EX.p2(EX.O2)
...> |> RTC.Compound.new(EX.Compound)
...> |> Enum.to_list()
[
{~I<http://example.com/S>, ~I<http://example.com/p1>,
~I<http://example.com/O1>},
{~I<http://example.com/S>, ~I<http://example.com/p2>,
~I<http://example.com/O2>}
]
Also the RDF.Data
protocol is implemented for RTC.Compound
. In the RDF.Data.merge/2
function the graph name plays a crucial role, as the merge of different graphs with different names results in a RDF.Dataset
. But since compounds are just a bunch of triples inside a graph, the protocol implementation of RDF.Data.merge/2
behaves different than the one for RDF.Graph
s. The compound id or configured graph name of a compound is ignored and the compound is treated just like a set of triples. So, applying RDF.Data.merge/2
on a RTC.Compound
with any other RDF.ex data structure or another compound always returns a RDF.Graph
.
iex> RDF.Graph.new({EX.S, EX.p(), EX.O1})
...> |> RDF.Data.merge(RTC.Compound.new([{EX.S, EX.p(), EX.O2}], EX.Compound))
#RDF.Graph<name: nil
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rtc: <https://w3id.org/rtc#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://example.com/S>
<http://example.com/p> <http://example.com/O1>, <http://example.com/O2> .
>
The only difference from this rule is when merging with a RDF.Dataset
. In that case the graph name, i.e. by default the compound id, is taken into account and the triples will be merged into a graph of that name
iex> RDF.Dataset.new({EX.S, EX.p(), EX.O1})
...> |> RDF.Data.merge(RTC.Compound.new([{EX.S, EX.p(), EX.O2}], EX.Compound))
#RDF.Dataset{name: nil, graph_names: [nil, ~I<http://example.com/Compound>]}
# Retrieving the graph of the included triples
An RDF.Graph
with just the contained triples in a compound can be retrieved with the RTC.Compound.graph/2
function.
iex> EX.S
...> |> EX.p1(EX.O1)
...> |> EX.p2(EX.O2)
...> |> RTC.Compound.new(EX.Compound)
...> |> RTC.Compound.graph()
#RDF.Graph<name: ~I<http://example.com/Compound>
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://example.com/S>
<http://example.com/p1> <http://example.com/O1> ;
<http://example.com/p2> <http://example.com/O2> .
>
The following options can be used to customize the returned graph:
:name
: The name of the graph to be created. By default, the compound id is used as the graph name, unless it is a blank node, in which case the graph won't be named.:prefixes
: Prefix mappings which should be added to the RDF graph and will be used when serializing in a format with prefix support. Default are theRDF.default_prefixes/0
together with thertc
prefix.:base_iri
: The base IRI which should be stored in the RDF graph and will be used when serializing in a format with base IRI support.
iex> EX.S
...> |> EX.p1(EX.O1)
...> |> EX.p2(EX.O2)
...> |> RTC.Compound.new(EX.Compound)
...> |> RTC.Compound.graph(name: EX.GraphName, prefixes: [ex: EX])
#RDF.Graph<name: ~I<http://example.com/GraphName>
@prefix ex: <http://example.com/> .
ex:S
ex:p1 ex:O1 ;
ex:p2 ex:O2 .
>
The default values to be used when generating the graph can also be specified when initializing the compound with new/3
.
iex> EX.S
...> |> EX.p1(EX.O1)
...> |> EX.p2(EX.O2)
...> |> RTC.Compound.new(EX.Compound, name: EX.GraphName, prefixes: [ex: EX])
...> |> RTC.Compound.graph()
#RDF.Graph<name: ~I<http://example.com/GraphName>
@prefix ex: <http://example.com/> .
ex:S
ex:p1 ex:O1 ;
ex:p2 ex:O2 .
>
# Retrieving the complete graph representation of a compound
The complete RDF-star graph containing not only the triples but also their annotations (discussed in the next section), but more importantly also including the RTC statements which constitute the compound, can be generated with the function RTC.Compound.to_rdf/2
.
iex> EX.S
...> |> EX.p1(EX.O1)
...> |> EX.p2(EX.O2)
...> |> RTC.Compound.new(EX.Compound)
...> |> RTC.Compound.to_rdf()
#RDF.Graph<name: ~I<http://example.com/Compound>
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rtc: <https://w3id.org/rtc#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://example.com/S>
<http://example.com/p1> <http://example.com/O1> {| rtc:elementOf <http://example.com/Compound> |} ;
<http://example.com/p2> <http://example.com/O2> {| rtc:elementOf <http://example.com/Compound> |} .
>
Again, the generated graph can be configured with the same keyword options as the to_graph/2
function, and the defaults specified on new/3
are also used here. Additionally, the :element_style
keyword option can be used to specify which RTC property should be used to assign the statements to the compound.
- With
:element_of
the propertyrtc:elementOf
is used, which is the default as can be seen above. - With
:elements
the inverse propertyrtc:elements
is used.
iex> EX.S
...> |> EX.p1(EX.O1)
...> |> EX.p2(EX.O2)
...> |> RTC.Compound.new(EX.Compound)
...> |> RTC.Compound.to_rdf(element_style: :elements)
#RDF.Graph<name: ~I<http://example.com/Compound>
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rtc: <https://w3id.org/rtc#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://example.com/Compound>
rtc:elements
<< <http://example.com/S> <http://example.com/p1> <http://example.com/O1> >>,
<< <http://example.com/S> <http://example.com/p2> <http://example.com/O2> >> .
<http://example.com/S>
<http://example.com/p1> <http://example.com/O1> ;
<http://example.com/p2> <http://example.com/O2> .
>
The default :element_style
can also be configured in the config.exs
files of your application
config :rtc, :element_style, :elements
# Loading a compound from a graph
With the function RTC.Compound.from_rdf/2
a compound can be loaded from a RDF.Graph
, given as the first argument, followed by the id of the compound to be loaded.
RTC.Compound.from_rdf(graph, EX.Compound)
# Loading a compound from a SPARQL endpoint
With RTC.Compound.from_sparql/3
a compound can also be loaded from a triple store. The function expects the URL of a SPARQL endpoint and the id of the compound to be loaded.
RTC.Compound.from_sparql("http://example.com/sparql", EX.Compound)
WARNING
RTC.Compound.from_sparql/3
is available only when sparql_client
is added as a dependency.
See also the SPARQL.Client configuration page on how to setup the HTTP client to be used.
The options can be used to configure the SPARQL.Client.query/3
call made for sending the query. These can be quite important to work with the different SPARQL-star implementations of triple stores, which result from the unspecified media-types to be used for RDF-star results. Since under the hood a CONSTRUCT query is executed, the respective options to return RDF-star results must be provided. For example, Ontotext GraphDB requires to following options:
RTC.Compound.from_sparql("http://example.com/sparql", EX.Compound,
accept_header: "application/x-turtlestar", result_format: :turtle)
The options to be used by default on from_sparql/3
can also be configured on the :from_sparql_opts
environment key in your config.exs
files.
config :rtc, :from_sparql_opts,
accept_header: "application/x-turtlestar",
result_format: :turtle
See the SPARQL.Client API documentation (opens new window) for the available options.
WARNING
Since RTC is fundamentally based on RDF-star, this feature only works with triple stores that support SPARQL-star.
TIP
In case you're missing a function to insert a compound into a triple store: there is no such function in RTC.ex, since that can be done pretty straightforward with the general SPARQL.Client.insert_data/2
and RTC.Compound.to_rdf/1
function.
compound
|> RTC.Compound.to_rdf()
|> SPARQL.Client.insert_data("http://example.com/sparql")