ElasticSearch的 geo point - 地理位置搜索和聚合分析

出自 图灵学院 ElasticSearch课程, 我自己学完了,整理了一下,然后给老师说的话,记录了一下,发了个博客

概述

ES支持地理位置的搜索和聚合分析,可实现在指定区域内搜索数据、搜索指定地点附近的数据、聚合分析指定地点附近的数据等操作。
ES中如果使用地理位置搜索的话,必须提供一个特殊的字段类型。GEO - geo_point。地理位置的坐标点。

1、定义geo point mapping

如果需要使用地址坐标,则需要定义一个指定的mapping类型。具体如下:
使用什么数据可以确定,地球上的一个具体的点?经纬度。

PUT /hotel_app
{
  "mappings": {
    "properties": {
      "pin": {
        "type": "geo_point"
      },
      "name": {
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

“type”: “geo_point” // 特殊的数据类型

2、录入数据

新增一个基于geo point类型的数据,可以使用多种方式。

多种类型描述geo_point类型字段的时候,在搜索数据的时候,显示的格式和录入的格式是统一的。不影响搜索。任何数据描述的geo_point类型字段,都适用地理位置搜索。

数据范围要求:纬度范围是-9090之间,经度范围是-180180之间。

经纬度数据都是浮点数或字符串,但是字符串必须得全都是文字(数字组成的字符串)

最大精度:小数点后7位。(常用小数点后6位即可。)

下面有其中三种方式插入数据

基于数字:latitude:纬度、longitude:经度。语义清晰,建议使用。

PUT /hotel_app/_doc/1
{
  "name": "七天连锁酒店",
  "pin": {
    "lat": 40.12,  
    "lon": -71.34
  }
}

基于字符串:依次定义纬度、经度。不推荐使用

40.99代表纬度 -701.81代表经度

PUT /hotel_app/_doc/2
{
"name": "维多利亚大酒店",
"pin" : "40.99, -70.81"
}

基于数组:依次定义经度、纬度。不推荐使用

注意,这个基于数组的经度纬度顺序和上面的基于字符串的顺序不一样的.

40代表经度
-73.81代表纬度

PUT /hotel_app/_doc/3
{
"name": " 红树林宾馆",
"pin" : [40, -73.81]
}

3、搜索指定区域范围内的数据

总结:

多边形范围搜索:对传入的若干点的坐标顺序没有任何的要求。只要传入若干地理位置坐标点,即可形成多边形。

搜索矩形范围内的数据

geo_bounding_box的意思代表是矩形的形式

image.png

top_left 就是矩形的左上方这个点, bottom_right就是矩形的右下方这个点,指定了这两个点之后,就将这两个点连城一个矩形, 将矩形的数据查询出来

矩形范围搜索:传入的top_left和bottom_right坐标点是有固定要求的。地图中以北作为top,南作为bottom,西作为left,东作为right。也就是top_left应该从西北向东南。Bottom_right应该从东南向西北。Top_left的纬度应该大于bottom_right的纬度,top_left的经度应该小于bottom_right的经度。

 GET /hotel_app/_doc/_search
{
  "query": {
    "geo_bounding_box": {
      "pin": {
        "top_left": { //矩形的左上方这个点
          "lat": 41.73,
          "lon": -74.1
        },
        "bottom_right": {  // 矩形的右下方这个点
          "lat": 40.01,
          "lon": -70.12
        }
      }
    }
  }
}

但是上面的方式会对结果进行评分排序,效率不如filter效率高,下面这种效率是最高的.

GET /hotel_app/_doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_all": {}
        }
      ],
      "filter": {
        "geo_bounding_box": {
          "pin": {
            "top_left": {
              "lat": 41.73,
              "lon": -74.1
            },
            "bottom_right": {
              "lat": 40.01,
              "lon": -70.12
            }
          }
        }
      }
    }
  }
}
 

搜索多边形范围内的数据

多边形就是你有几个点,然后这几个点就串联起来,

假如说你有三个点,那么就是三个点串联起来成为一个三角形,ElasticSearch会将三角形里面的坐标数据返回给客户端

image.png

假如说你有四个点,ElasticSearch会将四个点串联起来,然后将这四个点串联的四边形里面的数据返回给你客户端
image.png

输入:

GET /hotel_app/_doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_all": {}
        }
      ],
      "filter": {
        "geo_polygon": {
          "pin": {
            "points": [
              {
                "lat": 40.73,
                "lon": -74.1
              },
              {
                "lat": 40.01,
                "lon": -71.12
              },
              {
                "lat": 50.56,
                "lon": -90.58
              }
            ]
          }
        }
      }
    }
  }
}

结果

{
  "took" : 22,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "hotel_app",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "name" : "七天连锁酒店",
          "pin" : {
            "lat" : 40.12,
            "lon" : -71.34
          }
        }
      }
    ]
  }
}

搜索某地点附近的数据

比如说我想去吃饭,我希望找以我为中心,直径两公里的以内的饭店

这个搜索在项目中更加常用。类似附近搜索功能。
Distance距离的单位,常用的有米(m)和千米(km)。
建议使用filter来过滤geo_point数据。因为geo_point数据相关度评分计算比较耗时。使用query来搜索geo_point数据效率相对会慢一些。建议使用filter。

pin就是指定当前的经纬度
distance是距离
geo_distance是圆圈的形式

GET /hotel_app/_doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_all": {}
        }
      ],
      "filter": {
        "geo_distance": {//指定以圆圈的形式,以lat是40,lon是-70的地点为中心,直径为200km内的数据
          "distance": "200km",
          "pin": {
            "lat": 40,
            "lon": -70
          }
        }
      }
    }
  }
}

下面这种会对结果打分排序,效率不如上面带filter的效率高,上面filter方式搜索不会对结果进行打分.效率更高.

GET hotel_app/_search
{
  "query": {
    "geo_distance": {
      "distance": "90km",
      "pin": {
        "lat": 40.55,
        "lon": -71.12
      }
    }
  }
}

统计某位置附近区域内的数据

比如说你打开一个app,里面有个功能,就显示距离你2公里有多少个商家,这个功能就是聚合统计.

聚合统计分别距离某位置80英里,300英里,1000英里范围内的数据数量。
其中unit是距离单位,常用单位有:米(m),千米(km),英里(mi)
distance_type是统计算法:sloppy_arc默认算法、arc最高精度、plane最高效率

dsl内容解释:

GET /hotel_app/_doc/_search
{
  "size": 0,// 这个意思是我不想看到统计的元数据信息
  "aggs": { //统计
    "agg_by_pin": { //统计的名字
      "geo_distance": { //圆圈的形式做统计
        "distance_type": "arc",//统计的类型 arc是最高精度 plane是最高效率
        "field": "pin", //字段名
        "origin": { // 原始的位置,就好比我用这个点做统计
          "lat": 40,
          "lon": -70
        },
        "unit": "mi", //指定单位是mi ,就是英里
        "ranges": [ //根据范围统计
          {    //距离0~80范围的
            "to": 80
          },
          {  //80~300范围的数据
            "from": 80,
            "to": 300
          },
          {  // 300~1000范围的数据
            "from": 300,
            "to": 1000
          }
        ]
      }
    }
  }
}

输入dsl:

GET /hotel_app/_doc/_search
{
  "size": 0,
  "aggs": {
    "agg_by_pin": {
      "geo_distance": {
        "distance_type": "arc",
        "field": "pin",
        "origin": {
          "lat": 40,
          "lon": -70
        },
        "unit": "mi",
        "ranges": [
          {
            "to": 80
          },
          {
            "from": 80,
            "to": 300
          },
          {
            "from": 300,
            "to": 1000
          }
        ]
      }
    }
  }
}

结果

{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "agg_by_pin" : {
      "buckets" : [
        {  // 0~80的数量为1
          "key" : "*-80.0",
          "from" : 0.0,
          "to" : 80.0,
          "doc_count" : 1
        },
        {  // 80~300的数量为1
          "key" : "80.0-300.0",
          "from" : 80.0,
          "to" : 300.0,
          "doc_count" : 1
        }, 
        { // 300~1000的数量为0
          "key" : "300.0-1000.0",
          "from" : 300.0,
          "to" : 1000.0,
          "doc_count" : 0
        }
      ]
    }
  }
}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: dereferencing type-punned pointer是指对一个类型转换后的指针进行解引用操作。这种操作可能会导致未定义的行为,因为不同类型的指针可能具有不同的内存布局和对齐方式,解引用操作可能会访问到错误的内存位置,导致程序崩溃或产生不可预测的结果。因此,应该避免使用这种类型的指针操作。 ### 回答2: dereferencing type-punned pointer(解引用类型转换指针) 首先需要了解的是类型转换指针,它是一个指针,它可以将一个变量解释为不同类型的变量。例如,一个int型指针可以转换为一个char型指针,它可以让程序员通过不同类型查看同一块内存区域,但是这个过程是非常危险的,尤其是在解引用指针时,很容易引发问题。 当我们使用一个指针解引用时,我们希望得到的是一个合法的内存地址,即指向有效的对象的指针。但是如果我们使用一个类型转换指针,则不一定能够保证指针指向的内存区域是有效的。这种情况可能导致非法的内存访问、程序崩溃,并可能导致安全漏洞。例如,以下代码使用了一个类型转换指针: ``` int a = 10; float b = *(float*)&a; ``` 这个代码段将一个整数a的地址解释为一个浮点数b的地址,并通过解引用类型转换指针来获取浮点数的值。如果a的内存区域不足以容纳一个浮点数,或者已经被释放了,那么这个代码就会引起问题。 解决这个问题的方法是使用正确的类型来解引用指针,或者使用内存操作函数来完成转换。例如,在上面的例子中,我们可以使用以下代码来完成类型转换: ``` int a = 10; float b; memcpy(&b, &a, sizeof(float)); ``` 这个代码使用了memcpy函数将一个整数a的地址复制到一个float类型的变量b中。这种方法可以保证我们不会出现类型转换指针导致的问题。 ### 回答3: dereferencing type-punned poin是指对通过类型转换得到指针的解引用操作。在C/C++中,类型转换是一种常见的操作,有时候我们需要将一个指针“重新解释”为另外一个指针类型,通过这种方式访问不同类型的数据。但是,这种做法实际上是非法的,可能会引起严重的后果。 具体来说,dereferencing type-punned poin可能会导致以下几个问题: 1.非法内存访问。由于类型转换后,指针所指向的数据类型发生了变化,新的数据类型可能会导致访问非法内存。 2.未定义的行为。C/C++标准中有很多规则限制了指针类型转换的行为,如果不遵守这些规则,可能会导致未定义的行为。 3.平台依赖性。某些类型转换可能会涉及平台依赖性,例如对指针进行位操作,这种做法可能会依赖于底层硬件的字节序等因素。 因此,避免dereferencing type-punned poin非常重要。如果确实需要进行类型转换,建议使用标准库中提供的转换函数,而不是手动进行位操作等操作。另外,在使用指针类型转换时,也要充分了解C/C++标准中的规则,以避免不必要的安全问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值