Elasticsearch基于经纬度及距离查询记录(无需修改映射)
基于现有映射实现Elasticsearch经纬度距离查询方案
你的Elasticsearch文档中addressList字段包含嵌套的经纬度数据(location.longitudeLatitude.lat/lon),但当前映射未将其定义为geo_point类型,以下是无需修改映射的可行查询方案:
方案1:使用脚本计算距离过滤
利用Haversine公式在查询脚本中计算目标点与文档经纬度的直线距离,过滤出符合距离要求的文档。示例查询如下(以查询距离lat:57.7123, lon:11.9677不超过10公里的文档为例):
{ "query": { "bool": { "must": [ { "nested": { "path": "addressList", "query": { "function_score": { "query": { "match_all": {} }, "functions": [ { "script_score": { "script": { "source": """ double lat1 = params.targetLat; double lon1 = params.targetLon; double lat2 = doc['addressList.location.longitudeLatitude.lat'].value; double lon2 = doc['addressList.location.longitudeLatitude.lon'].value; final int R = 6371; // 地球半径(公里) double dLat = Math.toRadians(lat2 - lat1); double dLon = Math.toRadians(lon2 - lon1); double a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); double distance = R * c; return distance <= params.maxDistance ? 1 : 0; """, "params": { "targetLat": 57.7123, "targetLon": 11.9677, "maxDistance": 10 } } } } ], "min_score": 1 } } } } ] } } }
说明:
- 使用
nested查询遍历addressList数组中的每个地址对象 - 通过Haversine公式计算两点间球面距离(单位:公里)
- 设置
min_score:1只保留距离符合条件的文档
方案2:先范围过滤再精确计算(优化性能)
针对大数据量场景,先通过经纬度的范围过滤缩小候选文档集,再用脚本计算精确距离,减少脚本运算量:
{ "query": { "bool": { "must": [ { "nested": { "path": "addressList", "query": { "bool": { "filter": [ // 先过滤纬度范围(示例:±0.09度,约10公里) { "range": { "addressList.location.longitudeLatitude.lat": { "gte": 57.6223, "lte": 57.8023 } } }, // 过滤经度范围(示例:±0.13度,约10公里) { "range": { "addressList.location.longitudeLatitude.lon": { "gte": 11.8377, "lte": 12.0977 } } } ], "must": [ { "function_score": { "query": { "match_all": {} }, "functions": [ { "script_score": { "script": { "source": """ double lat1 = params.targetLat; double lon1 = params.targetLon; double lat2 = doc['addressList.location.longitudeLatitude.lat'].value; double lon2 = doc['addressList.location.longitudeLatitude.lon'].value; final int R = 6371; double dLat = Math.toRadians(lat2 - lat1); double dLon = Math.toRadians(lon2 - lon1); double a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); double distance = R * c; return distance <= params.maxDistance ? 1 : 0; """, "params": { "targetLat": 57.7123, "targetLon": 11.9677, "maxDistance": 10 } } } } ], "min_score": 1 } } ] } } } } ] } } }
说明:
- 经纬度范围可根据目标距离估算(1度纬度≈111公里,经度距离随纬度变化)
- 先过滤减少后续脚本计算的文档数量,提升查询效率
注意事项
- 脚本计算距离的性能远低于原生
geo_point类型的地理查询,数据量较大时建议后续优化映射 - 若需按距离排序,可在
script_score中返回距离值,再通过sort字段基于脚本值排序
内容的提问来源于stack exchange,提问作者Code Noob
相关产品推荐
相关产品推荐

