Getting Started
As a developer, you might be thinking "this all seems awfully complex and painful". If that sounds like you, we have good news: you don't have to bother with most of it!
This section will take you through the main steps that are needed to get started. We will show this using the Java Developer API, but it can also be done using the REST Developer API.
We provide an implementation of a SmartConnector
that you can instantiate as a Java object.
When you want to exchange information within a Knowledge Network, you are responsible for:
- Registering the knowledge that your knowledge base requests or provides.
- Implementing handlers that are called when certain knowledge is requested or provided.
The SmartConnector uses this to register itself in the knowledge network. The following sections further explain how a SmartConnector can be instantiated and, how you can register the knowledge that your knowledge base provides.
Instantiating and Configuring a SmartConnector
Assuming this
is your knowledge base, you can make a SmartConnector
as follows:
SmartConnector sc = SmartConnectorBuilder.newSmartConnector(this).create();
Registering and Using Knowledge Interactions
Currently, a SmartConnector is required to register the patterns of knowledge that it will request from the network.
A knowledge request can be registered as follows:
AskKnowledgeInteraction asksForTemperatureMeasurements = new AskKnowledgeInteraction(graphPattern);
sc.register(
asksForTemperatureMeasurements
);
where graphPattern
is a string describing an RDF graph pattern where variables are prefixed with a ?
.
Graph patterns consist of one or more triples separated by a dot (.) and each triple consists of a subject, predicate and object node.
Each node can be either a variable (using a question mark ?var
prefix), a URI (using the <https://...>
or a literal (using quotes "hello"
).
Graph Pattern syntax is based on W3C's SPARQL 1.1 basic graph patterns.
As an example, assume graphPattern
is the following graph pattern:
?measurement rdf:type saref:Measurement .
?measurement saref:hasFeatureOfInterest ?room .
?room rdf:type saref:Room .
?measurement saref:observedProperty saref:Temperature .
?measurement saref:hasSimpleResult ?temperature .
It can be illustrated with this diagram:
where the variables are represented by circles and the fixed URIs are represented by rectangles.
The graph pattern above matches on temperature measurements in rooms.
Querying the Network
When querying the network for the pattern, the variables (?measurement
, ?room
, and ?temperature
) can be bound to known values, to limit the possible matches.
For example, if we know that there's a room called https://www.example.org/kitchen
, we can set up the bindings as such:
Set<Binding> queryBindings = new HashSet<Binding>();
queryBindings.add(new Binding(new String\[][] {{ "room", "<https://www.example.org/kitchen>" }}));
and subsequently query for matches:
AskResult askResult = sc.ask(asksForTemperatureMeasurements, queryBindings).get();
BindingSet resultBindings = askResult.getBindings();
The results from the knowledge network are in the set of bindings.
The AskResult
contains other useful information, such as AskExchangeInfo
, which gives information about the data's origins.
Other Types of Knowledge Interactions
Aside from ASK
knowledge interactions, there are also ANSWER
, REACT
, and POST
interactions.
ASK
is used to request information, ANSWER
is used to reply to a request, POST
is used to publish information, and REACT
can be used to subscribe to information.
They can be registered and executed in a similar way to the ASK
explained above (see here).
Bindings
The data that is shared is inside the Binding
objects.
A Binding
object describes a 'match' of a graph pattern.
For the graph pattern above, an example binding might be:
measurement --> <https://www.example.org/measurement-42>
room --> <https://www.example.org/kitchen>
temperature --> "21.2"^^<http://www.w3.org/2001/XMLSchema#float>
As you can see, a Binding
object is essentially a map from variable names to the values that they're bound to in the real world.
Two important things should be noted:
- The keys of the bindings MUST correspond to the variable names in the graph pattern, and they must be complete (all variables must have a value bound to them). (This last restriction does not apply to the bindings given with ASK requests; they can be partial of even empty.)
- The values of the bindings MUST be valid IRIs (https://www.w3.org/TR/turtle/#sec-iri) (for now without prefixes, so full IRIs) or valid literals (https://www.w3.org/TR/turtle/#literals).
Binding Sets
A result of a knowledge interaction can have more than 1 match. These matches are collected in a BindingSet
, which is simply a set of bindings.
The Knowledge Engine does not guarantee the ordering of bindings in a binding set. The reason why we call it a binding set is because the elements in a set are unordered. Due to the nature of RDF, the ordering of the bindings in a binding set cannot be used to encode any information when exchanging data. Thus, if you need ordering, this should be encoded explicitly, e.g. by using numbers or timestamps, and the receiving end should use this information to put the information in the correct order.
Expressiveness
The Graph Pattern syntax has a limited expressivity. This means there are certain things that you might want to express with them, but are unable to. Sometimes this means it limits the actual data exchange, but sometimes there are workarounds. One of the limitations is related to one-to-many relations. Take the following RDF about parents and children in which a parent has a one-to-many relation with its children:
ex:parent1 rdf:type ex:Parent .
ex:parent1 ex:hasChild ex:child1 .
ex:child1 rdf:type ex:Child .
ex:parent1 ex:hasChild ex:child2 .
ex:child2 rdf:type ex:Child .
ex:parent1 ex:hasChild ex:child3 .
ex:child3 rdf:type ex:Child .
As you can see, RDF is perfectly capable of expressing one-to-many relations. Now imagine that you want to use the Knowledge Engine to exchange information about parents and their children. For this you need to let the Knowledge Engine know that you can participate in data exchanges about parents and their children. You do this using a graph pattern (and for example an AnswerKnowledgeInteraction). Let's take the following graph pattern:
?parent rdf:type ex:Parent .
?parent ex:hasChild ?someChild .
?someChild rdf:type ex:Child .
Since the syntax of graph patterns is limited, you cannot express that the ex:hasChild
relation between a parent and a child is a one-to-many relation.
So, how does the Knowledge Engine exchange the RDF data about parents and their children above?
This is where binding sets come in.
A binding set provides a collection of 'solutions' to the graph pattern, i.e. combinations of values for the available variables (those start with a question mark ?
) in the graph pattern.
So, in the graph pattern above there are two variables (note that both variables occur twice).
A binding set consists of zero or more bindings and each binding represents a single mapping of some or all of the variables to a value.
For example, when the graph pattern above is applied to the RDF data above, this results in the following binding set (note that we use JSON here to express the binding set):
[
{
"parent": "<https://example.com/parent1>",
"someChild": "<https://example.com/child1>",
},
{
"parent": "<https://example.com/parent1>",
"someChild": "<https://example.com/child2>",
},
{
"parent": "<https://example.com/parent1>",
"someChild": "<https://example.com/child3>",
}
]
As you can see, the one-to-many relations in the RDF data is represented in the binding set by having 3 bindings.
Note that all bindings have the same value for the parent
variable.
Hierarchy Example
Imagine your graph pattern looks something like this (note that we use a non-existing ontology):
?ts rdf:type ex:TimeSeries .
?ts ex:hasUnitOfValue ?unit .
?ts ex:hasMeasurement ?meas .
?meas ex:hasTimestamp ?timestamp .
?meas ex:hasValue ?value .
Imagine you have JSON data of the form:
{
"unit": "degrees celcius",
"measurements": [
{
"timestamp": "2021-04-29T12:00:00Z",
"value": "22.8"
},
{
"timestamp": "2021-04-29T12:00:00Z",
"value": "20.8"
}
]
}
How would the binding set look like that represents the JSON corresponding to the graph pattern?
[
{
"ts": "<https://www.interconnectproject.eu/example/timeseries1>",
"unit": "<https://www.interconnectproject.eu/example/degreesCelcius>",
"meas": "<https://www.interconnectproject.eu/example/timeseries1/measurement1>",
"timestamp": "\"2021-04-29T12:00:00Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>",
"value": "\"22.8\"^^<http://www.w3.org/2001/XMLSchema#double>"
},
{
"ts": "<https://www.interconnectproject.eu/example/timeseries1>",
"unit": "<https://www.interconnectproject.eu/example/degreesCelcius>",
"meas": "<https://www.interconnectproject.eu/example/timeseries1/measurement2>",
"timestamp": "\"2021-04-29T13:00:00Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>",
"value": "\"20.8\"^^<http://www.w3.org/2001/XMLSchema#double>"
}
]
Note the following:
- the
meas
variable in the first array element is calledmeasurement1
and in the second array element it is calledmeasurement2
. - the value of the
ts
variable in the first array element is the same as thets
variable in the second array element. - both values of the
timestamp
andvalue
variable have this structure"..."^^<...>
, where the first...
contains the actual value and the second...
contains the type of this value. - quotes in the values need to be escaped using a
\
.