Ordering
orderBy sorts the entities a query returns. You can sort by an attribute's value, by one of the entity's own members, or by distance from a point. Without orderBy, entities come back in identifier order.
Ordering a query
Name one or more terms in orderBy, separated by commas. Each term is a member or an attribute path, optionally followed by ; and a direction. The directions are asc, which is the default, desc, dist-asc and dist-desc.
GET /ngsi-ld/v1/entities?type=Building&orderBy=category,height;desc
The terms apply in order. The result follows category, and among buildings in the same category the tallest comes first. Entities that still tie come back in identifier order.
An entity without the attribute comes last, whichever direction you pick. So orderBy=name and orderBy=name;desc both put the unnamed entities at the end. The specification puts it as "null values last" (clause 4.23.2), and Andromeda keeps that promise in both directions.
Terms
A term can be one of the following.
id,type,scope,createdAtormodifiedAt, the entity's own members.- An attribute name, such as
height. - A path into an attribute, written as in
q.name.observedAtsorts by a sub-attribute,address[city]by a member of a structured value, andsensor.rawdata[particulate]by both. - A LanguageProperty with a language, such as
label[en], which sorts by the English string.
A path takes up to 32 segments after the attribute name, the same depth an attribute may nest to (see Query language). name.createdAt sorts by when the name attribute was created, not by when the entity was. speed.unitCode sorts by the text of the code, so HM comes before KMH. It is compared as a String under the request's collation, the same text a q term compares. An entity whose speed has no unitCode sorts as if the attribute didn't exist.
Ordering a POST query
POST /ngsi-ld/v1/entityOperations/query takes the same ordering in the ordering member of the Query body.
{
"type": "Query",
"entities": [
{
"type": "Building"
}
],
"ordering": {
"orderBy": [
"category",
"height;desc"
],
"collation": "de-u-co-phonebk"
}
}
orderBy is required in the ordering member. An ordering that holds only a collation or a reference geometry is refused.
How values compare
Numbers compare by value and date-times as instants. Strings compare by collation. A Relationship sorts by the URI of its target, as text.
When entities hold values of different kinds, the kind decides before the value does. The kinds sort in this order.
- numbers
- strings
- objects
- arrays
- booleans
TimeDateDateTime- entities without the attribute
A Time, Date or DateTime only counts as a time when it is declared as one (see Values). An untyped string such as "12:00:00Z" sorts with the strings, by its text. A string declared as an XSD number sorts with the numbers, and one declared as xsd:boolean with the booleans (see Values). Among the numbers, -INF comes first and INF after every finite number, followed by NaN.
An entity without a scope comes after every entity with one, the same way a missing attribute comes last.
Rare cases
- An entity sorts by one value per term. For an attribute with several
datasetIdinstances, that is the instance written first, whatever itsdatasetId. An instance with adatasetIdcounts even when the entity has no default instance. - An array sorts by its first element.
- A JsonProperty member is addressed by its key as written, so
blob[k]reads the memberkof the raw JSON. orderBy=idsorts identifiers by collation, like any string. That isn't the same as the identifier order you get withoutorderBy, which compares bytes. Under the root collation,urn:ngsi-ld:Room:aBsorts beforeurn:ngsi-ld:Room:Ab.- With the Target languages in
qextension, a term can name a full tag such aslabel[en-GB], but never a range.
Collation
Strings sort by the ICU root collation, und-x-icu, unless you name another with collation. The root order compares letters before it looks at case and accents. So alpha comes before Bravo, and Schäfer sits between Schafer and Schaffer.
To pick another collation, name it as a BCP 47 tag, with the settings of RFC 6067 in a -u- extension.
GET /ngsi-ld/v1/entities?type=Person&orderBy=name&collation=de-u-co-phonebk
The German phonebook order reads ä as ae, so Schäfer comes first, before Schafer. Other settings change the order in other ways.
und-u-kn-truesorts digits as numbers, soRoom2comes beforeRoom10.und-u-ks-level1ignores case and accents.und-u-ks-identictakes every difference into account.
A collation only changes how strings are sorted. Without orderBy, it changes nothing.
Andromeda accepts only the settings RFC 6067 registers for collation. ICU silently replaces an unregistered key with its default, so a misspelt setting would sort your strings in an order you never asked for. Andromeda refuses the query instead.
Andromeda names the collation in every query that orders strings, so the answer never depends on the server's default. The storage layer follows the same rule, and so do subscriptions evaluated in memory.
Rare cases
- The first query that names a collation creates it in the tenant's database, once. Later queries reuse it.
- A collation and its settings are checked when the query is read. A locale ICU doesn't know, such as
zz-ZZ, is refused when Andromeda first tries to create it.
Distance
dist-asc and dist-desc sort by distance from a reference geometry. You give that geometry in orderFrom, as GeoJSON coordinates, and a Point is the default.
GET /ngsi-ld/v1/entities?type=Shop&orderBy=location;dist-asc&orderFrom=[13.40,52.52]
For another shape, name its type in orderGeometry, such as LineString. Distance is then measured from the nearest point of the line. A GeometryCollection isn't allowed.
orderFrom is always required for a distance term, even when the query has a geoquery. The geoquery decides which entities match, and orderFrom decides where distance is measured from. The two can name different places.
- Distance is measured horizontally. Altitude doesn't count, in the reference or in the entity.
- A GeoProperty with several
datasetIdinstances is measured by its nearest instance. - GeoProperties sort first, nearest or farthest first by direction. Other values follow, by value.
- Entities without the attribute come last.
- A distance term measures the GeoProperty it names, so
observationPoint;dist-ascignoreslocation. - A distance term can follow other terms.
orderBy=name,location;dist-ascsorts by name, then by distance where names tie.
Where you can order
- Query Entities, by GET or POST, and the queries of a Snapshot take any term, as long as no context source registration takes part in the answer (see Distributed operations).
- Query Temporal Evolution of Entities takes only
id, ascending or descending, under the query'scollation.
An ordered query pages by offset, so its links carry offset rather than a cursor (see Pagination). That has a cost. If an entity is created or deleted during the walk, an offset walk can skip or repeat entities, so take an ordered answer as a snapshot of one moment, not a stable stream.
What gets refused
400BadRequestDatafor a distance term withoutorderFrom. A geoquery's geometry doesn't stand in for it.400BadRequestDatafororderByon a query a registration takes part in. Sendlocal=true, or leaveorderByout.400BadRequestDatafor a malformed term, such as an unknown direction, a dangling dot, an unclosed or repeated bracket, the[*]ofq, or a language range.400BadRequestDatafor a term that follows a Relationship.rel{name}is aqconstruct, not an ordering term.400BadRequestDatafor a collation that isn't one. Every-u-key and value must be one RFC 6067 registers for collation.400BadRequestDatafor a-u-key that states no collation, such asde-u-nu-latnorde-u-ca-buddhist.400BadRequestDatafororderGeometrywithoutorderFrom, a GeometryCollection, or coordinates that don't fit the type.400BadRequestDatafor anorderByother thanidon a temporal query, or a distance term there.400BadRequestDataforcursorwithorderBy. Ordered queries page by offset, so leave the cursor out.
When a registration takes part in the answer, an ordered query like this one is refused.
GET /ngsi-ld/v1/entities?type=Building&orderBy=height;desc
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"type": "https://uri.etsi.org/ngsi-ld/errors/BadRequestData",
"status": 400
}
Where Andromeda interprets the specification
Entity ordering is defined by the specification. The grammar is the Entity Ordering Language, whose AttrName is the attribute path.
und-x-icu is the default string order, and distance sorting is limited to GeoProperties. The datatype order puts "null values last" and GeoProperties ahead of other values in a distance sort. Andromeda breaks the remaining ties by id, because the specification asks for a consistent order across pages.
orderFrom is mandatory for a distance ordering, which is why a geoquery's geometry is never used instead. orderBy is mandatory in an ordering member, and orderGeometry defaults to Point.
The specification names "a specific ICU collation order as defined by IETF RFC 6067", and refuses a collation setting that doesn't conform. RFC 6067 registers its keywords per purpose, so a keyword that isn't a collation setting doesn't conform.
An ordering is refused when the operation isn't limited to the local scope, and "Sort ordering is never applied to distributed operations". A temporal query is limited to ordering by id, and any other member is refused.
The specification doesn't say how two values of one kind compare when the kind is an object or an array. Andromeda sorts an array by its first element. It also leaves open which instance of a multi-valued attribute counts towards the sort, and Andromeda uses the one written first.