Skip to main content
Meilisearch allows you to filter and sort results based on their geographic location. This can be useful when you only want results within a specific area or when sorting results based on their distance from a specific location.
Due to Meilisearch allowing malformed _geo fields in the following versions (v0.27, v0.28 and v0.29), please ensure the _geo field follows the correct format.
To start filtering documents based on their geographic location, you must make sure they contain a valid _geo or _geojson field. If you also want to sort documents geogeraphically, they must have a valid _geo field. _geo and _geojson are reserved fields. If you include one of them in your documents, Meilisearch expects its value to conform to a specific format. When using JSON and NDJSON, _geo must contain an object with two keys: lat and lng. Both fields must contain either a floating point number or a string indicating, respectively, latitude and longitude:
{

  "_geo": {
    "lat": 0.0,
    "lng": "0.0"
  }
}
_geojson must be an object whose contents follow the GeoJSON specification:
{

  "_geojson": {
    "type": "Feature",
    "geometry": {
      "type": "Point",
      "coordinates": [0.0, 0.0]
    }
  }
}
Meilisearch does not support transmeridian shapes. If your document includes a transmeridian shape, split it into two separate shapes grouped as a MultiPolygon or MultiLine. Transmeridian shapes are polygons or lines that cross the 180th meridian. Meilisearch does not support polygons with holes. If your polygon consists of an external ring and an inner empty space, Meilisearch ignores the hole and treats the polygon as a solid shape.

Using _geo and _geojson together

If your application requires both sorting by distance to a point and filtering by shapes other than a circle or a rectangle, you will need to add both _geo and _geojson to your documents.When handling documents with both fields, Meilisearch:
  • Ignores _geojson values when sorting
  • Ignores _geo values when filtering with _geoPolygon
  • Matches both _geo and _geojson values when filtering with _geoRadius and _geoBoundingBox

Examples

Suppose we have a JSON array containing a few restaurants:
[
  {
    "id": 1,
    "name": "Nàpiz' Milano",
    "address": "Viale Vittorio Veneto, 30, 20124, Milan, Italy",
    "type": "pizza",
    "rating": 9
  },
  {
    "id": 2,
    "name": "Bouillon Pigalle",
    "address": "22 Bd de Clichy, 75018 Paris, France",
    "type": "french",
    "rating": 8
  },
  {
    "id": 3,
    "name": "Artico Gelateria Tradizionale",
    "address": "Via Dogana, 1, 20123 Milan, Italy",
    "type": "ice cream",
    "rating": 10
  }
]
Our restaurant dataset looks like this once we add _geo data:
[
  {
    "id": 1,
    "name": "Nàpiz' Milano",
    "address": "Viale Vittorio Veneto, 30, 20124, Milan, Italy",
    "type": "pizza",
    "rating": 9,
    "_geo": {
      "lat": 45.4777599,
      "lng": 9.1967508
    }
  },
  {
    "id": 2,
    "name": "Bouillon Pigalle",
    "address": "22 Bd de Clichy, 75018 Paris, France",
    "type": "french",
    "rating": 8,
    "_geo": {
      "lat": 48.8826517,
      "lng": 2.3352748
    }
  },
  {
    "id": 3,
    "name": "Artico Gelateria Tradizionale",
    "address": "Via Dogana, 1, 20123 Milan, Italy",
    "type": "ice cream",
    "rating": 10,
    "_geo": {
      "lat": 45.4632046,
      "lng": 9.1719421
    }
  }
]
Trying to index a dataset with one or more documents containing badly formatted _geo values will cause Meilisearch to throw an invalid_document_geo_field error. In this case, the update will fail and no documents will be added or modified.

Using _geo with CSV

If your dataset is formatted as CSV, the file header must have a _geo column. Each row in the dataset must then contain a column with a comma-separated string indicating latitude and longitude:
"id:number","name:string","address:string","type:string","rating:number","_geo:string"
"1","Nàpiz Milano","Viale Vittorio Veneto, 30, 20124, Milan, Italy","pizzeria",9,"45.4777599,9.1967508"
"2","Bouillon Pigalle","22 Bd de Clichy, 75018 Paris, France","french",8,"48.8826517,2.3352748"
"3","Artico Gelateria Tradizionale","Via Dogana, 1, 20123 Milan, Italy","ice cream",10,"48.8826517,2.3352748"
CSV files do not support the _geojson attribute.

Filtering results with _geoRadius, _geoBoundingBox, and _geoPolygon

You can use _geo and _geojson data to filter queries so you only receive results located within a given geographic area.

Configuration

To filter results based on their location, you must add _geo or _geojson to the filterableAttributes list:
curl \
  -X PUT 'MEILISEARCH_URL/indexes/restaurants/settings/filterable-attributes' \
  -H 'Content-type:application/json' \
  --data-binary '["_geo"]'
Meilisearch will rebuild your index whenever you update filterableAttributes. Depending on the size of your dataset, this might take a considerable amount of time. You can read more about configuring filterableAttributes in our dedicated filtering guide.

Usage

Use the filter search parameter along with _geoRadius and _geoBoundingBox. These are special filter rules that ensure Meilisearch only returns results located within a specific geographic area. If you are using GeoJSON for your documents, you may also filter results with _geoPolygon.

_geoRadius

_geoRadius(lat, lng, distance_in_meters, resolution)

_geoBoundingBox

_geoBoundingBox([LAT, LNG], [LAT, LNG])

_geoPolygon

_geoPolygon([LAT, LNG], [LAT, LNG], [LAT, LNG], …)

Examples

Using our example dataset, we can search for places to eat near the center of Milan with _geoRadius:
curl \
  -X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
  -H 'Content-type:application/json' \
  --data-binary '{ "filter": "_geoRadius(45.472735, 9.184019, 2000)" }'
We also make a similar query using _geoBoundingBox:
curl \
  -X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
  -H 'Content-type:application/json' \
  --data-binary '{ "filter": "_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])" }'
And with _geoPolygon:
[
  {
    "id": 1,
    "name": "Nàpiz' Milano",
    "address": "Viale Vittorio Veneto, 30, 20124, Milan, Italy",
    "type": "pizza",
    "rating": 9,
    "_geo": {
      "lat": 45.4777599,
      "lng": 9.1967508
    }
  },
  {
    "id": 3,
    "name": "Artico Gelateria Tradizionale",
    "address": "Via Dogana, 1, 20123 Milan, Italy",
    "type": "ice cream",
    "rating": 10,
    "_geo": {
      "lat": 45.4632046,
      "lng": 9.1719421
    }
  }
]
It is also possible to combine _geoRadius, _geoBoundingBox, and _geoPolygon with other filters. We can narrow down our previous search so it only includes pizzerias:
curl \
  -X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
  -H 'Content-type:application/json' \
  --data-binary '{ "filter": "_geoRadius(45.472735, 9.184019, 2000) AND type = pizza" }'
[
  {
    "id": 1,
    "name": "Nàpiz' Milano",
    "address": "Viale Vittorio Veneto, 30, 20124, Milan, Italy",
    "type": "pizza",
    "rating": 9,
    "_geo": {
      "lat": 45.4777599,
      "lng": 9.1967508
    }
  }
]
_geo, _geoDistance, and _geoPoint are not valid filter rules. Trying to use any of them with the filter search parameter will result in an invalid_search_filter error.

Sorting results with _geoPoint

Configuration

Before using geosearch for sorting, you must add the _geo attribute to the sortableAttributes list:
curl \
  -X PUT 'MEILISEARCH_URL/indexes/restaurants/settings/sortable-attributes' \
  -H 'Content-type:application/json' \
  --data-binary '["_geo"]'
It is not possible to sort documents based on the _geojson attribute.

Usage

_geoPoint(0.0, 0.0):asc

Examples

The _geoPoint sorting function can be used like any other sorting rule. We can order documents based on how close they are to the Eiffel Tower:
curl \
  -X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
  -H 'Content-type:application/json' \
  --data-binary '{ "sort": ["_geoPoint(48.8561446,2.2978204):asc"] }'
With our restaurants dataset, the results look like this:
[
  {
    "id": 2,
    "name": "Bouillon Pigalle",
    "address": "22 Bd de Clichy, 75018 Paris, France",
    "type": "french",
    "rating": 8,
    "_geo": {
      "lat": 48.8826517,
      "lng": 2.3352748
    }
  },
  {
    "id": 3,
    "name": "Artico Gelateria Tradizionale",
    "address": "Via Dogana, 1, 20123 Milan, Italy",
    "type": "ice cream",
    "rating": 10,
    "_geo": {
      "lat": 45.4632046,
      "lng": 9.1719421
    }
  },
  {
    "id": 1,
    "name": "Nàpiz' Milano",
    "address": "Viale Vittorio Veneto, 30, 20124, Milan, Italy",
    "type": "pizza",
    "rating": 9,
    "_geo": {
      "lat": 45.4777599,
      "lng": 9.1967508
    }
  }
]
_geoPoint also works when used together with other sorting rules. We can sort restaurants based on their proximity to the Eiffel Tower and their rating:
curl \
  -X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
  -H 'Content-type:application/json' \
  --data-binary '{
    "sort": [
      "_geoPoint(48.8561446,2.2978204):asc",
      "rating:desc"
    ]
  }'
[
  {
    "id": 2,
    "name": "Bouillon Pigalle",
    "address": "22 Bd de Clichy, 75018 Paris, France",
    "type": "french",
    "rating": 8,
    "_geo": {
      "lat": 48.8826517,
      "lng": 2.3352748
    }
  },
  {
    "id": 3,
    "name": "Artico Gelateria Tradizionale",
    "address": "Via Dogana, 1, 20123 Milan, Italy",
    "type": "ice cream",
    "rating": 10,
    "_geo": {
      "lat": 45.4632046,
      "lng": 9.1719421
    }
  },
  {
    "id": 1,
    "name": "Nàpiz' Milano",
    "address": "Viale Vittorio Veneto, 30, 20124, Milan, Italy",
    "type": "pizza",
    "rating": 9,
    "_geo": {
      "lat": 45.4777599,
      "lng": 9.1967508
    }
  }
]
I