Elasticsearch中有幾個關鍵屬性容易混淆,很多人搞不清楚_source字段里存儲的是什么?store屬性的true或false和_source字段有什么關系?store屬性設置為true和_all有什么關系?index屬性又起到什么作用?什么時候設置store屬性為true?什么時候應該開啟_all字段?本文通過圖解的方式,深入理解Elasticsearch中的_source、_all、store和index屬性。
如 圖1
所示, 第二象限是一份原始文檔,有title和content2個字段,字段取值分別為”我是中國人”和” 熱愛***”,這一點沒什么可解釋的。我們把原始文檔寫入Elasticsearch,默認情況下,Elasticsearch里面有2份內容,一份是原始文檔,也就是_source字段里的內容,我們在Elasticsearch中搜索文檔,查看的文檔內容就是_source中的內容,如圖2,相信大家一定非常熟悉這個界面。
另一份是倒排索引,倒排索引中的數據結構是倒排記錄表,記錄了詞項和文檔之間的對應關系,比如關鍵詞”中國人”包含在文檔ID為1的文檔中,倒排記錄表中存儲的就是這種對應關系,當然也包括詞頻等更多信息。Elasticsearch底層用的是Lucene的API,Elasticsearch之所以能完成全文搜索的功能就是因為存儲的有倒排索引。如果把倒排索引拿掉,Elasticsearch是不是和mongoDB很像?
那么文檔索引到Elasticsearch的時候,默認情況下是對所有字段創建倒排索引的(動態mapping解析出來為數字類型、布爾類型的字段除外),某個字段是否生成倒排索引是由字段的index屬性控制的,在Elasticsearch 5之前,index屬性的取值有三個:
- analyzed:字段被索引,會做分詞,可搜索。反過來,如果需要根據某個字段進搜索,index屬性就應該設置為analyzed。
- not_analyzed:字段值不分詞,會被原樣寫入索引。反過來,如果某些字段需要完全匹配,比如人名、地名,index屬性設置為not_analyzed為佳。
- no:字段不寫入索引,當然也就不能搜索。反過來,有些業務要求某些字段不能被搜索,那么index屬性設置為no即可。
再說_all字段,顧名思義,_all字段里面包含了一個文檔里面的所有信息,是一個超級字段。以圖中的文檔為例,如果開啟_all字段,那么title+content會組成一個超級字段,這個字段包含了其他字段的所有內容,當然也可以設置只存儲某幾個字段到_all屬性里面或者排除某些字段。
回到圖一的第一象限,用戶輸入關鍵詞" 中國人"
,分詞以后,Elasticsearch從倒排記錄表中查找哪些文檔包含詞項"中國人 "
,注意變化,分詞之前" 中國人"
是用戶查詢(query),分詞之后在倒排索引中" 中國人"
是詞項(term)。Elasticsearch根據文檔ID(通常是文檔ID的集合)返回文檔內容給用戶,如圖一第四象限所示。
通常情況下,對於用戶查詢的關鍵字要做高亮處理,如圖3所示:
關鍵字高亮實質上是根據倒排記錄中的詞項偏移位置,找到關鍵詞,加上前端的高亮代碼。這里就要說到store屬性,store屬性用於指定是否將原始字段寫入索引,默認取值為no。如果在Lucene中,高亮功能和store屬性是否存儲息息相關,因為需要根據偏移位置到原始文檔中找到關鍵字才能加上高亮的片段。在Elasticsearch,因為_source中已經存儲了一份原始文檔,可以根據_source中的原始文檔實現高亮,在索引中再存儲原始文檔就多余了,所以Elasticsearch默認是把store屬性設置為no。
注意:如果想要對某個字段實現高亮功能,_source和store至少保留一個。下面會給出測試代碼。
至此,文章開頭提出的幾個問題都給出了答案。下面給出這幾個字段常用配置的代碼。
1. _source
配置
_source
字段默認是存儲的, 什么情況下不用保留_source
字段?如果某個字段內容非常多,業務里面只需要能對該字段進行搜索,最后返回文檔id
,查看文檔內容會再次到mysql
或者hbase
中取數據,把大字段的內容存在Elasticsearch
中只會增大索引,這一點文檔數量越大結果越明顯,如果一條文檔節省幾KB,放大到億萬級的量結果也是非常可觀的。
如果想要關閉_source
字段,在mapping
中的設置如下:
{ "yourtype":{ "_source":{ "enabled":false }, "properties": { ... } } }
如果只想存儲某幾個字段的原始值到Elasticsearch
,可以通過incudes
參數來設置,在mapping
中的設置如下:
{ "yourtype":{ "_source":{ "includes":["field1","field2"] }, "properties": { ... } } }
同樣,可以通過excludes
參數排除某些字段:
{ "yourtype":{ "_source":{ "excludes":["field1","field2"] }, "properties": { ... } } }
示例:
# 首先創建一個索引 PUT test # 設置mapping,禁用_source: PUT test/test/_mapping { "test": { "_source": { "enabled": false } } } # 寫入一條文檔: POST test/test/1 { "title":"我是中國人", "content":"熱愛***" #敏感詞 } # 搜索關鍵詞”中國人”: GET test/_search { "query": { "match": { "title": "中國人" } } } { "took": 9, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 1, "max_score": 0.30685282, "hits": [ { "_index": "test", "_type": "test", "_id": "1", "_score": 0.30685282 } ] } }
從返回結果中可以看到,搜到了一條文檔,但是禁用_source
以后查詢結果中不會再返回文檔原始內容。(注,測試基於ELasticsearch 2.3.3
,配置文件中已默認指定ik分詞
。)
2. _all
配置
_all
字段默認是關閉的,如果要開啟_all
字段,索引增大是不言而喻的。_all
字段開啟適用於不指定搜索某一個字段,根據關鍵詞,搜索整個文檔內容。
開啟_all
字段的方法和_source
類似,mapping
中的配置如下:
{ "yourtype": { "_all": { "enabled": true }, "properties": { ... } } }
也可以通過在字段中指定某個字段是否包含在_all
中:
{ "yourtype": { "properties": { "field1": { "type": "string", "include_in_all": false }, "field2": { "type": "string", "include_in_all": true } } } }
如果要把字段原始值保存,要設置store
屬性為true
,這樣索引會更大,需要根據需求使用。下面給出測試代碼。
創建test
索引:
DELETE test
PUT test
設置mapping
,這里設置所有字段都保存在_all
中並且存儲原始值:
PUT test/test/_mapping { "test": { "_all": { "enabled": true, "store": true } } }
插入文檔:
POST test/test/1 { "title":"我是中國人", "content":"熱愛***" }
對_all
字段進行搜索並高亮:
POST test/_search { "fields": ["_all"], "query": { "match": { "_all": "中國人" } }, "highlight": { "fields": { "_all": {} } } } # 查詢結果 { "took": 3, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 1, "max_score": 0.15342641, "hits": [ { "_index": "test", "_type": "test", "_id": "1", "_score": 0.15342641, "_all": "我是中國人 熱愛*** ", "highlight": { "_all": [ "我是<em>中國人</em> 熱愛*** " ] } } ] } }
Elasticsearch
中的query_string
和simple_query_string
默認就是查詢_all
字段,示例如下:
GET test/_search { "query": { "query_string": { "query": "***" } } }
3. index
和store
配置
index
和store
屬性實在字段內進行設置的,下面給出一個例子,設置test
索引不保存_source
,title字
段索引但不分析,字段原始值寫入索引,content
字段為默認屬性,代碼如下:
DELETE test PUT test PUT test/test/_mapping { "test": { "_source": { "enabled": false }, "properties": { "title": { "type": "string", "index": "not_analyzed", "store": "true" }, "content": { "type": "string" } } } }
對title
字段進行搜索並高亮,代碼如下:
GET test/_search { "query": { "match": { "title": "我是中國人" } }, "highlight": { "fields": { "title": {} } } } # 查詢結果 { "took": 6, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 1, "max_score": 0.30685282, "hits": [ { "_index": "test", "_type": "test", "_id": "1", "_score": 0.30685282, "highlight": { "title": [ "<em>我是中國人</em>" ] } } ] } }
從返回結果中可以看到,雖然沒有保存title
字段到_source
, 但是依然可以實現搜索高亮。
原文鏈接:https://blog.csdn.net/napoay/article/details/62233031