Using kTBS with REST and JSON

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

Tools

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 JSON code displayed by the editor might differ from the one presented in those examples, for example in the order of the properties. 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. Select the POST operation (this opens a text-area), and ensure that the selected content-type is application/json. Then copy the following JSON code in the text-area, and press Send.

{
    "@id": "base1/",
    "@type": "Base",
    "label": "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 JSON description of your base.

You will notice that, besides the properties that you set when posting, the kTBS created two other properties, @context and inRoot. The latter links the base to the kTBS root it belongs to. We will explain the @context property later in this tutorial.

Note

A number of JSON properties expect URIs, as for example @id or inBase. In the example above, all URIs are relative to the URI of the resource to which we post it; for example:

  • 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 application/json, copy the following JSON code in the text-area, and click Send. Then click on the link appearing between the text-area.

{
    "@id": "t01/",
    "@type": "StoredTrace",
    "hasModel": "http://liris.cnrs.fr/silex/2011/simple-trace-model",
    "origin": "1970-01-01T00:00:00Z"
}

You will notice that, besides the properties that you set for the new trace, the kTBS created three other properties:

  • @context (which we will explain later, be patient),
  • inBase linking to the base containing this trace, and
  • hasObselList 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:

{
    "@id": "obs1",
    "@type": "m:SimpleObsel"
}

Note that m:SimpleObsel is a so-called compact URI, where the prefix m: stands for the URI of the model of the trace (followed by a hash #). So the type of the obsel is actually http://liris.cnrs.fr/silex/2011/simple-trace-model#SimpleObsel .

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.
  • The hasTrace links the obsel to the trace containing it.
  • The @context property.

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

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

{
    "@id": "obs0",
    "@type": "m:SimpleObsel",
    "begin": 1361462605000,
    "end":   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:

{
    "@type": "m:SimpleObsel",
    "m:value": "a new obsel",
    "m:hasRelatedObsel": { "@id": "obs1" }
}

Note

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):

{

    "@context": [
        "http://liris.cnrs.fr/silex/2011/ktbs-jsonld-context",
        { "m": "http://liris.cnrs.fr/silex/2011/simple-trace-model#" }
    ],
    "@id": "./",
    "hasObselList": {"@id":"", "@type": "StoredTraceObsels" },
    "obsels": [
        {
            "@id": "obs0",
            "@type": "m:SimpleObsel",
            "begin": 1361462605000,
            "end": 1361462647000
        },
        {
            "@id": "obs1",
            "@type": "m:SimpleObsel",
            "begin": 1394791006055,
            "end": 1394791006055,
            "@reverse": {
                "m:hasRelatedObsel": {"hasTrace": "./", "@id": "o-8g"}
            }
        },
        {
            "@id": "o-8g",
            "@type": "m:SimpleObsel",
            "begin": 1394791489228,
            "end": 1394791489228,
            "m:hasRelatedObsel": {"hasTrace": "./", "@id": "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:

{
    "@id": "filtered1/",
    "@type": "ComputedTrace",
    "hasMethod": "filter",
    "hasSource": [ "t01/" ],
    "parameter": [ "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:

{
    "@id": "joinRelated1/",
    "@type": "ComputedTrace",
    "hasMethod": "sparql",
    "hasSource": [ "t01/" ],
    "parameter": [ "sparql=    PREFIX : <http://liris.cnrs.fr/silex/2009/ktbs#>\nPREFIX m:  <http://liris.cnrs.fr/silex/2011/simple-trace-model#>\n\nCONSTRUCT {\n    [ a m:SimpleObsel ;\n      m:value ?value ;\n      :hasTrace <%(__destination__)s> ;\n      :hasBegin ?begin ;\n      :hasEnd ?end ;\n      :hasSourceObsel ?o1, ?o2 ;\n    ] .\n} WHERE {\n    ?o1 :hasBegin ?begin .\n    ?o2 :hasEnd ?end ;\n        m:hasRelatedObsel ?o1 .\n    OPTIONAL { ?o2 m:value ?value }\n}\n" ]
}

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.

As the SPARQL query is not very legible when encoded as a JSON string, it is provided below:

PREFIX : <http://liris.cnrs.fr/silex/2009/ktbs#>
PREFIX m:  <http://liris.cnrs.fr/silex/2011/simple-trace-model#>

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

Note

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:

{
    "@id": "fusioned1/",
    "@type": "ComputedTrace",
    "hasMethod": "fusion",
    "hasSource": [ "filtered1/", "joinRelated1/" ]
}

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

So what about this @context thing?

Internally, kTBS uses RDF to represent its data. The JSON representations are therefore converted to/from RDF data. For this, kTBS uses a technology called JSON-LD. The @context property is JSON-LD specific, and provides the additional information required for the conversion to/from RDF.

It is worth noting that kTBS accepts both content types application/json (generic JSON) and application/ld+json (JSON-LD). When posting application/json, you may omit the @context property (as well as other properties, such as inRoot, inBase and inTrace), as we have done along this tutorial, but your JSON has to comply more closely to the structure expected by kTBS. When posting application/json-ld, you are free to structure your JSON as you wish as long as it translates into an RDF graph acceptable by kTBS; this usually implies that you provide the @context property explicitly.