??記得小時(shí)候去北京時(shí),關(guān)于北京城的地圖還賣(mài)兩塊錢(qián)一份,得益于科技的進(jìn)步,現(xiàn)在我們總是可以知道自己的準(zhǔn)確位置,比如距離自己 5 km 內(nèi)的餐館,距離自己最近的酒店等等。
??es 中不僅提供了地理位置的功能,還可以將地理位置、全文搜索、結(jié)構(gòu)化搜索和分析結(jié)合到一起。
??es提供了 兩種表示地理位置的方式:
??(1)用緯度-經(jīng)度表示的坐標(biāo)點(diǎn)使用 geo_point 字段類(lèi)型。
??(2) 以 GeoJSON 格式定義的復(fù)雜地理形狀,使用 geo_shape 字段類(lèi)型。
??當(dāng)然兩種方式有著不同的作用,geo_point計(jì)算距離某個(gè)坐標(biāo)點(diǎn)一定距離的所有坐標(biāo)點(diǎn),并根據(jù)坐標(biāo)點(diǎn)之間的距離進(jìn)行打分、或者聚合到顯示在地圖上的一個(gè)網(wǎng)絡(luò); 而 geo_shape完全是用來(lái)過(guò)濾的。他可以判斷兩個(gè)地理形狀是否有重合,或者一個(gè)地理形狀是否包含另一個(gè)地理形狀。
1. 經(jīng)緯度坐標(biāo)表示
??注意,地理坐標(biāo)點(diǎn)不能被自動(dòng)映射,必須手動(dòng)指定該字段的類(lèi)型為 geo_point,比如下列:
PUT /attractions
{
"mappings": {
"restaurant": {
"properties": {
"name": {
"type": "string"
},
"location": {
"type": "geo_point"
}
}
}
}
}
??mapping定義完成之后,就可以索引包含位置信息的文檔了,經(jīng)緯度信息的形式可以使字符串、數(shù)組或者對(duì)象。
PUT /attractions/restaurant/1
{
"name": "Chipotle Mexican Grill",
"location": "40.715, -74.011" // lat, lon
}
PUT /attractions/restaurant/2
{
"name": "Pala Pizza",
"location": {
"lat": 40.722,
"lon": -73.989
}
}
PUT /attractions/restaurant/3
{
"name": "Mini Munchies Pizza",
"location": [ -73.983, 40.719 ] // lon, lat
}
??注意: 使用字符串進(jìn)行表示地理位置時(shí)是經(jīng)度在前,緯度在后,但是是數(shù)組表示時(shí),卻正好相反,唯獨(dú)在前,經(jīng)度在后。在es內(nèi)部,無(wú)論是什么形式,都是按照經(jīng)度在前,緯度在后進(jìn)行保存的。
2. 地理坐標(biāo)點(diǎn)過(guò)濾
??有四種地理坐標(biāo)點(diǎn)相關(guān)的過(guò)濾器可以用來(lái)選中或者排除文檔:
??(1)geo_bounding_box: 找出落在矩形框中的點(diǎn)
??該過(guò)濾器是目前為止最有效的過(guò)濾器,使用簡(jiǎn)單,只需要指定矩形的頂部,底部和左右邊界勾勒出一個(gè)矩形,就可以尋找在該矩形內(nèi)的所有文檔,使用方式如下:
GET /attractions/restaurant/_search
{
"query": {
"bool": {
"filter": {
"geo_bounding_box": {
"location": { // 也可以使用 bottom_left, top_right
"top_left": {
"lat": 40.8,
"lon": -74.0
},
"bottom_right": {
"lat": 40.7,
"lon": -73.0
}
}
}
}
}
}
}
??(2)geo_distance: 找出與指定位置給定距離的點(diǎn)
??地理距離過(guò)濾器,是指定一個(gè)圓心和半徑,尋找該圓中的所有文檔。兩點(diǎn)之間的距離運(yùn)算是非常耗時(shí)的,也許我們并不需要非常精確的結(jié)果,所以我們可以指定計(jì)算距離的算法,我們可以根據(jù)需要從精度和性能之間做出權(quán)衡。
??1): arc ,最慢但是最精確。
??2): plane:這種方式是將地球看成是平面,所以這種方式相對(duì)于arc快一些,但是不是很精確。
??3):sloppy_arc:使用 Haversine formula 來(lái)計(jì)算距離。它比 arc 計(jì)算方式快 4 到 5 倍,并且距離精度達(dá) 99.9%。這也是默認(rèn)的計(jì)算方式。
GET /attractions/restaurant/_search
{
"query": {
"bool": {
"filter": {
"geo_distance": {
"distance": "1km",
"distance_type": "plane",
"location": {
"lat": 40.715,
"lon": -73.988
}
}
}
}
}
}
?? 對(duì)于distance參數(shù),訪問(wèn) Distance Units 查看所支持的距離表示單位。
??(3)geo_distance_range: 找出與指定位置給定最小距離和最大距離之間的點(diǎn)
??地理距離區(qū)間過(guò)濾器,相比于上一個(gè)過(guò)濾器的區(qū)別,就是它是一個(gè)環(huán),它會(huì)排除內(nèi)圈中的所有文檔。
GET /attractions/restaurant/_search
{
"query": {
"bool": {
"filter": {
"geo_distance_range": {
"gte": "1km",
"lt": "2km",
"location": {
"lat": 40.715,
"lon": -73.988
}
}
}
}
}
}
??(4)geo_polygon: 找出落在多邊形中的點(diǎn)
??注意:這些過(guò)濾器判斷點(diǎn)是否落在指定區(qū)域時(shí)的計(jì)算方法稍有不同,但過(guò)程類(lèi)似。指定的區(qū)域被轉(zhuǎn)換成一系列以quad/geohash為前綴的tokens,并被用來(lái)在倒排索引中搜索擁有相同tokens的文檔。由于需要做很多復(fù)雜的操作,所以地理過(guò)濾器的代價(jià)比較昂貴,在使用時(shí)應(yīng)該盡可能使用其他代價(jià)較小的過(guò)濾器比如 bool 過(guò)濾器過(guò)濾掉更多的文檔,最后再使用地理坐標(biāo)過(guò)濾器進(jìn)行篩選。
3. 按距離排序
??搜索結(jié)果可以按照距離進(jìn)行排序
GET /attractions/restaurant/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance": {
"location": { // 計(jì)算所有文檔按照該指定位置的距離
"lat": 40.715,
"lon": -73.998
},
"order": "asc",
"unit": "km", // 將距離以 km 為單位寫(xiě)入每個(gè)返回結(jié)果的sort鍵中
"distance_type": "plane"
}
}
]
}