ES在處理大數據搜索方面擁有關係型數據庫不可比擬的速度優點。這篇不是什麼專業的ES指南,也不是ES分析,這裏只是我在使用ES中遇到的一些問題,一些搜索方式。由於ES的文檔和API查詢起來比較困難,所以本身在查詢翻譯文檔時老是耗費不少時間,因而就想把本身遇到過的問題和搜索記錄下來,給本身概括一個簡單的經常使用的ES使用指南。html
ES中文檔是以key-value的json數據包形式存儲的,它有三個元數據。mysql
數據被存儲和索引在分片「shards」中,索引只是把一個或多個分片組合在一塊兒的邏輯空間。這些由ES實現。使用者無需關心。
ES爲對字段類型進行猜想,動態生成了字段和類型的映射關係。sql
- date 類型的字段和 string 類型的字段的索引方式不一樣的,搜索的結果也是不一樣的。每一種數據類型都以不一樣的方式進行索引。
GET /user/_mapping/user --Response { "user": { "mappings": { "user": { "properties": { "height": { "type": "long" }, "nickname": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, } } } } }
確切值是肯定的,表示date或number等;
全文文本指 非結構化數據,如這是一個文本
值 | 解釋 |
---|---|
analyzed | 首先分析這個字符串,而後索引。(以全文形式索引此字段) |
not_analyzed | 索引這個字段,使之能夠被索引,但索引內容和指定值同樣 |
no | 不索引這個字段 |
string 類型字段默認值是 analyzed。若是想映射字段爲確切值,則設置爲 not_analyzed
{ "nickname": { "type": "string", "index": "not_analyzed" } }
GET /_search --Response { "took": 7, // 整個搜索的請求花費的時間(毫秒) "timed_out": false, // 是否請求超時 "_shards": { "total": 25, // 參與查詢的分片 "successful": 25, // 成功的分片 "skipped": 0, // 跳過的 "failed": 0 // 失敗的 }, "hits": { "total": 1291, // 匹配到到文檔數量 "max_score": 1, // 查詢結果 _score 中的最大值 "hits": [ { "_index": "feed", "_type": "feed", "_id": "1JNC42oB07Tkhuy89JSd", "_score": 1, "_source": { } } ] } }
建立一個名爲 megacorp 的索引
PUT /user/ --Response { "megacorp": { "aliases": {}, "mappings": {}, "settings": { "index": { "creation_date": "1558956839146", // 建立時間(微秒時間戳) "number_of_shards": "5", "number_of_replicas": "1", "uuid": "pGBqNmxrR1S8I7_jAYdvBA", // 惟一id "version": { "created": "6060199" }, "provided_name": "megacorp" } } } }
PUT /user/user/1001 --Body { "nickname": "你有病啊", "height": 180, "expect": "我喜歡看_書", "tags": ["大學黨", "求偶遇"] } --Response { "_index": "user", "_type": "user", "_id": "10001", "_version": 1, // 版本號,ES中每一個文檔都有版本號,每當文檔變化(包括刪除)_version會增長 "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 1, "_primary_term": 1 }
搜索 height 大於 30 且 nickname 爲 threads 的文檔
GET /user/user/_search --Body { "query": { "filtered": { "filter": { "range":{ "height":{ "gt": 170 } } }, "query": { "match": { "nickname": "threads" } } } } }
結構化查詢須要傳遞
query
參數。
GET /_search { "query": 子查詢 } # 子查詢 { "query_name": { "argument": value,... } } # 子查詢指向特定字段 { "query_name": { "field_name": { "argument": value,... } } }
查詢子句能夠合併簡單的子句爲一個複雜的查詢語句。如:數據庫
- 簡單子句用以在將查詢字符串與一個字段(或多個字段)進行比較。
- 複合子句用以合併其餘的子句。
GET /user/user/_search --Body { "query": { "bool": { "must": { // must:必須知足該子句的條件 "match": { "nickname": "threads" } }, "must_not": { // must_not: 必須不知足該子句條件 "match": { "height": 170 } }, "should": [ // should: 結果可能知足該數組內的條件 { "match": { "nickname": "threads" } }, { "match": { "expect": "我喜歡唱歌、跳舞、打遊戲" } } ] } } } --Response { "hits": { "total": 1, "max_score": 0.91862875, "hits": [ { "_index": "user", "_type": "user", "_id": "98047", "_score": 0.91862875, "_source": { "nickname": "threads", "height": 175, "expect": "我喜歡唱歌、跳舞、打遊戲", "tags": [ "工做黨", "吃雞" ] } } ] } }
GET /user/user/_search --Body { "query":{ "match":{ "expect": "打遊戲" } } } --Response { "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 0.8630463, "hits": [ { "_index": "user", "_type": "user", "_id": "98047", "_score": 0.8630463, // 匹配分 "_source": { "nickname": "threads", "height": 175, "expect": "我喜歡唱歌、跳舞、打遊戲", "tags": [ "工做黨", "吃雞" ] } }, { "_index": "user", "_type": "user", "_id": "94302", "_score": 0.55900055, "_source": { "nickname": "搖了搖頭", "height": 173, "expect": "我喜歡rap、跳舞、打遊戲", "tags": [ "工做黨", "吃飯" ] } }, { "_index": "user", "_type": "user", "_id": "91031", "_score": 0.53543615, "_source": { "nickname": "你有病啊", "height": 180, "expect": "我喜歡學習、逛街、打遊戲", "tags": [ "大學黨", "求偶遇" ] } } ] } }
ES根據結果相關性評分來對結果集進行排序。即文檔與查詢條件的匹配程度
確切的匹配若干單詞或短語。
GET /user/user/_search --Body { "query": { "match_phrase": { "expect": "學習" } } } --Response { "took": 3, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 1.357075, "hits": [ { "_index": "user", "_type": "user", "_id": "91031", "_score": 1.357075, "_source": { "nickname": "你有病啊", "height": 180, "expect": "我喜歡學習、逛街、打遊戲", "tags": [ "大學黨", "求偶遇" ] } } ] } }
一條過濾語句會詢問每一個文檔的字段值是否包含着特定值:json
- 是否
created
的日期範圍在xxx
到xxx
- 是否
expect
包含學習
- 是否
location
字段中的地理位置與目標點相距不超過xxkm
使用過濾語句獲得的結果集,快速匹配運算並存入內存是十分方便的,每一個文檔僅須要1個字節。
查詢語句不只要查詢相匹配的文檔,還須要計算每一個文檔的相關性,因此通常來講查詢語句要比過濾語句更耗時,而且查詢結果也不可緩存。
數組
原則上來講:使用查詢語句作全文文本搜索或其餘須要進行相關性評分的時候,剩下的所有使用過濾語句。
緩存
GET /user/user/_search --Body { "query": { "match_phrase": { "expect": "學習" } }, "highlight": { "fields": { "expect": {} } } }
GET /user/user/_search --Body { "query": { "match_all": { } }, "size": 1000, // 返回的數據集大小 "sort": { "nickname.keyword": { // 按nickname排序 "order": "desc" }, "height": { // 按height排序 "order": "desc" } } }
GET /user/user/_search --Body { "from": 2, // 同mysql 的 offset "size": 1 // 同mysql 的 limit }
GET /user/user/_search --Body { "aggs": { "all_tags": { "terms": { "field": "tags" // 這種分詞對於中文很不友好,會把「學習」分爲「學」,「習」 "field": "tags.keyword" // 5.x後的ES,使用這種寫法能夠完美分詞 }, "aggs": { "avg_height": { "avg": { "field": "height" } } } } }
term
過濾term
主要用於精確匹配哪些值,好比數字,日期,布爾值或not_analyzed
的字符串
{"term": {"height": 175}} // height值爲175 (number類型) {"term": {"date": "2014-09-01"}} // date值爲2014-09-01(date類型) {"term": {"public": true}} // public值爲true(布爾類型) {"term": {"nickname": "threads"}} // nickname值爲threads(full_text類型)
terms
過濾terms 跟 term 同樣,但 terms 容許指定多個匹配條件。若是某個字段指定了多個值,那麼文檔須要一塊兒去作匹配文檔。
// 匹配 nickname 爲 threads 或 搖了搖頭 的結果(使用keyword關鍵詞匹配中文) { "query": { "terms": { "nickname.keyword": ["threads", "搖了搖頭"] } } }
range
過濾
range
過濾是按照指定範圍查找數據。
{ "query": {"range":{"height":{"gte": 150, "lt": 180}}} } // gt: 大於 // gte: 大於等於 // lt: 小於 // lte: 小於等於
exists
和 missing
過濾exists
和missing
過濾能夠用於查找文檔中是否包含指定字段或沒有某個字段,相似於SQL語句中的IS_NULL
條件。
// 查詢存在 nickname 字段的結果 {"exists": {"field": "nickname"}}
bool
過濾bool
過濾能夠用來合併多個過濾條件查詢結果的布爾邏輯:數據結構
must
:多個查詢條件的徹底匹配,至關於 and
must_not
: 多個查詢條件的相反匹配,至關於 not
should
:至少有一個查詢條件匹配,至關於 or
{ "bool": { "must": {"term": {"nickname": "threads"}}, "must_not": {"term": {"height": 165}}, "should": [ {"term": {"height": 175}}, {"term": {"nickname": "threads"}}, ] } }
match_all
查詢使用 match_all
能夠查詢到因此文檔,是沒有查詢條件下的默認語句。(經常使用於合併過濾條件)app
{ "match_all":{} }
match
查詢是一個標準查詢,無論你須要全文本查詢仍是精確查詢基本上都要用到它。elasticsearch
{ "match": {"nickname": "threads"} }
multi_match
查詢multi_match
查詢容許你作 match 查詢的基礎上同時搜索多個字段:
// 搜索 nickname 或 expect 包含 threads 的結果 { "multi_match": { "query": "threads", "fields": ["nickname", "expect"] } }
bool
查詢若是bool
查詢下沒有must
子句,那至少應該有一個should
子句。可是若是有must
子句,那麼沒有should
子句也能夠進行查詢。
{ "query": { "bool": { "must": { "multi_match": {"query": "學習", "fields": ["nickname", "expect"]} }, "must_not": { "match": {"height": 175} }, "should": [ {"match": {"nickname": "threads"}} ] } } }
filter
帶過濾的查詢使用filter
來同時使一個語句中包含查詢
和過濾
// nickname 爲 threads 的結果,並在此結果集中篩選出 height 爲 175 的結果 { "query": { "bool": { "must": {"match": {"nickname": "threads"}}, "filter": {"term": {"height":175}} } } }
GET /user/user/_validate/query { "query": { "match_all": {"nickname": "threads"} } } --Response { "valid": false, "error": "ParsingException[[4:13] [match_all] unknown field [nickname], parser not found]; nested: XContentParseException[[4:13] [match_all] unknown field [nickname], parser not found];; org.elasticsearch.common.xcontent.XContentParseException: [4:13] [match_all] unknown field [nickname], parser not found" }
GET /user/user/_validate/query { "query": { "match_all": {"nickname": "threads"} } } --Response { "valid": false, "error": "ParsingException[[4:13] [match_all] unknown field [nickname], parser not found]; nested: XContentParseException[[4:13] [match_all] unknown field [nickname], parser not found];; org.elasticsearch.common.xcontent.XContentParseException: [4:13] [match_all] unknown field [nickname], parser not found" } // 經過返回能夠看出,驗證結果是非法的
{ "error": { "root_cause": [ { "type": "illegal_argument_exception", "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [tags] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead." } ], ... }, "status": 400 }
5.x後對排序,聚合這些操做用單獨的數據結構(fielddata)緩存到內存裏了,須要單獨開啓。
開啓fielddata
PUT user/_mapping/user/ --Body { "properties": { "tags": { "type": "text", "fielddata": true } } }