【问题标题】:Elasticsearch custom geo distance filterElasticsearch 自定义地理距离过滤器
【发布时间】:2021-06-01 11:30:40
【问题描述】:

我想从 Elasticsearch 查询中检索可变距离内的所有点。 假设我有 2 家商店,一家愿意在最长 3 公里的范围内送货,另一家愿意在最长 5 公里的范围内送货:

PUT /my_shops/_doc/1
{
    "location": {
      "lat": 40.12,
      "lon": -71.34
    },
    "max_delivery_distance": 3000
}

PUT /my_shops/_doc/2
{
    "location": {
      "lat": 41.12,
      "lon": -72.34
    },
    "max_delivery_distance": 5000
}

对于给定的位置,我想知道哪些商店可以送货。如果给定位置在 3 公里以内,IE 查询应该返回 shop1,如果给定位置在 5 公里以内,则返回 shop2

GET /my_shops/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_distance": {
          "distance": max_delivery_distance,
          "location": {
            "lat": 40,
            "lon": -70
          }
        }
      }
    }
  }
}

【问题讨论】:

    标签: elasticsearch filter distance


    【解决方案1】:

    还有另一种无需编写脚本即可解决此问题的方法(大性能消耗者!!)并让 ES 使用原生 Geo 形状对其进行排序。

    我会将每个文档建模为circle,具有中心位置和(交付)半径。首先,您的索引映射应如下所示:

    PUT /my_shops
    {
      "mappings": {
        "properties": {
          "delivery_area": {
            "type": "geo_shape",
            "strategy": "recursive"
          }
        }
      }
    }
    

    那么,您的文件需要具有以下格式:

    PUT /my_shops/_doc/1
    {
      "delivery_area" : {
        "type" : "circle",
        "coordinates" : [-71.34, 40.12],
        "radius" : "3000m"
      }
    }
    
    PUT /my_shops/_doc/2
    {
      "delivery_area" : {
        "type" : "circle",
        "coordinates" : [-72.34, 41.12],
        "radius" : "5000m"
      }
    }
    

    最后,查询变成了geo_shape 查询,查看送货点和每个商店的送货区域之间的交叉点。

    GET /my_shops/_search
    {
      "query": {
        "bool": {
          "filter": {
            "geo_shape": {
              "delivery_area": {
                "shape": {
                  "type": "point",
                  "coordinates": [ -70, 40 ]
                },
                "relation": "contains"
              }
            }
          }
        }
      }
    }
    

    就是这样!没有脚本,只有地理操作。

    【讨论】:

      【解决方案2】:

      我认为您需要使用脚本才能使用另一个字段作为参数。经过一番研究,我得出了这个答案:

      GET my_shops/_search
      {
        "query": {
          "script": {
            "script": {
              "params": {
                "location": {
                  "lat": 40,
                  "lon": -70
                }
              },
              "source": """
            return doc['location'].arcDistance(params.location.lat, params.location.lon)/1000 <= doc['max_delivery_distance'].value"""
            }
          }
        }
      }
      

      基本上,我们利用与 GEO 点相关的类在无痛https://github.com/elastic/elasticsearch/pull/40180/ 中列入白名单这一事实,并且脚本接受其他参数(您的固定位置)。

      根据 arcDistance 的文档,我们以米为单位检索大小,因此您需要将此值除以 1000 转换为 km。

      补充说明

      我假设 location 和 max_delivery_distance 总是(对于每个文档)定义的。如果不是这种情况,您需要覆盖这种情况。

      参考

      【讨论】:

      • location 和 max_delivery_distance 确实总是被定义的。非常感谢您的帮助以及您提供详细记录的答案
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-08
      • 1970-01-01
      • 1970-01-01
      • 2014-03-06
      • 2013-04-13
      • 1970-01-01
      • 2011-03-12
      相关资源
      最近更新 更多