這是ElasticSearch 2.4 版本系列的第九篇:html
ElasticSearch 2.4版本支持Java正則表達式查詢,可是,在對大段的文本(Text Block)進行挖掘以前,必須瞭解正則表達式查詢的特殊之處。因爲分析器會對文本字段進行分詞,移除停用詞,小寫轉換等操做,最終存儲在倒轉索引中的是小寫的標記流(Token Stream),默認狀況下,每個標記是一個分詞(Term),這沒法知足正則表達式查詢的通常要求,這就是說,正則表達式查詢的是原始文本,須要注意的是,ElasticSearch引擎都是從原始文本的第一個字符開始執行正則表達式匹配。正則表達式
在ElasticSearch 2.4版本中啓用正則表達式查詢以前,須要考慮兩個問題:分詞嗎?大小寫敏感嗎?編程
一,分詞仍是不分詞?數組
一般狀況下,ElasticSearch引擎對文本字段進行分詞,移除停用詞,轉換成小寫,這是全文搜索的標準配置,在這種設置下,正則表達式只能匹配文本字段的單個分詞,而沒法對原始文本執行正則表達式查詢,爲了實現正則表達式查詢,必須設置文本字段不被分詞,也就是是設置該字段的index屬性爲not_analyzed。在實際的產品環境中,對一個字段同時執行正則表達式查詢和全文搜索的狀況是常常存在的,ElasticSearch 2.4版本提供的映射參數 fields 可以知足該需求。網絡
fields 參數:多元字段(multi-fields),用不一樣的處理方式,把一個相同的字段編入索引,以實現不一樣的目的。多元字段使用相同的數據派生新的字段,例如,一個字段field被編入索引做爲分析字段(analyzed field)以執行全文搜索,把該字段設置爲多元字段,那麼ElasticSearch引擎派生一個新的字段field .raw,該字段文本做爲一個詞被編入索引,只對該字段執行排序或聚合操做。app
注:多元字段(multi-fields)不一樣於多值字段(multi-values field),字段的多值是ElasticSearch內在支持的特性,「開箱即用」,不須要作任何配置。每一個字段都能存儲多個數據值,這就是意味着,每一個字段都是數組類型,只不過在字段中存儲的數據,其數據類型都是相同的。elasticsearch
1,多元字段使用示例ide
在示例索引映射中,eventdescription是一個多元字段,其index屬性是analyzed,表示該字段是分析字段,ElasticSearch引擎把該字段的文本分析成分詞流,編入索引,以執行全文搜索,這就意味着,倒排索引中存儲的不是該字段的原始文本,而是分割的單個分詞;而eventdescription.raw是多元字段的派生字段,其index屬性是not_analyzed,表示該派生字段不會被分詞,整個文本字段總體做爲一個分詞被編入索引,這就意味着,倒排索引中存儲的是派生字段原始的文本值。post
"eventdescription":{ "type":"string",
"index":"analyzed", "fields":{ "raw":{ "type":"string",
"index":"not_analyzed" } } }
2,對原始文本執行正則表達式查詢性能
ElasticSearch引擎在處理分析字段(analyzed field)時,使用指定的分析器執行分析操做,包括分詞,移除停用詞,轉換大小寫等,若是一個字段不是分析字段,那麼ElasticSearch引擎不會對其執行任何分析工做。
映射參數index:決定ElasticSearch引擎是否對文本字段執行分析操做,也就是說分析操做將分割文本,把分詞編入索引,並使分詞可以被搜索到:
這也就意味着,要對原始文本執行正則表達式查詢,必須設置index屬性爲not_analyzed,這也就意味着,保留文本的原始形式,例如大小寫,空格等。
3,單個分詞的最大長度
若是設置字段的index屬性爲not_analyzed,原始文本將做爲單個分詞,其最大長度跟UTF8 編碼有關,默認的最大長度是32766Bytes,若是字段的文本超過該限制,那麼ElasticSearch將跳過(Skip)該文檔,並在Response中拋出異常消息:
operation[607]: index returned 400 _index: ebrite _type: events _id: 76860 _version: 0 error: Type: illegal_argument_exception Reason: "Document contains at least one immense term in field="event_raw" (whose UTF8 encoding is longer than the max length 32766), all of which were skipped. Please correct the analyzer to not produce such terms. The prefix of the first immense term is: '[112, 114,... 115]...', original message: bytes can be at most 32766 in length; got 35100" CausedBy:Type: max_bytes_length_exceeded_exception Reason: "bytes can be at most 32766 in length; got 35100"
能夠在字段中設置ignore_above屬性,該屬性值指的是字符數量,而不是字節數量;因爲一個UTF8字符最多佔用3個字節,所以,能夠設置
「ignore_above」:10000
這樣,超過30000字節以後的字符將會被分析器忽略,單個分詞(Term)的最大長度是30000Bytes。
The value for
ignore_above
is the character count, but Lucene counts bytes. If you use UTF-8 text with many non-ASCII characters, you may want to set the limit to32766 / 3 = 10922
since UTF-8 characters may occupy at most 3 bytes.
二,大小寫敏感?
正則表達式查詢通常是區分大小寫的,有時,咱們可能會但願,正則表達式查詢忽略大小寫,在這種狀況下,多元字段(fields)就沒法知足需求了,多元字段不能執行文本大小寫轉換。爲了解決這個問題,咱們能夠新建一個字段,在更新索引時,把原始文本導入到分析字段,把相同的數據轉換成小寫形式導入到另外一個字段中,這樣作之後,分析字段及其派生字段,用於支持全文搜索和大小寫敏感的正則表達式查詢,另一個字段用於忽略大小寫的正則表達式搜索。
"eventdescription":{ "type":"string",
"index":"analyzed", "fields":{ "raw":{ "type":"string",
"index":"not_analyzed" } } }, "eventdescription_lowcase":{ "type":"string",
"index":"not_analyzed" } }
三,存儲控制
爲了實現正則表達式查詢,上例爲一個數據建立三個字段,eventdescription、eventdescription.raw和 eventdescription_lowcase,這三個字段都須要存儲在倒排索引中,ElasticSearch引擎是否使用3倍的容量來存儲這個數據?
默認狀況下,一旦字段值被編入索引,該字段可以被搜索,可是,字段的原始值沒有存儲到倒排索引中,這就意味着,該字段可以被搜索,卻不能從倒排索引中獲取該字段的原始值。一般狀況下,這樣設計可以節省硬盤存儲空間,不會對應用程序有什麼影響,實際上,ElasticSearch引擎把該字段的原始值存儲在_source元字段中,默認狀況下,_source元字段是存儲的。
1,存儲屬性(store)
字段的原始值是否被存儲到倒排索引,是由映射參數store決定的,默認值是false,也就是,原始值不存儲到倒排索引中。在特定的狀況下,存儲字段的原始值是有意義的。例如,爲了存儲一篇博客,文檔必須有title,date和一個很是大的正文字段(content),若是僅僅是獲取title和date,而不用獲取正文字段,那麼你能夠存儲title和date,並把content字段的store屬性設置爲false。
"title":{ "type":"string", "store":true, "index":"analyzed" }, "date":{ "type":"date", "store":true, "index":"not_analyzed" }, "content":{ "type":"string", "store":false, "index":"analyzed" }
映射參數index和store的區別在於:
2,源字段(_source)
當把原始的JSON文檔傳遞到ElasticSearch引擎時,ElasticSearch引擎使用_source字段存儲最原始的JSON文檔。_source字段自己不會被索引,也不會被搜索,可是,該字段會存儲在倒排索引中,用於返回查詢的結果。
_source字段會致使索引存儲空間的增長,所以,能夠禁用_source字段,可是,在禁用_source字段以前,請認真閱讀官方文檔《_source field》。
"mappings": { "tweet": { "_source": { "enabled": false } } }
通常狀況下,不要禁用_source字段,當須要考慮佔用的Disk空間時,請有限考慮壓縮存儲,提升壓縮等級。在配置文檔 中,壓縮選項是 index.codec,默認值是LZ4壓縮,設置best_compression 可以提供更高的壓縮率,代價是下降數據存儲的性能。
四,一個字段包含全部文本?
爲了實現正則表達式查詢,上例爲一個數據建立三個字段,eventdescription、eventdescription.raw和 eventdescription_lowcase,這三個字段都須要存儲在倒排索引中,一般作法是,同時對這三個字段執行正則表達式查詢,可是在ElasticSearch中,在編碼上,能夠更簡單。元字段 _all是一個特殊的「一應俱全」(catch-all)的字段,把其餘字段的值拼接成一個大的字符串,字段值之間使用空格分隔。ElasticSearch引擎先分析_all字段,而後編入索引,可是,默認狀況下,不會存儲字段的原始值,這就意味着,_all字段可以被搜索,可是不會返回原始值。_all 字段把全部字段的原始值,都視爲字符類型,並把字段的原始值經過分隔符空格拼接在一塊兒。
注意,添加到_all字段的是原始值,而不是字段分析以後的詞條(term)。字段的原始值是否包含到_all字段,是由該字段的屬性 include_in_all控制的,默認值是true。啓用_all字段是須要付出代價的,_all字段會消耗額外的CPU時鐘週期和更多的硬盤空間,若是不是必需,推薦把_all字段禁用掉。
"content": { "type": "string", "include_in_all": false },
當把_all字段禁用以後,用戶能夠建立自定義的"_all"字段:新建一個數據類型爲string的字段,並在須要拼接的字段中設置屬性「copy_to」。
在ElasticSearch中,每一個索引只有一個_all字段,經過字段的屬性copy_to可以建立自定義的"_all"字段。例如,字段 first_name和 last_name可以經過分隔符空格被拼接到一塊兒,做爲full_name字段的值。
{ "mappings": { "mytype": { "properties": { "first_name": { "type": "string", "copy_to": "full_name" }, "last_name": { "type": "string", "copy_to": "full_name" }, "full_name": { "type": "string" } } } } } PUT myindex/mytype/1 { "first_name": "John", "last_name": "Smith" } GET myindex/_search { "query": { "match": { "full_name": "John Smith" } } }
默認狀況下,_all字段不會存儲_source字段的值,也不會存儲原始值,這是由於_all字段是其餘字段結合在一塊兒組成的,存儲_all字段會佔用大量的硬盤存儲空間,若是設置_all字段的屬性store爲true,那麼ElasticSearch引擎將會存儲_all字段的原始值,其原始值也可以被獲取到。
五,示例
綜上所述,爲了實現正則表達式查詢,爲了實現正則表達式的查詢,有兩個設計思路
示例1,原始文本和小寫文本各使用一個字段
經過bool查詢的should子句,對多個字段執行正則表達式查詢,當字段較多,或者字段的文本特別大時,使用該方式,節省硬盤空間,可是要編寫更多的查詢代碼:
"eventdescription":{ "type":"string", "index":"analyzed", "fields":{ "raw":{ "type":"string", "index":"not_analyzed", "ignore_above":10000 } } }, "eventdescription_lowcase":{ "type":"string", "index":"not_analyzed", "ignore_above":10000 } }
示例2,添加冗餘字段
在冗餘字段上執行正則表達式查詢,當在多個字段上執行相同的正則表達式時,使用該方式,便於編程,可是要注意,拼接字段的大小不能超過限制(32766Bytes):
"eventdescription":{ "type":"string", "index":"analyzed", "copy_to":"eventdescription_regexp" }, "eventdescription_lowcase":{ "type":"string", "index":"not_analyzed", "copy_to":"eventdescription_regexp" }, "eventdescription_regexp":{ "type":"string", "index":"not_analyzed" }
參考文檔:
Elasticsearch Reference [2.4] » Mapping » Meta-Fields
Elasticsearch Reference [2.4] » Mapping » Mapping parameters
Elasticsearch Reference [2.4] » Query DSL » Term level queries » Regexp Query