Knowhere

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. $options is accepted and ignored, and $not supports 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" is 30), 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 value tag_values reports, and does not match 114.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 } }