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 and y coordinates.
  • a class, for instance table or chair.
  • 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 is 03643194-0cbe-4701-89e5-8dd1f758e09e
  • location is x = 10, y = 12
  • class is table
  • color is brown
  • material is steel

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 to http://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e
  • %class, in this example it should be set to http://askco.re/examples#table
  • %xcoord, in this example it should be set to 10.0
  • %ycoord, in this example it should be set to 12.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 to http://askco.re/examples#03643194-0cbe-4701-89e5-8dd1f758e09e
  • %property_type, in this example it should be set to http://askco.re/examples#color for the first property, and to http://askco.re/examples#material for the second
  • %value, in this example it should be set to http://askco.re/examples#brown for the first property, and to http://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 to http://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 to http://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.