Elasticsearch-7.15.2
附近查询,也叫做距离查询(geo_distance):查询到指定中心点小于某个距离值的所有文档。
GEO字段的创建:添加一个字段location,类型为 geo_point。
GEO类型的字段是不能使用动态映射自动生成的,我们需要在创建索引时指定字段的类型为geo_point,geo_point 类型的字段存储的经纬度。
curl -X PUT http://192.168.11.21:9200/my_geo -H 'Content-Type:application/json' -d' { "mappings": { "properties": { "name": {"type": "text"}, "location": {"type":"geo_point"} } } }'
插入2条数据
curl -X POST 192.168.11.21:9200/my_geo/_doc/1 -H 'Content-Type: application/json' -d '{ "name": "路人甲北京站", "location": { "lat": 39.90279998006104, "lon": 116.42703999493406 } }' curl -X POST 192.168.11.21:9200/my_geo/_doc/2 -H 'Content-Type: application/json' -d '{ "name": "路人乙朝阳公园", "location": { "lat": 39.93367367974064, "lon": 116.47845257733152 } }'
我的位置在“工体”,“北京站”的路人甲和“朝阳公园”的路人乙都在5km的范围内,查询5km和3km范围内都有谁。
把范围缩短distance改为3km,请求如下:
curl -XGET '192.168.11.21:9200/my_geo/_search?pretty=true' -H 'Content-Type:application/json' -d ' { "query":{ "bool":{ "must":{"match_all":{ }}, "filter":{ "geo_distance":{ "distance":"3km", "location":{"lat": 39.93031708627304,"lon": 116.4470385453491} } } } }}'
结果:在“朝阳公园”的路人乙被搜索了出来。
{ "took" : 4, "timed_out" : false, "_shards" : {"total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0}, "hits" : {"total" : { "value": 1, "relation" : "eq"}, "max_score" : 1.0, "hits" : [ { "_index" : "my_geo", "_type" : "_doc", "_id" : "2", "_score" : 1.0, "_source" : { "name" : "路人乙朝阳公园", "location" : {"lat" : 39.93367367974064,"lon": 116.47845257733152} } } ] }}
5公里范围内排序查询。
curl -XGET 'http://192.168.11.21:9200/my_geo/_search?pretty=true' -H 'Content-Type:application/json' -d ' { "query":{ "bool":{ "must":{ "match_all":{ } }, "filter":{ "geo_distance":{ // 按距离搜索 "distance":"5km", // 搜索范围 "location":{"lat": 39.93031708627304,"lon": 116.4470385453491} // 当前纬度 经度 } } } }, "sort": [ { "_geo_distance": { // _geo_distance代表根据距离排序 "location": { // 根据location存储的经纬度计算距离 "lat": 39.93031708627304, // 当前纬度 经度 "lon": 116.4470385453491 }, "order": "asc" } } ] }'
curl查询结果:离我“工体”比较近的“路人乙”排在了第一个,也是符合预期的。
{ "took" : 10, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : null, "hits" : [ { "_index" : "my_geo", "_type" : "_doc", "_id" : "2", "_score" : null, "_source" : { "name" : "路人乙", "location" : { "lat" : 39.93367367974064, "lon" : 116.47845257733152 } }, "sort" : [ 2704.400492813901 ] }, { "_index" : "my_geo", "_type" : "_doc", "_id" : "1", "_score" : null, "_source" : { "name" : "路人甲", "location" : { "lat" : 39.90279998006104, "lon" : 116.42703999493406 } }, "sort" : [ 3503.0165324004943 ] } ] }}
在定义实体类时,对应的GEO字段要使用特殊的类型。location的类型是GeoPoint,添加数据时转成Json存储。
import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; import org.springframework.data.elasticsearch.annotations.GeoPointField; import org.springframework.data.elasticsearch.core.geo.GeoPoint; @Data @Document(indexName = "my_geo") public class MyGeo { @Field(type = FieldType.Keyword) private String goodsName; @Field(store = true) @GeoPointField private GeoPoint location; }
public void geoDistanceQuery(){ //创建查询请求对象 SearchRequest request = new SearchRequest("my_geo"); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); GeoPoint geoPoint = new GeoPoint(39.93031708627304, 116.4470385453491);//工体的坐标 //geo距离查询 QueryBuilder queryBuilder = QueryBuilders.geoDistanceQuery("location") .distance(5, DistanceUnit.KILOMETERS) .point(geoPoint); sourceBuilder.query(queryBuilder); request.source(sourceBuilder); try { SearchResponse response = client.search(request, RequestOptions.DEFAULT); for(SearchHit hit : response.getHits().getHits()){ System.out.println(hit.getSourceAsString()); } }catch (Exception e){ e.printStackTrace(); } } 结果: {"name":"路人甲","location":{"lat":39.90279998006104,"lon":116.42703999493406}} {"name":"路人乙","location":{"lat":39.93367367974064,"lon":116.47845257733152}}
public void geoDistanceSortQuery(){ SearchRequest request = new SearchRequest("my_geo"); //创建查询请求对象 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); GeoPoint geoPoint = new GeoPoint(39.93031708627304, 116.4470385453491);//工体的坐标 GeoDistanceSortBuilder sortBuilder = SortBuilders.geoDistanceSort("location", geoPoint).order(SortOrder.ASC); sourceBuilder.sort(sortBuilder); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); for(SearchHit hit : response.getHits().getHits()){ System.out.println(hit.getSourceAsString()); } 结果: {"name":"路人乙","location":{"lat":39.93367367974064,"lon":116.47845257733152}} {"name":"路人甲","location":{"lat":39.90279998006104,"lon":116.42703999493406}}
距离排序(带分页)
GeoDistanceQueryBuilder
/** * ElasticSearchRepository和 RestHighLevelClient ElasticsearchRestTemplate的区别 * https://blog.csdn.net/zhiyikeji/article/details/128908596 * * 从名字就能看出来,QueryBuilder主要用来构建查询条件、过滤条件,SortBuilder主要是构建排序。 * 譬如,我们要查询距离某个位置100米范围内的所有人、并且按照距离远近进行排序: */ public void findGeoDistanceSort(){ double lat = 39.93031708627304, lng = 116.4470385453491; //工体 //设定搜索半径 GeoDistanceQueryBuilder queryBuilder = QueryBuilders.geoDistanceQuery("location") //.geoDistance(GeoDistance.PLANE) .point(lat, lng).distance(300, DistanceUnit.KILOMETERS); //计算距离多少公里 获取点与点之间的距离 GeoDistanceSortBuilder sortBuilder = SortBuilders.geoDistanceSort("location", lat, lng) .point(lat, lng).unit(DistanceUnit.METERS).order(SortOrder.ASC); Pageable pageable = PageRequest.of(0, 10); NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder().withPageable(pageable) .withFilter(queryBuilder).withSort(sortBuilder); NativeSearchQuery nativeSearchQuery = builder.build(); org.springframework.data.elasticsearch.core.SearchHitssearchHits = elasticsearchRestTemplate.search(nativeSearchQuery, MyGeo.class); List > searchHitList = searchHits.getSearchHits(); if(searchHitList.isEmpty()){ System.out.println("没有查询到数据!"); return; } searchHitList.forEach(hit ->{ // 此处的索引和查询返回结果中sort集合的索引一致,目的在于取返回结果中的距离计算结果,以免二次计算,造成资源浪费 //Object geoDistance = hit.getSortValues().get(2); System.out.println("hit -- " + JSONObject.toJSONString(hit)); }); } 结果: {"name":"路人乙","location":{"lat":39.93367367974064,"lon":116.47845257733152}} {"name":"路人甲","location":{"lat":39.90279998006104,"lon":116.42703999493406}}
ES7学习笔记(十三)GEO位置搜索
https://www.modb.pro/db/73991
ES GEO地理空间查询 基于geo-point的多边形查询
https://huaweicloud.csdn.net/637eedd2df016f70ae4c9b19.html
通过ElasticsearchRestTemplate 完成地理搜索 矩形搜索,附近人搜索, 距离搜索
https://blog.csdn.net/qq_41712271/article/details/134881584
###复杂查询包含ES按距离排序
https://blog.csdn.net/m0_56726104/article/details/120785048
geo 距离排序检索
https://blog.csdn.net/wenxingchen/article/details/95448215/
GEO位置搜索 https://www.modb.pro/db/73991
ElasticsearchTemplate 经纬度按距离排序 http://www.javashuo.com/article/p-uqiafsey-hx.html
ES 位置查询之geo_point
https://blog.csdn.net/weixin_43918355/article/details/118366065