A tutorial that can complement this howto can be found at https://github.com/FIWARE/tutorials.Linked-Data.
The FIWARE NGSI v2 information model has been evolved to better support linked
data (entity relationships), property graphs and semantics (exploiting the
capabilities offered by JSON-LD). This
work has been conducted under the ETSI ISG CIM initiative and has been branded
as
NGSI-LD.
The main constructs of NGSI-LD are: Entity, Property and
Relationship. NGSI-LD Entities (instances) can be the subject of Properties
or Relationships. In terms of the traditional NGSI v2 data model, Properties can
be seen as the combination of an attribute and its value. Relationships allow to
establish associations between instances using linked data. In practice, they
are conveyed by means of a special NGSI v2 attribute with a special value
(relationship’s object), which happens to be a URI which points to another
entity. They are similar to the ref attributes recommended by the Data Models
guidelines.
Properties and Relationships can be the subject of other Properties or
Relationships. Thus, in the NGSI-LD information model there are no attribute’s
metadata, but just “properties of properties” or “properties of relationships”.
It is not expected to have infinite graphs, and in practice, only one or two
levels of Property or Relationship “chaining” will happen. Typically, there will
be one, equivalent to the NGSI v2 metadata abstraction. NGSI-LD Entities are
represented using JSON-LD, a JSON-based serialization format for Linked Data.
The main advantage of JSON-LD is that it offers the capability of expanding JSON
terms to URIs, so that vocabularies can be used to define terms unambiguously.
First of all, each Data Model shall have a JSON-LD @context, providing an
unambiguous definition by mapping terms to URIs. For practicality reasons, it is
recommended to have a unique @context resource, containing all terms, subject
to be used in every FIWARE Data Model, the same way as
http://schema.org does. The following steps have to be
followed in order to migrate existing NGSI v2 instantiations of the FIWARE Data
Models to NGSI-LD:
id attributes have to be converted to URIs, preferably
using the NGSI-LD URNProperty.ref attributes (pointing to other entities) have to be converted to
JSON-LD nodes of type Relationship.timestamp metadata item has to be mapped to the observedAt member of
a Property node.unitCode metadata item has to be mapped to the unitCode member of a
Property node.DateTime type has to be properly encoded as per the JSON-LD
rules.geo:json type has to be renamed to GeoProperty.The FIWARE Community has already provided a simple script to migrate FIWARE NGSI entity representations to NGSI-LD, see normalized2LD.py
The figure below shows how air quality information at a certain point of
interest can be conveyed using the FIWARE Data Models (involving the entity
types AirQualityObserved, PointOfInterest) in NGSI v2 format.
{
"id": "AirQualityObserved:RZ:Obsv4567",
"type": "AirQualityObserved",
"dateObserved": {
"type": "DateTime",
"value": "2018-08-07T12:00:00"
},
"NO2": {
"type": "Number",
"value": 22,
"metadata": {
"unitCode": {
"type": "Text",
"value": "GP"
}
}
},
"refPointOfInterest": {
"type": "Reference",
"value": "PointOfInterest:RZ:MainSquare"
}
}
{
"id": "PointOfInterest:RZ:MainSquare",
"type": "PointOfInterest",
"category": {
"type": "List",
"value": ["113"]
},
"description": {
"type": "Text",
"value": "Beach of RZ"
},
"location": {
"type": "geo:json",
"value": {
"type": "Point",
"coordinates": [-8, 44]
}
}
}
The figure below shows how air quality information, at a certain point of
interest, can be conveyed using the FIWARE Data Models (involving the entity
types AirQualityObserved, PointOfInterest) in NGSI-LD format. The new
representation has been obtained by applying the conversion rules described
before.
Please note that the FIWARE Data Models @context could also be served by
https://fiware.github.io/data-models/context.jsonld
and by
https://fiware.github.io/data-models/full-context.jsonld.
The latter includes both the FIWARE Data Models and the Core @context.
{
"id": "urn:ngsi-ld:AirQualityObserved:RZ:Obsv4567",
"type": "AirQualityObserved",
"dateObserved": {
"type": "Property",
"value": {
"@type": "DateTime",
"@value": "2018-08-07T12:00:00Z"
}
},
"NO2": {
"type": "Property",
"value": 22,
"unitCode": "GP"
},
"refPointOfInterest": {
"type": "Relationship",
"object": "urn:ngsi-ld:PointOfInterest:RZ:MainSquare"
},
"@context": [
"https://schema.lab.fiware.org/ld/context",
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld"
]
}
{
"id": "urn:ngsi-ld:PointOfInterest:RZ:MainSquare ",
"type": "PointOfInterest",
"category": {
"type": "Property",
"value": ["113"]
},
"description": {
"type": "Property",
"value": "Beach of RZ"
},
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [-8, 44]
}
},
"@context": [
"https://schema.lab.fiware.org/ld/context",
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld"
]
}
The content of the JSON-LD @context would contain the mappings enumerated
below, plus many others. Observe that refPointOfInterest is labelled as an
@id, as it is actually pointing to another Entity (linked data). On the other
hand, there are certain terms such as location or unitCode which are not
included in the @context, as they pertain to the Core JSON-LD @context which
is always implicit (and cannot be overwritten).
{
"@context": {
"dateObserved": "http://schema.fiware.org/dateObserved",
"NO2": "http://schema.fiware.org/NO2",
"refPointOfInterest": {
"@type": "@id",
"@id": "http://schema.fiware.org/refPointOfInterest"
},
"category": "http://schema.fiware.org/category",
"description": "http://schema.org/description"
}
}
application/ld+json)Observe that the request MIME type is set to application/ld+json. The
@context contains two parts: the ETSI Core @context and the FIWARE Data
Models @context. The ETSI core @context part could have been omitted as it
is always implicit (and cannot be overwritten).
Note: When using application/ld+json the payload must always contain a
@context member.
curl -X POST \
http://localhost:3000/ngsi-ld/v1/entities/ \
-H 'Content-Type: application/ld+json' \
-H 'Content-Length: 903' \
-d '{
"id": "urn:ngsi-ld:ParkingSpot:santander:daoiz_velarde_1_5:3",
"type": "ParkingSpot",
"status": {
"type": "Property",
"value": "free",
"observedAt": "2018-09-21T12:00:00Z"
},
"category": {
"type": "Property",
"value": [
"onstreet"
]
},
"refParkingSite": {
"type": "Relationship",
"object": "urn:ngsi-ld:ParkingSite:santander:daoiz_velarde_1_5"
},
"name": {
"type": "Property",
"value": "A-13"
},
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [
-3.80356167695194,
43.46296641666926
]
}
},
"@context": [
"https://schema.lab.fiware.org/ld/context",
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld"
]
}'
application/json)In this case the payload should not contain any @context member, since the
@context is conveyed as a Link header in the request. It is noteworthy, that
only one Link header pointing to the JSON-LD @context is allowed. That’s
why only the FIWARE Data Models URI @context is provided as the target of a
Link header. Remember that the ETSI Core @context is always implicit.
Note: If no Link header is provided the Entity members will be mapped to the
Default @context which implies that they will be under the dummy
example.org/ngsi-ld namespace.
curl -X POST \
http://localhost:3000/ngsi-ld/v1/entities/ \
-H 'Link: <https://schema.lab.fiware.org/ld/context>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"' \
-H 'content-length: 884' \
-d '{
"id": "urn:ngsi-ld:ParkingSpot:santander:daoiz_velarde_1_5:3",
"type": "ParkingSpot",
"status": {
"type": "Property",
"value": "free",
"observedAt": "2018-09-21T12:00:00Z"
},
"category": {
"type": "Property",
"value": [
"onstreet"
]
},
"refParkingSite": {
"type": "Relationship",
"object": "urn:ngsi-ld:ParkingSite:santander:daoiz_velarde_1_5"
},
"name": {
"type": "Property",
"value": "A-13"
},
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [
-3.80356167695194,
43.46296641666926
]
}
}
}'
application/ld+json)GET requests should always contain a Link header to the corresponding
@context, so that the Broker can be informed of what is the @context of a
query or retrieval operation.
In this case if a Link header had not been provided, the resulting JSON
object would have contained long URIs as member keys, and not the short names
that were used when creating the Entity. (as per the @context provided)
Note: Remember that if no Link header is provided the default @context will
be used. The default @context maps every JSON member to the
http://example.org/ngsi-ld dummy namespace.
curl -X GET \
http://localhost:3000/ngsi-ld/v1/entities/urn:ngsi-ld:ParkingSpot:santander:daoiz_velarde_1_5:3 \
-H 'Accept: application/ld+json' \
-H 'Link: <https://schema.lab.fiware.org/ld/context>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"' \
Response will contain what is shown below (headers and payload). Observe that
this response does not include any Link header as it is indeed
application/ld+json, and, therefore, the @context is already a payload
member.
Content-Type: application/ld+json
{
"id": "urn:ngsi-ld:ParkingSpot:santander:daoiz_velarde_1_5:3",
"type": "ParkingSpot",
"status": {
"type": "Property",
"value": "free",
"observedAt": "2018-09-21T12:00:00Z"
},
"category": {
"type": "Property",
"value": [
"onstreet"
]
},
"refParkingSite": {
"type": "Relationship",
"object": "urn:ngsi-ld:ParkingSite:santander:daoiz_velarde_1_5"
},
"name": {
"type": "Property",
"value": "A-13"
},
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [
-3.80356167695194,
43.46296641666926
]
}
},
"@context": [
"https://schema.lab.fiware.org/ld/context",
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld"
]
}
application/json)GET requests should always contain a Link header to the corresponding
@context, so that the Broker can be informed of what is the @context of a
query or retrieval operation. In this case if a Link header had not been
provided the resulting JSON object would have contained long URIs as member keys
and not the short names that were used when creating the Entity.
Note: If no Link header is provided the default @context will be used.
Remember that the default @context maps every JSON member to the
http://example.org/ngsi-ld dummy namespace.
curl -X GET \
http://localhost:3000/ngsi-ld/v1/entities/urn:ngsi-ld:ParkingSpot:santander:daoiz_velarde_1_5:3 \
-H 'Accept: application/json' \
-H 'Link: <https://schema.lab.fiware.org/ld/context>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"' \
Response will contain what is shown below. Observe that this response does
include the Link header, as it is application/json, and, therefore, the
@context does not appear as a member of the JSON payload.
Content-Type: application/json
Link: <https://schema.lab.fiware.org/ld/context>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"
{
"id": "urn:ngsi-ld:ParkingSpot:santander:daoiz_velarde_1_5:3",
"type": "ParkingSpot",
"status": {
"type": "Property",
"value": "free",
"observedAt": "2018-09-21T12:00:00Z"
},
"category": {
"type": "Property",
"value": [
"onstreet"
]
},
"refParkingSite": {
"type": "Relationship",
"object": "urn:ngsi-ld:ParkingSite:santander:daoiz_velarde_1_5"
},
"name": {
"type": "Property",
"value": "A-13"
},
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [
-3.80356167695194,
43.46296641666926
]
}
}
}
application/ld+json)GET requests should always contain a Link header to the corresponding
@context, so that the Broker can be informed of what is the @context of a
query.
In this case if a Link header had not been provided, there would not have
been query results, as all the Query terms would have been mapped to the
default @context and no matching would have happened.
Note: Remember that if no Link header is provided the default @context will
be used. The default @context maps every JSON member to the
http://example.org/ngsi-ld dummy namespace.
curl -X GET \
http://localhost:3000/ngsi-ld/v1/entities/?type=ParkingSpot&q=status==free&attrs=name,location \
-H 'Accept: application/ld+json' \
-H 'Link: <https://schema.lab.fiware.org/ld/context>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"' \
Response will contain what is shown below (headers and payload). Observe that
this response does not include any Link header as it is indeed
application/ld+json, and, therefore, the @context is already a payload
member of the matching Entities. The Core @context is referenced and included
for the sake of completeness although in this particular case, as the FIWARE
Data Models already contains the Core @context that could have been omitted.
Content-Type: application/ld+json
[
{
"id": "urn:ngsi-ld:ParkingSpot:santander:daoiz_velarde_1_5:3",
"type": "ParkingSpot",
"name": {
"type": "Property",
"value": "A-13"
},
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [
-3.80356167695194,
43.46296641666926
]
}
},
"@context": [
"https://schema.lab.fiware.org/ld/context",
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld"
]
}
]
application/json)GET requests should always contain a Link header to the corresponding
@context, so that the Broker can be informed of what is the @context of a
query.
In this case if a Link header had not been provided, there would not have
been query results, as all the Query terms would have been mapped to the default
@context and no matching would have happened.
Note: If no Link header is provided the default @context will be used.
Remember that the default @context maps every JSON member to the
http://example.org/ngsi-ld dummy namespace.
curl -X GET \
http://localhost:3000/ngsi-ld/v1/entities/?type=ParkingSpot&q=status==free&attrs=name,location \
-H 'Accept: application/json' \
-H 'Link: <https://schema.lab.fiware.org/ld/context>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"' \
Response will contain what is shown below. Observe that this response does
include the Link header, as its MIME type is application/json, and,
therefore, the @context does not appear as a member of the JSON payload.
Content-Type: application/json
Link: <https://schema.lab.fiware.org/ld/context>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"
[
{
"id": "urn:ngsi-ld:ParkingSpot:santander:daoiz_velarde_1_5:3",
"type": "ParkingSpot",
"name": {
"type": "Property",
"value": "A-13"
},
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [
-3.80356167695194,
43.46296641666926
]
}
}
}
]