ES實(shí)現(xiàn)GEO位置搜索
Elasticsearch-7.15.2
附近查詢,也叫做距離查詢(geo_distance):查詢到指定中心點(diǎn)小于某個(gè)距離值的所有文檔。
創(chuàng)建索引 (my_geo),直接設(shè)置mapping
GEO字段的創(chuàng)建:添加一個(gè)字段location,類型為 geo_point。
GEO類型的字段是不能使用動態(tài)映射自動生成的,我們需要在創(chuàng)建索引時(shí)指定字段的類型為geo_point,geo_point 類型的字段存儲的經(jīng)緯度。
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條數(shù)據(jù)
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
}
}'
查詢語句 curl
我的位置在“工體”,“北京站”的路人甲和“朝陽公園”的路人乙都在5km的范圍內(nèi),查詢5km和3km范圍內(nèi)都有誰。
把范圍縮短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}
}
}
}
}}'
結(jié)果:在“朝陽公園”的路人乙被搜索了出來。
{
"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公里范圍內(nèi)排序查詢。
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} // 當(dāng)前緯度 經(jīng)度
}
}
}
},
"sort": [
{
"_geo_distance": { // _geo_distance代表根據(jù)距離排序
"location": { // 根據(jù)location存儲的經(jīng)緯度計(jì)算距離
"lat": 39.93031708627304, // 當(dāng)前緯度 經(jīng)度
"lon": 116.4470385453491
},
"order": "asc"
}
}
]
}'
curl查詢結(jié)果:離我“工體”比較近的“路人乙”排在了第一個(gè),也是符合預(yù)期的。
{
"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
]
}
]
}}
JAVA程序中使用GEO搜索
在定義實(shí)體類時(shí),對應(yīng)的GEO字段要使用特殊的類型。location的類型是GeoPoint,添加數(shù)據(jù)時(shí)轉(zhuǎn)成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;
}
geo距離查詢
public void geoDistanceQuery(){
//創(chuàng)建查詢請求對象
SearchRequest request = new SearchRequest("my_geo");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
GeoPoint geoPoint = new GeoPoint(39.93031708627304, 116.4470385453491);//工體的坐標(biāo)
//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();
}
}
結(jié)果:
{"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"); //創(chuàng)建查詢請求對象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
GeoPoint geoPoint = new GeoPoint(39.93031708627304, 116.4470385453491);//工體的坐標(biāo)
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());
}
結(jié)果:
{"name":"路人乙","location":{"lat":39.93367367974064,"lon":116.47845257733152}}
{"name":"路人甲","location":{"lat":39.90279998006104,"lon":116.42703999493406}}
其他
距離排序(帶分頁)
GeoDistanceQueryBuilder
/**
* ElasticSearchRepository和 RestHighLevelClient ElasticsearchRestTemplate的區(qū)別
* https://blog.csdn.net/zhiyikeji/article/details/128908596
*
* 從名字就能看出來,QueryBuilder主要用來構(gòu)建查詢條件、過濾條件,SortBuilder主要是構(gòu)建排序。
* 譬如,我們要查詢距離某個(gè)位置100米范圍內(nèi)的所有人、并且按照距離遠(yuǎn)近進(jìn)行排序:
*/
public void findGeoDistanceSort(){
double lat = 39.93031708627304, lng = 116.4470385453491; //工體
//設(shè)定搜索半徑
GeoDistanceQueryBuilder queryBuilder = QueryBuilders.geoDistanceQuery("location")
//.geoDistance(GeoDistance.PLANE)
.point(lat, lng).distance(300, DistanceUnit.KILOMETERS);
//計(jì)算距離多少公里 獲取點(diǎn)與點(diǎn)之間的距離
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.SearchHits<MyGeo> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, MyGeo.class);
List<org.springframework.data.elasticsearch.core.SearchHit<MyGeo>> searchHitList = searchHits.getSearchHits();
if(searchHitList.isEmpty()){
System.out.println("沒有查詢到數(shù)據(jù)!");
return;
}
searchHitList.forEach(hit ->{
// 此處的索引和查詢返回結(jié)果中sort集合的索引一致,目的在于取返回結(jié)果中的距離計(jì)算結(jié)果,以免二次計(jì)算,造成資源浪費(fèi)
//Object geoDistance = hit.getSortValues().get(2);
System.out.println("hit -- " + JSONObject.toJSONString(hit));
});
}
結(jié)果:
{"name":"路人乙","location":{"lat":39.93367367974064,"lon":116.47845257733152}}
{"name":"路人甲","location":{"lat":39.90279998006104,"lon":116.42703999493406}}
參考資料
ES7學(xué)習(xí)筆記(十三)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
###復(fù)雜查詢包含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 經(jīng)緯度按距離排序 http://www.javashuo.com/article/p-uqiafsey-hx.html文章來源:http://www.zghlxwxcb.cn/news/detail-793945.html
ES 位置查詢之geo_point
https://blog.csdn.net/weixin_43918355/article/details/118366065文章來源地址http://www.zghlxwxcb.cn/news/detail-793945.html
到了這里,關(guān)于Elasticsearch ES實(shí)現(xiàn)GEO位置搜索的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!