Natural Language to Queries
For translating user intent into Knowhere queries: the patterns that come up most, and the rules for choosing among them.
Rules
- Category words ("restaurant", "museum", "park") map to a category key —
amenity,leisure,tourism,shop.knowhere://docs/categoriessays which key holds which concept. - Place words map to
(area=…),snake_caseand lower-case. Unsure, or ambiguous ("Denver" with no state)? Calllist_areas. Never invent a name. - Brand words map to
name=~(contains), orbrand=when the user clearly means the chain. Chain vs. independent is[brand]vs.[!brand], never a name regex — seeknowhere://docs/schema. - Unsure of a value?
tag_values(area, key)before filtering, not a guess.tag_keys(area)if unsure the key exists. - "near a point" with real coordinates is
(around=radiusMetres,lat,lon)(5 mi ≈8047). Without coordinates ("near downtown") it is too fuzzy for a filter — use(area=…)alone. For "A near a set of B", use theneartool. - Anything about walking goes to
walk_reach, nevernearor(around=…). Seeknowhere://docs/routing. - Favour recall when the user is exploring. If several values could
satisfy the intent, prefer the OR form (
[amenity=cafe,restaurant]) over one narrow guess.
Mappings
| Ask | Query |
|---|---|
| "restaurants in California" | nw[amenity=restaurant](area=california) |
| "Starbucks in Seattle" | nw[name=~Starbucks](area=seattle) |
| "the Starbucks chain" | nw[brand=Starbucks](area=seattle) |
| "independent coffee shops in Bozeman" | nw[amenity=cafe][!brand](area=bozeman) |
| "cafes or restaurants in Denver" | nw[amenity=cafe,restaurant](area=denver) |
| "restaurants in LA that aren't McDonald's" | nw[amenity=restaurant][name!~McDonald](area=los_angeles) |
| "cities in Texas over 100,000 people" | n[place=city][population>100000](area=texas) |
| "universities in Massachusetts with a name" | wr[amenity=university][name](area=massachusetts) |
| "sushi restaurants in San Francisco" | nw[amenity=restaurant][cuisine=~sushi](area=san_francisco) |
| "parks without a name in Portland" | nw[leisure=park][!name](area=portland) |
| "museums between -122.5,37.7 and -122.4,37.8" | nw[tourism=museum](area=san_francisco)(bb=-122.5,37.7,-122.4,37.8) |
| "boundaries in Colorado named Denver" | *[boundary=administrative][name=~Denver](area=colorado) |
Travel questions are not queries
"What's within a 15-minute walk?", "is this house walkable to a coffee
shop?", "any parks I can walk to?" — walk_reach, with find carrying the
query:
walk_reach area=colorado from_lat=39.7392 from_lon=-104.9847
max_walk_meters=1250
find="nw[amenity=cafe](area=colorado)"
Report no_walkable_network: true as "the area has no pedestrian paths", not
as "nothing nearby" — one is a fact about sidewalks, the other a verdict on
the neighbourhood.
"How far is the nearest Costco by car?" — search for candidates, then
drive_times to rank them. There is no driving reachability tool; you must
name the destinations.
"I want to walk two miles in a loop" — walk_loop, with the length as
target_meters or the time as target_minutes ("walk for an hour" is
target_minutes=60). "A mile loop that sees all the parks" adds
passing with the query for what to see:
walk_loop area=colorado from_lat=39.7392 from_lon=-104.9847
target_meters=1609 passing="nw[leisure=park](area=colorado)"
Report the length each loop actually came out at (meters), never the
length that was asked for. A result with none set is a real answer —
quote its reason rather than retrying; a cul-de-sac subdivision genuinely
has no two-mile loop.
The limits behind all three — snapping, area boundaries, walking caps — are in
knowhere://docs/routing.
Pitfalls
- Don't forget
(area=…). Every query needs one. - Don't map "near Seattle" to a bounding box without real coordinates.
=~is contains, not regex — no anchors, no wildcards.- Chain searches return more than the stores.
name=~Costcoalso matches its gas station, pharmacy, and per-aisle nodes. Filter by the primary feature tag as well. - Broad and unfiltered is slow. A bare category over a large area can time
out. Narrow with a
(bb=…)— the cheapest, since it prunes on the spatial cell index — a second tag filter, orcount_onlyfirst. =~is only indexed on the name-ish tags (name,name:<lang>,alt_name,brand,operator). Contains on any other tag is a scan: correct, but it reads every row the other filters left. Pair it with something selective —nw[amenity=cafe][cuisine=~coffee], notnw[cuisine=~coffee]alone — and prefer=when the value is a known category.
Related
knowhere://docs/query— syntax reference.knowhere://docs/categories— concept to tag key.knowhere://docs/errors— codes when a translation misses.