Skip to main content

Scopes

A scope is a place in a hierarchy, written onto the entity itself, such as /Madrid/Gardens, /CompanyA/OrganizationB/UnitC or /North/Plant/Floor3. You choose what the hierarchy means. Entities stamped with scopes can then be selected with one query parameter, scopeQ, without a q term and without a property reserved for the purpose.

The rules of the format and the wildcard patterns are in Scopes. This guide shows how to use the two halves together, stamping and filtering.

Stamping a scope​

Write one scope, or an array of them, in the entity's scope member.

POST /ngsi-ld/v1/entities
Content-Type: application/json

{
"id": "urn:ngsi-ld:Building:1",
"type": "Building",
"scope": [
"/Madrid/Gardens/ParqueNorte",
"/CompanyA/OrganizationB/UnitC"
]
}
HTTP/1.1 201 Created

A scope is levels separated by /, each level starting with a letter and continuing with letters, digits or _. The leading / is optional and is kept as you wrote it, so /Madrid and Madrid are two different strings on the entity, and both are legal. An entity can carry scopes from unrelated hierarchies at once, which is how one building sits in both a geography and an organisation chart.

To remove a scope, send the NGSI-LD Null, urn:ngsi-ld:null, as the value of scope in a partial update or a merge.

Filtering with scopeQ​

scopeQ selects the entities whose scope matches a pattern. Without wildcards, it matches exactly that scope, and nothing below it.

GET /ngsi-ld/v1/entities?type=Building&scopeQ=/Madrid/Gardens/ParqueNorte
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json

[
{
"id": "urn:ngsi-ld:Building:1",
"type": "Building",
"scope": [
"/Madrid/Gardens/ParqueNorte",
"/CompanyA/OrganizationB/UnitC"
]
}
]

A building stamped only /Madrid/Gardens is not answered by that query, because matching a scope exactly means the whole path, and reaching descendants takes a wildcard. + stands for exactly one level below, and a trailing # covers the level before it and everything underneath, so scopeQ=/Madrid/# matches /Madrid itself and every path under it. In a URL the # must be percent-encoded as %23, or the client cuts the query short. In Rust, scope_query::subtree("/Madrid") writes that pattern for you.

Matching looks at levels, not spellings, and the pattern and the stored scope each work with or without the leading slash.

Combining scope tests​

Several scopes can be required or offered. A disjunction selects entities in any of the scopes, written with , or |.

GET /ngsi-ld/v1/entities?type=Area&scopeQ=/Root/AreaA|/Root/AreaB

A conjunction selects entities holding every scope named, and the grammar admits it only inside parentheses.

GET /ngsi-ld/v1/entities?type=Building&scopeQ=(/Madrid/Gardens/ParqueNorte;/CompanyA/OrganizationB/UnitC)

Only the building stamped with both is answered. A group can be joined to a disjunction, scopeQ=(/Aa;/Bb)|/Cc, but a ; outside parentheses refuses the whole query with 400 BadRequestData, as does a group nested inside a group. Write the conjunction first, wrap it once, then disjoin.

Where scopeQ runs​

scopeQ is a query parameter of the entity query, on the URL and in the POST /ngsi-ld/v1/entityOperations/query body alike. It composes with type, q, the geoquery parameters and pagination. The scope narrows the candidate entities first, and the other tests apply to what remains.

What gets refused​

  • 400 BadRequestData for a scope whose levels break the format, such as /a-b, /1st or a trailing /.
  • 400 BadRequestData for a scopeQ with an unparenthesised conjunction, or a group inside a group.
  • An entity without a scope member never matches a scopeQ, whatever the pattern.