使用ES完成“离我最近”相关功能

文章目录

  • 前言
  • 准备工作
    • 1.构建索引
    • 2.插入数据
  • 完成需求
    • 1.根据经纬度进行距离排序并获取距离
    • 2.获取距离并筛选距离
  • 总结

前言

最近项目中有个需求是完成离我最近的功能,经过讨论决定使用ElasticSearch根据用户所在位置经纬度来完成相关需求,这里使用一个小的Demo来做记录。

准备工作

1.构建索引

创建索引location_test,索引只包含两个字段,一个景点名字另一个是景点经纬度,保存经纬度使用geo_point类型存储。

PUT http://localhost:9200/location_test/_mapping
{"properties":{"locationName":{"type":"text","analyzer":"ik_max_word"},"location":{"type":"geo_point"}}
}

2.插入数据

因为是Demo所以这里只插入七条上海的数据来进行演示。

POST http://localhost:9200/location_test/_doc
{"locationName": "召稼楼古镇","location": {"lat": 31.081128,"lon": 121.555511}
}
{"locationName": "上海奇思妙想减压馆","location": {"lat": 31.24249,"lon": 121.490283}
}
{"locationName": "上海JOYPOLIS世嘉都市乐园","location": {"lat": 31.239446,"lon": 121.419129}
}
{"locationName": "震旦博物馆","location": {"lat": 31.240324,"lon": 121.506166}
}
{"locationName": "江南三民文化村","location": {"lat": 31.723805,"lon": 121.504962}
}
{"locationName": "海湾3D错觉艺术馆","location": {"lat": 30.828349,"lon": 121.535577}
}
{"locationName": "上海迪士尼度假区","location": {"lat": 31.148267,"lon": 121.671964}
}

完成需求

接下来就是完成需求的过程了,主要是完成两块需求:获取距离并排序、根据距离进行筛选。

1.根据经纬度进行距离排序并获取距离

假设你现在正在外滩和女朋友散步(外滩经纬度:31.243453,121.497204),这时你们要找离你们最近的哪些景点可以去看看,使用ES可以很轻松的完成这一功能,ES的请求如下:

POST http://localhost:9200/location_test/_search
{"sort": [{"_geo_distance": {"location": [{"lat": 31.243453,"lon": 121.497204}],"unit": "m","order": "asc"}}]
}

这里使用 _geo_distance 关键字进行距离查询,存储文档经纬度的字段是localtion,location中的值就是你们现在位置的经纬度,unit指的是查询的单位,这里是用米,也可以使用千米(km)做单位,排序方式用升序。得到查询结果如下:

{"total":{"value":7,"relation":"eq"},"max_score":null,"hits":[{"_index":"location_test","_type":"_doc","_id":"Umg6oHQB29lS14JF6ljQ","_score":null,"_source":{"locationName":"上海奇思妙想减压馆","location":{"lat":31.24249,"lon":121.490283}},"sort":[666.6306788021509]},{"_index":"location_test","_type":"_doc","_id":"W2g7oHQB29lS14JFE1jZ","_score":null,"_source":{"locationName":"震旦博物馆","location":{"lat":31.240324,"lon":121.506166}},"sort":[920.3164557098329]},{"_index":"location_test","_type":"_doc","_id":"Wmg7oHQB29lS14JFAlgP","_score":null,"_source":{"locationName":"上海JOYPOLIS世嘉都市乐园","location":{"lat":31.239446,"lon":121.419129}},"sort":[7436.0041059579125]},{"_index":"location_test","_type":"_doc","_id":"8mg1oHQB29lS14JFUFBe","_score":null,"_source":{"locationName":"召稼楼古镇","location":{"lat":31.081128,"lon":121.555511}},"sort":[18883.13042012399]},{"_index":"location_test","_type":"_doc","_id":"RGg9oHQB29lS14JF5Vpq","_score":null,"_source":{"locationName":"上海迪士尼度假区","location":{"lat":31.148267,"lon":121.671964}},"sort":[19706.20435968952]},{"_index":"location_test","_type":"_doc","_id":"bWg7oHQB29lS14JFOlgq","_score":null,"_source":{"locationName":"海湾3D错觉艺术馆","location":{"lat":30.828349,"lon":121.535577}},"sort":[46302.09271440355]},{"_index":"location_test","_type":"_doc","_id":"XWg7oHQB29lS14JFJ1hP","_score":null,"_source":{"locationName":"江南三民文化村","location":{"lat":31.723805,"lon":121.504962}},"sort":[53417.844250374095]}]
}

查询结果把文档数据根据location中的经纬度进行计算排序,结果中的sort就是根据经纬度计算出来的距离,单位是米。可以看到目前离你们最近的是上海奇思妙想减压馆距离666米左右。

Java代码如下:

public void searchNearest() throws IOException {// 待查询的经纬度double lat = 31.243453;double lon = 121.497204;SearchRequest searchRequest = new SearchRequest("location_test");SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 使用GeoDistanceSortBuilder构建查询条件GeoDistanceSortBuilder distanceSortBuilder = new GeoDistanceSortBuilder("location", lat, lon);// 设置单位为米,升序排序distanceSortBuilder.unit(DistanceUnit.METERS).order(SortOrder.ASC);sourceBuilder.sort(distanceSortBuilder);SearchRequest request = searchRequest.source(sourceBuilder);SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);SearchHit[] hitResult = response.getHits().getHits();// 打印输出,距离在使用getSortValues()获取for (int i = 0; i < hitResult.length; i++) {BigDecimal distance = new BigDecimal(hitResult[i].getSortValues()[0].toString()).setScale(2, BigDecimal.ROUND_HALF_UP);System.out.println(hitResult[i].getSourceAsMap().get("locationName").toString()+"据你"+ distance +"米");}
}

使用Java代码计算经纬度主要是使用GeoDistanceSortBuilder来进行组织筛选条件,并且将组织好的GeoDistanceSortBuilder对象传入sourceBuilder.sort()中得到排序结果。上面结果打印出来的结果如下:
在这里插入图片描述

2.获取距离并筛选距离

在得到了周边景点距离后,你和你女朋友发现有些很远有些很近,想再筛选一下距离,比如一公里内的景点有哪些,使用ES同样可以完成这样的功能。
这里主要是在上面查询的基础上使用post_filter做后置过滤,在post_filter中使用distance关键字来决定筛选的距离。

{"sort":[{"_geo_distance":{"location":[{"lat":31.243453,"lon":121.497204}],"unit":"m","order":"asc"}}],"post_filter":{"geo_distance":{"distance":"1km","location":{"lat":31.243453,"lon":121.497204}}}
}

查询筛选结果如下:

{"total":{"value":2,"relation":"eq"},"max_score":null,"hits":[{"_index":"location_test","_type":"_doc","_id":"Umg6oHQB29lS14JF6ljQ","_score":null,"_source":{"locationName":"上海奇思妙想减压馆","location":{"lat":31.24249,"lon":121.490283}},"sort":[666.6306788021509]},{"_index":"location_test","_type":"_doc","_id":"W2g7oHQB29lS14JFE1jZ","_score":null,"_source":{"locationName":"震旦博物馆","location":{"lat":31.240324,"lon":121.506166}},"sort":[920.3164557098329]}]
}

可以看到在你们一公里以内的有两个景点可以去参观,于是你和女朋友就可以愉快的去参观了。使用Java来查询查询部分和上面大致相同,筛选使用GeoDistanceQueryBuilder构建距离筛选条件,并传入sourceBuilder的postFilter()方法中使用,相关Java代码如下:

public void searchFilter() throws IOException {double lat = 31.243453;double lon = 121.497204;SearchRequest request = new SearchRequest("location_test");SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 使用GeoDistanceSortBuilder构建查询条件GeoDistanceSortBuilder distanceSortBuilder = new GeoDistanceSortBuilder("location", lat, lon);// 设置单位为米,升序排序distanceSortBuilder.unit(DistanceUnit.METERS).order(SortOrder.ASC);// 构建筛选条件GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("location");distanceQueryBuilder.distance(1,DistanceUnit.KILOMETERS).point(lat,lon);sourceBuilder.sort(distanceSortBuilder).postFilter(distanceQueryBuilder);request.source(sourceBuilder);// 获取结果SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);SearchHit[] hitResult = response.getHits().getHits();System.out.println("一公里范围内有以下景点:");for (int i = 0; i < hitResult.length; i++) {BigDecimal distance = new BigDecimal(hitResult[i].getSortValues()[0].toString()).setScale(2, BigDecimal.ROUND_HALF_UP);System.out.println(hitResult[i].getSourceAsMap().get("locationName").toString()+",据你"+ distance +"米");}
}

上面代码打印的结果如下:
在这里插入图片描述

总结

使用ElasticSearch可以很好的完成经纬度的距离计算和查询筛选,但是使用ElasticSearch计算的距离是直线距离,如果要计算路线距离计算需要使用地图相关API进行计算了,并且使用ElasticSearch的话在数据增量同步的会稍微麻烦一点。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部