Meilisearch 允许您根据地理位置对结果进行过滤和排序。当您只需要特定区域内的结果,或者需要根据结果与特定位置的距离进行排序时,这个功能非常有用。
由于 Meilisearch 在以下版本(v0.27、v0.28 和 v0.29)中允许格式错误的 _geo 字段,请确保 _geo 字段遵循正确的格式。
为基于位置的搜索准备文档
为了开始根据地理位置过滤和排序文档,您必须确保文档包含有效的 _geo 字段。
_geo 是一个保留字段。如果您在文档中包含它,Meilisearch 会要求其值符合特定格式。
使用 JSON 和 NDJSON 时,_geo 必须包含一个具有两个键的对象:lat 和 lng。这两个字段必须分别包含表示纬度和经度的浮点数或字符串:
{
…
"_geo": {
"lat": 0.0,
"lng": "0.0"
}
}
假设我们有一个包含几家餐厅的 JSON 数组:
[
{
"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
}
]
当我们添加地理定位数据后,餐厅数据集会变成这样:
[
{
"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
}
}
]
在 CSV 中使用 _geo
如果您的数据集格式为 CSV,文件头部必须包含 _geo 列。数据集中的每一行都应包含一个以逗号分隔的字符串表示纬度和经度的列:
"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"
使用 _geoRadius 和 _geoBoundingBox 过滤结果
您可以使用 _geo 数据来过滤查询,确保只返回位于指定地理区域内的结果。
要基于地理位置过滤结果,必须将 _geo 属性添加到 filterableAttributes 列表中:
curl \
-X PUT 'MEILISEARCH_URL/indexes/restaurants/settings/filterable-attributes' \
-H 'Content-type:application/json' \
--data-binary '["_geo"]'
每当您更新 filterableAttributes 时,Meilisearch 会重建索引。根据数据集的大小,这可能需要相当长的时间。
关于配置 filterableAttributes 的更多信息,请参阅我们的专用过滤指南。
使用方法
结合 filter 搜索参数 使用 _geoRadius 或 _geoBoundingBox。这些特殊的过滤规则确保 Meilisearch 只返回位于特定地理区域内的结果。
_geoRadius
_geoRadius(lat, lng, distance_in_meters)
_geoBoundingBox
_geoBoundingBox([{lat}, {lng}], [{lat}, {lng}])
使用我们的示例数据集,我们可以通过 _geoRadius 搜索米兰市中心附近的餐饮场所:
curl \
-X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
-H 'Content-type:application/json' \
--data-binary '{ "filter": "_geoRadius(45.472735, 9.184019, 2000)" }'
我们也可以使用 _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])" }'
[
{
"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
}
}
]
还可以将 _geoRadius 和 _geoBoundingBox 与其他筛选条件结合使用。我们可以缩小之前的搜索范围,仅包含披萨店:
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
}
}
]
使用 _geoPoint 排序结果
在使用地理搜索进行排序之前,必须将 _geo 属性添加到 sortableAttributes 列表中:
curl \
-X PUT 'MEILISEARCH_URL/indexes/restaurants/settings/sortable-attributes' \
-H 'Content-type:application/json' \
--data-binary '["_geo"]'
了解更多关于 sortableAttributes 的信息。
使用方法
_geoPoint 排序函数可以像其他排序规则一样使用。我们可以根据文档与埃菲尔铁塔的距离来排序:
curl \
-X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
-H 'Content-type:application/json' \
--data-binary '{ "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
}
},
{
"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 也可以与其他排序规则结合使用。我们可以根据餐厅与埃菲尔铁塔的距离和评分来排序:
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
}
}
]