Skip to main content

Temporal aggregation

Ask for format=aggregatedValues and Andromeda summarises an attribute's history period by period, with a count, a sum, an average and so on. Which methods you can use depends on what the attribute held in the window you asked about. A period that mixes units also refuses the methods that would add those units together.

Asking for an aggregation​

GET /ngsi-ld/v1/temporal/entities/urn:ngsi-ld:Room:1?format=aggregatedValues&aggrMethods=avg,max&aggrPeriodDuration=PT1H&timerel=between&timeAt=2026-10-01T00:00:00Z&endTimeAt=2026-10-02T00:00:00Z

This gives one average and one maximum per hour for each attribute of the room. How an aggregated answer is paged is covered under Paging temporal instances.

Which parameter picks the aggregation​

aggregatedValues can come from format or from options, and format wins when both are given. format=aggregatedValues&options=temporalValues is an aggregated answer and needs aggrMethods. format=temporalValues&options=aggregatedValues is a temporal values answer. There aggrMethods and aggrPeriodDuration are accepted because options names aggregatedValues, but they are ignored, nothing is aggregated, and aggrMethods isn't required. If neither format nor options names aggregatedValues, aggrMethods or aggrPeriodDuration is refused with 400 InvalidRequest.

Andromeda has no parameter for moving where the periods start. aggrPeriodOrigin is a parameter the broker doesn't define, and like any other undefined parameter it gets 400 InvalidRequest.

Which methods an attribute takes​

The methods are totalCount, distinctCount, sum, avg, min, max, stddev and sumsq. Whether one applies depends on two things together. The first is the attribute type, such as a Property, a Relationship or a ListRelationship. The second is the shape of the value, such as a number, a string or a boolean.

You can average numbers but not strings. Asking for avg on an attribute that holds only strings gets 400 InvalidRequest. You might expect totalCount to fail the same way on a string attribute. It doesn't, since counting works on any value.

totalCount and distinctCount therefore work on anything. An attribute that was never recorded can only be aggregated with these two counting methods.

Only what the request aggregates counts​

Andromeda checks the methods against the instances the request actually aggregates. It doesn't check against everything ever stored under that name. That covers three things.

  • the entities it selects, meaning the one entity of a retrieval or every entity a query selects, before pagination
  • the instances inside the time window, on the time axis you asked for
  • the datasets datasetId names

So a different shape held by another entity under the same attribute name doesn't block your request. Neither does a shape this entity held outside the window. Otherwise a value stored years ago could stop you from averaging today.

The check follows the history as it stands now. If you remove the only instance of a shape, the attribute takes every method its remaining shapes allow again. That works whether you delete the instance, modify it into another shape, replace it by writing its instanceId again, or delete a whole window of history.

Mixed units, types and value shapes​

Each period gets one aggregate value per attribute and datasetId. Andromeda never converts between units (see Units). So when a period's instances disagree, some methods have no meaningful answer.

sum, avg, min, max, stddev and sumsq are refused with 400 when the instances of one period disagree in one of these ways.

  • carry two different unitCodes, or a unitCode next to instances with none
  • were written as two different attribute types
  • were recorded in two value shapes with no common order or scale, such as strings and numbers

The error names the attribute and the two values that disagree. If a request asks for one of these methods, the whole request is refused. Andromeda doesn't return the methods that would have worked.

A string declared as an XSD number is a number here, and one declared as xsd:boolean a Boolean. sum, avg, min, max, stddev and sumsq skip a declared INF, -INF or NaN, since their result is a JSON number.

totalCount and distinctCount work over any mixture. How many instances there were doesn't depend on what they hold.

The unit isn't part of the aggregation group. Grouping by unit would give one entry per unit for each period, where the specification asks for one entry per period. The Temporal aggregation grouping extension lets you group by other keys.

Periods​

aggrPeriodDuration is an ISO 8601 duration that cuts the window into periods, starting at an anchor. The anchor is timeAt. A retrieval with no time window has no timeAt, so its anchor is the entity's earliest recorded instant.

A duration made of days and time lays an exact grid. Each boundary is the anchor moved on by a whole number of durations, so with P1D and an anchor at 15:00 every period runs from 15:00 to 15:00 the next day.

A duration with a month or year part lays a calendar grid instead. Andromeda works out each boundary from the anchor itself, never from the boundary before it, and two rules decide the day of month.

  • An anchor on the last day of its month puts every boundary on the last day of its month. A monthly grid from 29 February 2024 has its boundaries on 31 March, 30 April and 31 May. A yearly grid from the same anchor has them on 28 February 2025 and, in the next leap year, 29 February 2028.
  • Any other anchor keeps its day of month, moved back to the last day when a month is shorter. A monthly grid from 30 January has its boundaries on 28 February, 30 March and 30 April.

A duration that mixes a month or year part with days or time leaves no boundary from which a later page could lay the same grid again, so such a read is never paged. Its periods are served whole, and the read is refused with 403 TooManyResults when it holds more than temporal.query.max_last_n periods for one attribute. How a next link continues the other calendar grids is under Following the next link.

A query with no aggrPeriodDuration, or a zero one (PT0S, P0D), aggregates the whole time range as one period. The range belongs to the response, so every aggregate in it reports the same period. A side the query bounds is that bound. A side it leaves open (the end of after, the start of before) is the extreme instant the response holds, the most recent for an end and the least recent for a start.

Under splitEntities with a filter, the aggregates this broker computes over its own and its sources' merged instances are closed to the widest instant across all of them. An aggregate a source answered on its own keeps the period the source answered.

Where Andromeda interprets the specification​

Aggregation is defined by the specification. The methods that apply to each value datatype of a Property are tabulated, and so are those for a Relationship. A ListRelationship is a kind of Relationship.

A query that asks for a method that doesn't apply is refused. The retrieval states no such rule, so Andromeda applies the same rule to the one entity a retrieval names.