一、搜索功能
想要实现类似美团那种按照距离排序并展示距离的功能,如图
图中的预计分钟数(出餐时间+骑手到店+骑手到配送地)和距离,这些都是可变的动态值,当然这里面分钟和距离也可以使用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"
}
}
}
}
这个查询的核心功能是:
- 检索距离坐标点(41.07, 116.14) 100 公里内的所有文档
- 将结果按距离升序排列
- 在返回结果中添加一个动态计算的字段"distance",显示每个文档与指定点的实际距离
- 尽量使用上面查询到的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 倍