Triple Stores
This tutorial assumes that a connection to a store has been established (when needed) as in the getting started tutorial. The goal of this tutorial is to introduce how to make use of the triples store functionality in kDB, we will show how to insert, remove and query RDF triples. We assume the reader has some basic knowledge of RDF, such as what is presented in our introduction to RDF.
As a basis for this tutorial, we will consider the use case of storing a semantic map of objects, in an RDF Document with uri http://askco.re/examples#map
.
Each object has:
- a location, in term of
x
andy
coordinates. - a class, for instance
table
orchair
. - an optional list of properties, such as
color
,material
…
Each object will be represented in the RDF Graph as follow (using the RDF Turtle format):
@prefix ex: <http://askco.re/examples#>
@prefix ssn: <http://www.w3.org/ns/ssn/>
<object_id> a <object_class>;
ex:location [ ex:x xcoord; ex:y ycoord ] ;
ssn:hasProperty [
a ssn:Property, <property_uri> ;
ssn:hasValue "property_value" .
]
.
Where object_id
, object_class
and property_uri
are URI identifying respectively the object, the class and the property.
object_id
is assumed to be unique, and is best constructed from a UUID.
While object_class
and property_uri
are constants defined in the ontology used for building the map.
Finally xcoord
and ycoord
are the position of the object in the world, while property_value
is the value of the property, and is also defined by the ontology.
There can be any number of ssn:hasProperty [ ... ]
statements, including none.
Inserting Triples
Insert operations can be done using the API, Turtle files or SPARQL/Update queries. All three options are presented in this tutorial.
We will consider the insertion of an object with the following definition:
id
is03643194-0cbe-4701-89e5-8dd1f758e09e
location
isx = 10, y = 12
class
istable
color
isbrown
material
issteel
The object has the following RDF structure (using Turtle format):
@prefix ex: <http://askco.re/examples#>
@prefix ssn: <http://www.w3.org/ns/ssn/>
ex:03643194-0cbe-4701-89e5-8dd1f758e09e a ex:table;
ex:location [ ex:x 10.0; ex:y 12.0 ] ;
ssn:hasProperty [
a ssn:Property, ex:color ;
ssn:hasValue ex:brown
] ;
ssn:hasProperty [
a ssn:Property, ex:material ;
ssn:hasValue ex:steel
]
.
API
The main access point in the API for inserting triples is the kDB::Repository::TriplesStore
class.
The following will add a table
in the triple store.
-
#include <knowCore/TypeDefinitions.h> #include <knowCore/Uris/rdf.h> #include <knowRDF/BlankNode.h> #include <knowRDF/Object.h> #include <knowRDF/Subject.h> #include <kDB/Repository/GraphsManager.h> #include <kDB/Repository/Transaction.h> #include <kDB/Repository/TriplesStore.h> using rdf = knowCore::Uris::rdf; using ssn = knowCore::Uris::ssn; // Get or create the triples store we use to store the map. kDB::Repository::TriplesStore map_ts = connection.graphsManager()->getOrCreateTriplesStore("http://askco.re/examples#map"_kCu).expectSuccess(); kDB::Repository::Transaction transaction(connection); // Insert a table with the given uri in the map map_ts.insert("http://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e"_kCu, rdf::a, "http://askco.re/examples#table"_kCu, // Specify the location of the table // The std::make_tuple indicates an anonymous RDF group, similar to the turtle construct. // Which means the insert function will creates a blank node that serves as a "root" for x, y. "http://askco.re/examples#location"_kCu, std::make_tuple( "http://askco.re/examples#x"_kCu, knowRDF::Object::fromValue(10.0), "http://askco.re/examples#y"_kCu, knowRDF::Object::fromValue(12.0) ), // Defines the two properties following the same principle as for the location. ssn::hasProperty, std::make_tuple( rdf::a, ssn::Property, rdf::a, "http://askco.re/examples#color"_kCu, ssn::hasValue, "http://askco.re/examples#brown"_kCu ), ssn::hasProperty, std::make_tuple( rdf::a, ssn::Property, rdf::a, "http://askco.re/examples#material"_kCu, ssn::hasValue, "http://askco.re/examples#steel"_kCu )); // When inserting multiple objects at the same time, it is recommended to use a transaction. kDB::Repository::Transaction transaction(connection); // Same insertion, but with transaction. map_ts.insert(transaction, "http://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e"_kCu, ... );
-
from knowCore import Uri from knowRDF import Object # Define the 'a' url rdf_a = Uri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type") # Get or create the triples store we use to store the map. map_ts = connection.graphsManager().getOrCreateTriplesStore(Uri("http://askco.re/examples#map")) # Insert a table with the given uri in the map map_ts.insert(Uri("http://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e"), rdf_a, Uri("http://askco.re/examples#table"), # Specify the location of the table # The std::make_tuple indicates an anonymous RDF group, similar to the turtle construct. # Which means the insert function will creates a blank node that serves as a "root" for x, y. Uri("http://askco.re/examples#location"), ( Uri("http://askco.re/examples#x"), Object.fromValue(10.0), Uri("http://askco.re/examples#y"), Object.fromValue(12.0) ), # Defines the two properties following the same principle as for the location. Uri("http://www.w3.org/ns/ssn/hasProperty"), ( rdf_a, Uri("http://www.w3.org/ns/ssn/Property"), rdf_a, Uri("http://askco.re/examples#color"), Uri("http://www.w3.org/ns/ssn/hasValue"), Uri("http://askco.re/examples#brown") ), Uri("http://www.w3.org/ns/ssn/hasProperty"), ( rdf_a, Uri("http://www.w3.org/ns/ssn/Property"), rdf_a, Uri("http://askco.re/examples#material"), Uri("http://www.w3.org/ns/ssn/hasValue"), Uri("http://askco.re/examples#steel") )) # When inserting multiple objects at the same time, it is recommended to use a transaction. transaction = kDB.Repository.Transaction(connection); # Same insertion, but with transaction. map_ts.insert(transaction, Uri("http://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e"), ... );
-
require 'knowCore' require 'knowRDF' # Define the 'a' url # kCu is an alias for knowCore::Uri.new rdf_a = kCu("http://www.w3.org/1999/02/22-rdf-syntax-ns#type") # Get or create the triples store we use to store the map. map_ts = connection.graphsManager.getOrCreateTriplesStore kCu("http://askco.re/examples#map") # Insert a table with the given uri in the map map_ts.insert kCu("http://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e"), rdf_a, kCu("http://askco.re/examples#table"), # Specify the location of the table # The std::make_tuple indicates an anonymous RDF group, similar to the turtle construct. # Which means the insert function will creates a blank node that serves as a "root" for x, y. kCu("http://askco.re/examples#location"), [ kCu("http://askco.re/examples#x"), KnowRDF::Object.fromValue(10.0), kCu("http://askco.re/examples#y"), KnowRDF::Object.fromValue(12.0) ], # Defines the two properties following the same principle as for the location. kCu("http://www.w3.org/ns/ssn/hasProperty"), [ rdf_a, kCu("http://www.w3.org/ns/ssn/Property"), rdf_a, kCu("http://askco.re/examples#color"), kCu("http://www.w3.org/ns/ssn/hasValue"), kCu("http://askco.re/examples#brown") ], kCu("http://www.w3.org/ns/ssn/hasProperty"), [ rdf_a, kCu("http://www.w3.org/ns/ssn/Property"), rdf_a, kCu("http://askco.re/examples#material"), kCu("http://www.w3.org/ns/ssn/hasValue"), kCu("http://askco.re/examples#steel") ] # When inserting multiple objects at the same time, it is recommended to use a transaction. transaction = KDB::Repository::Transaction.new connection # Same insertion, but with transaction. map_ts.insert transaction, kCu("http://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e"), ...
-
From the command line, only the Turtle and Query approaches are supported.
Turtle
The second approach for inserting information in the database is to represent the information as turtle
files (with extension .ttl
).
It can be then inserted using an API call or the command line.
The turtle
definition is provided above and is assumed to be available in a variable called data_ttl
or in a file called data.ttl
.
For inserting large amount of data, the file approach is more efficient and desirable.
-
#include <QBuffer> #include <knowRDF/TripleStream.h> #include <kDB/Repository/TriplesStore.h> #include <kDB/Repository/TriplesStreamInserter.h> // Get or create the triples store we use to store the map. kDB::Repository::TriplesStore map_ts = connection.graphsManager()->getOrCreateTriplesStore("http://askco.re/examples#map"_kCu).expectSuccess(); // Create a triple stream inserter kDB::Repository::TripleStreamInserter inserter(map_ts); // Create a triple stream knowRDF::TripleStream stream; // Add the insterter as a listener stream.addListener(&inserter); // Option 1: For reading from a string in a variable // This variable contains the text of the variable defined earlier QByteArray data_ttl = "..."; // Create a QIODevice with the string QBuffer data_ttl_buffer; data_ttl_buffer.setData(data_ttl); // Insertion the triples in the database stream.start(&data_ttl_buffer, nullptr, knowCore::Format::Turtle).expectSuccess(); // Option 2: For reading from a file QFile data_ttl_file("data.ttl"); data_ttl_file.open(QIODevice::ReadOnly); // Insertion the triples in the database stream.start(&data_ttl_file, nullptr, knowCore::Format::Turtle).expectSuccess();
-
import knowCore import knowRDF import kDB.Repository # Get or create the triples store we use to store the map. map_ts = connection.graphsManager().getOrCreateTriplesStore(knowCore.Uri("http://askco.re/examples#map")) # Create a triple stream inserter inserter = kDB.Repository.TripleStreamInserter(map_ts); # Create a triple stream stream = knowRDF.TripleStream(); # Add the insterter as a listener stream.addListener(inserter); # Option 1: For reading from a string in a variable # This variable contains the text of the variable defined earlier data_ttl = "..."; # Insertion the triples in the database stream.start_from_string(data_ttl, None, "turtle") # Option 2: For reading from a file # Insertion the triples in the database stream.start_from_file("data.ttl", None, "turtle")
-
require 'kDB/Repository' require 'knowRDF' # Get or create the triples store we use to store the map. map_ts = connection.graphsManager.getOrCreateTriplesStore knowCore.Uri("http://askco.re/examples#map" # Create a triple stream inserter inserter = KDB::Repository::TripleStreamInserter.new map_ts # Create a triple stream stream = KnowRDF::TripleStream.new # Add the insterter as a listener stream.addListener inserter # Option 1: For reading from a string in a variable # This variable contains the text of the variable defined earlier data_ttl = "..."; # Insertion the triples in the database stream.start_from_string data_ttl, nil, "turtle" # Option 2: For reading from a file # Insertion the triples in the database stream.start_from_file "data.ttl", nil, "turtle"
-
# The following command will load the RDF data from the `data.ttl` file kdb triple-store --path path/to --port 1242 --ttl --load data.ttl "http://askco.re/examples#map"
SPARQL/Update Query
The last approach for insertion, involve the use of queries. This tutorial does not intend to cover the full usage of SPARQL/Update, a lot of information is available in its official documentation or in other tutorials (check our introduction to RDF for more resources). For this purpose, we will use two queries. The first query is used to create an object
PREFIX ex: <http://askco.re/examples#>
PREFIX ssn: <http://www.w3.org/ns/ssn/>
INSERT DATA
{
%object_uri a %class ;
ex:location [ ex:x %xcoord; ex:y %ycoord ] .
}
For the execution of the query, it is suggested to follow the advanced querying tutorial. This query uses four bindings:
%object_uri
, in this example it should be set tohttp://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e
%class
, in this example it should be set tohttp://askco.re/examples#table
%xcoord
, in this example it should be set to10.0
%ycoord
, in this example it should be set to12.0
To add any properties, we can use the following query:
PREFIX ex: <http://askco.re/examples#>
PREFIX ssn: <http://www.w3.org/ns/ssn/>
INSERT DATA
{
%object_uri
ssn:hasProperty [
a ssn:Property, %property_type ;
ssn:hasValue %value .
] .
}
This query has three bindings:
%object_uri
, in this example it should be set tohttp://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e
%property_type
, in this example it should be set tohttp://askco.re/examples#color
for the first property, and tohttp://askco.re/examples#material
for the second%value
, in this example it should be set tohttp://askco.re/examples#brown
for the first property, and tohttp://askco.re/examples#black
for the second
Removing Triples
Remove operations can be done using the API or SPARQL/Update queries. However the API approach is difficult of use and not recommended, as it requires to explicitely list the specific triples to be removed.
The query to remove a specific objects and its associated properties would look like this: PREFIX dc: http://purl.org/dc/elements/1.1/ PREFIX xsd: http://www.w3.org/2001/XMLSchema#
DELETE WHERE { %object_uri a ?class ; ex:location [ ex:x ?xcoord; ex:y ?ycoord ] . OPTIONAL {
%object_uri
ssn:hasProperty [
a ssn:Property, ?property_type ;
ssn:hasValue ?value .
] . } }
This query has a single binding:
%object_uri
, in this example it should be set tohttp://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e
Querying for Triples
Query operations can be done using the API or SPARQL/Update queries. Both options are presented in this tutorial. However, the API is limited to retrieving all triples in the store, while querying allow for a more rich experience.
API
The main access point in the API for quertying triples is the kDB::Repository::TriplesStore
class.
-
#include <knowRDF/Serialiser.h> #include <kDB/Repository/TriplesStore.h> // Get or create the triples store we use to store the map. kDB::Repository::TriplesStore map_ts = connection.graphsManager()->getOrCreateTriplesStore("http://askco.re/examples#map"_kCu).expectSuccess(); // Get all the triples QList<knowRDF::Triple> triples = map_ts.triples(); // For instance, they can be saved in a turtle file with: QFile all_ttl_file("all.ttl"); all_ttl_file.open(QIODevice::WriteOnly); knowRDF::Serialiser serialiser(&all_ttl_file, knowCore::UriManager()); serialiser.serialise(triples);
-
# Get or create the triples store we use to store the map. map_ts = connection.graphsManager().getOrCreateTriplesStore(knowCore.Uri("http://askco.re/examples#map")) # Get all the triples triples = map_ts.triples(); # For instance, they can be saved in a turtle file with: serialiser = knowRDF.Serialiser.create_to_file("all.ttl", knowCore.UriManager(), "turtle") serialiser.serialise(triples)
-
# Get or create the triples store we use to store the map. map_ts = connection.graphsManager.getOrCreateTriplesStore KnowCore::Uri.new("http://askco.re/examples#map") # Get all the triples triples = map_ts.triples # For instance, they can be saved in a turtle file with: serialiser = KnowRDF::Serialiser.create_to_file "all.ttl", KnowCore::UriManager.new, "turtle" serialiser.serialise triples
-
kdb triple-store --path path/to --port 1242 --ttl --save all.ttl "http://askco.re/examples#map"
SPARQL Query
This tutorial does not intend to cover the full usage of SPARQL, a lot of information is available in its official documentation or in other tutorials (check our introduction to RDF for more resources). For this purpose, we will use two queries. The first query is used to query all the objects in a region:
PREFIX ex: <http://askco.re/examples#>
PREFIX ssn: <http://www.w3.org/ns/ssn/>
SELECT ?object_uri ?class ?xcoord ?ycoord
{
?object_uri a ?class ;
ex:location [ ex:x ?xcoord; ex:y ?ycoord ] .
FILTER (?xcoord > 8 && ?xcoord < 12 && ?ycoord > 10 && ?ycoord < 14 )
}
This query has no bindings and will return all the objects between (8,10)
and (12, 14)
.
The next query can be used to select all the properties of a given object:
PREFIX ex: <http://askco.re/examples#>
PREFIX ssn: <http://www.w3.org/ns/ssn/>
SELECT ?property_type ?value WHERE
{
%object_uri
ssn:hasProperty [
a ssn:Property, ?property_type ;
ssn:hasValue ?value .
] .
}
This query has a single binding:
%object_uri
, in this example it should be set tohttp://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e
Next
This tutorial used custom turtle and SPARQL query to manipulate RDF Graph. In practice, kDB provides many standardised API for storing data related to autonomous systems:
- The salient regions tutorial covers an API for storing salient regions in an RDF Graph, which is very similar to the
map
idea presented in this tutorial.