Temporal linked retrieval
Use join on temporal queries. Ask for a vehicle's history with join=flat&joinLevel=1, and you also get the history of whatever it was parked in, over the same time window. Off by default.
At a glance
- Setting:
extensions.temporal_linked_retrieval.mode,"disabled"(default) or"enabled" - Environment variable:
ANDROMEDA__EXTENSIONS__TEMPORAL_LINKED_RETRIEVAL__MODE - Advertised as:
temporalLinkedRetrievalincontextSourceExtras.extensions - Context sources: a source joins for itself only when every source involved advertises the extension. Otherwise Andromeda follows the links.
- When off:
join,joinLevelandcontainedByon a temporal URL get400InvalidRequest, and linked entity terms orqhops get400BadRequestData
Turning it on
[extensions.temporal_linked_retrieval]
mode = "enabled"
Using it
Say vehicle a was parked in b at 12:03 and 12:05, and in c a month later. Each of the three vehicles has a speed history. Ask for a between 12:00 and 12:10, with its linked entities one level deep:
GET /ngsi-ld/v1/temporal/entities?type=Vehicle&id=urn:ngsi-ld:Vehicle:a&timerel=between&timeAt=2020-08-01T12:00:00Z&endTimeAt=2020-08-01T12:10:00Z&join=flat&joinLevel=1
Accept: application/json
You get a and then b. Trimmed to what matters here:
[
{
"id": "urn:ngsi-ld:Vehicle:a",
"type": "Vehicle",
"isParkedIn": [ ... ],
"speed": [ ... ]
},
{
"id": "urn:ngsi-ld:Vehicle:b",
"type": "Vehicle",
"speed": [
{
"type": "Property",
"value": 30,
"observedAt": "2020-08-01T12:03:00Z"
},
{
"type": "Property",
"value": 40,
"observedAt": "2020-08-01T12:05:00Z"
}
]
}
]
b comes back as its history over the same window as a. c isn't there at all: the only isParkedIn instance that names it is outside the window.
Otherwise the rules are the Core API ones from Linked entities, applied over the request's window:
join=flatappends each linked entity once, andjoin=inlinenests a copy under every Relationship instance that reaches it.joinLevelsets the depth, andcontainedBylists entities you have already seen.pickandomitnarrow linked entities with terms such asisParkedIn{speed}. A term needsjoin, and can't go deeper thanjoinLevel.- A linked entity is reached only through a Relationship instance that is valid inside the window.
- A linked entity is joined even if its current state has been deleted, since what you get is its history.
This works on:
- Retrieve Temporal Evolution of an Entity
- Query Temporal Evolution of Entities by
GET - the same query by
POST /ngsi-ld/v1/temporal/entityOperations/query, withjoinandjoinLevelin the Query body - an EntityMap created from a temporal query, which then records the linked entities too
q hops over the window
A temporal q can look into linked entities as well, such as q=isParkedIn{speed}>5. The hop follows every object the Relationship named while it was valid inside the window, and checks the linked entity's instances that were valid inside the same window. The filter judges the linked entity by the same history the join shows you, not by its current state. Like a linked entity term, a hop needs a join that retrieves linked entities.
Valid inside the window includes the instance each attribute held when the window opens, on the linked entity as on the entity the query returns. When the linked entity is held by more than one broker, that instance is the latest across all of them: this broker and every context source registered for it. A linked entity whose speed was last written before the window is judged on that value, a later value on another holder replaces it, and one deleted before the window opens passes nothing.
What gets refused
Context sources
Your temporal query reaches context sources without join, joinLevel and containedBy. Andromeda follows the links itself, once every holder has answered.
A linked entity that a context source holds is fetched with a standard Retrieve Temporal Evolution of that one entity, over your window. If every source the retrieval reaches advertises temporalLinkedRetrieval, the source is asked to resolve the graph below that entity itself, with the remaining depth and the entities already seen. If not, it is asked for the entity alone, and Andromeda follows the deeper levels. What each registration's source advertises is checked at most once every five minutes.
Why it is an extension
The temporal operations of the specification have no linked entity retrieval. Their URL parameters define no join, joinLevel or containedBy, and a temporal request whose projection or filter conditions indicate linked entity retrieval is answered with BadRequestData. A conformant broker refuses all of it.
The temporal Query body does define join, joinLevel and containedBy, which is why join there is BadRequestData rather than an unknown member when the extension is off. The rules applied with the extension on are the Core ones for linked entities, and how a q hop reads the window is Andromeda's own, since the specification refuses the hop on temporal queries.