這篇文章已是第四篇了,前面不少都只是講了基礎的使用,沒有講到內層的原理,因此這裏就要補一下原理知識了。node
先回顧一下咱們前面學過了什麼,再想一想咱們漏了什麼。
第一篇咱們認識了ElasticSearch,大概知道了ElasticSearch的做用--搜索,也瞭解了一些倒排索引和分詞器的知識(須要補充),而後學習瞭如何搭建環境(須要補充一下關於基礎的集羣知識),而後講了一些基礎的ElasticSearch概念(從新講述,加深瞭解)。
第二篇講了索引和文檔的CRUD,講建立索引的時候,沒有講mapping,講文檔的時候沒有講文檔的數據類型和元數據,這些都須要補充。
第三篇講了文檔的搜索,主要是語法方面的問題,但相關度分數是怎麼計算出來的,咱們並無講。git
因此下面將對前面漏了的基礎知識進行補充:github
咱們說了,ElasticSearch是分佈式的,但咱們以前只說瞭如何啓動ElasticSearch,而後使用Kibana操做ElasticSearch,並無講ElasticSearch的集羣式如何創建的,因此下面將會講一下集羣的知識。但如何管理集羣會單獨作成一篇來說。web
首先,每個ElasticSearch服務端就是一個集羣節點,當咱們啓動一個elasticsearch時,就至關於啓動了一個集羣節點。
那麼,多個節點之間如何創建聯繫呢?當咱們啓動一個節點的時候,這個節點會自動建立一個集羣,集羣的名稱默認爲elasticsearch,當咱們再次啓動一個節點的時候,它首先會嘗試尋找名稱爲elasticsearch的集羣,而後加入其中,(全部的都是先嚐試尋找,沒有再本身建立)。【集羣名稱能夠自行配置,下面講】
當節點加入到集羣中後,這個集羣就算是創建起來了。算法
上面講了集羣名稱能夠自行配置,下面講一下怎麼配置。
ElasticSearch的配置文件是config/elasticsearch.yml,裏面有如下幾個配置項:
【左邊是配置項,右邊是值,修改配置也就是修改右邊的值】數據庫
cluster.name: my-application
path.data: /path/to/data
path.logs: /path/to/logs
network.host: 192.168.0.1
http.port: 9200
action.destructive_requires_name
,true爲禁止。
當集羣創建後,咱們可使用命令來查看集羣狀態:json
GET /_cluster/health
:以json方式顯示集羣狀態GET /_cat/health?v
:行列式顯示集羣狀態
集羣狀態解析:
集羣狀態受當前的primary shared和replica shared的數量影響。
當每一個索引的primary shared和replica shared都是active的時候,狀態爲green;【什麼是active?shard是位於節點上的,一個shard被分配到了運行的節點上,那麼此時就是active的,若是shard沒有分配到節點上,那麼就是inactive】
當每一個索引的primary shared都是active的,但replica shared不徹底是active的時候,狀態爲yellow;
當每一個索引的primary shared不徹底是active的時候,此時發生了數據丟失,狀態爲red。api
爲何如今是yellow?
當咱們第一次啓動的時候,kibana會默認爲咱們建立一個名爲kibana的索引,這個索引的primary shard和replica shard都爲1,因爲此時只有一個節點,因此會優先分配primary shard,而忽略replica shard(不要忘了基於備份的安全性的shard排斥考慮:主分片和副本分片不能位於同一個節點上),因此此時就符合了「每一個索引的primary shared都是active的,但replica shared不全是active的」,因此此時是yellow.
你能夠嘗試啓動多一個elasticsearch節點來調整狀態。【固然,我以爲你學到這裏了,此時replica shard的數量可能已經變了,因此這裏只啓動多一個可能已經不夠了,具體的下面「分片的管理」講】數組
上面講了當啓動了一個elasticsearch以後,這個節點先尋找集羣,沒有的話就本身建立一個,而後後續的其餘節點也會加入這個節點,從而自動實現了自動集羣化部署。
以前講了一些分片的知識,但並無具體用到,可能有些人已經忘了這個概念。這裏從新講一下。
文檔的數據是存儲到索引中的,而索引的數據是存儲在分片上的,而分片是位於節點上的。
分片shard有主分片primary shard和複製分片replica shard兩種,其中主分片是主要存儲的分片,能夠進行讀寫操做;副本分片是主分片的備份,能夠進行讀操做(不支持寫操做)。
查詢能夠在主分片或副本分片上進行查詢,這樣能夠提供查詢效率。【但數據的修改只發生在主分片上。】
分片是面向索引的,分片上的數據屬於同一個索引,在咱們建立索引的時候,能夠指定主分片和副本分片的數量,默認是5個主分片,5個副本分片。
索引的replica shard的數量能夠修改,但primary shard的數量不能夠修改。【一個Primary Shard能夠有多個Replica Shard,默認建立是1個。】
修改replica shard數量: PUT /douban/_settings { "number_of_replicas":1 }
爲了保證數據的不丟失,一般來講Replica Shard不能與其對應的Primary Shard處於同一個節點中。【由於萬一這個節點損壞了,那麼存儲在這個節點上的原數據(primary shard)和備份數據(replica shard)就所有丟失了】,可是能夠和其餘primary shard的replica shard放在一塊兒
replica shard是primary shard的副本分片,負責承載必定的讀請求,當primary shard都掛掉後,其中一個replica shard會變成primary shard來維持寫功能。
分片是分配到節點上的,但它會默認地均衡分配。
所謂均衡分配,以狀況舉例:【priX表明主分片XX,repX表明副本分片XX】
1.索引有1個priA,1個repA,但當前只有一個節點A,那麼priA分配到A節點;repA沒有分配。優先分配主分片。
2.索引有1個priA,1個repA,當前有兩個節點A和B,那麼priA分配到A節點;repA分配到B節點(priA分配到B節點也有可能)。
3.索引當前有2個priA,4個repA,有3個節點,那麼如今每臺節點上有兩個分片(但要考慮主分片不能與本身的副本分片同在一個節點上,在下面的「 主副分片的排斥」中由一個例子)。
【當後續新增節點的時候,會自動再次從新均衡分配。】
考慮到備份的安全性,不該該讓主分片和副本分片位於一個節點上,否則可能徹底丟失數據。(好比你爲了不丟失鑰匙,你給你家的門準備兩條鑰匙,你有一個錢包和一個揹包,結果你把兩條鑰匙都放到錢包中,某天你錢包丟了,因而你回不了家了。。。因此你應該把鑰匙分開放。)
對應的排斥狀況就是:
1.索引有1個priA,1個repA,但當前只有一個節點A,那麼priA分配到A節點;repA沒有分配。
2.索引有1個priA,1個repA,當前有兩個節點A和B,那麼priA分配到A節點;repA分配到B節點(priA分配到B節點也有可能)。
【請注意,主分片與副本分片不能在一塊兒,但副本分片和副本分片能存放在一塊兒】
在上面的「分片的均衡分配」沒有提到主副分片的排斥的問題,下面再舉一個例子【下述的分配結果還有多是其餘的】:
既然提到了備份的安全性,那麼就不得不提一下容錯性了。節點是有可能宕機的,宕機後,那麼這個節點的數據起碼會暫時性的丟失,那麼對於不一樣狀況下,最多能夠宕機多少個節點呢?下面舉例:【pri = x表明主分片數量爲X,rep是副本分片】
1.若是你只有一個節點,那麼容錯性爲0,你不能宕機,宕機不徹底意味着數據徹底丟失,但暫停服務仍是有的。
2.若是你有兩個節點,pri = 2,rep = 2,那麼此時分片分配應該是[P1R2, P2R1],此時容錯性爲一個,由於某個節點上有完整的兩個分片的數據【此處一提,假設丟失了P2R1,那麼R1R2中的R2會升級成primary shard來保持寫功能】
3.後面的本身嘗試算一下吧!要綜合均衡分片和排斥性來考慮。
每個document會存儲到惟一的一個primary shared和其副本分片replica shard 中,咱們是怎麼根據ID來知道這個document存儲在哪一個分片的呢?
ElasticSearch會自動根據document的id值來進行計算,最終得出一個小於分片數量的數值,這個數值就是分片在ElasticSearch中的序號,最終得出應該存儲到哪一個分片上。
相似原理舉例,假設我如今有4個主分片,那麼我給它們標序號0,1,2,3。如今進來一個ID爲15的數據,我通過一系列計算以後,15入參以後假設獲得243這個數值,而後咱們使用243來對4求餘,餘數是3,因此就把這個數據放在序號爲3的分片上。
依據這個原理,存儲數據的時候就知道把數據放在哪一個分片上;讀取數據的時候也知道從哪一個分片上讀取數據。
【稍微提一下,在ElasticSearch全文搜索完成以後,此時內部獲得的是ID組成的數組,內部再會根據ID來查找數據】
上面說了,集羣狀態受當前的primary shared和replica shared的數量影響。
當每一個索引的primary shared不徹底是active的時候,此時發生了數據丟失,狀態爲red。
如今學會了「分片的均衡分配」和「主副分片的排斥」後,你應該能分析出要啓動多少個節點才能改變集羣的健康狀態。
下面舉個例子,以變green爲例:
當前節點中只有一個索引A,索引A當前有2個priA,4個repA,那麼此時須要兩個節點才能夠變green,
一個的時候就不說了,此時全部副本分片都inactive;兩個節點的時候,因爲每一個主分片有兩個副本分片,此時的分配是[P1-R11-R12,P2-R21-R22]
本節從新講了主分片和副本分片的功能,主分片和副本分片都有存儲數據的功能,一個索引的數據平分到多個主分片上,副本分片拷貝對應主分片的數據;而後講了ElasticSearch對於分片的自動均衡分配,和主分片和副本分片不能存儲在一塊兒的問題。而後講了ElasticSearch如何根據ID來判斷數據存儲在哪一個分片上。最後講了節點上分片的分配對於集羣健康狀態的影響。
索引的數據是存儲在節點上的,當一個請求發到節點上的時候,可能這個節點上並無這個索引的數據,那麼這個時候就須要把請求轉發給另外一個節點了,這時候本來的節點就是一個「協調節點」。
一個索引存儲在多個主分片和副本分片上,索引的數據會平均分配到每個主分片中,而後每個副本分片拷貝對應主分片的數據。
對於數據存儲,數據是存儲在分片上的,而分片位於節點上,在上面說了分片會均衡分配到每個節點上,這樣也保證了節點上的數據量是平均的。除此以外,對於讀取某一個主分片及其副本分片上的數據的時候,會使用輪詢算法來將讀請求平均分配(大概意思就是,假設如今對於主分片1有三個副本分片,那麼總數爲4,假設分別編號一、二、三、4,那麼可能地,第一次請求交給了1,那麼第二次請求要交給2,第三次要交給3。。。以此類推,超過4則從頭開始)。
本節簡單說了一下讀寫操做的流程,ID類的讀請求直接根據ID來查找文檔,搜索類的讀請求先搜索索引表,再根據ID來查找文檔;寫數據請求會根據ID來計算應該把這個數據存儲到哪一個主分片上,而後經過主分片來修改數據;最後講了一下對於請求分發的負載均衡,一方面經過數據量的平均分配來均衡,一方面使用輪詢算法來下降單個分片的處理壓力。
你在查詢文檔的時候,你可能看到返回結果中有以下的內容:
上面的幾個前綴有_
的就是元數據。
_index
:表明當前document存放到哪一個index中。_type
:表明當前document存放在index的哪一個type中。_id
:表明document的惟一標識,與index和type一塊兒,能夠惟一標識和定位一個document。【在前面咱們都是手動指定的,其實能夠不手動指定,那樣會隨機產生要給惟一的字符串做爲ID】_version
:是當前document的版本,這個版本用於標識這個document的「更新次數」(新建、刪除、修改都會增長版本)_source
:返回的結果是查詢出來的當前存儲在索引中的完整的document數據。以前在搜索篇中講到了,咱們可使用_source
來指定返回docuemnt的哪些字段。元數據與具體的文檔數據無關,每個文檔都有這些數據。
對於web中的json,數據格式主要有字符串
、數值
、數組
和{}
這幾種。
好比:
{ "name": "neo", "age": 18, "tool": ["clothes", "computer", "gun"], "gf": { "feature": "beauty" } }
ElasticSearch並非這樣的,由於它要考慮分詞,就算是字符串,它也要考慮裏面的數據是否是日期類型的,日期類型一般不會分詞。
ElasticSearch主要有如下這幾種數據類型:
string
:string在5.x以前可使用,如今已被text和keyword取代。
mapping負責維護index中文檔的結構,包括文檔的字段名、字段數據類型、分詞器、字段是否進行分詞等。這些屬性會對咱們的搜索形成影響。
在前面,其實咱們都沒有定義過mapping,直接就是插入數據了。其實這時候ElasticSearch會幫咱們自動定義mapping,這個mapping會依據文檔的數據來自動生成。
此時,若是數據是字符串的,會認爲是text類型,而且默認進行分詞;若是數據是日期類型類(字符串裏面的數據是日期格式的),那麼這個字段會認爲是date類型的,是不分詞的;若是數據是整數,那麼這個字段會認爲是long類型的數據;若是數據是小數,那麼這個字段會認爲是float類型的;若是是true或者false,會認爲是boolean類型的。
咱們插入如下數據【若是你用了這個索引,那麼能夠自定義一個索引,避免我隨意建立的數據污染了你的測試數據】:
PUT /test/person/1 { "name":"suke", "age":18, "tall":178.5, "birthdate":"2018-01-01" }
而後查看索引,其中mapping定義了咱們剛剛定義的字段的信息:
GET /test
【以前在第二篇中有提到,能夠經過查看索引來查看mapping】
語法:
【[]表明裏面的內容是可選的,非必須的】 PUT /index/ { "settings": { "index": { "number_of_shards": 3, "number_of_replicas": 1 } }, "mappings": { "type名": { "properties": { "字段名": { "type": "數據類型", // 是否索引,不索引則不將這個字段列入索引表,沒法對這個字段進行搜索 ["index": "是否索引,值爲true或者fasle",] // 是否新舊keyword保留原始數據 ["fields": { "keyword": { "type": "keyword",【這個是保留原始數據採用的數據類型,也可使用text,通常用keyword】 "ignore_above": 256【超過多少字符就忽略,不創建keyword】 } },] // (選擇什麼分詞器來對這個字段進行分詞) ["analyzer": "分詞器名稱" ,] } } } } }
【內容補充:在舊版中,index的值有not_analyzed這樣的值,如今只有true或false,它的原意是不分詞,如今是不索引,在舊版中,不分詞則會保留原始數據,在新版中使用keyword來保留原始數據】
舉例:
1.建立一個mapping,只定義數據類型:【定義了每一個字段的數據類型後,插入數據的時候並無說要嚴格遵循,數據類型的做用是提早聲明字段的數據類型,例如在以前第二篇說type的時候,就提到了多個type中的字段其實都會彙總到mapping中,若是不提早聲明,那麼可能致使由於使用dynamic mapping而使得數據類型定義出錯。好比在type1中birthdate字段】 > 第二篇中這樣說的:當咱們直接插入document的時候,若是不指定document的數據結構,那麼ElastciSearch會基於dynamic mapping來自動幫咱們聲明每個字段的數據類型,好比"content":"hello world!"會被聲明成字符串類型,"post_date":"2017-07-07"會被認爲是date類型。若是咱們首先在一個type中聲明瞭content爲字符串類型,再在另一個type中聲明成日期類型,這會報錯,由於對於index來講,這個content已經被聲明成字符串類型了。 PUT /test0101 { "settings": { "index":{ "number_of_shards":3, "number_of_replicas":1 } }, "mappings": { "person":{ "properties": { "name":{ "type": "text" }, "age":{ "type": "long" }, "birthdate":{ "type":"date" } } } } } 2.設置某個字段不進行索引【設置後,你能夠嘗試對這個字段搜索,會報錯!】 PUT /test0102 { "settings": { "index":{ "number_of_shards":3, "number_of_replicas":1 } }, "mappings": { "person":{ "properties": { "name":{ "type": "text", "index": "false" }, "age":{ "type": "long" }, "birthdate":{ "type":"date" } } } } } 測試: PUT /test0102/person/1 { "name":"Paul Smith", "age":18, "birthdate":"2018-01-01" } GET /test0102/person/_search { "query": { "match": { "name": "Paul" } } } 3.給某個字段增長keyword PUT /test0103 { "settings": { "index":{ "number_of_shards":3, "number_of_replicas":1 } }, "mappings": { "person":{ "properties": { "name":{ "type": "text", "index": "false", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "age":{ "type": "long" }, "birthdate":{ "type":"date" } } } } } 測試: PUT /test0103/person/1 { "name":"Paul Smith", "age":18, "birthdate":"2018-01-01" } 【注意這裏是不能使用name來搜索的,要使用name.keyword來搜索,並且keyword是對原始數據進行**不分詞**的搜索的,因此你搜單個詞是找不到的。】 GET /test0103/person/_search { "query": { "match": { "name.keyword": "Paul Smith" } } }
mapping只能新增字段,不能修改原有的字段。
// 給索引test0103的類型person新增一個字段height PUT /test0103/_mapping/person { "properties": { "height":{ "type": "float" } } }
1.以前說過了,能夠經過查看索引來查看mapping:GET /index
,例如GET /test0103/_mapping
2.經過GET /index/_mapping
,例子:GET /test0103/_mapping
3.你也能夠附加type來查看指定type包裹的mapping。GET /test0103/_mapping/person
上面定義mapping的時候有定義一個keyword,這是什麼?有什麼用?
ElasticSearch對於一些類型的字段,例如text類型的字段,默認是會進行分詞的。但若是咱們並不想分詞呢?一些數據咱們想要很是精確地查找,而且只找到咱們搜索的數據的時候,這個字段是不該該分詞的。那麼咱們可使用keyword來存儲完整的原有的數據,keyword會做爲一個索引詞,而後咱們針對字段.keyword
來搜索。【例子上面已經舉例了】理論上,這個不分詞的字段的數據應該是比較簡短的,太長的話可能就沒有必要不分詞了。因此在定義keyword的時候還能夠提供
"ignore_above": 數值
來限制超過多少個字符就不使用原有數據建立keyword.
如今版本中,對於默認分詞的字段,如今會默認附加一個keyword。
1.數據類型的影響:
以date和text類型存儲的「日期」類型的數據的分詞差別化爲例:
date和string這二者都是使用雙引號包裹的,與其餘數值型的不分詞的數據類型不同。因此以他們兩個爲例。
PUT /test0104 { "settings": { "index":{ "number_of_shards":3, "number_of_replicas":1 } }, "mappings": { "test":{ "properties": { "post_date":{ "type": "text" }, "birthdate":{ "type":"date" } } } } } PUT /test0104/test/1 { "post_date":"2019-09-30", "birthdate":"2018-08-29" } PUT /test0104/test/2 { "post_date":"2018-09-30", "birthdate":"2017-08-29" } PUT /test0104/test/3 { "post_date":"2017-09-30", "birthdate":"2016-08-29" } //測試1:結果是ID:1 GET /test0104/test/_search { "query": { "match": { "post_date": "2019" } } } // 測試2:結果是ID=1,2,3的文檔,理論上text存儲的日期格式的沒有分詞區別 GET /test0104/test/_search { "query": { "match": { "post_date": "30" } } } //測試3:結果無,【單搜索08或2017,2018,2016也是無,】 GET /test0104/test/_search { "query": { "match": { "birthdate": "29" } } } //測試4, 結果是ID=2的文檔 GET /test0104/test/_search { "query": { "match": { "birthdate": "2017-08-29" } } }
2.不索引的影響:
不索引的時候不會進行分詞,甚至不能用於搜索。【低版本對於properties中的index設置不同,5.x如下是不分詞,5.x以上是不索引,不索引就不能夠用於搜索】
3.keyword的影響:
keyword適用於不分詞的搜索的狀況,在keyword中的數據不會分詞。
4.其餘:
mapping還能夠設置分詞器來使用不一樣的分詞器來分詞。
// 你可使用格式相似以下的代碼來測試mapping中某個字段的分詞結果,若是是不容許分詞的,則會報Analysis requests are only supported on tokenized fields錯誤。 GET /test0104/_analyze { "field": "post_date", "text": "2017-09-30" }
本節介紹了什麼是mapping,mapping負責管理索引的數據結構和字段的分詞等一些配置。在直接存儲數據的時候,會dynamic mapping;而後介紹瞭如何建立mapping,如何修改mapping,如何查看mapping;而後介紹了keyword這個保留原數據的一個特殊的字段。
相關度分數的具體算法咱們其實並不須要關心。但可能仍是須要大概瞭解一下計算的方式。
爲何說不須要關心呢?由於實際上相關度是由索引詞直接決定的,分析好哪些是用於搜索的詞就能夠大體分析出相關度排序了。
固然,這只是大概的,由於內部可能由於索引詞的重複性問題會下降某個詞的score,但差距通常不會太大。
TF算法,全稱Term frequency,索引詞頻率算法。
意義就像它的名字,會根據索引詞的頻率來計算,索引詞出現的次數越多,分數越高。
例子以下:
搜索
hello
有兩份文檔:A文檔:hello world!
,B文檔:hello hello hello
結果是B文檔的score大於A文檔。搜索
hello world
有兩份文檔:A文檔:hello world!
,B文檔:hello,are you ok?
結果是A文檔的score大於B文檔。要根據索引詞來綜合考慮。
IDF算法全稱Inverse Document Frequency,逆文本頻率。
搜索文本的詞在整個索引的全部文檔中出現的次數越多,這個詞所佔的score的比重就越低。
例子以下:
搜索
hello world
,其中索引中hello出現次數1000次,world出現100次。
有三份文檔:A文檔hello,are you ok?
,B文檔The world is interesting!
,C文檔hello world!
結果是:C>B>A
因爲hello出現頻率高,因此單個hello獲得的score比不上world。
例子以下:
搜索
hello world!
有兩份文檔:A文檔hello world!
,B文檔hello world,I'm xxx!
結果是:A>B
(下面屬於理論分析,並不真實這樣計算)
TF算法針對在Field中,索引詞出現的頻率;
IDF算法針對在整個索引中的索引詞出現的頻率;
Field-length norm算法針對Field的長度。
那麼能夠這樣分析,因爲Field-length norm算法並不直接針對score,因此它是最後起做用的,它理論上相似於一個除數。而TF和IDF是平等的,IDF計算出每個索引詞的score量,TF來計算整個文檔中索引詞的score的加和。
也就是以下的計算:
1.IDF:計算索引詞的單位score,好比hello=0.1,world=0.2,
2.TF:計算整個文檔的sum(score),hello world!I'm xxx.
獲得0.1+0.2=0.3
3.Field-length norm:將sum(score)/對應Field的長度,得出的結果就是score。
elasticsearch提供了測試計算score的API,語法相似以下:
GET /index/type/_search?explain=true { "query": { "match": { "搜索字段": "搜索值" } } } 例子: GET /douban/book/_search?explain=true { "query": { "match": { "book_name": "Story" } } }
返回結果數據解析:
_explanation是score的緣由,_explanation的格式是先列出分數,而後detail部分解釋分數,內層也是。
這節介紹了相關度分數的計算方式,能夠大體瞭解一下score是怎麼得出來得,TF是索引詞頻率算法,是對索引詞分數的加和;IDF是基於整個索引對每一個索引詞的計算分數;Field-length用於下降數據長的文檔的相關度分數。
什麼是分詞器?
分詞器負責對document進行處理,以提升搜索效率。
分詞器一般由分解器tokenizer和詞元過濾器token filter組成。分詞器對數據的分詞處理:爲了提升索引的效率,ElasticSearch會數據進行處理,處理方式主要有字符過濾、詞轉換、詞拆分
字符過濾:過濾一些特殊字符,例如&
、||
、html標籤,由於這些詞一般搜索意義不大。詞轉換:把一些意義相同的詞統一轉成一個詞,(同詞義轉換)好比mom,mother統一轉成mom;(大小寫轉換)he,He統一歸爲He;還處理一些詞意義不大的詞(停用詞清除),好比英文的「the」,「to」,這些詞使用頻率很高,但沒有具體意義。
詞拆分:進行數據的拆分,拆分紅詞,好比把
good morning,mom
拆分紅good
,morining
,mom
。另外,詞拆分並不徹底是按照數據的最小單位分解的,某一些分詞器會把一些詞進行組合,由於一些詞的組合起來纔有索引的意義,好比中文的一些詞一般要組合起來纔有意義,好比「大」和「家」要組成「你們」纔有比較具體的意義,這是爲了確保索引詞的最小單位是有意義的(好比英文mom的最小單位是m,o,m,內部的分詞器要可以區分出mom整個是有意義的才能夠確保是採用mom做爲索引,而不是採用m和o,也正是由於這個問題,因此英文分詞器不能用於中文分詞器)。【分詞器有不少個,默認的分詞器是不能適當對中文數據分詞的,它只能把一個個數據按最小的單位拆分,由於英文分詞器不能分清楚怎麼把詞拆分纔有意義,因爲配置分詞器是一個較爲靠後的知識點,因此前期將以英文數據爲測試數據。】
每個分詞器的都有一些本身的規則,好比一些分詞器會把a當成停用詞,有些則不會;有些分詞器會去掉標點符號,有些則不會;有些分詞器能識別英文詞,但識別不了中文詞。要按須要來選擇分詞器。
可使用下面的格式的命令來測試不一樣分詞器的分詞效果:
GET /_analyze { "analyzer": "分詞器", "text": "要測試的文本" } // 舉例: GET /_analyze { "analyzer": "simple", "text": "I am a little boy,I have 5$.dogs,long-distance,<html></html>" }
示例:
原句:I am a little boy,I have 5$.dogs,long-distance,<html></html>
i
,am
,a
,little
,boy
,i
,have
,5
,dogs
,long
,distance
,html
,html
【對於拆分紅同一個詞的,會造成同一個索引詞】【這個分詞器,作了小寫、去掉特殊字符等操做】i
,am
,a
,little,boy
,i
,have
,dogs
,long
,distance
,html
,html
【這個分詞器作了小寫、去掉特殊字符和數字等操做】I
,am
,a
,little
,boy,I
,have
,5$.dogs,long-distance,<html></html>
【根據空格分詞】英文分詞器英文分詞器english::i
,am
,littl
,boi
,i
,have
,5
,dog
,long
,distanc
,html
,html
【語言分詞器會比較特殊,會作一部分的形式轉換,有些時候會盲目地切分單詞,好比er和e這些常見後綴,會被切掉,因此little變成了littl。】
可能有人不是很懂分詞器的做用,這裏再次重談一下:
若是使用的分詞器是standard,那你輸入的loves會認爲是loves,loved會認爲是loved;
而english會把loves認爲是love,loved認爲是love.
因此在english分詞器中,loves和loved的搜索用的是love的索引詞搜索,而standard中用的是loves和loved的索引詞,從搜索效率來講,english的搜索纔是咱們想要的,它比較靈活。
並且有些分詞器會幫你把詞組合起來,好比「中國人」這個詞不應被拆分紅「中」「國」「人」】
修改分詞器就是修改mapping中的analyzer,mapping中某個字段一但建立就不能針對這個字段修改,因此只能刪除再修改或新增。
PUT /test0106 { "settings": { "number_of_shards": 1, "number_of_replicas": 1 }, "mappings": { "test":{ "properties": { "content":{ "type": "text", "analyzer": "standard" }, "detail":{ "type": "text", "analyzer": "english" } } } } }
在前面,咱們都是使用英文做爲字段的數據,由於ElasticSearch默認狀況下沒法很好地對中文進行分詞,它默認只能一個個字地分詞,因此它會把「中國人」這個詞拆分紅「中」「國」「人」。
因此咱們須要使用「插件」來對ElasticSearch來進行擴展。
咱們經常使用的中文分詞器就是IK分詞器。
1.去github上下載IK的zip文件,下載的插件版本要與當前使用的elasticsearch版本一致,好比你是elasticsearch6.2.3,就下載6.2.3的【對於沒有本身版本的,這時候可能須要下載新的elasticsearch,有些人說能夠經過修改pom.xml來強行適配,但仍是存在一些問題的。】。github-IK
2.把zip文件中的elasticsearch
文件夾解壓到elasticsearch安裝目錄\plugins
中,而且重命名文件夾爲ik-analyzer
3.而後重啓elasticsearch
IK分詞器提供了兩種分詞器ik_max_word和ik_smart
北京天安門廣場
會被拆分爲北京
,京
,天安門廣場
,天安門
,天安
,門
,廣場
,會嘗試各類在IK中可能的組合;北京天安門廣場
拆分爲北京
,天安門廣場
。// 測試一:比較標準分詞器和IK分詞器的區別: // 結果:標準分詞器只會一個個字地拆分 GET /_analyze { "analyzer": "standard", "text": "北京天安門廣場" } GET /_analyze { "analyzer": "ik_max_word", "text": "北京天安門廣場" } GET /_analyze { "analyzer": "ik_smart", "text": "北京天安門廣場" }
// 測試二:給某個字段的mapping配置分詞器爲ik_max_word,插入測試數據,並進行搜索 PUT /test0107 { "mappings": { "test": { "properties": { "content": { "type": "text", "analyzer": "ik_max_word" } } } } } PUT /test0107/test/1 { "content":"北京天安門廣場" } PUT /test0107/test/2 { "content":"上海世博會" } PUT /test0107/test/3 { "content":"北京鳥巢" } PUT /test0107/test/4 { "content":"上海外灘" } // 開始搜索: GET /test0107/test/_search { "query": { "match": { "content": "北京" } } } GET /test0107/test/_search { "query": { "match": { "content": "鳥巢" } } } GET /test0107/test/_search { "query": { "match": { "content": "世博會" } } } // 這條搜索是搜索不出結果的,由於分詞的結果沒有「京」字 GET /test0107/test/_search { "query": { "match": { "content": "北" } } }
本節從新解釋了分詞器的做用(字符過濾,詞轉換,詞拆分),介紹了幾種常見的內置的分詞器(standard,english這些),並舉了一些分詞的例子,以及如何修改字段的分詞器(mapping的analyzer),最後還介紹了怎麼安裝支持中文分詞的ik分詞器。
文檔的ID實際上是能夠不指定的,不指定的時候會隨機生成惟一的一串字符串,
手動指定和不指定的區別:
手動指定一般適用於一些將數據庫的數據轉存到ElasticSearch的場景,由於這時候ID有特殊意義,讓數據庫的數據與ElasticSearch的數據關聯起來,(有時候可能須要同時查數據庫和ElasticSearch,那麼能夠直接根據數據庫中的ID來查ElasticSearch中的數據)。
不指定的時候(這時候一般ID是沒有特殊意義的),ElasticSearch會自動地幫咱們生成一個ID值,自動生成的ID長度爲20個字符,這個ID它能確保是不會重複的,就算是在分佈式併發狀況下也不會發生衝突。【因爲此時的ID是隨機字符串,因此根據ID來查詢數據會比較麻煩】
這裏講的仍是比較偏應用方面的基礎知識,後面可能還會補充偏底層的一些基礎知識,好比底層寫入流程和NRT這些。