在我們使用elasticsearch創(chuàng)建索引時葱轩,經(jīng)常會遇到一種字段類型為geo_point
的數(shù)據(jù)類型,該類型的字段接收經(jīng)緯度的值,那么geo_point
類型的字段可以用來做什么靴拱?
- 基于Geo的地理位置范圍查詢
- 基于Geo范圍內(nèi)到中心點距離的聚合統(tǒng)計
- 加入到相關性得分計算中
- 基于Geo地理位置信息到中心點距離的排序
通過閱讀本文垃喊,可以學到以上知識點,學到就是賺到袜炕,還不快快開始閱讀吧
環(huán)境
- MacOS 10.14.6
- Elasticsearch 8.1
- Kibana 8.1
幫助信息
latitude: 緯度;longitude: 經(jīng)度;
-
獲取地理位置坐標網(wǎng)站本谜,看自己喜好
-
geohash生成
如何獲取一個地理位置的geohash以及根據(jù)geohash獲取經(jīng)緯度
根據(jù)地理位置信息查看地圖上展示
Geopoint(點)
如何指定一個地理位置信息
Elasticsearch中geo_point
類型的數(shù)據(jù)字段支持以下5種方式錄入地理位置數(shù)據(jù),分別如下:
首先我們還是先定義一個索引偎窘,創(chuàng)建一個數(shù)據(jù)類型為geo_point
的字段
-
創(chuàng)建索引
PUT my-index-000001 { "mappings": { "properties": { "text":{ "type": "text" }, "location":{ "type": "geo_point" } } } }
-
作為一個對象傳入位置信息
PUT my-index-000001/_doc/1 { "text": "Geopoint as an object", "location": { "lat": 41.12, "lon": -71.34 } }
-
字符串形式寫入
"lat,lon"
PUT my-index-000001/_doc/2 { "text": "Geopoint as a string", "location": "41.12,-71.34" }
-
geohash形式寫入
PUT my-index-000001/_doc/3 { "text": "Geopoint as a geohash", "location": "drm3btev3e86" }
-
數(shù)組
[lon,lat]
PUT my-index-000001/_doc/4 { "text": "Geopoint as an array", "location": [ -71.34, 41.12 ] }
-
POINT 文本
"POINT(lon lat)"
PUT my-index-000001/_doc/5 { "text": "Geopoint as a WKT POINT primitive", "location" : "POINT (-71.34 41.12)" }
-
基于一定地理位置邊界的查詢
GET my-index-000001/_search { "query": { "geo_bounding_box": { "location": { "top_left": { "lat": 43, "lon": -72 }, "bottom_right": { "lat": 40, "lon": -74 } } } } }
通過上面的例子可以看出
geo_point
類型的字段可以同時接收五種參數(shù)形式的數(shù)據(jù)寫入乌助,并且絲毫不影響查詢,就是這么強大陌知,還不快用起來他托,溫馨提醒,千萬不要為了用而用哦
geo_point 類型字段支持的參數(shù)
-
ignore_malformed
默認
false
,寫入文檔時遇到不符合規(guī)范的地理位置信息字段值會拋出異常纵诞,如果為true
上祈,則會忽略。如果設置了script
字段浙芙,該字段將不能使用 -
ignore_z_value
默認
true
登刺,三維空間信息將被記錄,但是只有經(jīng)緯度的值能被索引嗡呼,如果為false
纸俭,寫入文檔時如果有超過二維的信息,拋出異常拒絕文檔寫入 -
index
是否可以被索引,默認
true
,如果為false
,未建立索引的字段將不能被檢索 -
null_value
默認值為
null
颊咬,如果設置為null
,則意味著該字段是不存在的窒悔,如果使用了script
,則該字段不可被設置 -
on_script_error
只有設置了
script
字段的時候才可以設置該字段敌买,定義使用腳步發(fā)生錯誤時如何處理简珠,默認fail
,拒絕整個文檔虹钮,但是會繼續(xù)聋庵,在元數(shù)據(jù)字段中注冊該字段 -
script
腳本字段,如果設置了該值芙粱,將索引該腳本執(zhí)行之后的結果祭玉,如果在寫文檔的時候給該字段設置了值,文檔將會被拒絕春畔。腳本的格式與運行時相同脱货,應該是一對
(lat,lon)
雙值的形式
在腳本中使用geopoints
當在腳本中訪問geopoint
類型的值時岛都,該值做為GeoPoint
對象返回,可以使用如下方式讀取
def geopoint = doc['location'].value;
def lat = geopoint.lat;
def lon = geopoint.lon;
但是更推下如下這樣讀取
def lat = doc['location'].lat;
def lon = doc['location'].lon;
Geoshape(形狀)
對幾何形狀進行索引和查詢(矩形和多邊形)蹭劈,當索引的數(shù)據(jù)和查詢包含除了點以外的形狀時應該使用geo_shape
類型
通過上面這倆類型的描述疗绣,我們可以得出一個結論线召,就是geo_point
表示一個點铺韧,geo_shape
表示多個點連接線組成的形狀
支持的映射參數(shù)
geo_shape
映射將GeoJSON
幾何對象映射到geo_shape
,要是用該映射類型缓淹,必須顯式的設置哈打,這句話意思就是我們使用該數(shù)據(jù)類型的時候必須明確指定索引類型
-
orientation
可選參數(shù),WKT多邊形的默認方向讯壶,默認
RIGHT
逆時針料仗。LEFT
順時針如果要指定
RGIHT
可以使用如下值設置- right
- counterclockwise
- ccw
如果要指定
LEFT
可以使用如下值設置- left
- clockwise
- cw
WKT(Well-known text)是一種文本標記語言,用于表示矢量幾何對象伏蚊、空間參照系統(tǒng)及空間參照系統(tǒng)之間的轉換
-
ignore_malformed
默認
false
立轧,如果是不符合GeoJSON
或者WKT
的格式拋出異常,如果為true
躏吊,則忽略 -
ignore_z_value
默認
true
氛改,三維空間信息將被記錄,但是只有經(jīng)緯度的值能被索引比伏,如果為false
胜卤,寫入文檔時如果有超過二維的信息,拋出異常拒絕文檔寫入 -
Coerce
默認
false
赁项,如果為true
葛躏,如果是非封閉的多邊形將自動閉合形成封閉的多邊形
創(chuàng)建索引
PUT /example
{
"mappings": {
"properties": {
"location": {
"type": "geo_shape"
}
}
}
}
輸入類型
地理位置形狀信息可以使用GeoJSON
或者WKT
表示,下面是GeoJSON
悠菜,WKT
與Elasticsearch
中類型的對應關系
GeoJSON Type | WKT Type | Elasticsearch Type | description |
---|---|---|---|
Point | POINT | Point | 單一的經(jīng)緯度坐標 |
LineString | LINTSTRING | lingstring | 給定兩個點或多個點組成的任意直線 |
Polygon | POLYGON | polygon | 封閉的多邊形舰攒,第一個點和最后一個點必須匹配,也就是n+1 個點形成的n 邊多邊形悔醋,至少4 個頂點 |
MultiPoint | MULTIPOINT | multipoint | 一組不相連但是可能相關的點 |
MultiLineString | MULTILINESTRING | multilinestring | 單獨的行字符串組成的數(shù)組 |
MultiPolygon | MULTIPOLYGON | multipolygon | 單獨的多邊形數(shù)組 |
GeometryCollection | GEOMETRYCOLLECTION | geometrycollection | 與multi開頭的類型類似的一個GeoJSON形狀摩窃,但是多個類型可以共同存在比如(Point和LineString同時存在) |
N/A | BBOX | envelope | 僅指定左上角和右下角兩個點組成的邊框或包 |
對于所有的類型都應該有子類型和字段坐標,在GeoJSON
篙顺、WKT
以及Elasticsearch
中偶芍,坐標信息都是(經(jīng)度,緯度)德玫,這與很多的地理信息API是不同的匪蟀,地圖地理信息API通常使用(緯度,經(jīng)度)宰僧,這一點是需要注意的一個地方材彪,下面我將針對以上集中類型情況分別做一個寫入數(shù)據(jù)的測試
-
點
點的話比較簡單,就比如手機定位,或者某個建筑的位置信息
# GeoJSON POST /example/_doc { "location" : { "type" : "Point", "coordinates" : [-77.03653, 38.897676] } } # WKT POST /example/_doc { "location" : "POINT (-77.03653 38.897676)" }
-
線
由兩個或者多個數(shù)組以上定義的行字符串段化,通過指定兩個點嘁捷,linestring將表示一條線,兩個以上的點表示任意路徑
# GeoJSON POST /example/_doc { "location" : { "type" : "LineString", "coordinates" : [[-77.03653, 38.897676], [-77.009051, 38.889939]] } } # WKT POST /example/_doc { "location" : "LINESTRING (-77.03653 38.897676, -77.009051 38.889939)" }
-
多邊形
多邊形是由多個點定義的显熏,列表的第一個點和最后一個點必須相同
# GeoJSON POST /example/_doc { "location" : { "type" : "Polygon", "coordinates" : [ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] ] } } # WKT POST /example/_doc { "location" : "POLYGON ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0))" }
上面這個定義的是一個多邊形雄嚣,下面定義一個帶洞的多邊形,下面例子中喘蟆,第一個數(shù)組代表多邊形外部邊界缓升,第二個數(shù)組代表內(nèi)部洞的邊界
# GeoJSON POST /example/_doc { "location" : { "type" : "Polygon", "coordinates" : [ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ], [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ] ] } } # WKT POST /example/_doc { "location" : "POLYGON ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0), (100.2 0.2, 100.8 0.2, 100.8 0.8, 100.2 0.8, 100.2 0.2))" }
-
多邊形的方向
多邊形的方向也就是頂點的順序,有
Right
逆時針和Left
順時針蕴轨,Elasticsearch使用多邊形的方向來確認是否跨越了國際日期線(+-180度)因為WKT沒有指定或者強制默認方向港谊,我們可以使用方向映射的參數(shù)
orientation
來設置多邊形的方向GeoJSON默認使用Right的方向,與參數(shù)
orientation
設置無關,GeoJSON規(guī)范要求外部多邊形使用逆時針,內(nèi)部多邊形使用順時針但是可以使用文檔級別的參數(shù)
orientation
覆蓋來重寫GeoJSON的默認方向,如下所示多邊形方向就是Left
POST /example/_doc { "location" : { "type" : "Polygon", "orientation" : "LEFT", "coordinates" : [ [ [-177.0, 10.0], [176.0, 15.0], [172.0, 0.0], [176.0, -15.0], [-177.0, -10.0], [-177.0, 10.0] ] ] } }
Elasticsearch 僅使用多邊形的方向來確定是否跨越了國際日期線橙弱,如果一個多邊形的最小經(jīng)度與最大經(jīng)度差值小于180歧寺,那多邊形就沒有跨越國際日期線,對多邊形的方向也就不會有影響
如果多邊形的最小經(jīng)度與最大經(jīng)度差值等于180或者大于180棘脐,則Elasticsearch會檢查文檔級別的orientation參數(shù)方向與默認的方向是否不同斜筐,如果方向不同,Elasticsearch會在跨越國際日期線的地方分割多邊形
-
點的列表
# GeoJSON POST /example/_doc { "location" : { "type" : "MultiPoint", "coordinates" : [ [102.0, 2.0], [103.0, 2.0] ] } } # WKT POST /example/_doc { "location" : "MULTIPOINT (102.0 2.0, 103.0 2.0)" }
-
線的列表
# GeoJSON POST /example/_doc { "location" : { "type" : "MultiLineString", "coordinates" : [ [ [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0] ], [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0] ], [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8] ] ] } } #WKT POST /example/_doc { "location" : "MULTILINESTRING ((102.0 2.0, 103.0 2.0, 103.0 3.0, 102.0 3.0), (100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0), (100.2 0.2, 100.8 0.2, 100.8 0.8, 100.2 0.8))" }
-
多個多邊形
如下代表兩個多邊形荆残,其中第二個多邊形包含一個洞
# GeoJSON POST /example/_doc { "location" : { "type" : "MultiPolygon", "coordinates" : [ [ [[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]] ], [ [[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]], [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]] ] ] } } # WKT POST /example/_doc { "location" : "MULTIPOLYGON (((102.0 2.0, 103.0 2.0, 103.0 3.0, 102.0 3.0, 102.0 2.0)), ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0), (100.2 0.2, 100.8 0.2, 100.8 0.8, 100.2 0.8, 100.2 0.2)))" }
-
集合對象
包含
Point
和LineString
的例子# GeoJSON POST /example/_doc { "location" : { "type": "GeometryCollection", "geometries": [ { "type": "Point", "coordinates": [100.0, 0.0] }, { "type": "LineString", "coordinates": [ [101.0, 0.0], [102.0, 1.0] ] } ] } } # WKT POST /example/_doc { "location" : "GEOMETRYCOLLECTION (POINT (100.0 0.0), LINESTRING (101.0 0.0, 102.0 1.0))" }
-
envelope
指定對角線的多邊形奴艾,格式為
[[minLon,maxLat],[maxLon,minLat]]
POST /example/_doc { "location" : { "type" : "envelope", "coordinates" : [ [100.0, 1.0], [101.0, 0.0] ] } }
下面使用WKT的BBOX類型,WKT的格式順序為
minLon内斯,maxLon蕴潦,maxLat,minLat
POST /example/_doc { "location" : "BBOX (100.0, 102.0, 2.0, 0.0)" }
-
圓
GeoJSON
和WKT
都不支持一個點的半徑圓類型俘闯,但是可以使用circle ingest processor
來近似的模擬一個圓來作為一個多邊形
排序和檢索
由于形狀的輸入結構復雜度和索引表示形狀的復雜性潭苞,目前不能對索引進行排序或者直接檢索他們的字段,目前geo_shape
只能通過_source
來檢索
基于地理位置信息的范圍查詢
參考:https://kucw.github.io/blog/2019/12/elasticsearch-geo-point/
Geo-bounding box query(矩形過濾)
https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-geo-bounding-box-query.html
可以對數(shù)據(jù)類型為geo_point
和geo_shape
的值在矩形邊框內(nèi)進行檢索真朗,找出落在矩形內(nèi)的點
Geopoint 值的匹配
下面是一個演示一個點在一個矩形邊框范圍的一個例子此疹,首先還是創(chuàng)建一個索引
-
創(chuàng)建索引
索引包含兩個字段,
desc
為地點的描述信息遮婶,pin.location
為具體的地理位置信息PUT /my_locations { "mappings": { "properties": { "desc":{ "type": "text" }, "pin": { "properties": { "location": { "type": "geo_point" } } } } } }
- 插入一條測試數(shù)據(jù)蝗碎,使用地圖獲取故宮博物院的坐標信息(位置信息與截圖稍微有微微的偏差,為后期補充圖片)
PUT /my_locations/_doc/1
{
"desc":"故宮博物院",
"pin": {
"location": {
"lat": 39.92375,
"lon": 116.40348
}
}
}
- 定義下圖所示的邊界范圍旗扑,矩形框內(nèi)查詢,
top_left
就是頂點1蹦骑,bottom_right
就是頂點2
GET my_locations/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_bounding_box": {
"pin.location": {
"top_left": {
"lat": 39.93872,
"lon": 116.37958
},
"bottom_right": {
"lat": 39.90924,
"lon": 116.42475
}
}
}
}
}
}
}
查看返回結果可以獲取到剛才插入的故宮博物院地點信息
Geoshape 值的匹配
-
新建索引
PUT /my_geoshapes { "mappings": { "properties": { "desc":{ "type": "text" }, "pin": { "properties": { "location": { "type": "geo_shape" } } } } } }
-
插入測試數(shù)據(jù),如下數(shù)據(jù)在地圖展示如下,5個地理位置信息為圖中矩形的頂點臀防,順時針第一個和最后一個保持相同如下地理位置信息數(shù)據(jù)的順序依次為[1,2,3,4,1]
PUT /my_geoshapes/_doc/1 { "desc":"故宮博物院", "pin": { "location": { "type" : "polygon", "coordinates" : [[[116.39801,39.92905], [116.40774,39.92905], [116.40936,39.92063], [116.39761,39.92000], [116.39801,39.92905]]] } } }
-
查詢范圍內(nèi)形狀匹配的值
GET my_geoshapes/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_bounding_box": { "pin.location": { "top_left": { "lat": 39.93872, "lon": 116.37958 }, "bottom_right": { "lat": 39.90794, "lon": 116.42486 } } } } } } }
上面的
top_left
對應的就是圖中的頂點1眠菇,bottom_right
對應的就是圖中的頂點2
Geo-distance query(圓形過濾)
以給定位置作圓點边败,畫一個給定距離的圓,以匹配圓范圍內(nèi)的文檔數(shù)據(jù)捎废,下面我們用例子來說明這個圓形過濾
-
創(chuàng)建索引,
my_locations
測試geo_point
,my_geoshapes
測試geo_shape
PUT /my_locations { "mappings": { "properties": { "desc":{ "type": "text" }, "pin": { "properties": { "location": { "type": "geo_point" } } } } } } PUT /my_geoshapes { "mappings": { "properties": { "desc":{ "type": "text" }, "pin": { "properties": { "location": { "type": "geo_shape" } } } } } }
-
插入一個測試數(shù)據(jù)笑窜,地點信息為西單大悅城(經(jīng)緯度:116.37927 , 39.91706)
PUT /my_locations/_doc/1 { "desc":"西單大悅城", "pin": { "location": { "lat":39.91706 , "lon": 116.37927 } } } PUT /my_geoshapes/_doc/1 { "desc":"西單大悅城", "pin": { "location": { "type" : "polygon", "coordinates" : [[[116.37855,39.91761], [116.38018,39.91756], [116.38024,39.91651], [116.37862,39.91636 ], [116.37855,39.91761]]] } } }
-
查詢故宮博物院周邊
5公里
的文檔信息GET /my_locations/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_distance": { "distance": "5km", "pin.location": { "lat": 39.92402, "lon": 116.4036 } } } } } } GET my_geoshapes/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_distance": { "distance": "5km", "pin.location": { "lat": 39.92402, "lon": 116.40360 } } } } } }
查看結果可以得到距離為周邊3公里內(nèi)是搜索不到的,大于3公里就可以搜索到我們定義的西單大悅城的地點信息
看下面例子登疗,同時在
my_locations
和my_geoshapes
兩個索引中搜索故宮博物院周邊5公里內(nèi)的地點信息GET my_locations,my_geoshapes/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_distance": { "distance": "5km", "pin.location": { "lat": 39.92402, "lon": 116.40360 } } } } } }
擴展知識
在上文中我們也看到了排截,錄入測試數(shù)據(jù)的方式支持多種,在使用過濾器查詢數(shù)據(jù)的時候同樣如此可以使用多種方式進行過濾谜叹,如下簡單舉例幾種匾寝,比如
-
使用數(shù)組形式(
經(jīng)度,緯度
)GET /my_locations/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_distance": { "distance": "5km", "pin.location": [ 116.40360,39.92402 ] } } } } }
-
使用字符串(
緯度荷腊,經(jīng)度
)GET /my_locations/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_distance": { "distance": "5km", "pin.location": "39.92402,116.40360" } } } } } GET /my_geoshapes/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_distance": { "distance": "5km", "pin.location": "39.92402,116.40360" } } } } }
-
使用geohash
geohash
生成可以參考文章開頭幫助信息中生成geohash
的網(wǎng)站,也可以復制如下鏈接到瀏覽器打開急凰,任選其一即可GET my_locations,my_geoshapes/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_distance": { "distance": "5km", "pin.location": "wx4g0gfw22x" } } } } }
過濾查詢接收的參數(shù)說明
-
distance
以指定的點為圓心女仰,以
distance
的值為半徑畫圓,匹配圓內(nèi)的地點信息數(shù)據(jù)抡锈,距離單位默認米(m),其他距離單位可以參考官網(wǎng)https://www.elastic.co/guide/en/elasticsearch/reference/8.1/api-conventions.html#distance-units
-
distance_type
計算距離的方式疾忍,也就是
distance
生效的方式,默認是上述所說的為圓也就是弧形計算(arc
)床三,另一個可選值是平面的(plane
)一罩,效率比arc
快,但是精度有所損失撇簿,尤其是distance
的值很大時或者兩極處聂渊。plane
比喻地球是平的,在赤道附近時精度是最佳的四瘫,靠近兩級時精度略有損失汉嗽。比如想找附近5km
的地點,然后找到了5.2km
的地點找蜜,這個誤差取決于我們是否可以接受 -
_name
該查詢語句的名稱
-
validation_method
是否接受錯誤的地點坐標信息
-
STRICT
默認值
饼暑,拋出異常 -
COERCE
嘗試修正錯誤坐標信息為正確的坐標 -
IGNORE_MALFORMED
允許無效的錯誤的地點坐標信息
-
geo_distance
的過濾請求可以匹配一篇文檔中的多個地點值,只要有一個地點值匹配洗做,該文檔就會被返回
ignore_unmapped
字段的值設置為true
弓叛,查詢時如果字段不匹配
,會忽略報錯诚纸,返回空文檔
撰筷;如果設置為false
,遇到不匹配的字段會 拋出異常
如下示例咬清,匹配pin.location1
字段闭专,我們知道該字段在索引中是不存在的奴潘,所以當ignore_unmapped
設置為true
時返回空文檔,設置為false
時拋出異常
GET my_locations,my_geoshapes/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_distance": {
"ignore_unmapped":true,
"_name":"zuiyuquery",
"distance": "5km",
"pin.location1": "wx4g0gfw22x"
}
}
}
}
}
Geo-polygon query(自定義多邊形過濾)
https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-geo-polygon-query.html
多邊形過濾影钉,顧名思義就是查詢條件定義個多邊形画髓,返回該多邊形區(qū)域內(nèi)的點的文檔信息
官網(wǎng)已經(jīng)說明es7.12版本開始廢棄,推薦使用geoshape平委,參考后文的 geoshape query
該查詢接收的參數(shù)有如下
-
_name
該查詢語句的名稱
-
validation_method
是否接受錯誤的地點坐標信息
-
STRICT
默認值
奈虾,拋出異常 -
COERCE
嘗試修正錯誤坐標信息為正確的坐標 -
IGNORE_MALFORMED
允許無效的錯誤的地點坐標信息
-
需要注意的是,查詢的字段需要設置為geo_point
類型,同樣ignore_unmapped
字段的值設置為true
廉赔,查詢時如果字段不匹配
肉微,會忽略報錯,返回空文檔
蜡塌;如果設置為false
碉纳,遇到不匹配的字段會 拋出異常
-
我們先插入一條故宮位置的信息文檔
PUT /my_locations/_doc/2 { "desc":"故宮", "pin":{ "location":"39.92307,116.40291" } }
如下是對pin.location
字段定義一個三條邊的形狀的過濾查詢語句,三個點順序【1,2馏艾,3】排列,此時該 查詢會返回故宮的信息劳曹,但是剛才定的西單大悅城的信息就不會返回
GET my_locations/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_polygon": {
"pin.location": {
"points": [
{ "lat": 39.93506, "lon": 116.41043 },
{ "lat": 39.91101, "lon": 116.42383 },
{ "lat": 39.92217, "lon": 116.35836 }
]
}
}
}
}
}
}
同樣的自定義查詢語句也可以接收【字符串,數(shù)組,geohash】的地點信息
-
字符串
# 字符串 GET my_locations/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_polygon": { "pin.location": { "points": [ "39.93506,116.41043","39.91101,116.42383","39.92217,116.35836" ] } } } } } }
-
數(shù)組
# 數(shù)組 GET my_locations/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_polygon": { "pin.location": { "points": [ [116.41043,39.93506], [116.42383,39.91101], [116.35836,39.92217] ] } } } } } }
-
geohash
# geohash GET my_locations/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_polygon": { "pin.location": { "points": [ "wx4g0vzqxdg", "39.91101,116.42383", "39.92217,116.35836" ] } } } } }
Geoshape query(形狀查詢)
https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-geo-shape-query.html
支持geo_shape和geo_point兩種字段類型的過濾查詢
形狀查詢琅摩,支持傳入一個完整圖形形狀的數(shù)據(jù)或者提前寫入的圖形形狀數(shù)據(jù)铁孵,首先還是先演示一下如何使用傳入的圖形形狀數(shù)據(jù)檢索
輸入圖形形狀查詢
-
定義索引,
geo_shape
字段PUT /example { "mappings": { "properties": { "location": { "type": "geo_shape" } } } }
-
插入測試數(shù)據(jù)
POST /example/_doc?refresh { "name": "東單", "location": { "type": "point", "coordinates": [ 116.426466,39.914743] } } POST /example/_doc?refresh { "name": "天安門東", "location": { "type": "point", "coordinates": [ 116.40771,39.914079] } } POST /example/_doc?refresh { "name": "天安門西", "location": { "type": "point", "coordinates": [ 116.397936,39.913802] } } POST /example/_doc?refresh { "name": "西單", "location": { "type": "point", "coordinates": [ 116.383132,39.913581] } }
-
過濾檢索,返回文檔中形狀與檢索形狀相交的文檔數(shù)據(jù),如下查詢語句返回經(jīng)度
116-117
之間房资,緯度39-40
之間的文檔GET /example/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_shape": { "location": { "shape": { "type": "envelope", "coordinates": [ [116.0,40.0 ], [ 117.0,39.0 ] ] }, "relation": "within" } } } } } }
-
下面是geo_point類型字段的demo
PUT /example_points { "mappings": { "properties": { "location": { "type": "geo_point" } } } } PUT /example_points/_doc/1?refresh { "name": "Wind & Wetter, Berlin, Germany", "location": [13.400544, 52.530286] } PUT /example_points/_doc/1?refresh { "name": "東單", "location": [116.426466,39.914743] } PUT /example_points/_doc/2?refresh { "name": "天安門東", "location": [116.40771,39.914079] } PUT /example_points/_doc/3?refresh { "name": "天安門西", "location": [116.397936,39.913802] } PUT /example_points/_doc/4?refresh { "name": "西單", "location": [116.383132,39.913581] } GET /example_points/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_shape": { "location": { "shape": { "type": "envelope", "coordinates": [ [ 116.0, 40.0 ], [ 117.0, 39.0 ] ] }, "relation": "intersects" } } } } } }
通過上面兩個不同字段的檢索demo蜕劝,我們應該也發(fā)現(xiàn)了,relation這個的值我們竟然使用的不一樣轰异,那么他們有什么區(qū)別呢岖沛,各自代表的含義如下:
-
INTERSECTS
默認值
,返回字段類型為geo_shape
或者geo_point
與檢索的圖形相交
的文檔 -
DISJOINT
返回字段類型為
geo_shape
或者geo_point
與檢索圖形不相交
的文檔 -
WITHIN
返回字段類型為
geo_shape
或者geo_point``在檢索圖形范圍內(nèi)
的文檔溉浙,不支持線的幾何圖形 -
CONTAINS
返回文檔中字段類型為
geo_shape
或者geo_point
的信息包含檢索圖形形狀
的文檔
預置圖形形狀查詢
預置就是提前設置好圖形的數(shù)據(jù)烫止,保存在索引中,查詢時根據(jù)提前定義好圖形的名稱來過濾數(shù)據(jù)戳稽,首先它有如下幾個參數(shù)
- id 預索引形狀的id
- index 預索引形狀所在的索引位置馆蠕,默認
shapes
- path 預索引形狀所在索引中的字段名稱,默認
shape
- routing 可選惊奇,預索引形狀的路由參數(shù)
查詢示例如下
PUT /shapes
{
"mappings": {
"properties": {
"location": {
"type": "geo_shape"
}
}
}
}
PUT /shapes/_doc/oneline
{
"location": {
"type": "envelope",
"coordinates" : [[116.0, 40.0], [ 117.0, 39.0]]
}
}
GET /example/_search
{
"query": {
"bool": {
"filter": {
"geo_shape": {
"location": {
"indexed_shape": {
"index": "shapes",
"id": "oneline",
"path": "location"
}
}
}
}
}
}
}
基于地理位置信息或者到中心點距離的聚合統(tǒng)計
Geo網(wǎng)格聚合
網(wǎng)格聚合互躬,將字段類型為geo_point
或者geo_shape
的數(shù)據(jù)劃分為一個個的單元格,也就是一個個的單元格的桶中颂郎,既然是單元格那也就是有大有小吼渡,也就是高精確度與低精確度值,精度值的范圍為1-12
乓序,1
表示精確度最低寺酪,范圍最大坎背,12
表示精確度最高但是范圍也最小,甚至可能出現(xiàn)百萬個以上的桶的聚合寄雀,所以這個是需要注意的地方得滤,如果精確度要求比較高的話,可以使用geo_bounding box query
過濾縮小聚合范圍盒犹。網(wǎng)格聚合通過geohash
來實現(xiàn)懂更,通過指定聚合類型為geohash_grid
來實現(xiàn)網(wǎng)格聚合,下面是演示demo
geo_point
-
創(chuàng)建索引
PUT /gugong_map { "mappings": { "properties": { "location": { "type": "geo_point" } } } }
-
插入測試數(shù)據(jù)
POST /gugong_map/_bulk?refresh {"index":{"_id":1}} {"location": "39.91466,116.42633", "name": "東單"} {"index":{"_id":2}} {"location": "39.914105,116.407934", "name": "天安門東"} {"index":{"_id":3}} {"location": "39.913662,116.398232", "name": "天安門西"} {"index":{"_id":4}} {"location": "39.913441,116.3835", "name": "西單A"} {"index":{"_id":5}} {"location": "39.913385,116.380625", "name": "西單B"} {"index":{"_id":6}} {"location": "39.922184,116.380338", "name": "靈境胡同"}
-
聚合查詢
POST /gugong_map/_search?size=0 { "aggregations": { "large-grid": { "geohash_grid": { "field": "location", "precision": 5 } } } }
-
使用過濾條件的聚合
POST /gugong_map/_search?size=0 { "aggregations": { "zoomed-in": { "filter": { "geo_bounding_box": { "location": { "top_left": "39.930429,116.379619", "bottom_right": "39.909788,116.403981" } } }, "aggregations": { "zoom1": { "geohash_grid": { "field": "location", "precision": 8 } } } } } }
-
使用過濾參數(shù)的聚合
POST /gugong_map/_search?size=0 { "aggregations": { "tiles-in-bounds": { "geohash_grid": { "field": "location", "precision": 8, "bounds": { "top_left": "39.930429,116.379619", "bottom_right": "39.909788,116.403981" } } } } }
geo_shape
geo_shape
與geo_point
類似
支持參數(shù)
-
field
聚合的字段名稱急膀,必選字段
-
precision
精度值沮协,范圍從1-12
,也可以使用距離比如1km
卓嫂,10m
這種距離值可選
-
bounds
等價于geo_bounding box query
過濾邊界的參數(shù)值設置可選
-
size
返回聚合桶的數(shù)量可選
-
shard_size
為了對返回的聚合結果進行更精確的計算慷暂,該值為每個分片返回的聚合桶的數(shù)量,默認max(10,(size x number-of-shards))
的最大值
圓點距離聚合
給定一圓點命黔,以給出距離為半徑畫圓呜呐,將落在圓內(nèi)的數(shù)據(jù)按照到原點的距離聚合,我們可以定一個起始值悍募,一組范圍的值來規(guī)定聚合桶的范圍,下面跟我一起來看下如何使用到圓點距離的聚合
-
創(chuàng)建一個索引保存故宮周圍地鐵站的信息
PUT /gugong_map { "mappings": { "properties": { "location": { "type": "geo_point" } } } }
-
插入故宮周圍地鐵站信息
聚合 的字段必須顯示設置為
geo_point
類型,POST /gugong_map/_bulk?refresh {"index":{"_id":1}} {"location": [116.426259,39.91488], "name": "東單站"} {"index":{"_id":2}} {"location": [116.426259,39.91488], "name": "天安門東站"} {"index":{"_id":3}} {"location": [116.397873,39.913828], "name": "天安門西站"} {"index":{"_id":4}} {"location": [116.383284,39.913441], "name": "西單站"} {"index":{"_id":5}} {"location": [116.379979,39.922184], "name": "靈境胡同站"} {"index":{"_id":6}} {"location": [116.379979,39.922184], "name": "西四站"} {"index":{"_id":7}} {"location": [116.39313,39.939668], "name": "北海北站"} {"index":{"_id":8}} {"location": [116.410665,39.939944], "name": "南鑼鼓巷站"} {"index":{"_id":9}} {"location": [116.417061,39.92224], "name": "金魚胡同站"} {"index":{"_id":10}} {"location": [116.418067,39.916097], "name": "王府井站"}
-
根據(jù)地鐵站距離到故宮的距離進行聚合查詢
聚合的默認距離單位是
m
,我們可以通過unit
參數(shù)指定POST /gugong_map/_search?size=0 { "aggs": { "rings_around_amsterdam": { "geo_distance": { "field": "location", "origin": [116.403119,39.923568], "ranges": [ { "to": 1000 }, { "from": 1000, "to": 2000 }, { "from": 2000 } ] } } } }
-
通過指定
unit
距離單位聚合unit
支持mi
(miles),in
(inches),yd
(yards),km
(kilometers),cm
(centimeters),mm
(millimeters),m
(meters)POST /gugong_map/_search?size=0 { "aggs": { "rings": { "geo_distance": { "field": "location", "origin": [116.403119,39.923568], "unit": "m", "ranges": [ { "to": 1000 }, { "from": 1000, "to": 2000 }, { "from": 2000 } ] } } } }
-
指定聚合計算方式洋机,精確度
arc
坠宴,默認,精確度高plane
精確度低绷旗,距離僅時推薦使用喜鼓,比如5km
內(nèi)POST /gugong_map/_search?size=0 { "aggs": { "rings": { "geo_distance": { "field": "location", "origin": [116.403119,39.923568], "unit": "m", "distance_type": "plane", "ranges": [ { "to": 1000 }, { "from": 1000, "to": 2000 }, { "from": 2000 } ] } } } }
-
對聚合桶返回唯一的key
設置
keyed
為true
返回唯一的key
與聚合桶綁定,如果為false
返回的就是聚合桶的數(shù)組POST /gugong_map/_search?size=0 { "aggs": { "rings_around_amsterdam": { "geo_distance": { "field": "location", "origin": [ 116.403119, 39.923568 ], "ranges": [ { "to": 1000 }, { "from": 1000, "to": 2000 }, { "from": 2000 } ], "keyed": true } } } }
還可以
自定義聚合桶
的名稱POST /gugong_map/_search?size=0 { "aggs": { "rings_around_amsterdam": { "geo_distance": { "field": "location", "origin": [ 116.403119, 39.923568 ], "ranges": [ { "to": 1000, "key": "first_ring" }, { "from": 1000, "to": 2000, "key": "second_ring" }, { "from": 2000, "key": "third_ring" } ], "keyed": true } } } }
將地理位置信息加入到分數(shù)計算中
https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-function-score-query.html
- 首先字段必須是
geo_point
類型 - 提供一個圓心點的位置坐標
-
scale
和offset
必須設置
滿足以上三點就可以加入到分數(shù)計算中,gauss 使用高斯函數(shù)來衰減衔肢,具體參考官網(wǎng)庄岖,后面專門出一期衰減函數(shù)的文章
GET gugong_map/_search
{
"query": {
"function_score": {
"gauss": {
"location": {
"origin": [116.385728,39.870814],
"scale": "3000m",// origin + offset,衰減率角骤,也就得分衰減的速度
"offset": "3000m",// 3000m以內(nèi)的文檔得分不處理隅忿,3000m以外的文檔得分慢慢衰減
"decay": 0.5 // 從 origin 衰減到 scale 所得的評分_score,默認為0.5
}
}
}
}
}
基于距離信息的排序
https://www.elastic.co/guide/en/elasticsearch/reference/8.1/sort-search-results.html#geo-sorting
需要指定圓點的坐標信息邦尊,根據(jù)到原點的距離進行排序,比如如下語句背桐,查詢name
字段帶站
的,根據(jù)到北京南站
(116.385728,39.870814)的距離升序輸出,坐標信息蝉揍,與geo_point
支持的方式相同链峭,可以為數(shù)組
,字符串
又沾,對象
弊仪,geohash
_geo_distance
指定為geo類型字段排序的類型order
升序還是降序 熙卡,asc
、desc
unit
距離排序使用的單位励饵,默認m
mode
如果一個地點值的字段包含多個地理位置信息驳癌,怎么取值,如果升序排序時選最短距離曲横,降序排序時選最長距離的喂柒。支持min
,max
,median
,avg
distance_type
默認arc
精確度高,plane
速度快禾嫉,再靠近兩級附近或者距離遠時精度低-
ignore_unmapped
字段不存在映射時是否報錯灾杰,默認false
,未匹配時報錯熙参,如果為true
艳吠,等于使用unmapped_type
的字段排序地理位置排序時,如果文檔中沒有地理位置信息孽椰,距離被默認為
infinity
GET gugong_map/_search
{
"sort" : [
{
"_geo_distance" : {
"location" : [116.385728,39.870814],
"order" : "asc",
"unit" : "km",
"mode" : "min",
"distance_type" : "arc",
"ignore_unmapped": true
}
}
],
"query" : {
"term" : { "name" : "站" }
}
}
總結
在上面的章節(jié)中昭娩,我們由淺入深的學習了geo_point
和geo_shape
兩種geo
數(shù)據(jù)類型,以及如何通過對這兩字段進行檢索黍匾,聚合排序栏渺,相信大家讀到這也基本有了一個概念了,再深點還是要去看官方文檔了锐涯,不過實踐是檢驗真理的唯一標準磕诊,多實操吧,總沒有錯纹腌,加油v铡!升薯!
如果有寫的不對的地方歡迎指出哦@嘲!涎劈!共同進步才能走的更遠9阃埂!责语!