萬字長文:一文徹底搞懂Elasticsearch中Geo數(shù)據(jù)類型查詢、聚合呻拌、排序

在我們使用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

幫助信息

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悠菜,WKTElasticsearch中類型的對應關系

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)))"
    }
    
  • 集合對象

    包含PointLineString的例子

    # 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)"
    }
    
  • GeoJSONWKT都不支持一個點的半徑圓類型俘闯,但是可以使用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_pointgeo_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(圓形過濾)

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-geo-distance-query.html#query-dsl-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_locationsmy_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"
            }
          }
        }
      }
    }
    

過濾查詢接收的參數(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)格聚合

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-bucket-geohashgrid-aggregation.html

網(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_shapegeo_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))的最大值

圓點距離聚合

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-bucket-geodistance-aggregation.html

給定一圓點命黔,以給出距離為半徑畫圓呜呐,將落在圓內(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

    設置keyedtrue返回唯一的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類型
  • 提供一個圓心點的位置坐標
  • scaleoffset必須設置

滿足以上三點就可以加入到分數(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 升序還是降序 熙卡,ascdesc

  • 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_pointgeo_shape兩種geo數(shù)據(jù)類型,以及如何通過對這兩字段進行檢索黍匾,聚合排序栏渺,相信大家讀到這也基本有了一個概念了,再深點還是要去看官方文檔了锐涯,不過實踐是檢驗真理的唯一標準磕诊,多實操吧,總沒有錯纹腌,加油v铡!升薯!

如果有寫的不對的地方歡迎指出哦@嘲!涎劈!共同進步才能走的更遠9阃埂!责语!

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炮障,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子坤候,更是在濱河造成了極大的恐慌胁赢,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件白筹,死亡現(xiàn)場離奇詭異智末,居然都是意外死亡谅摄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門系馆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來送漠,“玉大人,你說我怎么就攤上這事由蘑∶龉眩” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵尼酿,是天一觀的道長爷狈。 經(jīng)常有香客問我,道長裳擎,這世上最難降的妖魔是什么涎永? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮鹿响,結果婚禮上羡微,老公的妹妹穿的比我還像新娘。我一直安慰自己惶我,他們只是感情好妈倔,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绸贡,像睡著了一般启涯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恃轩,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音黎做,去河邊找鬼叉跛。 笑死,一個胖子當著我的面吹牛蒸殿,可吹牛的內(nèi)容都是我干的筷厘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宏所,長吁一口氣:“原來是場噩夢啊……” “哼酥艳!你這毒婦竟也來了?” 一聲冷哼從身側響起爬骤,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤充石,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后霞玄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骤铃,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡拉岁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了惰爬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喊暖。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖撕瞧,靈堂內(nèi)的尸體忽然破棺而出陵叽,到底是詐尸還是另有隱情,我是刑警寧澤丛版,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布巩掺,位于F島的核電站,受9級特大地震影響硼婿,放射性物質發(fā)生泄漏锌半。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一寇漫、第九天 我趴在偏房一處隱蔽的房頂上張望刊殉。 院中可真熱鬧,春花似錦州胳、人聲如沸记焊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遍膜。三九已至,卻和暖如春瓤湘,著一層夾襖步出監(jiān)牢的瞬間瓢颅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工弛说, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挽懦,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓木人,卻偏偏與公主長得像信柿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子醒第,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容