Skip to main content

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: temporalLinkedRetrieval in contextSourceExtras.extensions
  • Context sources: a source joins for itself only when every source involved advertises the extension. Otherwise Andromeda follows the links.
  • When off: join, joinLevel and containedBy on a temporal URL get 400 InvalidRequest, and linked entity terms or q hops get 400 BadRequestData

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=flat appends each linked entity once, and join=inline nests a copy under every Relationship instance that reaches it.
  • joinLevel sets the depth, and containedBy lists entities you have already seen.
  • pick and omit narrow linked entities with terms such as isParkedIn{speed}. A term needs join, and can't go deeper than joinLevel.
  • 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, with join and joinLevel in 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​

  • 400 BadRequestData for a q hop or a linked entity term without a retrieving join, or deeper than joinLevel

    Add join=flat or join=inline, and a joinLevel deep enough.

  • With the extension off, 400 InvalidRequest for join, joinLevel or containedBy in a temporal URL

    They aren't parameters of the temporal operations in the specification.

  • With the extension off, 400 BadRequestData for "join": "flat" or "join": "inline" in the Query body of a temporal query

    join is a known member of the Query body, but a temporal query can't use it. "join": "@none" is accepted.

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.