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 ashttp://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, andhasObselList
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
andend
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.