Discovery
Discovery answers a question about the whole tenant, namely which entity types are out there, which attributes they carry, and how widely each one is used. The answer is built from the entities as they are, not from a schema anyone uploaded. Write entities and the type list grows, and delete the last one of a kind and its entry disappears.
Four resources do the work, under /ngsi-ld/v1/types and /ngsi-ld/v1/attributes.
Which types exist
GET /ngsi-ld/v1/types lists every entity type any stored entity carries.
- http
- curl
- rust
- python
GET /ngsi-ld/v1/types
Link: <https://example.org/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"
curl -i http://localhost:1056/ngsi-ld/v1/types \
-H 'Link: <https://example.org/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"'
let contextual = client.with_context(IriRefBuf::new("https://example.org/context.jsonld")?)?;
let types = contextual.entity_types().list().send().await?;
response = requests.get(
f"{BASE}/types",
headers={
"Link": '<https://example.org/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"',
},
)
assert response.status_code == 200
{
"type": "EntityTypeList",
"typeList": [
"Building",
"Vehicle"
]
}
The names come back through your @context. With a Link header naming one, the list holds its short terms, and without one, the same list holds full IRIs. A tenant holding no entities lists nothing.
What a type carries
GET /ngsi-ld/v1/types?details=true answers one entry per type, naming exactly the attributes that the type's own entities carry.
- http
- curl
- rust
- python
GET /ngsi-ld/v1/types?details=true
curl -i 'http://localhost:1056/ngsi-ld/v1/types?details=true'
let types = client.entity_types().list_details().send().await?;
response = requests.get(
f"{BASE}/types",
params={"details": "true"},
)
assert response.status_code == 200
[
{
"type": "EntityType",
"typeName": "Building",
"attributeNames": [
"marker",
"owner",
"position"
]
}
]
An entity with several types has its attributes counted under each of them, so a Store that is also a Building puts its marker in both entries. The entity scope member is never listed, because it is not an attribute.
Which attributes exist
GET /ngsi-ld/v1/attributes?details=true answers one entry per attribute, with attributeName and the typeNames of every entity type carrying it. The same attribute written as a Property on one entity and as a Relationship on another still makes one entry, because the name is the same, and the types it appears on are what the entry reports.
One attribute in detail
GET /ngsi-ld/v1/attributes/{attributeName} answers for a single attribute.
{
"type": "Attribute",
"attributeName": "airQualityLevel",
"attributeCount": 2,
"attributeTypes": [
"Property"
],
"typeNames": [
"Building"
]
}
attributeCount is the number of entities carrying the attribute, not the number of instances, since two entities with three datasetId instances each count twice. attributeTypes lists the kinds it is written as, and typeNames the entity types. An attribute whose last instance is gone, whether deleted or removed by the expiry sweep, answers 404 ResourceNotFound.
Naming things in requests and answers
The attribute name in the path goes through your @context like any name in the API, and the Link header compacts the names in the answers the same way. Under a @context that does not define your terms, every name is served fully qualified, which is how two systems that share IRIs but not short names still read each other's discovery answers.