6000+字講透ElasticSearch 索引設(shè)計(jì)

ElasticSearch 索引設(shè)計(jì)

在MySQL中數(shù)據(jù)庫(kù)設(shè)計(jì)非常重要,同樣在ES中數(shù)據(jù)庫(kù)設(shè)計(jì)也是非常重要的

概述

我們創(chuàng)建索引就像創(chuàng)建表結(jié)構(gòu)一樣,必須非常慎重的,索引如果創(chuàng)建不好后面會(huì)出現(xiàn)各種各樣的問(wèn)題

索引設(shè)計(jì)的重要性

索引創(chuàng)建后,索引的分片只能通過(guò)_split和_shrink接口對(duì)其進(jìn)行成倍的增加和縮減

主要是因?yàn)閑s的數(shù)據(jù)是通過(guò)_routing分配到各個(gè)分片上面的,所以本質(zhì)上是不推薦去改變索引的分片數(shù)量的,因?yàn)檫@樣都會(huì)對(duì)數(shù)據(jù)進(jìn)行重新的移動(dòng)。

還有就是索引只能新增字段,不能對(duì)字段進(jìn)行修改和刪除,缺乏靈活性,所以每次都只能通過(guò)_reindex重建索引了,還有就是一個(gè)分片的大小以及所以分片數(shù)量的多少嚴(yán)重影響到了索引的查詢和寫入性能,所以可想而知,設(shè)計(jì)一個(gè)好的索引能夠減少后期的運(yùn)維管理和提高不少性能,所以前期對(duì)索引的設(shè)計(jì)是相當(dāng)?shù)闹匾摹?br>
基于時(shí)間的Index設(shè)計(jì)

Index設(shè)計(jì)時(shí)要考慮的第一件事,就是基于時(shí)間對(duì)Index進(jìn)行分割,即每隔一段時(shí)間產(chǎn)生一個(gè)新的Index

這樣設(shè)計(jì)的目的

因?yàn)楝F(xiàn)實(shí)世界的數(shù)據(jù)是隨著時(shí)間的變化而不斷產(chǎn)生的,切分管理可以獲得足夠的靈活性和更好的性能

240510bk-1.png

如果數(shù)據(jù)都存儲(chǔ)在一個(gè)Index中,很難進(jìn)行擴(kuò)展和調(diào)整,因?yàn)镋lasticsearch中Index的某些設(shè)置在創(chuàng)建時(shí)就設(shè)定好了,是不能更改的,比如Primary Shard的個(gè)數(shù)。

而根據(jù)時(shí)間來(lái)切分Index,則可以實(shí)現(xiàn)一定的靈活性,既可以在數(shù)據(jù)量過(guò)大時(shí)及時(shí)調(diào)整Shard個(gè)數(shù),也可以及時(shí)響應(yīng)新的業(yè)務(wù)需求。

大多數(shù)業(yè)務(wù)場(chǎng)景下,客戶對(duì)數(shù)據(jù)的請(qǐng)求都會(huì)命中在最近一段時(shí)間上,通過(guò)切分Index,可以盡可能的避免掃描不必要的數(shù)據(jù),提高性能。

時(shí)間間隔

根據(jù)上面的分析,自然是時(shí)間越短越能保持靈活性,但是這樣做就會(huì)導(dǎo)致產(chǎn)生大量的Index,而每個(gè)Index都會(huì)消耗資源來(lái)維護(hù)其元信息的,因此需要在靈活性、資源和性能上做權(quán)衡

常見(jiàn)的間隔有小時(shí)、天、周和月:先考慮總共要存儲(chǔ)多久的數(shù)據(jù),然后選一個(gè)既不會(huì)產(chǎn)生大量Index又能夠滿足一定靈活性的間隔,比如你需要存儲(chǔ)6個(gè)月的數(shù)據(jù),那么一開(kāi)始選擇“周”這個(gè)間隔就會(huì)比較合適。

考慮業(yè)務(wù)增長(zhǎng)速度:假如業(yè)務(wù)增長(zhǎng)的特別快,比如上周產(chǎn)生了1億數(shù)據(jù),這周就增長(zhǎng)到了10億,那么就需要調(diào)低這個(gè)間隔來(lái)保證有足夠的彈性能應(yīng)對(duì)變化。

如何實(shí)現(xiàn)分割

切分行為是由客戶端(數(shù)據(jù)的寫入端)發(fā)起的,根據(jù)時(shí)間間隔與數(shù)據(jù)產(chǎn)生時(shí)間將數(shù)據(jù)寫入不同的Index中,為了易于區(qū)分,會(huì)在Index的名字中加上對(duì)應(yīng)的時(shí)間標(biāo)識(shí)

創(chuàng)建新Index這件事,可以是客戶端主動(dòng)發(fā)起一個(gè)創(chuàng)建的請(qǐng)求,帶上具體的Settings、Mappings等信息,但是可能會(huì)有一個(gè)時(shí)間錯(cuò)位,即有新數(shù)據(jù)寫入時(shí)新的Index還沒(méi)有建好,Elasticsearch提供了更優(yōu)雅的方式來(lái)實(shí)現(xiàn)這個(gè)動(dòng)作,即Index Template

分片設(shè)計(jì)

所謂分片設(shè)計(jì),就是如何設(shè)定主分片的個(gè)數(shù)

看上去只是一個(gè)數(shù)字而已,也許在很多場(chǎng)景下,即使不設(shè)定也不會(huì)有問(wèn)題(ES7默認(rèn)是1個(gè)主分片一個(gè)副本分片),但是如果不提前考慮,一旦出問(wèn)題就可能導(dǎo)致系統(tǒng)性能下降、不可訪問(wèn)、甚至無(wú)法恢復(fù),換句話說(shuō),即使使用默認(rèn)值,也應(yīng)該是通過(guò)足夠的評(píng)估后作出的決定,而非拍腦袋定的。

限制分片大小

單個(gè)Shard的存儲(chǔ)大小不超過(guò)30GB

Elastic專家根據(jù)經(jīng)驗(yàn)總結(jié)出來(lái)大家普遍認(rèn)為30GB是個(gè)合適的上限值,實(shí)踐中發(fā)現(xiàn)單個(gè)Shard過(guò)大(超過(guò)30GB)會(huì)導(dǎo)致系統(tǒng)不穩(wěn)定。

其次,為什么不能超過(guò)30GB?主要是考慮Shard Relocate過(guò)程的負(fù)載,我們知道,如果Shard不均衡或者部分節(jié)點(diǎn)故障,Elasticsearch會(huì)做Shard Relocate,在這個(gè)過(guò)程中會(huì)搬移Shard,如果單個(gè)Shard過(guò)大,會(huì)導(dǎo)致CPU、IO負(fù)載過(guò)高進(jìn)而影響系統(tǒng)性能與穩(wěn)定性。

評(píng)估分片數(shù)量

單個(gè)Index的Primary Shard個(gè)數(shù) = k * 數(shù)據(jù)節(jié)點(diǎn)個(gè)數(shù)

在保證第一點(diǎn)的前提下,單個(gè)Index的Primary Shard個(gè)數(shù)不宜過(guò)多,否則相關(guān)的元信息與緩存會(huì)消耗過(guò)多的系統(tǒng)資源,這里的k,為一個(gè)較小的整數(shù)值,建議取值為1,2等,整數(shù)倍的關(guān)系可以讓Shard更好地均勻分布,可以充分的將請(qǐng)求分散到不同節(jié)點(diǎn)上。

小索引設(shè)計(jì)

對(duì)于很小的Index,可以只分配1~2個(gè)Primary Shard的

有些情況下,Index很小,也許只有幾十、幾百M(fèi)B左右,那么就不用按照第二點(diǎn)來(lái)分配了,只分配1~2個(gè)Primary Shard是可以,不用糾結(jié)。

使用索引模板

就是把已經(jīng)創(chuàng)建好的某個(gè)索引的參數(shù)設(shè)置(settings)和索引映射(mapping)保存下來(lái)作為模板,在創(chuàng)建新索引時(shí),指定要使用的模板名,就可以直接重用已經(jīng)定義好的模板中的設(shè)置和映射

Elasticsearch基于與索引名稱匹配的通配符模式將模板應(yīng)用于新索引,也就是說(shuō)通過(guò)索引進(jìn)行匹配,看看新建的索引是否符合索引模板,如果符合,就將索引模板的相關(guān)設(shè)置應(yīng)用到新的索引,如果同時(shí)符合多個(gè)索引模板呢,這里需要對(duì)參數(shù)priority進(jìn)行比較,這樣會(huì)選擇priority大的那個(gè)模板進(jìn)行創(chuàng)建索引。

在創(chuàng)建索引模板時(shí),如果匹配有包含的關(guān)系,或者相同,則必須設(shè)置priority為不同的值,否則會(huì)報(bào)錯(cuò),索引模板也是只有在新創(chuàng)建的時(shí)候起到作用,修改索引模板對(duì)現(xiàn)有的索引沒(méi)有影響,同樣如果在索引中設(shè)置了一些設(shè)置或者mapping都會(huì)覆蓋索引模板中相同的設(shè)置或者mapping

索引模板的用途

索引模板一般用在時(shí)間序列相關(guān)的索引中。

也就是說(shuō), 如果你需要每間隔一定的時(shí)間就建立一次索引,你只需要配置好索引模板,以后就可以直接使用這個(gè)模板中的設(shè)置,不用每次都設(shè)置settings和mappings.

創(chuàng)建索引模板

COPYPUT _index_template/logstash-village
{
  "index_patterns": [
    "logstash-village-*"  // 可以通過(guò)"logstash-village-*"來(lái)適配創(chuàng)建的索引
  ],
  "template": {
    "settings": {
      "number_of_shards": "3", //指定模板分片數(shù)量
      "number_of_replicas": "2"  //指定模板副本數(shù)量
    },
    "aliases": {
      "logstash-village": {}  //指定模板索引別名
    },
    "mappings": {   //設(shè)置映射
      "dynamic": "strict", //禁用動(dòng)態(tài)映射
      "properties": {
        "@timestamp": {
          "type": "date",
           "format": "strict_date_optional_time||epoch_millis||yyyy-MM-dd HH:mm:ss"
        },
        "@version": {
          "doc_values": false,
          "index": "false",
          "type": "integer"
        },
        "name": {
          "type": "keyword"
        },
        "province": {
          "type": "keyword"
        },
        "city": {
          "type": "keyword"
        },
        "area": {
          "type": "keyword"
        },
        "addr": {
          "type": "text",
          "analyzer": "ik_smart"
        },
        "location": {
          "type": "geo_point"
        },
        "property_type": {
          "type": "keyword"
        },
        "property_company": {
          "type": "text",
          "analyzer": "ik_smart"
        },
        "property_cost": {
          "type": "float"
        },
        "floorage": {
          "type": "float"
        },
        "houses": {
          "type": "integer"
        },
        "built_year": {
          "type": "integer"
        },
        "parkings": {
          "type": "integer"
        },
        "volume": {
          "type": "float"
        },
        "greening": {
          "type": "float"
        },
        "producer": {
          "type": "keyword"
        },
        "school": {
          "type": "keyword"
        },
        "info": {
          "type": "text",
          "analyzer": "ik_smart"
        }
      }
    }
  }
}




模板參數(shù)

下面是創(chuàng)建索引模板的一些參數(shù)



參數(shù)名稱    參數(shù)介紹

index_patterns  必須配置,用于在創(chuàng)建期間匹配索引名稱的通配符(*)表達(dá)式數(shù)組
template  可選配置,可以選擇包括別名、映射或設(shè)置配置
composed_of  可選配置,組件模板名稱的有序列表。組件模板按指定的順序合并,這意味著最后指定的組件模板具有最高的優(yōu)先級(jí)
priority  可選配置,創(chuàng)建新索引時(shí)確定索引模板優(yōu)先級(jí)的優(yōu)先級(jí)。選擇具有最高優(yōu)先級(jí)的索引模板。如果未指定優(yōu)先級(jí),則將模板視為優(yōu)先級(jí)為0(最低優(yōu)先級(jí))
version  可選配置,用于外部管理索引模板的版本號(hào)
_meta  可選配置,關(guān)于索引模板的可選用戶元數(shù)據(jù),可能有任何內(nèi)容
映射配置

上面我們配置了映射模板,但是我們用到了映射,下面我們說(shuō)下映射






什么是映射

在創(chuàng)建索引時(shí),可以預(yù)先定義字段的類型(映射類型)及相關(guān)屬性

數(shù)據(jù)庫(kù)建表的時(shí)候,我們DDL依據(jù)一般都會(huì)指定每個(gè)字段的存儲(chǔ)類型,例如:varchar、int、datetime等,目的很明確,就是更精確的存儲(chǔ)數(shù)據(jù),防止數(shù)據(jù)類型格式混亂,在Elasticsearch中也是這樣,創(chuàng)建索引的時(shí)候一般也需要指定索引的字段類型,這種方式稱為映射(Mapping)

被動(dòng)創(chuàng)建(動(dòng)態(tài)映射)

此時(shí)字段和映射類型不需要事先定義,只需要存在文檔的索引,當(dāng)向此索引添加數(shù)據(jù)的時(shí)候當(dāng)遇到不存在的映射字段,ES會(huì)根據(jù)數(shù)據(jù)內(nèi)容自動(dòng)添加映射字段定義。

動(dòng)態(tài)映射規(guī)則

使用動(dòng)態(tài)映射的時(shí)候,根據(jù)傳遞請(qǐng)求數(shù)據(jù)的不同會(huì)創(chuàng)建對(duì)應(yīng)的數(shù)據(jù)類型

數(shù)據(jù)類型  Elasticsearch 數(shù)據(jù)類型
null  不添加任何字段
true或者false  boolean類型
浮點(diǎn)數(shù)據(jù)  float類型
integer數(shù)據(jù)  long類型
object  object類型
array  取決于數(shù)組中的第一個(gè)非空值的類型。
string  如果此內(nèi)容通過(guò)了日期格式檢測(cè),則會(huì)被認(rèn)為是date數(shù)據(jù)類型 如果此值通過(guò)了數(shù)值類型檢測(cè)則被認(rèn)為是double或者long數(shù)據(jù)類型 帶有關(guān)鍵字子字段會(huì)被認(rèn)為一個(gè)text字段
禁止動(dòng)態(tài)映射

一般生產(chǎn)環(huán)境下需要禁用動(dòng)態(tài)映射,使用動(dòng)態(tài)映射可能出現(xiàn)以下問(wèn)題

造成集群元數(shù)據(jù)一直變更,導(dǎo)致不穩(wěn)定;

可能造成數(shù)據(jù)類型與實(shí)際類型不一致;

如何禁用動(dòng)態(tài)映射,動(dòng)態(tài)mapping的dynamic字段進(jìn)行配置,可選值及含義如下

true:支持動(dòng)態(tài)擴(kuò)展,新增數(shù)據(jù)有新的字段屬性時(shí),自動(dòng)添加對(duì)于的mapping,數(shù)據(jù)寫入成功
false:不支持動(dòng)態(tài)擴(kuò)展,新增數(shù)據(jù)有新的字段屬性時(shí),直接忽略,數(shù)據(jù)寫入成功
strict:不支持動(dòng)態(tài)擴(kuò)展,新增數(shù)據(jù)有新的字段時(shí),報(bào)錯(cuò),數(shù)據(jù)寫入失敗
主動(dòng)創(chuàng)建(顯示映射)

動(dòng)態(tài)映射只能保證最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)的映射

所以很多時(shí)候我們需要對(duì)字段除了數(shù)據(jù)結(jié)構(gòu)定義更多的限制的時(shí)候,動(dòng)態(tài)映射創(chuàng)建的內(nèi)容很可能不符合我們的需求,所以可以使用PUT {index}/mapping來(lái)更新指定索引的映射內(nèi)容。

映射類型

我們要?jiǎng)?chuàng)建映射必須還要知道映射類型,否則就會(huì)走默認(rèn)的映射類型,下面我們看看常用的映射類型

準(zhǔn)備工作

我們先創(chuàng)建一個(gè)用于測(cè)試映射類型的索引

COPYPUT mapping_demo

字符串類型

字符串類型是我們最常用的類型之一,我們操作的時(shí)候字符串類型可以被設(shè)置為以下幾種類型

text

當(dāng)一個(gè)字段是要被全文搜索的,比如Email內(nèi)容、產(chǎn)品描述,應(yīng)該使用text類型,text類型會(huì)被分詞

設(shè)置text類型以后,字段內(nèi)容會(huì)被分詞,在生成倒排索引以前,字符串會(huì)被分析器分成一個(gè)一個(gè)詞項(xiàng),text類型的字段不用于排序,很少用于聚合

keyword

keyword類型不會(huì)被分詞,常用于關(guān)鍵字搜索,比如姓名、email地址、主機(jī)名、狀態(tài)碼和標(biāo)簽等

如果字段需要進(jìn)行過(guò)濾(比如查姓名是張三發(fā)布的博客)、排序、聚合,keyword類型的字段只能通過(guò)精確值搜索到,常常被用來(lái)過(guò)濾、排序和聚合

兩者區(qū)別

它們的區(qū)別在于text會(huì)對(duì)字段進(jìn)行分詞處理而keyword則不會(huì)進(jìn)行分詞

也就是說(shuō)如果字段是text類型,存入的數(shù)據(jù)會(huì)先進(jìn)行分詞,然后將分完詞的詞組存入索引,而keyword則不會(huì)進(jìn)行分詞,直接存儲(chǔ),這樣劃分?jǐn)?shù)據(jù)更加節(jié)省內(nèi)存。

使用案例

我們先創(chuàng)建一個(gè)映射,name是keyword類型,描述是text類型的

COPYPUT mapping_demo/_mapping
{
  "properties": {
    "name": {
        "type": "keyword"
     },
      "city": {
        "type": "text",
        "analyzer": "ik_smart"
     }
  }
}
插入數(shù)據(jù)

COPYPUT mapping_demo/_doc/1
{
  "name":"北京小區(qū)",
  "city":"北京市昌平區(qū)回龍觀街道"
}




對(duì)于keyword的name字段進(jìn)行精確查詢



COPYGET mapping_demo/_search
{
  "query": {
    "term": {
      "name": "北京小區(qū)"
    }
  }
}




對(duì)于text的city進(jìn)行模糊查詢

COPYGET mapping_demo/_search
{
  "query": {
    "term": {
      "city": "北京市"
    }
  }
}
數(shù)字類型

數(shù)字類型也是我們最常用的類型之一,下面我們看下數(shù)字類型的使用

類型    取值范圍

long  -263 ~ 263
integer  -231 ~ 231
short  -215 ~ 215
byte  -27 ~ 27
double  64位的雙精度 IEEE754 浮點(diǎn)類型
float  32位的雙精度 IEEE754 浮點(diǎn)類型
half_float  16位的雙精度 IEEE754 浮點(diǎn)類型
scaled_float  縮放類型的浮點(diǎn)類型
注意事項(xiàng)

在滿足需求的情況下,優(yōu)先使用范圍小的字段,字段長(zhǎng)度越小,索引和搜索的效率越高。

日期類型

JSON表示日期

JSON沒(méi)有表達(dá)日期的數(shù)據(jù)類型,所以在ES里面日期只能是下面其中之一



格式化的日期字符串,比如:"2015-01-01" or "2015/01/01 12:10:30"

用數(shù)字表示的從新紀(jì)元開(kāi)始的毫秒數(shù)

用數(shù)字表示的從新紀(jì)元開(kāi)始的秒數(shù)(epoch_second)

注意點(diǎn):毫秒數(shù)的值是不能為負(fù)數(shù)的,如果時(shí)間在1970年以前,需要使用格式化的日期表達(dá)






ES如何處理日期

在ES的內(nèi)部,時(shí)間會(huì)被轉(zhuǎn)換為UTC時(shí)間(如果聲明了時(shí)區(qū))并使用從新紀(jì)元開(kāi)始的毫秒數(shù)的長(zhǎng)整形數(shù)字類型的進(jìn)行存儲(chǔ),在日期字段上的查詢,內(nèi)部將會(huì)轉(zhuǎn)換為使用長(zhǎng)整形的毫秒進(jìn)行范圍查詢,根據(jù)與字段關(guān)聯(lián)的日期格式,聚合和存儲(chǔ)字段的結(jié)果將轉(zhuǎn)換回字符串

注意點(diǎn):日期最終都會(huì)作為字符串呈現(xiàn),即使最開(kāi)始初始化的時(shí)候是利用JSON文檔的long聲明的

默認(rèn)日期格式

日期的格式可以被定制化的,如果沒(méi)有聲明日期的格式,它將會(huì)使用默認(rèn)的格式:



COPY"strict_date_optional_time||epoch_millis"

這意味著它將會(huì)接收帶時(shí)間戳的日期,它將遵守strict_date_optional_time限定的格式(yyyy-MM-dd'T'HH:mm:ss.SSSZ 或者 yyyy-MM-dd)或者毫秒數(shù)



日期格式示例

COPYPUT mapping_demo/_mapping
{
  "properties": {
    "datetime": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
     }
  }
}
# 添加數(shù)據(jù)
PUT mapping_demo/_doc/2
{
  "name":"河北區(qū)",
  "city":"河北省小區(qū)",
  "datetime":"2022-02-21 11:35:42"
}
日期類型參數(shù)

下面表格里的參數(shù)可以用在date字段上面
參數(shù)    說(shuō)明

doc_values  該字段是否按照列式存儲(chǔ)在磁盤上以便于后續(xù)進(jìn)行排序、聚合和腳本操作,可配置 true(默認(rèn))或 false
format  日期的格式
locale  解析日期中時(shí)使用了本地語(yǔ)言表示月份時(shí)的名稱和/或縮寫,默認(rèn)是 ROOT locale
ignore_malformed  如果設(shè)置為true,則奇怪的數(shù)字就會(huì)被忽略,如果是false(默認(rèn))奇怪的數(shù)字就會(huì)導(dǎo)致異常并且該文檔將會(huì)被拒絕寫入。需要注意的是,如果在腳本參數(shù)中使用則該屬性不能被設(shè)置
index  該字段是否能快速的被查詢,默認(rèn)是true。date類型的字段只有在doc_values設(shè)置為true時(shí)才能被查詢,盡管很慢。
null_value  替代null的值,默認(rèn)是null
on_script_error  定義在腳本中如何處理拋出的異常,fail(默認(rèn))則整個(gè)文檔會(huì)被拒絕索引,continue:繼續(xù)索引
script  如果該字段被設(shè)置,則字段的值將會(huì)使用該腳本產(chǎn)生,而不是直接從source里面讀取。
store  true or false(默認(rèn))是否在 _source 之外在獨(dú)立存儲(chǔ)一份
布爾類型

boolean類型用于存儲(chǔ)文檔中的true/false

范圍類型

顧名思義,范圍類型字段中存儲(chǔ)的內(nèi)容就是一段范圍,例如年齡30-55歲,日期在2020-12-28到2021-01-01之間等



類型范圍

es中有六種范圍類型:



integer_range

float_range

long_range

double_range

date_range

ip_range

使用實(shí)例

COPYPUT mapping_demo/_mapping
{
  "properties": {
    "age_range": {
        "type": "integer_range"
     }
  }
}


# 指定年齡范圍,可以使用 gt、gte、lt、lte。

PUT mapping_demo/_doc/3
{
  "name":"張三",
  "age_range":{
    "gt":20,
    "lt":30
  }
}
分詞器

什么是分詞器

分詞器的主要作用將用戶輸入的一段文本,按照一定邏輯,分析成多個(gè)詞語(yǔ)的一種工具

顧名思義,文本分析就是把全文本轉(zhuǎn)換成一系列單詞(term/token)的過(guò)程,也叫分詞,在 ES 中,Analysis 是通過(guò)分詞器(Analyzer) 來(lái)實(shí)現(xiàn)的,可使用 ES 內(nèi)置的分析器或者按需定制化分析器。

舉一個(gè)分詞簡(jiǎn)單的例子:比如你輸入 Mastering Elasticsearch,會(huì)自動(dòng)幫你分成兩個(gè)單詞,一個(gè)是 mastering,另一個(gè)是 elasticsearch,可以看出單詞也被轉(zhuǎn)化成了小寫的。

240510bk-2.png

分詞器構(gòu)成

分詞器是專門處理分詞的組件,分詞器由以下三部分組成:

character filter
接收原字符流,通過(guò)添加、刪除或者替換操作改變?cè)址?br>


例如:去除文本中的html標(biāo)簽,或者將羅馬數(shù)字轉(zhuǎn)換成阿拉伯?dāng)?shù)字等,一個(gè)字符過(guò)濾器可以有零個(gè)或者多個(gè)



tokenizer

簡(jiǎn)單的說(shuō)就是將一整段文本拆分成一個(gè)個(gè)的詞



例如拆分英文,通過(guò)空格能將句子拆分成一個(gè)個(gè)的詞,但是對(duì)于中文來(lái)說(shuō),無(wú)法使用這種方式來(lái)實(shí)現(xiàn),在一個(gè)分詞器中,有且只有一個(gè)tokenizeer

token filters

將切分的單詞添加、刪除或者改變

例如將所有英文單詞小寫,或者將英文中的停詞a刪除等,在token filters中,不允許將token(分出的詞)的position或者offset改變,同時(shí),在一個(gè)分詞器中,可以有零個(gè)或者多個(gè)token filters。

分詞順序


240510bk-3.png


同時(shí) Analyzer 三個(gè)部分也是有順序的,從圖中可以看出,從上到下依次經(jīng)過(guò) Character Filters,Tokenizer 以及 Token Filters,這個(gè)順序比較好理解,一個(gè)文本進(jìn)來(lái)肯定要先對(duì)文本數(shù)據(jù)進(jìn)行處理,再去分詞,最后對(duì)分詞的結(jié)果進(jìn)行過(guò)濾。



測(cè)試分詞

可以通過(guò)_analyzerAPI來(lái)測(cè)試分詞的效果,我們使用下面的html過(guò)濾分詞

COPYPOST _analyze
{
    "text":"<b>hello world<b>"  # 輸入的文本
    "char_filter":["html_strip"], # 過(guò)濾html標(biāo)簽
    "tokenizer":"keyword", #原樣輸出
}
什么時(shí)候分詞

文本分詞會(huì)發(fā)生在兩個(gè)地方:

創(chuàng)建索引:當(dāng)索引文檔字符類型為text時(shí),在建立索引時(shí)將會(huì)對(duì)該字段進(jìn)行分詞。
搜索:當(dāng)對(duì)一個(gè)text類型的字段進(jìn)行全文檢索時(shí),會(huì)對(duì)用戶輸入的文本進(jìn)行分詞。






創(chuàng)建索引時(shí)指定分詞器

如果設(shè)置手動(dòng)設(shè)置了分詞器,ES將按照下面順序來(lái)確定使用哪個(gè)分詞器

先判斷字段是否有設(shè)置分詞器,如果有,則使用字段屬性上的分詞器設(shè)置

如果設(shè)置了analysis.analyzer.default,則使用該設(shè)置的分詞器

如果上面兩個(gè)都未設(shè)置,則使用默認(rèn)的standard分詞器

字段指定分詞器

為addr屬性指定分詞器,這里我們使用的是中文分詞器



COPYPUT my_index
{
  "mappings": {
    "properties": {
     "info": {
        "type": "text",
        "analyzer": "ik_smart"
       }
    }
  }
}
設(shè)置默認(rèn)分詞器

COPYPUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "default":{
          "type":"simple"
        }
      }
    }
  }
}


搜索時(shí)指定分詞器

在搜索時(shí),通過(guò)下面參數(shù)依次檢查搜索時(shí)使用的分詞器,這樣我們的搜索語(yǔ)句就會(huì)先分詞,然后再來(lái)進(jìn)行搜索



搜索時(shí)指定analyzer參數(shù)

創(chuàng)建mapping時(shí)指定字段的search_analyzer屬性

創(chuàng)建索引時(shí)指定setting的analysis.analyzer.default_search

查看創(chuàng)建索引時(shí)字段指定的analyzer屬性

如果上面幾種都未設(shè)置,則使用默認(rèn)的standard分詞器。

指定analyzer

搜索時(shí)指定analyzer查詢參數(shù)



COPYGET my_index/_search
{
  "query": {
    "match": {
      "message": {
        "query": "Quick foxes",
        "analyzer": "stop"
      }
    }
  }
}
指定字段analyzer

COPYPUT my_index
{
  "mappings": {
    "properties": {
      "title":{
        "type":"text",
        "analyzer": "whitespace",
        "search_analyzer": "simple"
      }
    }
  }
}




指定默認(rèn)default_seach

COPYPUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "default":{
          "type":"simple"
        },
        "default_seach":{
          "type":"whitespace"
        }
      }
    }
  }
}
內(nèi)置分詞器

es在索引文檔時(shí),會(huì)通過(guò)各種類型 Analyzer 對(duì)text類型字段做分析

不同的 Analyzer 會(huì)有不同的分詞結(jié)果,內(nèi)置的分詞器有以下幾種,基本上內(nèi)置的 Analyzer 包括 Language Analyzers 在內(nèi),對(duì)中文的分詞都不夠友好,中文分詞需要安裝其它 Analyzer



分析器  描述  分詞對(duì)象  結(jié)果
standard  標(biāo)準(zhǔn)分析器是默認(rèn)的分析器,如果沒(méi)有指定,則使用該分析器。它提供了基于文法的標(biāo)記化(基于 Unicode 文本分割算法,如 Unicode 標(biāo)準(zhǔn)附件 # 29所規(guī)定) ,并且對(duì)大多數(shù)語(yǔ)言都有效。The 2 QUICK Brown-Foxes jumped over the lazy dog’s bone.  [ the, 2, quick, brown, foxes, jumped, over, the, lazy, dog’s, bone ]
simple  簡(jiǎn)單分析器將文本分解為任何非字母字符的標(biāo)記,如數(shù)字、空格、連字符和撇號(hào)、放棄非字母字符,并將大寫字母更改為小寫字母。The 2 QUICK Brown-Foxes jumped over the lazy dog’s bone.  [ the, quick, brown, foxes, jumped, over, the, lazy, dog, s, bone ]
whitespace  空格分析器在遇到空白字符時(shí)將文本分解為術(shù)語(yǔ)  The 2 QUICK Brown-Foxes jumped over the lazy dog’s bone.  [ The, 2, QUICK, Brown-Foxes, jumped, over, the, lazy, dog’s, bone. ]
stop  停止分析器與簡(jiǎn)單分析器相同,但增加了刪除停止字的支持。默認(rèn)使用的是 _english_ 停止詞。The 2 QUICK Brown-Foxes jumped over the lazy dog’s bone.  [ quick, brown, foxes, jumped, over, lazy, dog, s, bone ]
keyword  不分詞,把整個(gè)字段當(dāng)做一個(gè)整體返回  The 2 QUICK Brown-Foxes jumped over the lazy dog’s bone.  [The 2 QUICK Brown-Foxes jumped over the lazy dog’s bone.]
pattern  模式分析器使用正則表達(dá)式將文本拆分為術(shù)語(yǔ)。正則表達(dá)式應(yīng)該匹配令牌分隔符,而不是令牌本身。正則表達(dá)式默認(rèn)為 w+ (或所有非單詞字符)。The 2 QUICK Brown-Foxes jumped over the lazy dog’s bone.  [ the, 2, quick, brown, foxes, jumped, over, the, lazy, dog, s, bone ]
多種西語(yǔ)系 arabic, armenian, basque, bengali, brazilian, bulgarian, catalan, cjk, czech, danish, dutch, english等等  一組旨在分析特定語(yǔ)言文本的分析程序。
    

IK中文分詞器

IKAnalyzer

IKAnalyzer是一個(gè)開(kāi)源的,基于java的語(yǔ)言開(kāi)發(fā)的輕量級(jí)的中文分詞工具包

從2006年12月推出1.0版開(kāi)始,IKAnalyzer已經(jīng)推出了3個(gè)大版本,在 2012 版本中,IK 實(shí)現(xiàn)了簡(jiǎn)單的分詞歧義排除算法,標(biāo)志著 IK 分詞器從單純的詞典分詞向模擬語(yǔ)義分詞衍化



中文分詞器算法

中文分詞器最簡(jiǎn)單的是ik分詞器,還有jieba分詞,哈工大分詞器等



分詞器    描述    分詞對(duì)象    結(jié)果

ik_smart    ik分詞器中的簡(jiǎn)單分詞器,支持自定義字典,遠(yuǎn)程字典    學(xué)如逆水行舟,不進(jìn)則退    [學(xué)如逆水行舟,不進(jìn)則退]

ik_max_word    ik_分詞器的全量分詞器,支持自定義字典,遠(yuǎn)程字典    學(xué)如逆水行舟,不進(jìn)則退    [學(xué)如逆水行舟,學(xué)如逆水,逆水行舟,逆水,行舟,不進(jìn)則退,不進(jìn),則,退]

ik_smart

原始內(nèi)容

COPY傳智教育的教學(xué)質(zhì)量是杠杠的

測(cè)試分詞

COPYGET _analyze
{
  "analyzer": "ik_smart",
  "text": "傳智教育的教學(xué)質(zhì)量是杠杠的"
}


ik_max_word

原始內(nèi)容

COPY傳智教育的教學(xué)質(zhì)量是杠杠的

測(cè)試分詞

COPYGET _analyze
{
  "analyzer": "ik_max_word",
  "text": "傳智教育的教學(xué)質(zhì)量是杠杠的"
}




作者:陳燁123


歡迎關(guān)注微信公眾號(hào) :java知路