1. Words become a safe query
The text is split into words of letters and digits, lower-cased, and capped at 12 words. Each word is written as a quoted TINQL term, so user input can never add query operators. The TINQL that ran is returned in geocoding.query.tinql.
2. All words first, then most of them
Every word is required first. If that finds fewer than size places, the query relaxes to about three quarters of the words (rounded up), written as an OR of AND groups, or as AT LEAST n OF [...] when there would be too many groups.
| Words | Required after relaxing |
|---|---|
| 1–3 | all |
| 4 | 3 |
| 5 | 4 |
| 8 | 6 |
3. Text score plus importance
Rows are ordered by
tin.score(ctid) + importance * IMPORTANCE_WEIGHT
tin.score is the BM25 text score and importance is a 0–1 value from the geolith build. The default weight is 10. This order is computed inside the TIN index, so a top 10 does not sort every match.
4. Focus points
With focus.point.lat and focus.point.lon, boxes of ±0.05°, ±0.25° and ±0.5° around the point are searched in that order. The search stops at the first box that holds size places. Each result then gets a distance boost of up to +5 that halves every 10 km, and places inside the largest box come before the rest. If the boxes hold fewer than size places, the global ranking fills the list. geocoding.query.ranking is then importance+distance.
5. Every query has a budget
| Query | Budget | When it runs out |
|---|---|---|
| Focus boxes | FOCUS_TIMEOUT_MS (1000), shared by all boxes | Keeps the rows of the smaller boxes |
| Importance-ranked | RANKED_TIMEOUT_MS (250) | Falls back to text score only; ranking becomes text |
| Text-only fallback | FALLBACK_TIMEOUT_MS (2000) | Returns focus rows if any, else 504 |
6. Autocomplete
The last word matches as a prefix (par*). See Autocomplete for the details.
Confidence
confidence is the share of query words found in the place's text, from 0 to 1. With autocomplete, the last word counts when a word starts with it.