Entity maps
An entity map remembers who answered a query. Run a query once, and the broker records which entities matched and which context sources each one came from, as a resource with its own identifier. A later request that carries the map's identifier is answered inside that membership, and the map is echoed back so you can tell what you were talking to.
The idea is the query-side sibling of a snapshot. Where a snapshot freezes the data, an entity map freezes the membership. The specification defines both, and clause 5.14 gives the EntityMap its own resource, representation and operations.
Creating a map from a query
Send the query to GET /ngsi-ld/v1/entityMaps instead of GET /ngsi-ld/v1/entities. The answer carries the matched entities, and it is also the new map.
- http
- curl
- rust
- python
GET /ngsi-ld/v1/entityMaps?type=Vehicle
Accept: application/json
curl -i 'http://localhost:1056/ngsi-ld/v1/entityMaps?type=Vehicle' \
-H 'Accept: application/json'
let created = client
.entity_maps()
.create()
.entity_type("Vehicle")
.send()
.await?;
response = requests.get(
f"{BASE}/entityMaps",
params={"type": "Vehicle"},
headers={"Accept": "application/json"},
)
assert response.status_code == 201
HTTP/1.1 201 Created
Content-Type: application/json
Location: /ngsi-ld/v1/entityMaps/urn:ngsi-ld:EntityMap:1
NGSILD-EntityMap: urn:ngsi-ld:EntityMap:1
{
"id": "urn:ngsi-ld:EntityMap:1",
"type": "EntityMap",
"expiresAt": "2026-10-26T11:00:00.000Z",
"entityMap": {
"urn:ngsi-ld:Vehicle:1": [
"@none"
]
},
"linkedMaps": {}
}
The entityMap member maps every matched entity to the context sources it came from, and ["@none"] marks an entity answered from the broker's own store. A query forwarded to registered sources records the source names instead. The NGSILD-EntityMap header and the Location header name the new resource, whose default lifetime the answer's expiresAt shows.
The ordinary query endpoint creates a map too. Adding entityMap=true to GET /ngsi-ld/v1/entities answers the entities as usual and adds the NGSILD-EntityMap header, so a client that wants the map keeps it and one that does not ignores it. A plain query without the option creates nothing.
Reading and keeping a map
GET /ngsi-ld/v1/entityMaps/urn:ngsi-ld:EntityMap:1 returns the map as shown above. PATCH sets a new expiresAt to keep it alive, and DELETE removes it, answering 204. A repeat delete answers 404, as does an unknown identifier from the start. An output-only member in a patch, such as the entityMap membership itself, is ignored.
Working under a map
Carry the identifier in an NGSILD-EntityMap header on a later request. The temporal API keeps the follow-up inside the map's membership, and echoes the header on the answer.
- http
- curl
- rust
- python
GET /ngsi-ld/v1/temporal/entities/urn:ngsi-ld:Vehicle:1
NGSILD-EntityMap: urn:ngsi-ld:EntityMap:1
curl -i http://localhost:1056/ngsi-ld/v1/temporal/entities/urn:ngsi-ld:Vehicle:1 \
-H 'NGSILD-EntityMap: urn:ngsi-ld:EntityMap:1'
let vehicle = EntityId::from(iri!("urn:ngsi-ld:Vehicle:1"));
let map = NgsiLdEntityMap::try_new(IriRefBuf::new("urn:ngsi-ld:EntityMap:1")?)?;
let reuse = EntityMapRequest::reuse(map);
let history = client
.temporal()
.retrieve(&vehicle)
.entity_map(&reuse)
.send()
.await?;
response = requests.get(
f"{BASE}/temporal/entities/urn:ngsi-ld:Vehicle:1",
headers={"NGSILD-EntityMap": "urn:ngsi-ld:EntityMap:1"},
)
assert response.status_code == 200
HTTP/1.1 200 OK
Content-Type: application/json
NGSILD-EntityMap: urn:ngsi-ld:EntityMap:1
An entity created after the map was made is not pulled in by a follow-up. The membership is fixed at creation, which is the point. A temporal query creates a map of its own the same way, with entityMap=true on the temporal query or by sending the temporal query to the entity-maps resource.
What gets refused
400BadRequestDatafor a map creation with no narrowing member, such as an identifier alone, or a query narrowed only by apick.400BadRequestDatafor URL parameters on a map creation whose query arrived in aPOSTbody.400InvalidRequestforentityMapLifetimewithoutentityMap=true.400BadRequestDatafor anexpiresAtin the past, or one with more than six fractional digits.