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

概述

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

1、定义geo point mapping

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

  1. PUT /hotel_app
  2. {
  3. "mappings": {
  4. "properties": {
  5. "pin": {
  6. "type": "geo_point"
  7. },
  8. "name": {
  9. "type": "text",
  10. "analyzer": "ik_max_word"
  11. }
  12. }
  13. }
  14. }

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

2、录入数据

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

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

数据范围要求:纬度范围是-90~90之间,经度范围是-180~180之间。

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

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

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

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

  1. PUT /hotel_app/_doc/1
  2. {
  3. "name": "七天连锁酒店",
  4. "pin": {
  5. "lat": 40.12,
  6. "lon": -71.34
  7. }
  8. }

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

40.99代表纬度 -701.81代表经度

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

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

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

40代表经度
-73.81代表纬度

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

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的经度。

  1. GET /hotel_app/_doc/_search
  2. {
  3. "query": {
  4. "geo_bounding_box": {
  5. "pin": {
  6. "top_left": { //矩形的左上方这个点
  7. "lat": 41.73,
  8. "lon": -74.1
  9. },
  10. "bottom_right": { // 矩形的右下方这个点
  11. "lat": 40.01,
  12. "lon": -70.12
  13. }
  14. }
  15. }
  16. }
  17. }

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

  1. GET /hotel_app/_doc/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match_all": {}
  8. }
  9. ],
  10. "filter": {
  11. "geo_bounding_box": {
  12. "pin": {
  13. "top_left": {
  14. "lat": 41.73,
  15. "lon": -74.1
  16. },
  17. "bottom_right": {
  18. "lat": 40.01,
  19. "lon": -70.12
  20. }
  21. }
  22. }
  23. }
  24. }
  25. }
  26. }

搜索多边形范围内的数据

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

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

image.png

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

输入:

  1. GET /hotel_app/_doc/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match_all": {}
  8. }
  9. ],
  10. "filter": {
  11. "geo_polygon": {
  12. "pin": {
  13. "points": [
  14. {
  15. "lat": 40.73,
  16. "lon": -74.1
  17. },
  18. {
  19. "lat": 40.01,
  20. "lon": -71.12
  21. },
  22. {
  23. "lat": 50.56,
  24. "lon": -90.58
  25. }
  26. ]
  27. }
  28. }
  29. }
  30. }
  31. }
  32. }

结果

  1. {
  2. "took" : 22,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 1,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 1.0,
  16. "hits" : [
  17. {
  18. "_index" : "hotel_app",
  19. "_type" : "_doc",
  20. "_id" : "1",
  21. "_score" : 1.0,
  22. "_source" : {
  23. "name" : "七天连锁酒店",
  24. "pin" : {
  25. "lat" : 40.12,
  26. "lon" : -71.34
  27. }
  28. }
  29. }
  30. ]
  31. }
  32. }

搜索某地点附近的数据

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

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

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

  1. GET /hotel_app/_doc/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match_all": {}
  8. }
  9. ],
  10. "filter": {
  11. "geo_distance": {//指定以圆圈的形式,以lat40,lon是-70的地点为中心,直径为200km内的数据
  12. "distance": "200km",
  13. "pin": {
  14. "lat": 40,
  15. "lon": -70
  16. }
  17. }
  18. }
  19. }
  20. }
  21. }

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

  1. GET hotel_app/_search
  2. {
  3. "query": {
  4. "geo_distance": {
  5. "distance": "90km",
  6. "pin": {
  7. "lat": 40.55,
  8. "lon": -71.12
  9. }
  10. }
  11. }
  12. }

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

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

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

dsl内容解释:

  1. GET /hotel_app/_doc/_search
  2. {
  3. "size": 0,// 这个意思是我不想看到统计的元数据信息
  4. "aggs": { //统计
  5. "agg_by_pin": { //统计的名字
  6. "geo_distance": { //圆圈的形式做统计
  7. "distance_type": "arc",//统计的类型 arc是最高精度 plane是最高效率
  8. "field": "pin", //字段名
  9. "origin": { // 原始的位置,就好比我用这个点做统计
  10. "lat": 40,
  11. "lon": -70
  12. },
  13. "unit": "mi", //指定单位是mi ,就是英里
  14. "ranges": [ //根据范围统计
  15. { //距离0~80范围的
  16. "to": 80
  17. },
  18. { //80~300范围的数据
  19. "from": 80,
  20. "to": 300
  21. },
  22. { // 300~1000范围的数据
  23. "from": 300,
  24. "to": 1000
  25. }
  26. ]
  27. }
  28. }
  29. }
  30. }

输入dsl:

  1. GET /hotel_app/_doc/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "agg_by_pin": {
  6. "geo_distance": {
  7. "distance_type": "arc",
  8. "field": "pin",
  9. "origin": {
  10. "lat": 40,
  11. "lon": -70
  12. },
  13. "unit": "mi",
  14. "ranges": [
  15. {
  16. "to": 80
  17. },
  18. {
  19. "from": 80,
  20. "to": 300
  21. },
  22. {
  23. "from": 300,
  24. "to": 1000
  25. }
  26. ]
  27. }
  28. }
  29. }
  30. }

结果

  1. {
  2. "took" : 8,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 3,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "aggregations" : {
  19. "agg_by_pin" : {
  20. "buckets" : [
  21. { // 0~80的数量为1
  22. "key" : "*-80.0",
  23. "from" : 0.0,
  24. "to" : 80.0,
  25. "doc_count" : 1
  26. },
  27. { // 80~300的数量为1
  28. "key" : "80.0-300.0",
  29. "from" : 80.0,
  30. "to" : 300.0,
  31. "doc_count" : 1
  32. },
  33. { // 300~1000的数量为0
  34. "key" : "300.0-1000.0",
  35. "from" : 300.0,
  36. "to" : 1000.0,
  37. "doc_count" : 0
  38. }
  39. ]
  40. }
  41. }
  42. }