Scopes
A Scope places an entity in a hierarchy, such as /Madrid/Gardens. You set it in the entity's scope member, you get it back exactly as you wrote it, and you filter on it with scopeQ. The leading / is optional, and scopeQ ignores it, so Madrid and /Madrid describe the same place to a filter, even though they come back as the two different strings you sent.
Writing a Scope
Put one Scope, or an array of them, in scope:
{
"id": "urn:ngsi-ld:Park:1",
"type": "Park",
"scope": [
"/Madrid/Gardens",
"/Spain/Parks"
]
}
A Scope is levels separated by /. Each level starts with a letter and goes on with letters, digits or _. A level can be up to 1,000 characters, and a whole Scope up to 3,000 bytes once encoded. The specification sets no limit here. These numbers are the limits of the index behind Scope search, and a longer Scope gets 400 BadRequestData.
Removing a Scope
Send the NGSI-LD Null, urn:ngsi-ld:null, on its own as the value of scope in a partial update or a merge. It only means "remove" in that position. It isn't a Scope value, and you can't use it anywhere else.
How Scopes come back
You get back exactly what you wrote, on the Core API, the Temporal API and in registrations. Madrid reads back as Madrid, and /Madrid as /Madrid.
Andromeda compares stored Scopes by their spelling, not by their levels. So if one scope member repeats a value, or several versions of an entity are merged, each spelling is kept once. That means Madrid and /Madrid can both show up on one entity. The filter treats them as one place, but the entity really does hold two spellings.
Filtering with scopeQ
Matching looks only at the levels. A leading / on either side makes no difference.
/MadridmatchesMadridand/Madrid./Madrid/+matches/Madrid/Gardens, but not/Madridor/Madrid/Gardens/East./Madrid/#matches/Madrid,Madrid/Gardensand/Madrid/Gardens/East./#on its own matches every entity that has a Scope.
+ stands for exactly one level. A trailing # covers the level before it and everything below.
You might expect /Madrid/# to reach only the places inside Madrid. It also matches /Madrid itself, because the # covers the level before it. Use /Madrid/+ when you want exactly one level down.
GET /ngsi-ld/v1/entities?type=Park&scopeQ=/Madrid/%23
Encode # as %23 in a URL. Left bare, the client treats it as the start of a fragment, and the broker never sees the rest of the pattern.
An entity without a Scope never matches a scopeQ, whatever the pattern.
What gets refused
400BadRequestDatafor a Scope such as/a-b,/1st,//x, a lone/or a trailing/: each level must start with a letter and hold only letters, digits and_. Scope under Strictness has the full list.
POST /ngsi-ld/v1/entities
Content-Type: application/json
{
"id": "urn:ngsi-ld:Park:2",
"type": "Park",
"scope": "/a-b"
}
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"type": "https://uri.etsi.org/ngsi-ld/errors/BadRequestData",
"status": 400
}
The body carries the rest of the problem document. Only type is shown here.
Where Andromeda interprets the specification
NGSI-LD defines Scopes and scopeQ. The specification shows both /Madrid and Madrid as Scope values, so both spellings are accepted. Values come back as written. When representations are merged, "the values of scope are merged". Andromeda merges them by spelling, so two spellings of one place stay two values.
The specification writes every scopeQ pattern with a leading /, but its fifth example queries the bare CompanyB. Without slash-blind matching a bare Scope could never be found, so a pattern works with or without the slash.