Query Language
Two syntaxes, same engine, same features. A query starting with { is parsed
as MongoDB-flavored JSON; anything else is bracket syntax (similar to Overpass
QL).
Bracket syntax is element types, then tag filters in […], then directives in
(…):
nw[amenity=restaurant][outdoor_seating=yes](area=california)
The JSON form is one object whose keys are directives (prefixed $) or tag
names. Multiple keys are an implicit AND:
{ "$type": ["node", "way"], "$area": "california",
"amenity": "restaurant", "outdoor_seating": "yes" }
An area is required. Without one the query returns AREA_REQUIRED. Names
are snake_case and lower-case — see knowhere://docs/schema.
Element types
| Bracket | $type |
Meaning |
|---|---|---|
n |
"node" |
nodes |
w |
"way" |
ways |
r |
"relation" |
relations |
nw |
array | nodes + ways |
nwr |
array | nodes + ways + relations |
* |
"*" |
everything |
Any combination of the single letters works (wr, nwr). $type defaults to
all types when omitted.
Tag operators
| Bracket | JSON | Meaning |
|---|---|---|
[amenity=cafe] |
"amenity": "cafe" / {"$eq":…} |
exact match |
[amenity=cafe,pub] |
{ "$in": ["cafe","pub"] } |
exact match, OR over values |
[amenity!=cafe] |
{ "$ne": "cafe" } |
does not equal |
[amenity!=cafe,pub] |
{ "$nin": ["cafe","pub"] } |
equals none of |
[name=~Starbucks] |
{ "$regex": "Starbucks" } |
case-insensitive contains |
[name!~McDonald] |
{ "$not": { "$regex": … } } |
case-insensitive does not contain |
[population>100000] |
{ "$gt": 100000 } |
numeric > (also >= < <= / $gte $lt $lte) |
[name] |
{ "$exists": true } |
tag exists |
[!name] |
{ "$exists": false } |
tag does not exist |
=~is a substring match, not a regex: no anchors, no wildcards.$optionsis accepted and ignored, and$notsupports only$regex.- The ordering operators (
>>=<<=/$gt$gte$lt$lte) need numeric values (1000, not"1000"), and they read the number out of the stored text — a value with a unit compares on its leading number (maxspeed="30 mph"is30), and a value that does not start with a number matches no ordering comparison at all. =and!=are always an exact text match, even when the value looks like a number:[bts:noise=114]matches the valuetag_valuesreports, and does not match114.0. Use>=/<=for a numeric range.- Values with spaces or punctuation are double-quoted:
[name="The King's Head"]. Each value in an OR quotes independently:n[name="Starbucks","Peet's Coffee"](area=new_york).
Directives
| Bracket | JSON | Purpose |
|---|---|---|
(area=<snake_case>) |
$area |
Required. Restrict to one area. |
(bb=minLon,minLat,maxLon,maxLat) |
$bb |
Bounding box — note lon, lat. |
(around=radiusMetres,lat,lon) |
$around |
Centre within radius — note lat, lon. |
(id=123,456) |
$id |
Restrict to specific OSM IDs. |
| — | $and |
Explicit AND; implicit when several keys. |
Multiple bounding boxes are allowed: pass 4N coordinates, grouped in fours.
AND, OR, NOT
- AND — several tag filters in sequence:
nw[amenity=cafe][wifi=yes](area=seattle) - OR within one tag — comma-separated values:
nw[amenity=cafe,restaurant](area=seattle) - NOT —
!=,!~, or[!key]
There is no top-level OR across different tag patterns. Issue two queries and
union them with query.union in knowhere://docs/runtime.
Examples
nw[amenity=restaurant](area=california)
n[name=~Starbucks][amenity=cafe](area=new_york)
wr[amenity=university](area=massachusetts)(bb=-71.2,42.3,-71.0,42.4)
nw[place=city][population>100000](area=texas)
*[tourism](area=paris)
{ "$area": "san_francisco", "$bb": [-122.5, 37.7, -122.4, 37.8],
"tourism": { "$exists": true } }
Related
knowhere://docs/natural-language— turning a request into one of these.knowhere://docs/categories— which tag key holds which concept.knowhere://docs/schema— coordinates, area names, result shape.knowhere://docs/errors— error codes and fixes.