地理搜索

    Meilisearch 允许您根据结果的地理位置进行过滤和排序。当您只想获取特定区域内的结果,或根据与特定位置的距离对结果进行排序时,这非常有用。

    v0.27、v0.28 和 v0.29 中的 _geo 字段

    由于 Meilisearch 在上述版本中允许格式错误的 _geo 字段,请确保 _geo 字段遵循正确的格式。

    为了开始根据文档的地理位置进行过滤和排序,您必须确保文档包含有效的 _geo 字段。

    _geo 是一个保留字段。如果将其包含在文档中,Meilisearch 会期望其值符合特定格式。

    使用 JSON 和 NDJSON 时,_geo 必须包含一个具有两个键的对象:latlng。这两个字段必须包含浮点数或字符串,分别表示纬度和经度。

    {"_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
        }
      }
    ]
    
    警告

    尝试索引包含一个或多个带有格式错误的 _geo 值的文档的数据集会导致 Meilisearch 抛出 invalid_document_geo_field 错误。在这种情况下,更新将失败,并且不会添加或修改任何文档。

    _geo 与 CSV 一起使用

    如果您的数据集格式为 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 'https://127.0.0.1:7700/indexes/restaurants/settings/filterable-attributes' \
      -H 'Content-type:application/json' \
      --data-binary '["_geo"]'

    每当您更新 filterableAttributes 时,Meilisearch 都会重建您的索引。根据数据集的大小,这可能需要相当长的时间。

    您可以在我们的专用过滤指南中阅读有关配置 filterableAttributes 的更多信息。

    用法

    使用 filter 搜索参数以及 _geoRadius_geoBoundingBox。这些是特殊的过滤规则,确保 Meilisearch 仅返回位于特定地理区域内的结果。

    _geoRadius

    _geoRadius 基于中心点和半径建立一个圆形区域。此过滤规则需要三个参数:latlngdistance_in_meters

    _geoRadius(lat, lng, distance_in_meters)
    

    latlng 必须是浮点数,表示地理位置。distance_in_meters 必须是整数,表示 _geoRadius 过滤器覆盖的半径。

    _geoBoundingBox

    _geoBoundingBox 基于其右上角和左下角的坐标建立一个矩形区域。此过滤规则需要两个数组

    _geoBoundingBox([{lat}, {lng}], [{lat}, {lng}])
    

    latlng 必须是浮点数,表示地理位置。第一个数组表示矩形区域右上角的地理坐标。第二个数组表示矩形区域左下角的坐标。

    示例

    使用我们的示例数据集,我们可以使用 _geoRadius 搜索米兰市中心附近的餐饮场所

    curl \
      -X POST 'https://127.0.0.1:7700/indexes/restaurants/search' \
      -H 'Content-type:application/json' \
      --data-binary '{ "filter": "_geoRadius(45.472735, 9.184019, 2000)" }'

    我们还使用 _geoBoundingBox 进行类似的查询

    curl \
      -X POST 'https://127.0.0.1:7700/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 'https://127.0.0.1:7700/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
        }
      }
    ]
    

    如果您之前已将 type 添加到 filterableAttributes,则上述命令才有效。

    警告

    _geo_geoDistance_geoPoint 不是有效的过滤规则。尝试使用 filter 搜索参数使用它们中的任何一个都会导致 invalid_search_filter 错误。

    使用 _geoPoint 对结果进行排序

    您可以使用 _geo 数据根据结果与特定位置的距离进行排序。

    配置

    在将地理搜索用于排序之前,您必须将 _geo 属性添加到 sortableAttributes 列表中。

    curl \
      -X PUT 'https://127.0.0.1:7700/indexes/restaurants/settings/sortable-attributes' \
      -H 'Content-type:application/json' \
      --data-binary '["_geo"]'

    请注意,每当您更新 sortableAttributes 时,Meilisearch 都会重建您的索引。根据数据集的大小,这可能需要相当长的时间。

    您可以在我们专用的排序指南中阅读有关配置 sortableAttributes 的更多信息。

    用法

    首先,请确保您的文档包含有效的地理位置数据,并且已将 _geo 属性添加到 sortableAttributes 列表中。然后,您可以使用 sort 搜索参数 以及一个特殊的排序函数 _geoPoint,根据结果与地理位置的距离对其进行排序。

    _geoPoint(0.0, 0.0):asc
    

    _geoPoint 需要两个浮点数,分别表示位置的纬度和经度。您还必须指定排序方式是升序 (asc) 还是降序 (desc)。升序排序将优先显示距离指定位置较近的结果,而降序排序将把距离最远的结果放在最前面。

    如果 latlng 无效或缺失,Meilisearch 将返回一个 invalid_search_sort 错误。如果您未能指定排序顺序,也会抛出错误。

    您可以在我们的专门指南中阅读有关排序的更多信息。

    警告

    _geo_geoDistance_geoRadius 不是有效的 sort 值。尝试将它们中的任何一个与 sort 搜索参数一起使用都会导致 invalid_search_sort 错误。

    示例

    _geoPoint 排序函数可以像任何其他排序规则一样使用。我们可以根据文档与埃菲尔铁塔的距离对其进行排序

    curl \
      -X POST 'https://127.0.0.1:7700/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 'https://127.0.0.1:7700/indexes/restaurants/search' \
      -H 'Content-type:application/json' \
      --data-binary '{
        "sort": [
          "_geoPoint(48.8561446,2.2978204):asc",
          "rating:desc"
        ]
      }'

    上述命令仅在您之前将 rating 添加到 sortableAttributes 中时才有效。

    [
      {
        "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
        }
      }
    ]
    

    排名规则

    默认情况下,Meilisearch 强调相关性排序而不是穷举排序。这意味着我们的引擎首先找到最相关的结果,然后才根据 sort 搜索参数给出的值对匹配项进行排序。因此,使用 _geoPoint 进行排序很少是决定用户首先看到哪些结果的最重要因素。通常,它将作为被认为与给定搜索查询同样相关的结果之间的决胜因素。

    由于 _geoPointsort 搜索参数的一部分,因此其在对结果进行排名时的权重由 rankingRules 数组中 "sort" 规则的位置控制。

    您可以在我们的专门排序指南中阅读有关 "sort" 排名规则以及如何自定义它的更多信息。

    查找文档与 _geoPoint 之间的距离

    使用 _geoPoint 时,所有返回的文档都将包含一个额外的字段:_geoDistance。顾名思义,_geoDistance 包含指定的 _geoPoint 和文档的 _geo 数据之间的距离(以米为单位)

    [
      {
        "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
        },
        "_geoDistance": 1532
      }
    ]
    
    警告

    使用 _geoRadius 过滤器不会导致结果包含 _geoDistance

    仅当查询使用 _geoPointsort 搜索参数时,才会计算返回文档中的 _geoDistance。此外,仅当 _geo 存在于 displayedAttributes 列表中时,返回的文档才会包含 _geoDistance