You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 10:14:53