返回地理点数组的 Elasticsearch 距离

Posted

技术标签:

【中文标题】返回地理点数组的 Elasticsearch 距离【英文标题】:Return Elasticsearch distance for array of geo points 【发布时间】:2021-01-31 17:51:49 【问题描述】:

我需要返回 Elasticsearch 数组中每个文档的 多个 地理点的距离。到目前为止,我的结果只返回为数组计算的一个距离。

我从以下 *** 问题的代码开始: Return distance in elasticsearch results?

我的 elasticsearch 查询正文包含以下内容:


  "stored_fields" : [ "_source" ],
    "script_fields" : 
      "distance" : 
        "script" : 
          "inline": "doc['locations.facility.address.coordinates'].arcDistance(params.lat,params.lon) * 0.001",
          "lang": "painless",
          "params": 
            "lat": 2.27,
            "lon": 50.3
          
        
      
    
  

而且,我的 Elasticsearch 源文档在返回时与此类似。 (注意位置是一个数组。)

"locations": [
    
      "facility": 
        "address": 
          "country_code": "US",
          "city": "San Diego",
          "coordinates": 
            "lon": -117.165,
            "lat": 32.8408
          ,
          "country_name": "United States",
          "state_province": "California",
          "postal_code": "92123"
        
      
    ,
    
      "facility": 
        "address": 
          "country_code": "US",
          "city": "Tampa",
          "coordinates": 
            "lon": -82.505,
            "lat": 28.0831
          ,
          "country_name": "United States",
          "state_province": "Florida",
          "postal_code": "33613"
        
      
    

]

目前,我的结果返回类似于以下内容:

    "fields": 
      "distance": [
        13952.518249603361
      ]
    

但在距离数组中,我需要为“位置”中的每个条目返回一个值。

【问题讨论】:

【参考方案1】:

这个很棘手。

根据documentation 和source code,arcDistance 方法仅适用于doc values,而不适用于这些文档值基础的单个geo point instances。

换句话说,虽然我们可以在 doc['locations.facility.address.coordinates'] 上进行迭代,但被迭代的对象并没有实现 any geo distance methods。

这真是太糟糕了。所以我们必须实现我们自己的地理距离函数,也许是usinghaversine formula:


  "stored_fields": [
    "_source"
  ],
  "script_fields": 
    "distance": 
      "script": 
        "inline": """
          float distFrom(float lat1, float lng1, float lat2, float lng2) 
            double earthRadius = 6371000; // meters
            double dLat = Math.toRadians(lat2-lat1);
            double dLng = Math.toRadians(lng2-lng1);
            double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                       Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
                       Math.sin(dLng/2) * Math.sin(dLng/2);
            double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
            float dist = (float) (earthRadius * c);
            
            return dist;
          
        
          return params._source.locations.stream().map(location -> 
              def lat = (float) location.facility.address.coordinates.lat;
              def lon = (float) location.facility.address.coordinates.lon;
              return distFrom(lat, lon, (float) params.lat, (float) params.lon) * 0.001;
          ).collect(Collectors.toList())
        """,
        "lang": "painless",
        "params": 
          "lat": 2.27,
          "lon": 50.3
        
      
    
  

屈服

"hits" : 
  ...
  "hits" : [
    
      ...
      "_source" : 
        "locations" : [
           ... ,
           ... 
        ]
      ,
      "fields" : 
        "distance" : [
          15894.470000000001,
          13952.498
        ]
      
    
  ]


说实话,当需要编写这么多脚本时,有些事情出了问题。

一般来说,脚本should be avoided。

但更重要的是,当您不按这些地理距离排序时,整个计算工作应该在 Elasticsearch 外部完成 - 而不是在您所在的地方重新对搜索结果进行后处理。例如,我使用 Turf 进行 javascript 地理计算。

最后,当您将多个位置/设施存储在一个数组中时,我建议使用nested fields。它们防止数组扁平化,并支持sorting that makes sense。

【讨论】:

以上是关于返回地理点数组的 Elasticsearch 距离的主要内容,如果未能解决你的问题,请参考以下文章

实战ELK Elasticsearch地理位置

Elasticsearch 地理距离排序不完全/错误的顺序

Elasticsearch:在 Elasticsearch 中按距离有效地对地理点进行排序

Elasticsearch:在 Elasticsearch 中按距离有效地对地理点进行排序

elasticsearch地理距离过滤器,但距离在文档上而不是在查询中

Android:查找两个地理点之间的距离