Knowhere

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

  1. Category words ("restaurant", "museum", "park") map to a category key — amenity, leisure, tourism, shop. knowhere://docs/categories says which key holds which concept.
  2. Place words map to (area=…), snake_case and lower-case. Unsure, or ambiguous ("Denver" with no state)? Call list_areas. Never invent a name.
  3. Brand words map to name=~ (contains), or brand= when the user clearly means the chain. Chain vs. independent is [brand] vs. [!brand], never a name regex — see knowhere://docs/schema.
  4. Unsure of a value? tag_values(area, key) before filtering, not a guess. tag_keys(area) if unsure the key exists.
  5. "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 the near tool.
  6. Anything about walking goes to walk_reach, never near or (around=…). See knowhere://docs/routing.
  7. 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=~Costco also 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, or count_only first.
  • =~ 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], not nw[cuisine=~coffee] alone — and prefer = when the value is a known category.