使用弹性搜索地理功能查找最常见的位置?

Posted

技术标签:

【中文标题】使用弹性搜索地理功能查找最常见的位置?【英文标题】:Using Elastic Search Geo Functionality To Find Most Common Locations? 【发布时间】:2016-08-21 04:28:44 【问题描述】:

我有一个 geojson 文件,其中包含一个位置列表,每个位置都有经度、纬度和时间戳。注意经度和纬度乘以 10000000。


  "locations" : [ 
    "timestampMs" : "1461820561530",
    "latitudeE7" : -378107308,
    "longitudeE7" : 1449654070,
    "accuracy" : 35,
    "junk_i_want_to_save_but_ignore" : [  ..  ]
  , 
    "timestampMs" : "1461820455813",
    "latitudeE7" : -378107279,
    "longitudeE7" : 1449673809,
    "accuracy" : 33
  , 
    "timestampMs" : "1461820281089",
    "latitudeE7" : -378105184,
    "longitudeE7" : 1449254023,
    "accuracy" : 35
  , 
    "timestampMs" : "1461820155814",
    "latitudeE7" : -378177434,
    "longitudeE7" : 1429653949,
    "accuracy" : 34
  
  ..

其中许多位置将是相同的物理位置(例如用户的家),但显然经度和纬度可能不完全相同。

我想使用 Elastic Search 及其地理功能来生成最常见位置的排名列表,如果位置在彼此之间(例如 100m)之内,则这些位置被认为是相同的?

对于每个常见位置,如果可能的话,我还想要他们在该位置的所有时间戳的列表!

非常感谢一个示例查询让我开始!

非常感谢。

【问题讨论】:

您能否展示一个示例文档和您的映射?让人们更容易引导您的问题并尝试解决它。​​ 嗨,瓦尔。好点子!我已经包含了上面的示例数据。 如果此 GeoJSON 数据被正确索引,那么您可以使用 geohash_grid 聚合来查看每个位置在二维空间(约 100m 矩形)中的分布情况。您还可以将其与 date_histogram 聚合结合起来,看看这些点是如何及时分布的。 优秀的 Val,听起来很完美。我对 Elastic Search 和 Geo 的东西完全陌生,所以我会看看我是否可以提出一个查询并让你知道它是否有效。 首先,确保正确索引您的数据。这样的事情应该可以帮助您入门:"location":"lat":40,"lon":70, "timestamp": 1458432895726, "accuracy": 15,其中locationgeo_pointtimestampaccuracylong 【参考方案1】:

为了使其正常工作,您需要像这样修改映射:

PUT /locations

  "mappings": 
    "location": 
      "properties": 
        "location": 
          "type": "geo_point"
        ,
        "timestampMs": 
          "type": "long"
        ,
        "accuracy": 
          "type": "long"
        
      
    
  

然后,当你索引你的文档时,你需要将经纬度除以 10000000,然后索引如下:

PUT /locations/location/1

  "timestampMs": "1461820561530",
  "location": 
    "lat": -37.8103308,
    "lon": 14.4967407
  ,
  "accuracy": 35

最后,您的搜索查询如下...

POST /locations/location/_search

  "aggregations": 
    "zoomedInView": 
      "filter": 
        "geo_bounding_box": 
          "location": 
            "top_left": "-37, 14",
            "bottom_right": "-38, 15"
          
        
      ,
      "aggregations": 
        "zoom1": 
          "geohash_grid": 
            "field": "location",
            "precision": 6
          ,
          "aggs": 
            "ts": 
              "date_histogram": 
                "field": "timestampMs",
                "interval": "15m",
                "format": "DDD yyyy-MM-dd HH:mm"
              
            
          
        
      
    
  

...将产生以下结果:


  "aggregations": 
    "zoomedInView": 
      "doc_count": 1,
      "zoom1": 
        "buckets": [
          
            "key": "k362cu",
            "doc_count": 1,
            "ts": 
              "buckets": [
                
                  "key_as_string": "Thu 2016-04-28 05:15",
                  "key": 1461820500000,
                  "doc_count": 1
                
              ]
            
          
        ]
      
    
  

更新

根据我们的讨论,这里有一个适合您的解决方案。使用 Logstash,您可以调用您的 API 并检索大 JSON 文档(使用 http_poller input),提取/转换所有位置并将它们发送到 Elasticsearch(使用 elasticsearch output)非常容易。

这是按照我最初的回答中所述格式化每个事件的方法。

    使用http_poller,您可以检索 JSON 位置(请注意,我已将轮询间隔设置为 1 天,但您可以将其更改为其他值,或者在每次要检索位置时手动运行 Logstash ) 然后我们将split位置数组转换为单个事件 然后我们将纬度/经度字段除以 10,000,000 以获得正确的坐标 我们还需要通过移动和移除一些字段来稍微清理一下 最后,我们只需将每个事件发送到 Elasticsearch

Logstash 配置locations.conf:

input 
  http_poller 
    urls => 
      get_locations => 
        method => get
        url => "http://your_api.com/locations.json"
        headers => 
          Accept => "application/json"
        
      
    
    request_timeout => 60
    interval => 86400000
    codec => "json"
  

filter 
  split 
    field => "locations" 
  
  ruby 
    code => "
      event['location'] = 
        'lat' => event['locations']['latitudeE7'] / 10000000.0,
        'lon' => event['locations']['longitudeE7'] / 10000000.0
      
    "
  
  mutate 
    add_field => 
      "timestampMs" => "%[locations][timestampMs]"
      "accuracy" => "%[locations][accuracy]"
      "junk_i_want_to_save_but_ignore" => "%[locations][junk_i_want_to_save_but_ignore]"
    
    remove_field => [
      "locations", "@timestamp", "@version" 
    ]
  

output 
  elasticsearch 
    hosts => ["localhost:9200"]
    index => "locations"
    document_type => "location"
  

然后您可以使用以下命令运行:

bin/logstash -f locations.conf

运行后,您可以启动搜索查询,您应该会得到您所期望的结果。

【讨论】:

感谢 Val,太好了!获取每个桶中所有时间戳的列表不是一件容易的事吗? 你想要每个时间戳,还是想要按分钟、小时、天对它们进行分组,然后简单地按组获取有多少? 嗨 Val,关于分组的好主意。如果可以按星期几、小时和 15 分钟的间隔对它们进行分组,那就太棒了!例如星期一 06 15-29: 3 意味着在上午 6 点 15 分到 6 点 29 分之间的任何一个星期一都有 3 场活动,但我认为这很难吗? 由于我无法控制源数据,因此是否可以使用脚本将我的 E7 数字转换为传统的经纬度数据来获取数据。干杯! 我已经用 date_histogram 子聚合更新了我的答案,它完全符合您的需要,即按 15 分钟的时间段存储所有时间戳

以上是关于使用弹性搜索地理功能查找最常见的位置?的主要内容,如果未能解决你的问题,请参考以下文章

查找附近的人。

地理位置示例代码查找我附近的特定商店 HTML 5

使用地理跟踪和 SQL 查询

常见搜索算法:二分查找

MySQL - 如何使用地理位置数据加快搜索速度?

使用 CLGeocoder 反向地理编码查找当前位置