📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 7.17-8.9

1. 错误异常的基本描述 #

GeoJSON 'coordinates' must be an array 是 Elasticsearch 在解析地理空间数据(GeoJSON)时抛出的异常,表示传入的 coordinates 字段不符合 GeoJSON 规范中"坐标必须是数组"的要求。该错误通常发生在索引文档写入阶段或地理查询解析阶段。

常见现象 #

  • 写入包含 geo_pointgeo_shape 类型字段的文档时,返回 400 Bad Request,错误信息包含 GeoJSON 'coordinates' must be an array
  • 使用地理查询(如 geo_distancegeo_bounding_boxgeo_polygon)时,如果查询 DSL 中的坐标格式不正确,也会触发类似错误。
  • Kibana 或客户端应用在执行地理可视化时失败,控制台显示坐标解析异常。
  • 批量写入(Bulk API)中部分文档失败,错误日志指向特定文档的坐标字段。

典型报错与异常栈 #

{
  "error": {
    "root_cause": [
      {
        "type": "parse_exception",
        "reason": "GeoJSON 'coordinates' must be an array"
      }
    ],
    "type": "parse_exception",
    "reason": "GeoJSON 'coordinates' must be an array"
  },
  "status": 400
}

底层 Java 异常栈通常类似:

ElasticsearchParseException[GeoJSON 'coordinates' must be an array]
    at org.elasticsearch.common.geo.GeoJsonParser.parseCoordinates(GeoJsonParser.java:XXX)
    at org.elasticsearch.common.geo.GeoJsonParser.parsePoint(GeoJsonParser.java:XXX)
    at org.elasticsearch.index.mapper.GeoPointFieldMapper.parse(GeoPointFieldMapper.java:XXX)

2. 为什么会发生这个错误 #

GeoJSON 是 RFC 7946 定义的地理空间数据交换格式,其中 coordinates 字段必须是一个数组。Elasticsearch 严格遵循该规范,在解析 geo_pointgeo_shape 类型数据时会校验坐标格式。

常见原因包括:

  • 坐标写成字符串而非数组:例如 "coordinates": "116.4,39.9" 而不是 "coordinates": [116.4, 39.9],这是最常见的错误形式。
  • 坐标缺少外层数组包裹:点坐标应为 [经度, 纬度] 的数组形式,直接传 [116.4, 39.9] 是正确的,但如果嵌套在 GeoJSON 对象中写成了 "coordinates": { "lon": 116.4, "lat": 39.9 } 且 Elasticsearch 期望纯数组时也会出错。
  • GeoJSON 对象结构不完整:使用 type: "Point" 时,缺少 coordinates 字段,或 coordinates 的值不是数组。
  • 客户端序列化问题:某些 JSON 序列化库在处理坐标数组时,可能将 [116.4, 39.9] 序列化成了 {"0": 116.4, "1": 39.9} 这样的对象形式。
  • 数据源格式不统一:从不同系统导入数据时,坐标可能是字符串、对象或单值,未做统一转换就直接写入 Elasticsearch。
  • 使用错误的数据结构:例如将多边形坐标写成了 [116.4, 39.9, 117.4, 40.9](一维数组),而多边形需要二维数组 [[[116.4, 39.9], [117.4, 39.9], [117.4, 40.9], [116.4, 40.9], [116.4, 39.9]]]

3. 如何排查这个异常 #

建议按以下顺序排查:

  1. 确认报错文档的坐标字段值:从报错信息中找到具体的文档 ID 或报错位置,检查该文档中 geo_pointgeo_shape 字段的原始数据。
  2. 对照 GeoJSON 规范检查格式:确认 coordinates 是数组,且数组元素的类型和嵌套层级正确(点为一维数组 [lon, lat],线为二维数组 [[lon,lat], ...],面为三维数组 [[[lon,lat], ...]])。
  3. 检查客户端代码中的坐标构造逻辑:确认坐标是通过数组形式构造的,而不是字符串拼接或对象字面量。
  4. 验证索引 Mapping:通过 GET /<index>/_mapping 确认目标字段的类型定义,确认是 geo_point 还是 geo_shape,以及是否有自定义 ignore_malformed 等设置。
  5. 在测试环境复现:用最小文档在测试索引中复现问题,逐步调整坐标格式直到写入成功。

排查时需要注意的问题 #

  • Elasticsearch 中 geo_point 支持多种格式(字符串 "lat,lon"、对象 {"lat": 39.9, "lon": 116.4}、数组 [lon, lat]、WKT),但 GeoJSON 格式严格要求数组形式,混合使用容易出错。
  • geo_shape 类型严格遵循 GeoJSON 规范,coordinates 必须是数组,不支持其他简写形式。
  • 批量写入时,单个文档的坐标错误会导致该文档失败,但不影响同批次其他文档,需要检查 Bulk API 的响应中每个 item 的状态。

4. 如何解决这个错误 #

常用修复思路 #

修复坐标格式(geo_point 类型)

确保坐标以数组形式传递,注意 Elasticsearch 的数组顺序是 [经度, 纬度](即 [longitude, latitude]),与日常习惯的"纬度在前"相反:

{
  "location": [116.4, 39.9]
}

或者使用 GeoJSON 完整结构:

{
  "location": {
    "type": "Point",
    "coordinates": [116.4, 39.9]
  }
}

修复坐标格式(geo_shape 类型)

geo_shape 必须使用标准 GeoJSON 格式,coordinates 必须是数组:

{
  "geometry": {
    "type": "Point",
    "coordinates": [116.4, 39.9]
  }
}

线(LineString)坐标示例:

{
  "geometry": {
    "type": "LineString",
    "coordinates": [
      [116.4, 39.9],
      [116.5, 40.0],
      [116.6, 40.1]
    ]
  }
}

多边形(Polygon)坐标示例:

{
  "geometry": {
    "type": "Polygon",
    "coordinates": [
      [
        [116.4, 39.9],
        [117.4, 39.9],
        [117.4, 40.9],
        [116.4, 40.9],
        [116.4, 39.9]
      ]
    ]
  }
}

修复客户端代码

以 Python 为例,确保坐标构造为列表(数组):

# 错误写法
doc = {"location": f"{lat},{lon}"}  # 字符串,可能触发错误

# 正确写法
doc = {"location": [lon, lat]}  # 数组形式,注意顺序是 [经度, 纬度]

# 或使用 GeoJSON 结构
doc = {
    "location": {
        "type": "Point",
        "coordinates": [lon, lat]
    }
}

以 Java 为例:

// 错误写法
Map<String, Object> doc = new HashMap<>();
doc.put("location", lat + "," + lon);  // 字符串

// 正确写法
List<Double> coords = Arrays.asList(lon, lat);  // 数组
Map<String, Object> geoJson = new HashMap<>();
geoJson.put("type", "Point");
geoJson.put("coordinates", coords);
doc.put("location", geoJson);

后续注意事项与推荐建议 #

  • 在数据写入前做坐标格式校验,确保 coordinates 是数组类型,可以借助 JSON Schema 或应用层校验拦截非法数据。
  • 如果数据源坐标格式不统一,建议在数据导入 ETL 流程中做标准化处理,统一转为 [lon, lat] 数组格式。
  • geo_shape 字段,建议在索引 Mapping 中设置 "ignore_malformed": false(默认),让格式错误尽早暴露,而不是静默忽略。
  • 建立地理字段的单元测试,覆盖点、线、面等常见 GeoJSON 类型的写入场景,防止回归。

借助 INFINI 产品提升排障效率 #

  • INFINI Console 适合查看索引 Mapping、文档内容、写入错误详情和集群健康状态,帮助快速判断坐标格式是否符合预期。
  • INFINI Gateway 适合部署在 Elasticsearch 前面做请求观测、流量录制和异常请求定位,可以直接捕获写入失败的原始请求体,快速找到格式错误的坐标数据。

5. 小结 #

GeoJSON 'coordinates' must be an array 错误的根因是传入的地理坐标数据不符合 GeoJSON 规范中"坐标必须是数组"的要求。修复时只需确保 coordinates 字段的值是数组类型,并注意 Elasticsearch 中坐标顺序为 [经度, 纬度]。对于 geo_shape 类型,严格使用标准 GeoJSON 格式即可避免此类问题。

在数据处理链路中尽早做格式校验和标准化,是防止此类异常反复出现的根本方法。

相关错误 #

相关链接 #

附:日志上下文 #

下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:

coordinates = new ArrayList<>();
while (subParser.nextToken() != Token.END_ARRAY) {
    coordinates.add(parseValidDouble(subParser, field));
}
} else {
    throw new ElasticsearchParseException("GeoJSON 'coordinates' must be an array");
}
} else if (TYPE.equals(field)) {
    if (subParser.currentToken() == Token.VALUE_STRING) {
        geojsonType = subParser.text();
    } else {