Geolocation Search (Proximity Search)
Filter results to an exact area or boost nearby products in ranking.
Overview
Proximity search lets you filter or rank products based on how close they are to a given location. It runs on a single indexed geo field (the examples on this page use a field named latlon) and works through two independent mechanisms.
| Mechanism | What it does | Documents outside the area |
|---|---|---|
geo.filter | Hard filter that excludes anything outside the requested area | Removed from the result set |
bf + loc_query | Score boost that ranks nearer documents higher | Kept, at base relevance |
Both mechanisms are built from the same two geo query functions: near() (radius search) and in() (bounding box search).
Prerequisite: Schema Configuration
Important Point to Remember
- Proximity search only works once the geo field is indexed as a spatial field.
near()andin()will not evaluate on a field indexed as any other type (text, string, etc.).- Geo filtering and boosting will silently fall back to standard relevance instead of erroring.
The field used for proximity search (e.g. latlon) must be declared with dataType: geo in the schema.
{
"fieldName": "latlon",
"dataType": "geo"
}During ingestion, supply the field as a comma separated "latitude,longitude" string:
{
"latlon": "40.67,-111.0"
}This is a schema level setting and can only be applied during a full upload. The field type cannot be changed on an existing index by an incremental or partial feed.
Checklist before querying:
latlon(or your chosen field) is declared asdataType: geoin the schema.- A full upload has been performed so the field is (re)indexed as spatial.
- Documents carry the value in
"lat,lon"format.
Geo Query Functions
These two functions are the building blocks used inside both geo.filter and loc_query. They're always written in field qualified form:
<geoField>:<function>(...)
Radius Search with near()
<field>:near(lat, lon, radius)
Matches documents whose geo point lies within radius of the center point (lat, lon). Defines a circular area.
| Argument | Range | Description |
|---|---|---|
lat | -90 to 90 | Center latitude (decimal degrees) |
lon | -180 to 180 | Center longitude (decimal degrees) |
radius | > 0 | Search radius, in kilometres |
latlon:near(40.67,-111.0,70)Matches points within 70 km of (40.67, -111.0).
Bounding Box Search with in()
<field>:in(lat1, lon1, lat2, lon2)
Matches documents whose geo point lies inside the rectangle defined by two opposite corners. Conventionally the first pair is the southwest corner and the second is the northeast corner.
| Argument | Description |
|---|---|
lat1, lon1 | First corner (e.g. southwest) |
lat2, lon2 | Opposite corner (e.g. northeast) |
latlon:in(40.65,-112.5,40.99,-111.0)Matches points inside the box bounded by (40.65, -112.5) and (40.99, -111.0).
Use near() for "within X km of a point"; use in() for a precomputed rectangular region such as a map viewport or a city bounding box.
Hard Proximity Filtering with geo.filter
geo.filter excludes any document that doesn't satisfy the geo expression. Out of area documents don't appear in the result set at all. Use it when you need to strictly exclude anything outside an area, such as a delivery radius or store pickup range.
geo.filter supports boolean composition with AND and OR, and clauses can freely mix near(), in(), and different geo fields.
AND (intersection): a document must satisfy every clause. Returns only points that fall inside all areas.
geo.filter=latlon:near(40.67,-111.0,84) AND latlon:near(40.59,-112.02,10)OR (union): a document must satisfy at least one clause. Returns points inside any of the areas.
geo.filter=latlon:in(40.65,-112.5,40.99,-111.0) OR latlon:near(40.67,-111.0,83)Proximity Boost with bf and loc_query
Use bf (boost function) with loc_query to rank nearer documents higher without removing anything from the result set. Every document stays; only the ordering changes. This is the option for a distance nudge or a delivery ETA style ranking where you still want to show everything.
Two parameters work together.
loc_query holds the geo query as a named parameter:
loc_query=latlon:near(40.67,-111.0,70)bf references loc_query and assigns a score:
bf=if(query($loc_query),100,0)| Token | Meaning |
|---|---|
$loc_query | Substitutes the value of the loc_query parameter |
query(...) | Evaluates that geo query against each document |
if(cond, then, else) | Returns 100 for matching documents, 0 otherwise |
100 | The boost magnitude, tunable |
Documents inside the area receive +100 to their score and float to the top. Documents outside the area keep their base relevance score. Raise the magnitude to make proximity dominate ranking, or lower it to make distance a mild tiebreaker.
Choosing a Mechanism: Filter vs. Boost
| Need | Use | Behaviour |
|---|---|---|
| Strictly exclude anything outside the area (delivery radius, store pickup) | geo.filter | Hard exclude |
| Keep every result but rank nearer higher (distance nudge, delivery ETA) | bf + loc_query | Soft rank |
| Combine several areas | geo.filter with AND / OR | Intersection / union |
Quick Reference
Functions
| Function | Syntax | Defines | Example |
|---|---|---|---|
near | field:near(lat, lon, radius) | Circle, radius in km | latlon:near(40.67,-111.0,70) |
in | field:in(lat1, lon1, lat2, lon2) | Bounding box, two opposite corners | latlon:in(40.65,-112.5,40.99,-111.0) |
Parameters
| Parameter | Required | Example value | Description |
|---|---|---|---|
geo.filter | For hard filtering | latlon:near(40.67,-111.0,84) AND latlon:near(40.59,-112.02,10) | Hard geo filter; supports AND / OR composition |
bf | For boosting | if(query($loc_query),100,0) | Boost function referencing loc_query |
loc_query | With bf | latlon:near(40.67,-111.0,70) | Named geo query referenced by bf via $loc_query |
Coordinate & Unit Notes
- Coordinates are decimal degrees: latitude -90 to 90, longitude -180 to 180.
- Negative longitude denotes the western hemisphere.
- The
radiusargument ofnear()is expressed in kilometres. near()andin()can be combined in a singlegeo.filterexpression, including across different geo fields if more than one is indexed.
FAQ
What happens if the geo field isn't declared as dataType: geo?near() and in() won't evaluate. Geo filtering and boosting silently fall back to standard relevance instead of returning an error, so it can look like proximity search is "on" when it isn't actually filtering or boosting anything.
Can I change an existing field to dataType: geo with a partial feed?
No. This is a schema level change and only takes effect on a full upload. An incremental or partial feed cannot retype an existing indexed field.
Can near() and in() be combined in the same expression?
Yes. They can be combined within a single geo.filter expression, including across different geo fields if your schema indexes more than one.
What unit is the near() radius in?
Kilometres.
Related Reading
Updated 24 days ago
