Using kTBS with REST and Turtle

This tutorial aims at showing how to create kTBS elements directly through the REST API with Turtle descriptions. If you are not familiar with Turtle or RDF, you might prefer the JSON version of that tutorial.


For interacting with the kTBS, we will use the simple HTTP client that is embeded within every HTML page generated by kTBS.

Don’t forget, though, that this embeded client is only a convenience shortcut for easily interacting with kTBS. You can just as well use any HTTP client, either interactive (such as curl) or programmatic.

Note also that the Turtle code displayed by the editor might differ from the one presented in those examples; this is because the namespace declarations may not be used in the exact same way. However, they represent the same data.

Create and populate a Stored Trace

In this first part, we will create and populate a stored trace. But for this, we first need to create a Base that will host our traces.

The kTBS Root

The kTBS root is where all bases live. It is automatically created when the kTBS is first launched. Its URI is that of the kTBS server, in our case: http://localhost:8001/ .

Create a new base

To create a new base in our kTBS root, we have to perform an HTTP POST request to it.

Visit the kTBS root and open the embeded HTTP client by clicking to edit at the lower-left of the page.

Visit the kTBS root. Select the POST operation (this opens a text-area), and ensure that the selected content-type is text/turtle. Then copy the following Turtle code in the text-area, and press Send.

@prefix : <> .
@prefix skos: <> .

<> :hasBase <base1/>.

    a :Base ;
    skos:prefLabel "My new base" .

The URI of the newly created base should appear below the text-area (in our example: http://localhost:8001/base1/). By clicking on it, you will see a Turtle description of your base.


All URIs in the example above are relative to the URI of the resource to which we post it; for example:

  • <> is interpreted as <http://localhost:8001/>,
  • <base1/> is interpreted as <http://localhost:8001/base1/>;

this rule is true for all POST and PUT requests to the kTBS.

Create a stored trace

Creating a stored trace inside a base is very similar to creating a base in the kTBS root. We first need to visit the base (you should already be there after the previous step).

Again, select the POST operation, ensure that the content-type is text/turtle, copy the following Turtle code in the text-area, and click Send. Then click on the link appearing between the text-area.

@prefix : <> .

<> :contains <t01/> .

    a :StoredTrace ;
    :hasModel <> ;
    :hasOrigin "1970-01-01T00:00:00Z" .

You will notice that, besides the properties that you set for the new trace, the kTBS created another property, hasObselCollection, pointing to http://localhost:8001/base1/t01/@obsels. This is where all the obsels that we are going to add to this trace will be created.

Add obsels to trace

Adding an obsel to a trace should be no surprise to you at this point: it is simply done by POSTing a description of the obsel to the trace itself.

Simply visit the trace and POST the following content to it:

@prefix : <> .
@prefix m: <> .

<obs1> a m:SimpleObsel ;
    :hasTrace <> .

In the description of the new obsel, you will notice that this time the kTBS added a number of properties in addition to the ones you specified above. More precisely, the begin and end of the obsel have been automatically set based on the moment you posted the obsel; this is expressed in milliseconds since the origin of the trace.

It would have been possible to specify those properties explicitly, if we wanted to override the values automatically computed by the kTBS.

For example, let’s go back to the trace and POST the following content to it:

@prefix : <> .
@prefix m: <> .

<obs0> a m:SimpleObsel ;
    :hasTrace <> ;
    :hasBegin 1361462605000 ;
    :hasEnd   1361462647000 .

We also note that, as with the base and the trace earlier, we had to mint a URI for our new obsels. As we are likely to create a large number of obsels, it sounds like a good idea to leave it to the kTBS to mint a fresh URI for each of them. For our third obsel, we will therefore use a blank node. We will also add attributes and relations to our new obsel to make it more interesting.

Let’s go back to the trace and POST the following content to it:

@prefix : <> .
@prefix m: <> .

[ a m:SimpleObsel ;
  :hasTrace <> ;
  m:value "a new obsel" ;
  m:hasRelatedObsel <obs1> ;


Every element of the kTBS can be created with a blank node instead of an explicit URI. The URI minted by kTBS is returned by the POST operation.

If we follow the hasObselCollection link from our trace, to the obsel collection, we can see the three obsels we have created so far (your timestamps will obviously differ):

@prefix : <> .
@prefix m: <> .

<@obsels> a :StoredTraceObsels .
<.> :hasObselCollection <@obsels> .

<obs0> a m:SimpleObsel;
    :hasBegin 1361462605000;
    :hasEnd 1361462647000;
    :hasTrace <.> .

<obs1> a m:SimpleObsel;
    :hasBegin 1361462685837;
    :hasEnd 1361462685837;
    :hasTrace <.> .

<o-3k> a m:SimpleObsel;
    :hasBegin 1361462707201;
    :hasEnd 1361462707201;
    :hasTrace <.>;
    m:hasRelatedObsel <obs1>;
    m:value "a new obsel" .

Creating computed traces

The kTBS has a number of builtin methods to create Computed Traces. As their name implies, computed trace differ from stored trace by the fact that their obsels are computed by the kTBS (in application of the corresponding method) rather than provided by external collectors.

Create a Computed Trace with a filter method

Let’s go back to the base and create a new computed trace by POSTing the following:

@prefix : <> .

<> :contains <filtered1/> .

    a :ComputedTrace ;
    :hasMethod :filter ;
    :hasSource <t01/> ;
    :hasParameter "after=1361462641000" .

This create a computed trace named filtered1 based on a temporal filter which copies the obsels from t01 obsels situated after timestamp 1361462641000. You may notice that we did not provide any model nor origin for the computed trace; those are automatically computed.

If you go and check the obsel collection of this computed trace, you will find two obsels. More precisely, all obsels from t01 have been copied, except for obs0 which has been filtered out, as it is not entierly after timestamp 1361462641000.

Create a Computed Trace with a SPARQL query

We will now define a more sophisticated computed trace, using the powerful query language SPARQL.

Let’s go back to the base and create a new computed trace by POSTing the following:

@prefix : <> .

<> :contains <joinRelated1/> .

    a :ComputedTrace ;
    :hasMethod :sparql ;
    :hasSource <t01/> ;
    :hasParameter """sparql=
PREFIX m:  <>

    [ a m:SimpleObsel ;
      m:value ?value ;
      :hasTrace <%(__destination__)s> ;
      :hasBegin ?begin ;
      :hasEnd ?end ;
      :hasSourceObsel ?o1, ?o2 ;
    ] .
    ?o1 :hasBegin ?begin .
    ?o2 :hasEnd ?end ;
        m:hasRelatedObsel ?o1 .
    OPTIONAL { ?o2 m:value ?value }
}""" .

This create a computed trace named joinRelated1 using a SPARQL construct query to builds an obsel for each pair of related obsels in t01, inheriting its begin and end timestamps respectively from each of them.


It is frequent that SPARQL construct queries build obsels that comply with a model different from the source trace’s. The target model can be specified with the special model parameter supported by the sparql method.

Create a Computed Trace with a fusion method

We will now use the fusion method, used to aggregate in a computed trace the obsels from several source traces.

Let’s go back to the base and create a new computed trace by POSTing the following:

@prefix : <> .

<> :contains <fusioned1/> .

    a :ComputedTrace ;
    :hasMethod :fusion ;
    :hasSource <filtered1/>, <joinRelated1/> .

This creates a computed trace named fusioned1 which is a merge of the filtered1 and the joinRelated1 traces.