Elasticsearch 的一些常見疑問(持續更新中)

weixin_34127717發表於2018-06-29

資料型別

keyword和text有什麼區別?

keyword用於索引結構化內容的欄位,例如電子郵件地址,主機名,狀態碼,郵政編碼或標籤。
通常用於過濾(找到我的所有部落格文章,其中 status為published),排序,和聚合。keyword型別的欄位只能按其準確值進行搜尋。text是用於全文檢索的資料型別,儲存時會通過分詞器對資料進行分詞儲存,搜尋時會對分詞後的多個短語進行搜尋。text型別不適用用於排序和聚合。

日期型別

Elasticsearch提供了date型別來處理日期,但是由於JSON是沒有日期型別的,所以在內部日期被轉換為UTC並且儲存為時間戳(帶毫秒)。
插入日期時如果插入的為常規日期格式(yyyy-MM-dd或者yyyy-MM-dd'T'HH:mm:ss.SSSZ),es會自動識別日期格式,將欄位型別設定為Date。如果mapping中欄位型別已經確定為Date,此時插入格式化日期字串或者時間戳(字串和Numeric型別)時都會被Es作為日期型別儲存,只不過在資料顯示上和儲存時的格式一致(存時間戳返回就是時間戳,字串返回就是字串,但是推薦存標準化的UTC時間或時間戳,避免型別不一致)。

時區問題

A:Elasticsearch預設為UTC時間,即零時區,查詢時若不指定時區,則預設以0時區查詢,和我們所在的東八區差8小時。yyyy-MM-dd'T'HH:mm:ss.SSSZ,這裡的Z就代表UTC時區。
Es在進行日期查詢/聚合時可以指定時區:

//日期範圍查詢
POST datatypetest/_search
{
  "query": {
    "range": {
      "date3": {
        "gte": "2018-07-05",
        "lte": "now",
        "time_zone": "Asia/Shanghai"//這就是東八區(北京時間/中國標準時間)
      }
    }
  }
}
//日期聚合
GET my_index/_search?size=0
{
  "aggs": {
    "by_day": {
      "date_histogram": {
        "field":     "date",
        "interval":  "day",
        "time_zone": "Asia/Shanghai"
      }
    }
  }
}
//Java獲取系統時區id
TimeZone.getDefault().getID()
//Es Java Api日期範圍查詢
QueryBuilders
                .rangeQuery("your date field")
                .gte("your date from")
                .lte("your date to")
                .timeZone(TimeZone.getDefault().getID());//此處只能設定時區id
//Es Java Api日期聚合查詢
AggregationBuilders
                .dateHistogram("your alias")
                .timeZone(DateTimeZone.getDefault());//獲取系統預設時區,此處timeZone物件是joda包中的DateTimeZone

在使用Kibana時,Kibana會預設獲取瀏覽器時區,在顯示資料時根據時區做日期的格式化。但使用DevTools寫DSL或者直接請求Es時預設返回的都是UTC時間,會發現時間少了8小時。

使用日期型別時推薦不修改日期格式時區,所有資料都用UTC時區,這樣時間統一才不容易出問題。

object和nested型別的區別,各自的應用場景是什麼

object為es的預設物件型別,巢狀的json物件存入es就會被預設設定為object型別。es內部會將巢狀的json物件扁平化轉換儲存。
例如,一個普通的json

{ 
  "region": "US",
  "manager": { 
    "age":     30,
    "name": { 
      "first": "John",
      "last":  "Smith"
    }
  }
}

在es內部會被轉換為扁平化的基礎json資料

{
  "region":             "US",
  "manager.age":        30,
  "manager.name.first": "John",
  "manager.name.last":  "Smith"
}

nested用於JSON物件的陣列,它允許物件陣列以可彼此獨立查詢的方式進行索引。由於lucene沒有內部物件的概念,因此es需要把資料轉換為簡單的扁平化結構。
一個物件陣列巢狀:

{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

在es內部會被轉換為:

{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}

user.first和user.last欄位被轉換為多值欄位,資料之間的關聯性就會丟失,導致查詢結果有誤。
此時用nested型別就可以解決此問題。

查詢

term和match匹配有什麼區別?

term為精確匹配,查詢時不對關鍵詞進行分詞。而match用於全文檢索,首先會對檢索關鍵詞進行分詞,然後進行全文搜尋。

must,filter,post_filter有什麼區別?

must為查詢上下文(query context),查詢時會計算分值(文件相關性)。filter為過濾上下文,查詢時不計算分值。而post_filter為後置過濾器,會對聚合的結果也進行過濾。執行順序為:filter -> aggregations - post_filter。

query context & filter context區別?

must/should為查詢上下文。filter或must_not在引數 bool的查詢中/filter在constant_score的查詢/filter aggretion這些場景中為過濾上下文。
查詢上下文會計算返回文件的分值,而過濾上下文不計算分值,語義只是此文件是否匹配,而不計算相關度。在過濾上下文時,es會自動對過濾器進行快取從而提高查詢效能。

相關文章