在我們使用elasticsearch創(chuàng)建索引時(shí),經(jīng)常會(huì)遇到一種字段類型為geo_point的數(shù)據(jù)類型,該類型的字段接收經(jīng)緯度的值,那么geo_point類型的字段可以用來做什么?
- 基于Geo的地理位置范圍查詢
- 基于Geo范圍內(nèi)到中心點(diǎn)距離的聚合統(tǒng)計(jì)
- 加入到相關(guān)性得分計(jì)算中
- 基于Geo地理位置信息到中心點(diǎn)距離的排序
通過閱讀本文,可以學(xué)到以上知識(shí)點(diǎn),學(xué)到就是賺到,還不快快開始閱讀吧
環(huán)境
- MacOS 10.14.6
- Elasticsearch 8.1
- Kibana 8.1
幫助信息
latitude: 緯度;longitude: 經(jīng)度;
-
獲取地理位置坐標(biāo)網(wǎng)站,看自己喜好
-
geohash生成
如何獲取一個(gè)地理位置的geohash以及根據(jù)geohash獲取經(jīng)緯度
根據(jù)地理位置信息查看地圖上展示
Geopoint(點(diǎn))
如何指定一個(gè)地理位置信息
Elasticsearch中geo_point類型的數(shù)據(jù)字段支持以下5種方式錄入地理位置數(shù)據(jù),分別如下:
首先我們還是先定義一個(gè)索引,創(chuàng)建一個(gè)數(shù)據(jù)類型為geo_point的字段
-
創(chuàng)建索引
PUT my-index-000001 { "mappings": { "properties": { "text":{ "type": "text" }, "location":{ "type": "geo_point" } } } }
-
作為一個(gè)對(duì)象傳入位置信息
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ù)形式的數(shù)據(jù)寫入,并且絲毫不影響查詢,就是這么強(qiáng)大,還不快用起來,溫馨提醒,千萬不要為了用而用哦
geo_point 類型字段支持的參數(shù)
-
ignore_malformed
默認(rèn)
false,寫入文檔時(shí)遇到不符合規(guī)范的地理位置信息字段值會(huì)拋出異常,如果為true,則會(huì)忽略。如果設(shè)置了script字段,該字段將不能使用 -
ignore_z_value
默認(rèn)
true,三維空間信息將被記錄,但是只有經(jīng)緯度的值能被索引,如果為false,寫入文檔時(shí)如果有超過二維的信息,拋出異常拒絕文檔寫入 -
index
是否可以被索引,默認(rèn)
true,如果為false,未建立索引的字段將不能被檢索 -
null_value
默認(rèn)值為
null,如果設(shè)置為null,則意味著該字段是不存在的,如果使用了script,則該字段不可被設(shè)置 -
on_script_error
只有設(shè)置了
script字段的時(shí)候才可以設(shè)置該字段,定義使用腳步發(fā)生錯(cuò)誤時(shí)如何處理,默認(rèn)fail,拒絕整個(gè)文檔,但是會(huì)繼續(xù),在元數(shù)據(jù)字段中注冊(cè)該字段 -
script
腳本字段,如果設(shè)置了該值,將索引該腳本執(zhí)行之后的結(jié)果,如果在寫文檔的時(shí)候給該字段設(shè)置了值,文檔將會(huì)被拒絕。腳本的格式與運(yùn)行時(shí)相同,應(yīng)該是一對(duì)
(lat,lon)雙值的形式
在腳本中使用geopoints
當(dāng)在腳本中訪問geopoint類型的值時(shí),該值做為GeoPoint對(duì)象返回,可以使用如下方式讀取
def geopoint = doc['location'].value;
def lat = geopoint.lat;
def lon = geopoint.lon;
但是更推下如下這樣讀取
def lat = doc['location'].lat;
def lon = doc['location'].lon;
Geoshape(形狀)
對(duì)幾何形狀進(jìn)行索引和查詢(矩形和多邊形),當(dāng)索引的數(shù)據(jù)和查詢包含除了點(diǎn)以外的形狀時(shí)應(yīng)該使用geo_shape類型
通過上面這倆類型的描述,我們可以得出一個(gè)結(jié)論,就是geo_point表示一個(gè)點(diǎn),geo_shape表示多個(gè)點(diǎn)連接線組成的形狀
支持的映射參數(shù)
geo_shape 映射將GeoJSON幾何對(duì)象映射到geo_shape,要是用該映射類型,必須顯式的設(shè)置,這句話意思就是我們使用該數(shù)據(jù)類型的時(shí)候必須明確指定索引類型
-
orientation
可選參數(shù),WKT多邊形的默認(rèn)方向,默認(rèn)
RIGHT逆時(shí)針。LEFT順時(shí)針如果要指定
RGIHT可以使用如下值設(shè)置- right
- counterclockwise
- ccw
如果要指定
LEFT可以使用如下值設(shè)置- left
- clockwise
- cw
WKT(Well-known text)是一種文本標(biāo)記語言,用于表示矢量幾何對(duì)象、空間參照系統(tǒng)及空間參照系統(tǒng)之間的轉(zhuǎn)換
-
ignore_malformed
默認(rèn)
false,如果是不符合GeoJSON或者WKT的格式拋出異常,如果為true,則忽略 -
ignore_z_value
默認(rèn)
true,三維空間信息將被記錄,但是只有經(jīng)緯度的值能被索引,如果為false,寫入文檔時(shí)如果有超過二維的信息,拋出異常拒絕文檔寫入 -
Coerce
默認(rèn)
false,如果為true,如果是非封閉的多邊形將自動(dòng)閉合形成封閉的多邊形
創(chuàng)建索引
PUT /example
{
"mappings": {
"properties": {
"location": {
"type": "geo_shape"
}
}
}
}
輸入類型
地理位置形狀信息可以使用GeoJSON或者WKT表示,下面是GeoJSON,WKT與Elasticsearch中類型的對(duì)應(yīng)關(guān)系
| GeoJSON Type | WKT Type | Elasticsearch Type | description |
|---|---|---|---|
| Point | POINT | Point | 單一的經(jīng)緯度坐標(biāo) |
| LineString | LINTSTRING | lingstring | 給定兩個(gè)點(diǎn)或多個(gè)點(diǎn)組成的任意直線 |
| Polygon | POLYGON | polygon | 封閉的多邊形,第一個(gè)點(diǎn)和最后一個(gè)點(diǎn)必須匹配,也就是n+1個(gè)點(diǎn)形成的n邊多邊形,至少4個(gè)頂點(diǎn) |
| MultiPoint | MULTIPOINT | multipoint | 一組不相連但是可能相關(guān)的點(diǎn) |
| MultiLineString | MULTILINESTRING | multilinestring | 單獨(dú)的行字符串組成的數(shù)組 |
| MultiPolygon | MULTIPOLYGON | multipolygon | 單獨(dú)的多邊形數(shù)組 |
| GeometryCollection | GEOMETRYCOLLECTION | geometrycollection | 與multi開頭的類型類似的一個(gè)GeoJSON形狀,但是多個(gè)類型可以共同存在比如(Point和LineString同時(shí)存在) |
| N/A | BBOX | envelope | 僅指定左上角和右下角兩個(gè)點(diǎn)組成的邊框或包 |
對(duì)于所有的類型都應(yīng)該有子類型和字段坐標(biāo),在GeoJSON、WKT以及Elasticsearch中,坐標(biāo)信息都是(經(jīng)度,緯度),這與很多的地理信息API是不同的,地圖地理信息API通常使用(緯度,經(jīng)度),這一點(diǎn)是需要注意的一個(gè)地方,下面我將針對(duì)以上集中類型情況分別做一個(gè)寫入數(shù)據(jù)的測試
-
點(diǎn)
點(diǎn)的話比較簡單,就比如手機(jī)定位,或者某個(gè)建筑的位置信息
# GeoJSON POST /example/_doc { "location" : { "type" : "Point", "coordinates" : [-77.03653, 38.897676] } } # WKT POST /example/_doc { "location" : "POINT (-77.03653 38.897676)" }
-
線
由兩個(gè)或者多個(gè)數(shù)組以上定義的行字符串,通過指定兩個(gè)點(diǎn),linestring將表示一條線,兩個(gè)以上的點(diǎn)表示任意路徑
# 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)" }
-
多邊形
多邊形是由多個(gè)點(diǎn)定義的,列表的第一個(gè)點(diǎn)和最后一個(gè)點(diǎn)必須相同
# 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))" }上面這個(gè)定義的是一個(gè)多邊形,下面定義一個(gè)帶洞的多邊形,下面例子中,第一個(gè)數(shù)組代表多邊形外部邊界,第二個(gè)數(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))" }
-
多邊形的方向
多邊形的方向也就是頂點(diǎn)的順序,有
Right逆時(shí)針和Left順時(shí)針,Elasticsearch使用多邊形的方向來確認(rèn)是否跨越了國際日期線(+-180度)因?yàn)閃KT沒有指定或者強(qiáng)制默認(rèn)方向,我們可以使用方向映射的參數(shù)
orientation來設(shè)置多邊形的方向GeoJSON默認(rèn)使用Right的方向,與參數(shù)
orientation設(shè)置無關(guān),GeoJSON規(guī)范要求外部多邊形使用逆時(shí)針,內(nèi)部多邊形使用順時(shí)針但是可以使用文檔級(jí)別的參數(shù)
orientation覆蓋來重寫GeoJSON的默認(rèn)方向,如下所示多邊形方向就是LeftPOST /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 僅使用多邊形的方向來確定是否跨越了國際日期線,如果一個(gè)多邊形的最小經(jīng)度與最大經(jīng)度差值小于180,那多邊形就沒有跨越國際日期線,對(duì)多邊形的方向也就不會(huì)有影響
如果多邊形的最小經(jīng)度與最大經(jīng)度差值等于180或者大于180,則Elasticsearch會(huì)檢查文檔級(jí)別的orientation參數(shù)方向與默認(rèn)的方向是否不同,如果方向不同,Elasticsearch會(huì)在跨越國際日期線的地方分割多邊形
-
點(diǎn)的列表
# 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))" } -
多個(gè)多邊形
如下代表兩個(gè)多邊形,其中第二個(gè)多邊形包含一個(gè)洞
# 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)))" } -
集合對(duì)象
包含
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
指定對(duì)角線的多邊形,格式為
[[minLon,maxLat],[maxLon,minLat]]POST /example/_doc { "location" : { "type" : "envelope", "coordinates" : [ [100.0, 1.0], [101.0, 0.0] ] } }下面使用WKT的BBOX類型,WKT的格式順序?yàn)?code>minLon,maxLon,maxLat,minLat
POST /example/_doc { "location" : "BBOX (100.0, 102.0, 2.0, 0.0)" } -
圓
GeoJSON和WKT都不支持一個(gè)點(diǎn)的半徑圓類型,但是可以使用circle ingest processor來近似的模擬一個(gè)圓來作為一個(gè)多邊形
排序和檢索
由于形狀的輸入結(jié)構(gòu)復(fù)雜度和索引表示形狀的復(fù)雜性,目前不能對(duì)索引進(jìn)行排序或者直接檢索他們的字段,目前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
可以對(duì)數(shù)據(jù)類型為geo_point和geo_shape的值在矩形邊框內(nèi)進(jìn)行檢索,找出落在矩形內(nèi)的點(diǎn)
Geopoint 值的匹配
下面是一個(gè)演示一個(gè)點(diǎn)在一個(gè)矩形邊框范圍的一個(gè)例子,首先還是創(chuàng)建一個(gè)索引
-
創(chuàng)建索引
索引包含兩個(gè)字段,
desc為地點(diǎn)的描述信息,pin.location為具體的地理位置信息PUT /my_locations { "mappings": { "properties": { "desc":{ "type": "text" }, "pin": { "properties": { "location": { "type": "geo_point" } } } } } }
- 插入一條測試數(shù)據(jù),使用地圖獲取故宮博物院的坐標(biāo)信息(位置信息與截圖稍微有微微的偏差,為后期補(bǔ)充圖片)

PUT /my_locations/_doc/1
{
"desc":"故宮博物院",
"pin": {
"location": {
"lat": 39.92375,
"lon": 116.40348
}
}
}
- 定義下圖所示的邊界范圍,矩形框內(nèi)查詢,
top_left就是頂點(diǎn)1,bottom_right就是頂點(diǎn)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
}
}
}
}
}
}
}
查看返回結(jié)果可以獲取到剛才插入的故宮博物院地點(diǎn)信息

Geoshape 值的匹配
-
新建索引
PUT /my_geoshapes { "mappings": { "properties": { "desc":{ "type": "text" }, "pin": { "properties": { "location": { "type": "geo_shape" } } } } } }
-
插入測試數(shù)據(jù),如下數(shù)據(jù)在地圖展示如下,5個(gè)地理位置信息為圖中矩形的頂點(diǎn),順時(shí)針第一個(gè)和最后一個(gè)保持相同如下地理位置信息數(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對(duì)應(yīng)的就是圖中的頂點(diǎn)1,bottom_right對(duì)應(yīng)的就是圖中的頂點(diǎn)2

Geo-distance query(圓形過濾)
以給定位置作圓點(diǎn),畫一個(gè)給定距離的圓,以匹配圓范圍內(nèi)的文檔數(shù)據(jù),下面我們用例子來說明這個(gè)圓形過濾
-
創(chuàng)建索引,
my_locations測試geo_point,my_geoshapes測試geo_shapePUT /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" } } } } } }
-
插入一個(gè)測試數(shù)據(jù),地點(diǎn)信息為西單大悅城(經(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 } } } } } }查看結(jié)果可以得到距離為周邊3公里內(nèi)是搜索不到的,大于3公里就可以搜索到我們定義的西單大悅城的地點(diǎn)信息
看下面例子,同時(shí)在
my_locations和my_geoshapes兩個(gè)索引中搜索故宮博物院周邊5公里內(nèi)的地點(diǎn)信息GET my_locations,my_geoshapes/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_distance": { "distance": "5km", "pin.location": { "lat": 39.92402, "lon": 116.40360 } } } } } }

擴(kuò)展知識(shí)
在上文中我們也看到了,錄入測試數(shù)據(jù)的方式支持多種,在使用過濾器查詢數(shù)據(jù)的時(shí)候同樣如此可以使用多種方式進(jìn)行過濾,如下簡單舉例幾種,比如
-
使用數(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)站,也可以復(fù)制如下鏈接到瀏覽器打開,任選其一即可GET my_locations,my_geoshapes/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "geo_distance": { "distance": "5km", "pin.location": "wx4g0gfw22x" } } } } }
過濾查詢接收的參數(shù)說明
-
distance
以指定的點(diǎn)為圓心,以
distance的值為半徑畫圓,匹配圓內(nèi)的地點(diǎn)信息數(shù)據(jù),距離單位默認(rèn)米(m),其他距離單位可以參考官網(wǎng)https://www.elastic.co/guide/en/elasticsearch/reference/8.1/api-conventions.html#distance-units
-
distance_type
計(jì)算距離的方式,也就是
distance生效的方式,默認(rèn)是上述所說的為圓也就是弧形計(jì)算(arc),另一個(gè)可選值是平面的(plane),效率比arc快,但是精度有所損失,尤其是distance的值很大時(shí)或者兩極處。plane比喻地球是平的,在赤道附近時(shí)精度是最佳的,靠近兩級(jí)時(shí)精度略有損失。比如想找附近5km的地點(diǎn),然后找到了5.2km的地點(diǎn),這個(gè)誤差取決于我們是否可以接受 -
_name
該查詢語句的名稱
-
validation_method
是否接受錯(cuò)誤的地點(diǎn)坐標(biāo)信息
-
STRICT默認(rèn)值,拋出異常 -
COERCE嘗試修正錯(cuò)誤坐標(biāo)信息為正確的坐標(biāo) -
IGNORE_MALFORMED允許無效的錯(cuò)誤的地點(diǎn)坐標(biāo)信息
-
geo_distance的過濾請(qǐng)求可以匹配一篇文檔中的多個(gè)地點(diǎn)值,只要有一個(gè)地點(diǎn)值匹配,該文檔就會(huì)被返回
ignore_unmapped字段的值設(shè)置為true,查詢時(shí)如果字段不匹配,會(huì)忽略報(bào)錯(cuò),返回空文檔;如果設(shè)置為false,遇到不匹配的字段會(huì) 拋出異常
如下示例,匹配pin.location1字段,我們知道該字段在索引中是不存在的,所以當(dāng)ignore_unmapped設(shè)置為true時(shí)返回空文檔,設(shè)置為false時(shí)拋出異常
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
多邊形過濾,顧名思義就是查詢條件定義個(gè)多邊形,返回該多邊形區(qū)域內(nèi)的點(diǎn)的文檔信息
官網(wǎng)已經(jīng)說明es7.12版本開始廢棄,推薦使用geoshape,參考后文的 geoshape query
該查詢接收的參數(shù)有如下
-
_name
該查詢語句的名稱
-
validation_method
是否接受錯(cuò)誤的地點(diǎn)坐標(biāo)信息
-
STRICT默認(rèn)值,拋出異常 -
COERCE嘗試修正錯(cuò)誤坐標(biāo)信息為正確的坐標(biāo) -
IGNORE_MALFORMED允許無效的錯(cuò)誤的地點(diǎn)坐標(biāo)信息
-
需要注意的是,查詢的字段需要設(shè)置為geo_point類型,同樣ignore_unmapped字段的值設(shè)置為true,查詢時(shí)如果字段不匹配,會(huì)忽略報(bào)錯(cuò),返回空文檔;如果設(shè)置為false,遇到不匹配的字段會(huì) 拋出異常
-
我們先插入一條故宮位置的信息文檔
PUT /my_locations/_doc/2 { "desc":"故宮", "pin":{ "location":"39.92307,116.40291" } }
如下是對(duì)pin.location字段定義一個(gè)三條邊的形狀的過濾查詢語句,三個(gè)點(diǎn)順序【1,2,3】排列,此時(shí)該 查詢會(huì)返回故宮的信息,但是剛才定的西單大悅城的信息就不會(huì)返回
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】的地點(diǎn)信息
-
字符串
# 字符串 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兩種字段類型的過濾查詢
形狀查詢,支持傳入一個(gè)完整圖形形狀的數(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" } } } } } }通過上面兩個(gè)不同字段的檢索demo,我們應(yīng)該也發(fā)現(xiàn)了,relation這個(gè)的值我們竟然使用的不一樣,那么他們有什么區(qū)別呢,各自代表的含義如下:
-
INTERSECTS默認(rèn)值,返回字段類型為geo_shape或者geo_point與檢索的圖形相交的文檔 -
DISJOINT返回字段類型為
geo_shape或者geo_point與檢索圖形不相交的文檔 -
WITHIN返回字段類型為
geo_shape或者geo_point``在檢索圖形范圍內(nèi)的文檔,不支持線的幾何圖形 -
CONTAINS返回文檔中字段類型為
geo_shape或者geo_point的信息包含檢索圖形形狀的文檔
預(yù)置圖形形狀查詢
預(yù)置就是提前設(shè)置好圖形的數(shù)據(jù),保存在索引中,查詢時(shí)根據(jù)提前定義好圖形的名稱來過濾數(shù)據(jù),首先它有如下幾個(gè)參數(shù)
- id 預(yù)索引形狀的id
- index 預(yù)索引形狀所在的索引位置,默認(rèn)
shapes - path 預(yù)索引形狀所在索引中的字段名稱,默認(rèn)
shape - routing 可選,預(yù)索引形狀的路由參數(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"
}
}
}
}
}
}
}
基于地理位置信息或者到中心點(diǎn)距離的聚合統(tǒng)計(jì)
Geo網(wǎng)格聚合
網(wǎng)格聚合,將字段類型為geo_point或者geo_shape的數(shù)據(jù)劃分為一個(gè)個(gè)的單元格,也就是一個(gè)個(gè)的單元格的桶中,既然是單元格那也就是有大有小,也就是高精確度與低精確度值,精度值的范圍為1-12 ,1表示精確度最低,范圍最大,12表示精確度最高但是范圍也最小,甚至可能出現(xiàn)百萬個(gè)以上的桶的聚合,所以這個(gè)是需要注意的地方,如果精確度要求比較高的話,可以使用geo_bounding box query過濾縮小聚合范圍。網(wǎng)格聚合通過geohash來實(shí)現(xiàn),通過指定聚合類型為geohash_grid來實(shí)現(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等價(jià)于geo_bounding box query過濾邊界的參數(shù)值設(shè)置可選 -
size返回聚合桶的數(shù)量可選 -
shard_size為了對(duì)返回的聚合結(jié)果進(jìn)行更精確的計(jì)算,該值為每個(gè)分片返回的聚合桶的數(shù)量,默認(rèn)max(10,(size x number-of-shards))的最大值
圓點(diǎn)距離聚合
給定一圓點(diǎn),以給出距離為半徑畫圓,將落在圓內(nèi)的數(shù)據(jù)按照到原點(diǎn)的距離聚合,我們可以定一個(gè)起始值,一組范圍的值來規(guī)定聚合桶的范圍,下面跟我一起來看下如何使用到圓點(diǎn)距離的聚合
-
創(chuàng)建一個(gè)索引保存故宮周圍地鐵站的信息
PUT /gugong_map { "mappings": { "properties": { "location": { "type": "geo_point" } } } }
-
插入故宮周圍地鐵站信息
聚合 的字段必須顯示設(shè)置為
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ù)地鐵站距離到故宮的距離進(jìn)行聚合查詢
聚合的默認(rèn)距離單位是
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 } ] } } } }
-
指定聚合計(jì)算方式,精確度
arc,默認(rèn),精確度高plane精確度低,距離僅時(shí)推薦使用,比如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 } ] } } } }
-
對(duì)聚合桶返回唯一的key
設(shè)置
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 } } } }
將地理位置信息加入到分?jǐn)?shù)計(jì)算中
https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-function-score-query.html
- 首先字段必須是
geo_point類型 - 提供一個(gè)圓心點(diǎn)的位置坐標(biāo)
-
scale和offset必須設(shè)置
滿足以上三點(diǎn)就可以加入到分?jǐn)?shù)計(jì)算中,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 所得的評(píng)分_score,默認(rèn)為0.5
}
}
}
}
}
基于距離信息的排序
https://www.elastic.co/guide/en/elasticsearch/reference/8.1/sort-search-results.html#geo-sorting
需要指定圓點(diǎn)的坐標(biāo)信息,根據(jù)到原點(diǎn)的距離進(jìn)行排序,比如如下語句,查詢name字段帶站的,根據(jù)到北京南站(116.385728,39.870814)的距離升序輸出,坐標(biāo)信息,與geo_point支持的方式相同,可以為數(shù)組,字符串,對(duì)象,geohash
_geo_distance指定為geo類型字段排序的類型order升序還是降序 ,asc、descunit距離排序使用的單位,默認(rèn)mmode如果一個(gè)地點(diǎn)值的字段包含多個(gè)地理位置信息,怎么取值,如果升序排序時(shí)選最短距離,降序排序時(shí)選最長距離的。支持min,max,median,avgdistance_type默認(rèn)arc精確度高,plane速度快,再靠近兩級(jí)附近或者距離遠(yuǎn)時(shí)精度低-
ignore_unmapped字段不存在映射時(shí)是否報(bào)錯(cuò),默認(rèn)false,未匹配時(shí)報(bào)錯(cuò),如果為true,等于使用unmapped_type的字段排序地理位置排序時(shí),如果文檔中沒有地理位置信息,距離被默認(rèn)為
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é)
在上面的章節(jié)中,我們由淺入深的學(xué)習(xí)了geo_point和geo_shape兩種geo數(shù)據(jù)類型,以及如何通過對(duì)這兩字段進(jìn)行檢索,聚合排序,相信大家讀到這也基本有了一個(gè)概念了,再深點(diǎn)還是要去看官方文檔了,不過實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),多實(shí)操吧,總沒有錯(cuò),加油?。?!
如果有寫的不對(duì)的地方歡迎指出哦?。?!共同進(jìn)步才能走的更遠(yuǎn)?。?!