Skip to main content
This guide walks you through indexing documents with geographic coordinates, then filtering and sorting results by location.

Add _geo to your documents

Documents must contain a _geo field with lat and lng values:
[
  {
    "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.
Meilisearch also supports GeoJSON for complex geometries like polygons and multi-polygons.

Configure filterable and sortable attributes

To filter results by location, add _geo to filterableAttributes:
curl \
  -X PUT 'MEILISEARCH_URL/indexes/restaurants/settings/filterable-attributes' \
  -H 'Content-type:application/json' \
  --data-binary '["_geo"]'
client.index('restaurants')
.updateFilterableAttributes([
  '_geo'
])
client.index('restaurants').update_filterable_attributes([
  '_geo'
])
$client->index('restaurants')->updateFilterableAttributes([
  '_geo'
]);
Settings settings = new Settings();
settings.setFilterableAttributes(new String[] { "_geo" });
client.index("restaurants").updateSettings(settings);
client.index('restaurants').update_filterable_attributes(['_geo'])
filterableAttributes := []interface{}{
  "_geo",
}
client.Index("restaurants").UpdateFilterableAttributes(&filterableAttributes)
List<string> attributes = new() { "_geo" };
TaskInfo result = await client.Index("movies").UpdateFilterableAttributesAsync(attributes);
let task: TaskInfo = client
  .index("restaurants")
  .set_filterable_attributes(&["_geo"])
  .await
  .unwrap();
client.index("restaurants").updateFilterableAttributes(["_geo"]) { (result) in
    switch result {
    case .success(let task):
        print(task)
    case .failure(let error):
        print(error)
    }
}
await client.index('restaurants').updateFilterableAttributes(['_geo']);
To sort results by distance, add _geo to sortableAttributes:
curl \
  -X PUT 'MEILISEARCH_URL/indexes/restaurants/settings/sortable-attributes' \
  -H 'Content-type:application/json' \
  --data-binary '["_geo"]'
client.index('restaurants').updateSortableAttributes([
  '_geo'
])
client.index('restaurants').update_sortable_attributes([
  '_geo'
])
$client->index('restaurants')->updateSortableAttributes([
  '_geo'
]);
client.index("restaurants").updateSortableAttributesSettings(new String[] {"_geo"});
client.index('restaurants').update_sortable_attributes(['_geo'])
sortableAttributes := []string{
  "_geo",
}
client.Index("restaurants").UpdateSortableAttributes(&sortableAttributes)
List<string> attributes = new() { "_geo" };
TaskInfo result = await client.Index("restaurants").UpdateSortableAttributesAsync(attributes);
let task: TaskInfo = client
  .index("restaurants")
  .set_sortable_attributes(&["_geo"])
  .await
  .unwrap();
client.index("restaurants").updateSortableAttributes(["_geo"]) { (result) in
  switch result {
  case .success(let task):
    print(task)
  case .failure(let error):
    print(error)
  }
}
await client.index('restaurants').updateSortableAttributes(['_geo']);
Meilisearch will rebuild your index whenever you update these settings. Depending on the size of your dataset, this might take a considerable amount of time.

Filter results by location

Use the filter search parameter with _geoRadius to find results within a given distance from a point. The following example searches for restaurants within 2km of central Milan:
curl \
  -X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
  -H 'Content-type:application/json' \
  --data-binary '{ "filter": "_geoRadius(45.472735, 9.184019, 2000)" }'
client.index('restaurants').search('', {
  filter: ['_geoRadius(45.472735, 9.184019, 2000)'],
})
client.index('restaurants').search('', {
  'filter': '_geoRadius(45.472735, 9.184019, 2000)'
})
$client->index('restaurants')->search('', [
  'filter' => '_geoRadius(45.472735, 9.184019, 2000)'
]);
SearchRequest searchRequest = SearchRequest.builder().q("").filter(new String[] {"_geoRadius(45.472735, 9.184019, 2000)"}).build();
client.index("restaurants").search(searchRequest);
client.index('restaurants').search('', { filter: '_geoRadius(45.472735, 9.184019, 2000)' })
resp, err := client.Index("restaurants").Search("", &meilisearch.SearchRequest{
  Filter: "_geoRadius(45.472735, 9.184019, 2000)",
})
SearchQuery filters = new SearchQuery() { Filter = "_geoRadius(45.472735, 9.184019, 2000)" };
var restaurants = await client.Index("restaurants").SearchAsync<Restaurant>("", filters);
let results: SearchResults<Restaurant> = client
  .index("restaurants")
  .search()
  .with_filter("_geoRadius(45.472735, 9.184019, 2000)")
  .execute()
  .await
  .unwrap();
let searchParameters = SearchParameters(
    filter: "_geoRadius(45.472735, 9.184019, 2000)"
)
client.index("restaurants").search(searchParameters) { (result: Result<Searchable<Restaurant>, Swift.Error>) in
    switch result {
    case .success(let searchResult):
        print(searchResult)
    case .failure(let error):
        print(error)
    }
}
await client.index('restaurants').search(
      '',
      SearchQuery(
        filterExpression: Meili.geoRadius(
          (lat: 45.472735, lng: 9.184019),
          2000,
        ),
      ),
    );
[
  {
    "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
    }
  }
]

Sort results by distance

Use _geoPoint in the sort search parameter to order results by proximity. The following example sorts restaurants by distance from 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"] }'
client.index('restaurants').search('', {
  sort: ['_geoPoint(48.8561446, 2.2978204):asc'],
})
client.index('restaurants').search('', {
  'sort': ['_geoPoint(48.8561446,2.2978204):asc']
})
$client->index('restaurants')->search('', [
  'sort' => ['_geoPoint(48.8561446,2.2978204):asc']
]);
SearchRequest searchRequest = SearchRequest.builder().q("").sort(new String[] {"_geoPoint(48.8561446,2.2978204):asc"}).build();
client.index("restaurants").search(searchRequest);
client.index('restaurants').search('', { sort: ['_geoPoint(48.8561446, 2.2978204):asc'] })
resp, err := client.Index("restaurants").Search("", &meilisearch.SearchRequest{
  Sort: []string{
    "_geoPoint(48.8561446,2.2978204):asc",
  },
})
SearchQuery filters = new SearchQuery()
{
    Sort = new string[] { "_geoPoint(48.8561446,2.2978204):asc" }
};

var restaurants = await client.Index("restaurants").SearchAsync<Restaurant>("", filters);
let results: SearchResults<Restaurant> = client
  .index("restaurants")
  .search()
  .with_sort(&["_geoPoint(48.8561446, 2.2978204):asc"])
  .execute()
  .await
  .unwrap();
let searchParameters = SearchParameters(
    query: "",
    sort: ["_geoPoint(48.8561446, 2.2978204):asc"]
)
client.index("restaurants").search(searchParameters) { (result: Result<Searchable<Restaurant>, Swift.Error>) in
    switch result {
    case .success(let task):
      print(task)
    case .failure(let error):
      print(error)
    }
  }
await client.index('restaurants').search(
    '', SearchQuery(sort: ['_geoPoint(48.8561446, 2.2978204):asc']));
[
  {
    "id": 2,
    "name": "Bouillon Pigalle",
    "address": "22 Bd de Clichy, 75018 Paris, France",
    "type": "french",
    "rating": 8,
    "_geo": {
      "lat": 48.8826517,
      "lng": 2.3352748
    }
  },

]

Next steps

Filter by radius

Find results within a circular area around a point

Filter by bounding box

Find results within a rectangular area

Filter by polygon

Find results within a custom polygon shape

Sort by distance

Rank results by proximity to a location

Use GeoJSON format

Index complex geometries with the GeoJSON standard