ES7学习笔记(十三)GEO位置搜索

ES7学习笔记(十三)GEO位置搜索

ES的基本内容介绍的已经差不多了,最后我们再来看看GEO位置搜索,现在大部分APP都有基于位置搜索的功能,比如:我们点外卖,可以按照离我们的距离进行排序,这样可以节省我们的配送费和送餐的时间;还有找工作时,也可以按照离自己家的距离进行排序,谁都想找个离家近的工作,对吧。这些功能都是基于GEO搜索实现的,目前支持GEO搜索功能的中间件有很多,像MySQL、Redis、ES等。我们看看在ES当中怎么实现GEO位置搜索。

GEO字段的创建

GEO类型的字段是不能使用动态映射自动生成的,我们需要在创建索引时指定字段的类型为geo_pointgeo_point类型的字段存储的经纬度,我们看看经纬度是怎么定义的,

英文 简写 正数 负数
维度 latitude lat 北纬 南纬
经度 longitude lon或lng 东经 西经

经度的简写有2个,一般常用的是lon,lng则在第三方地图的开放平台中使用比较多。下面我们先创建一个带有geo_point类型字段的索引,如下:

PUT /my_geo
{
   "settings":{
       "analysis":{
           "analyzer":{
               "default":{
                   "type":"ik_max_word"
              }
          }
      }
  },
   "mappings":{
       "dynamic_date_formats":[
           "MM/dd/yyyy",
           "yyyy/MM/dd HH:mm:ss",
           "yyyy-MM-dd",
           "yyyy-MM-dd HH:mm:ss"
      ],
       "properties":{
           "location":{
               "type":"geo_point"
          }
      }
  }
}

创建了一个my_geo索引,在索引中有一些基础的配置,默认IK分词器,动态映射的时间格式。重点是最后我们添加了一个字段location,它的类型是geo_point

索引创建完了,我们添加两条数据吧,假设,路人甲在北京站,路人乙在朝阳公园。那么我们怎么“北京站”和“朝阳公园”的经纬度呢?我们在做项目时,前端都会接地图控件,经纬度的信息可以调用地图控件的API获取。在咱们的示例中,也不接地图控件了,太麻烦了,直接在网上找到“北京站”和“朝阳公园”的坐标吧。

我们查到“北京站”的坐标如下:

ES7学习笔记(十三)GEO位置搜索

然后添加一条数据:

POST /my_geo/_doc
{
   "name":"路人甲",
   "location":{
       "lat": 39.90279998006104,
       "lon": 116.42703999493406
  }
}

再查“朝阳公园”的坐标

ES7学习笔记(十三)GEO位置搜索

再添加“路人乙”的信息

POST /my_geo/_doc
{
   "name":"路人乙",
   "location":{
       "lat": 39.93367367974064,
       "lon": 116.47845257733152
  }
}

我们再用elasticsearch-head插件看一下索引中的数据:

ES7学习笔记(十三)GEO位置搜索

GEO查询

“路人甲”和“路人乙”的信息都有了,但是没有location字段的信息,因为location是特性类型的字段,在这里是展示不出来的。我们搜索一下吧,看看怎么用geo搜索,假设“我”的位置在“工体”,我们先要查到“工体”的坐标,

ES7学习笔记(十三)GEO位置搜索

然后再查询5km范围内都有谁,发送请求如下:

POST /my_geo/_search
{
   "query":{
       "bool":{
           "filter":{
               "geo_distance":{
                   "distance":"5km",
                   "location":{
                       "lat":39.93031708627304,
                       "lon":116.4470385453491
                  }
              }
          }
      }
  }
}

在查询的时候用的是filter查询,在filter查询里再使用geo_distance查询,我们定义距离distance为5km,再指定geo类型的字段location,当前的坐标为:39.93031708627304N,116.4470385453491E。查询一下,看看结果:

{
   ……
   "hits":[
      {
           "_index":"my_geo",
           "_type":"_doc",
           "_id":"AtgtXnIBOZNtuLQtIVdD",
           "_score":0,
           "_source":{
               "name":"路人甲",
               "location":{
                   "lat": 39.90279998006104,
      "lon": 116.42703999493406
              }
          }
      },
      {
           "_index":"my_geo",
           "_type":"_doc",
           "_id":"ZdguXnIBOZNtuLQtMVfA",
           "_score":0,
           "_source":{
               "name":"路人乙",
               "location":{
                   "lat": 39.93367367974064,
      "lon": 116.47845257733152
              }
          }
      }
  ]
}

看来,我们站在“工体”,“北京站”的路人甲和“朝阳公园”的路人乙都在5km的范围内。把范围缩短一点如何,改为3km看看,搜索的请求不变,只是把distance改为3km,看看结果吧,

{
   ……
   "hits":[
      {
           "_index":"my_geo",
           "_type":"_doc",
           "_id":"ZdguXnIBOZNtuLQtMVfA",
           "_score":0,
           "_source":{
               "name":"路人乙",
               "location":{
                   "lat": 39.93367367974064,
      "lon": 116.47845257733152
              }
          }
      }
  ]
}

只有在“朝阳公园”的路人乙被搜索了出来。完全符合预期,我们再看看程序中怎么使用GEO搜索。

JAVA 代码

在定义实体类时,对应的GEO字段要使用特殊的类型,如下:

@Setter@Getter
public class MyGeo {

   private String name;
   private GeoPoint location;

}

location的类型是GeoPoint,添加数据的方法没有变化,转化成Json就可以了。再看看查询怎么用,

public void searchGeo() throws IOException {
   SearchRequest searchRequest = new SearchRequest("my_geo");
   SearchSourceBuilder ssb = new SearchSourceBuilder();

   //工体的坐标
   GeoPoint geoPoint = new GeoPoint(39.93367367974064d,116.47845257733152d);
   //geo距离查询 name=geo字段
   QueryBuilder qb = QueryBuilders.geoDistanceQuery("location")
       //距离 3KM
      .distance(3d, DistanceUnit.KILOMETERS)
       //坐标工体
      .point(geoPoint);

   ssb.query(qb);
   searchRequest.source(ssb);
   SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

   for (SearchHit hit : response.getHits().getHits()) {
       System.out.println(hit.getSourceAsString());
  }


}
  • SearchRequest指定索引my_geo

  • 创建”工体“的坐标点GeoPoint

  • 创建geo距离查询,指定geo字段location,距离3km,坐标点工体

  • 其他的地方没有变化

运行一下,看看结果,

{"name":"路人乙","location":{"lat":39.93360786576342,"lon":116.47853840802}}

只有在“朝阳公园”的路人乙被查询了出来,符合预期。

距离排序

有的小伙伴可能会有这样的疑问,我不想按照距离去查询,只想把查询结果按照离“我”的距离排序,该怎么做呢?再看一下,

public void searchGeoSort() throws IOException {
   SearchRequest searchRequest = new SearchRequest("my_geo");
   SearchSourceBuilder ssb = new SearchSourceBuilder();

   //工体的坐标
   GeoPoint geoPoint = new GeoPoint(39.93367367974064d,116.47845257733152d);

   GeoDistanceSortBuilder sortBuilder = SortBuilders
      .geoDistanceSort("location", geoPoint)
      .order(SortOrder.ASC);

   ssb.sort(sortBuilder);
   searchRequest.source(ssb);
   SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

   for (SearchHit hit : response.getHits().getHits()) {
       System.out.println(hit.getSourceAsString());
  }
}

这次查询并没有设置查询条件,而是创建了一个geo距离排序,同样,先指定geo字段location,和当前的坐标”工体“,再设置排序是升序。运行一下,看看结果,

{"name":"路人乙","location":{"lat":39.93360786576342,"lon":116.47853840802}}
{"name":"路人甲","location":{"lat":39.902799980059335,"lon":116.42721165631102}}

离“工体”比较近的“路人乙”排在了第一个,也是符合预期的。有问题大家评论区留言吧~

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/2498.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • python语言程序设计教程赵璐主编_一加6第三方ROM推荐

    python语言程序设计教程赵璐主编_一加6第三方ROM推荐从数据处理到人工智能python数据分析方向第三方库有:Numpy,SciPy,PandasPython数据可视化方向的第三方库有:Seaborn,Matplotblib,Mayavi,Bokeh,Gleam,Plotly,visoy,ggplot,geoplotlib,pygal,Arcade,missingno,dataswimPython文本处理方向…

    2022年10月14日
  • rsyslogd 重启_rsyslogd配置文件详解

    rsyslogd 重启_rsyslogd配置文件详解rsyslog服务和logrotate服务======================================================================rsyslog是一个syslogd的多线程增强版。现在Fedora和Ubuntu,rhel6默认的日志系统都是rsyslog了rsyslog负责写入日志,logrotate负责备份和删除旧日志,以及更新日志…

  • 查看android证书签名

    查看android证书签名虽然以前搞过,一直忘了怎么查看android证书签名。最近因为需要查看android证书签名,在网上找了很多都不是印象中的方法,最后看到一篇搜索才想起,看来以后要多提高搜索技巧,好记性不如好笔记。正题—-其实查看android签名很简单,对于签过名的apk文件中,用winrar打开后在在META-INF文件下的cert.rsa属于pkcs7证书文件,所以加改后缀为.p7b打开,在

  • Charles抓包安卓端

    Charles抓包安卓端Charles抓包安卓端电脑和手机须要在同一个WIFI下安装好CharlesAndroid手机一部->接下来会以(一加手机)测试1.打开Charles依次选择Proxy->ProxySettings…在这里插入图片描述2.安装需要的证书依次选择Help->SSLProxying->installCharlesRootCertif…

  • 用C++Builder 6编写VCL控件

    用C++Builder 6编写VCL控件用C++Builder6编写VCL控件warton2002.12西安  如今天使用Borland的开发工具的程序员越来越多了,许多程序员开始从VC++,BC++等工具转向C++Builder(以下简称BCB 即BorlandC++Builder)和Delphi。要使用这两种开发工具,你就要用到VCL(VisualComponentLibrary可视化控件库)。关

  • Pytorch实战2:ResNet-18实现Cifar-10图像分类(测试集分类准确率95.170%)[通俗易懂]

    Pytorch实战2:ResNet-18实现Cifar-10图像分类(测试集分类准确率95.170%)[通俗易懂]版权说明:此文章为本人原创内容,转载请注明出处,谢谢合作!Pytorch实战2:ResNet-18实现Cifar-10图像分类实验环境:Pytorch0.4.0torchvision0.2.1Python3.6CUDA8+cuDNNv7(可选)Win10+Pycharm整个项目代码:点击这里ResNet-18网络结构:ResN…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号