Elasticsearch 实现类似美团附近场所搜索功能(2)

199 阅读3分钟

一、搜索功能

想要实现类似美团那种按照距离排序并展示距离的功能,如图

图中的预计分钟数(出餐时间+骑手到店+骑手到配送地)和距离,这些都是可变的动态值,当然这里面分钟和距离也可以使用ruby或其他代码来对Elasticsearch搜索结果自己计算

下面使用 Elasticsearch 地理空间查询,用于检索距离指定坐标点一定范围内的文档并排序:

GET idnex_geo_point/_search  
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}  
      },
      "filter": {
        "geo_distance": {  
          "distance": "100km",  
          "location": {  
            "lat": 41.07,
            "lon": 116.14
          }
        }
      }
    }
  },
  "sort": [
    {
      "_geo_distance": {  
        "location": {
          "lat": 41.07,
          "lon": 116.14
        },
        "order": "asc",  
        "unit": "km",  
        "mode": "min",  
        "distance_type": "arc"  
      }
    }
  ],
  "script_fields": {  
    "distance": {
      "script": {
        "lang": "painless",  
        "params": {  
          "lat": 41.07,
          "lon": 116.14
        },
        "source": "doc['location'].arcDistance(params.lat, params.lon) / 1000"  
      }
    }
  }
}

这个查询的核心功能是:

  1. 检索距离坐标点(41.07, 116.14) 100 公里内的所有文档
  1. 将结果按距离升序排列
  1. 在返回结果中添加一个动态计算的字段"distance",显示每个文档与指定点的实际距离
  1. 尽量使用上面查询到的doc文档,这样比较快

二、script_fields 详解

1. 核心概念

script_fields是 Elasticsearch 提供的强大功能,允许在查询时动态计算并返回自定义字段。这些字段不需要预先定义在索引中,而是在查询时实时生成。

2. 应用场景

  • 地理计算:计算距离、面积等地理指标
  • 数据转换:格式化字段值(如货币、日期)
  • 条件逻辑:基于其他字段值生成新字段(如打折状态判断)
  • 聚合计算:实现复杂的加权评分算法

3. 典型示例

"script_fields": {
    "distance": {  
        "script": {
            "lang": "painless",  
            "params": {  
                "lat": 41.07,
                "lon": 116.14
            },
            "source": "doc['location'].arcDistance(params.lat, params.lon) / 1000"  
        }
    }
}

4. 优缺点分析

优点缺点
灵活:无需修改索引结构即可动态计算性能开销:每次查询都需要执行脚本
支持复杂逻辑:条件判断、数学运算等安全风险:需防范恶意脚本注入
提高数据可用性:实时生成衍生字段缓存困难:难以利用查询缓存优化

三、地理查询参数详解

1. mode 参数

mode参数控制当文档包含多个地理坐标点时如何计算距离:

模式值说明适用场景
min取最小距离(默认)查找最近的设施(如最近的商店入口)
max取最大距离确保所有点都在范围内(如围栏检查)
avg取平均距离计算多点分布的中心性
median取中位数距离避免异常值影响(如不规则区域)

示例

一个商场文档包含多个入口坐标:

{
    "name": "购物中心",
    "location": [
        {"lat": 41.071, "lon": 116.141},  
        {"lat": 41.075, "lon": 116.145}  
    ]
}

当设置mode: "min"时,该文档的排序距离为 1km。

2. distance_type 参数

distance_type控制地理距离的计算方式:

类型值说明精度性能
arc使用球面几何计算弧线距离高(考虑地球曲率)较低
plane使用平面几何计算直线距离低(小范围近似)

适用场景

  • arc:适用于长距离计算(如城市间距离)
  • plane:适用于短距离计算(如建筑物内定位),性能约为arc的 2-3 倍