因为只有 geo_point 支持地理距离排序、半径内检索(geo_distance)、矩形范围查询(geo_bounding_box)等核心地图功能;设为 text 或 keyword 会导致解析失败或查询异常,且坐标顺序错误(如数组用 [lon,lat] 而非字符串的 lat,lon)将引发上千公里偏移。

为什么用 geo_point 字段类型而不是 text 或 keyword
因为只有 geo_point 支持地理距离排序、半径内检索(geo_distance)、矩形范围查询(geo_bounding_box)等核心地图功能。如果误设为 text,ES 会报 failed to parse field [location] of type [geo_point];设为 keyword 则查询时直接抛 illegal_argument_exception: field [location] is not a geo_point field。
建索引时必须显式定义:
{"mappings": {"properties": {"location": {"type": "geo_point"}}}}。Gin 接收的 JSON 中该字段应为 {"lat": 31.2304, "lon": 121.4737} 或 "31.2304,121.4737" 格式,不能是数组 [121.4737, 31.2304](ES 默认是 lon,lat 顺序,反了会导致坐标偏移上千公里)。
Gin 路由里怎么安全解析并校验地理位置参数
别直接用 c.Query("lat") 拼字符串塞进 ES 查询——容易注入、精度丢失、缺少范围校验。正确做法是统一收口为 location 查询参数,值格式为 "121.4737,31.2304",然后在 handler 里拆解:
- 用
strings.Split(c.Query("location"), ",")拆成两段,长度不等于 2 就返回 400 - 用
strconv.ParseFloat转lat和lon,任一失败就拒掉 - 手动检查纬度是否在
-90到90、经度是否在-180到180,超界直接 400 - 把合法坐标存进
map[string]interface{}再传给 ES 查询构造函数,避免拼接字符串
ES 的 geo_distance 查询怎么写才不慢
距离查询天生比关键词查重,但加错条件会让性能断崖下跌。关键点:
-
distance值别写成"5km"这种带单位的字符串——虽然语法允许,但 ES 内部要反复解析,建议统一用数字 +unit参数:"distance": 5000, "unit": "m" - 必须加
"_source": ["name", "address", "location"],否则默认返回全部字段,网络传输和序列化开销翻倍 - 如果业务只要最近 10 家店,一定要加
"size": 10,别依赖前端截断——ES 默认只返 10 条,但明确写出来更可控 - 别在
geo_distance外层套bool.must堆一堆match查询——先做地理过滤再文本匹配,顺序反了会全表扫
典型查询体:
{"query": {"geo_distance": {"distance": 3000, "location": {"lat": 31.2304, "lon": 121.4737}}}, "_source": ["name", "address"], "size": 10}
Gin 返回给前端的坐标数据要不要转 WGS84
要,而且必须做。国内高德、百度地图 SDK 默认用 GCJ-02 坐标系,而 Elasticsearch 存储和计算都基于 WGS84(GPS 原始坐标)。如果你把 ES 查出来的 lat/lon 直接丢给高德 JS API,所有标记点会整体偏移 200–500 米。
解决方案只有两个:
- 后端查完 ES 后,调用开源转换库(如
github.com/axgle/gcj02)把 WGS84 转 GCJ-02,再返回给前端 - 前端用相同算法自行转换(需确保 JS 端和 Go 端用同一套转换逻辑,否则偏差不一致)
没做这步转换的地图点位问题,在测试环境几乎看不出来,上线后用户密集上报“位置不准”才暴露——这是最常被跳过的环节。


















