把文本切割成詞元(token)只是這項(xiàng)工作的一半。為了讓這些詞元(token)更容易搜索, 這些詞元(token)需要被 歸一化(normalization)–這個(gè)過(guò)程會(huì)去除同一個(gè)詞元(token)的無(wú)意義差別,例如大寫(xiě)和小寫(xiě)的差別??赡芪覀冞€需要去掉有意義的差別, 讓 esta、ésta 和 está 都能用同一個(gè)詞元(token)來(lái)搜索。你會(huì)用 déjà vu 來(lái)搜索,還是 deja vu?
這些都是語(yǔ)匯單元過(guò)濾器的工作。語(yǔ)匯單元過(guò)濾器接收來(lái)自分詞器(tokenizer)的詞元(token)流。還可以一起使用多個(gè)語(yǔ)匯單元過(guò)濾器,每一個(gè)都有自己特定的處理工作。每一個(gè)語(yǔ)匯單元過(guò)濾器都可以處理來(lái)自另一個(gè)語(yǔ)匯單元過(guò)濾器輸出的單詞流。
舉個(gè)例子
用的最多的語(yǔ)匯單元過(guò)濾器(token filters)是 lowercase 過(guò)濾器,它的功能正和你期望的一樣;它將每個(gè)詞元(token)轉(zhuǎn)換為小寫(xiě)形式:
GET /_analyze
{
"text": "The QUICK Brown FOX!",
"tokenizer": "standard",
"filter": ["lowercase"]
}
- 得到的詞元(token)是 the, quick, brown, fox
只要查詢和檢索的分析過(guò)程是一樣的,不管用戶搜索 fox 還是 FOX 都能得到一樣的搜索結(jié)果。lowercase 過(guò)濾器會(huì)將查詢 FOX 的請(qǐng)求轉(zhuǎn)換為查詢 fox 的請(qǐng)求, fox 和我們?cè)诘古潘饕写鎯?chǔ)的是同一個(gè)詞元(token)。
為了在分析過(guò)程中使用 token 過(guò)濾器,我們可以創(chuàng)建一個(gè) custom 分析器:
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"my_lowercaser": {
"tokenizer": "standard",
"filter": [
"lowercase"
]
}
}
}
}
}
我們可以通過(guò) analyze API 來(lái)驗(yàn)證:
GET /my_index/_analyze
{
"analyzer": "my_lowercaser",
"text": "The QUICK Brown FOX! "
}
- 得到的詞元是 the, quick, brown, fox
如果有口音
英語(yǔ)用變音符號(hào)(例如 ′, ^, 和 ¨) 來(lái)強(qiáng)調(diào)單詞—?例如 r?le, déjà, 和 d?is —?但是是否使用他們通常是可選的. 其他語(yǔ)言則通過(guò)變音符號(hào)來(lái)區(qū)分單詞。當(dāng)然,只是因?yàn)樵谀愕乃饕衅磳?xiě)正確的單詞并不意味著用戶將搜索正確的拼寫(xiě)。 去掉變音符號(hào)通常是有用的,讓 r?le 對(duì)應(yīng) role, 或者反過(guò)來(lái)。 對(duì)于西方語(yǔ)言,可以用 asciifolding 字符過(guò)濾器來(lái)實(shí)現(xiàn)這個(gè)功能。 實(shí)際上,它不僅僅能去掉變音符號(hào)。它會(huì)把Unicode字符轉(zhuǎn)化為ASCII來(lái)表示:
? ? ss
? ? ae
? ? l
? ? m
? ? ??
? ? 2
? ? 6
像 lowercase 過(guò)濾器一樣, asciifolding 不需要任何配置,可以被 custom 分析器直接使用:
DELETE my_index
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"folding": {
"tokenizer": "standard",
"filter": [ "lowercase", "asciifolding" ]
}
}
}
}
}
GET /my_index/_analyze
{
"analyzer": "folding",
"text": "My ?sophagus caused a débacle "
}
- 得到的詞元 my, oesophagus, caused, a, debacle
保留原意
理所當(dāng)然的,去掉變音符號(hào)會(huì)丟失原意。 例如, 參考 這三個(gè) 西班牙單詞:
esta
形容詞 this 的陰性形式, 例如 esta silla (this chair) 和 esta (this one).
ésta
esta 的古代用法.
está
動(dòng)詞 estar (to be) 的第三人稱形式, 例如 está feliz (he is happy).
通常我們會(huì)合并前兩個(gè)形式的單詞,而去區(qū)分和他們不相同的第三個(gè)形式的單詞。類似的:
sé
動(dòng)詞 saber (to know) 的第一人稱形式 例如 Yo sé (I know).
se
與許多動(dòng)詞使用的第三人稱反身代詞, 例如 se sabe (it is known).
不幸的是,沒(méi)有簡(jiǎn)單的方法,去區(qū)分哪些詞應(yīng)該保留變音符號(hào)和哪些詞應(yīng)該去掉變音符號(hào)。而且很有可能,你的用戶也不知道.
相反, 我們對(duì)文本做兩次索引: 一次用原文形式,一次用去掉變音符號(hào)的形式:
PUT /my_index/_mapping
{
"properties": {
"title": {
"type": "text",
"analyzer": "standard",
"fields": {
"folded": {
"type": "text",
"analyzer": "folding"
}
}
}
}
}
- 在 title 字段用 standard 分析器,會(huì)保留原文的變音符號(hào).
- 在 title.folded 字段用 folding 分析器,會(huì)去掉變音符號(hào)
你可以使用 analyze API 分析 Esta está loca (This woman is crazy)這個(gè)句子,來(lái)驗(yàn)證字段映射:
GET /my_index/_analyze
{
"field": "title",
"text": "Esta está loca"
}
GET /my_index/_analyze
{
"field": "title.folded",
"text": "Esta está loca"
}
- 得到的詞元 esta, está, loca
- 得到的詞元 esta, esta, loca
可以用更多的文檔來(lái)測(cè)試:
GET /my_index/_analyze
{
"field": "title",
"text": "Está loca!"
}
GET /my_index/_analyze
{
"field": "title.folded",
"text": "Está loca!"
}
現(xiàn)在,我們可以通過(guò)聯(lián)合所有的字段來(lái)搜索。在multi_match
查詢中通過(guò) most_fields mode 模式來(lái)聯(lián)合所有字段的結(jié)果:
POST my_index/_doc
{"title": "Está loca!"}
GET /my_index/_search
{
"query": {
"multi_match": {
"type": "most_fields",
"query": "esta loca",
"fields": [ "title", "title.folded" ]
}
}
}
通過(guò) validate-query API 來(lái)執(zhí)行這個(gè)查詢可以幫助你理解查詢是如何執(zhí)行的:
GET /my_index/_validate/query?explain=true
{
"query": {
"multi_match": {
"type": "most_fields",
"query": "Está loca!",
"fields": [ "title", "title.folded" ]
}
}
}
multi-match 查詢會(huì)搜索在 title 字段中原文形式的單詞 (está),和在 title.folded 字段中去掉變音符號(hào)形式的單詞 esta:
(title:está title:loca )
(title.folded:esta title.folded:loca)
無(wú)論用戶搜索的是 esta 還是 está; 兩個(gè)文檔都會(huì)被匹配,因?yàn)槿サ糇円舴?hào)形式的單詞在 title.folded 字段中。然而,只有原文形式的單詞在 title 字段中。此額外匹配會(huì)把包含原文形式單詞的文檔排在結(jié)果列表前面。
我們用 title.folded 字段來(lái) 擴(kuò)大我們的網(wǎng) (widen the net)來(lái)匹配更多的文檔,然后用原文形式的 title 字段來(lái)把關(guān)聯(lián)度最高的文檔排在最前面。在可以為了匹配數(shù)量犧牲文本原意的情況下,這個(gè)技術(shù)可以被用在任何分析器里。
asciifolding 過(guò)濾器有一個(gè)叫做 preserve_original 的選項(xiàng)可以讓你這樣來(lái)做索引,把詞的原文詞元(original token)和處理—?折疊后的詞元(folded token)放在同一個(gè)字段的同一個(gè)位置。開(kāi)啟了這個(gè)選項(xiàng),結(jié)果會(huì)像這樣:
Position 1 Position 2
--------------------------
(ésta,esta) loca
--------------------------
雖然這個(gè)是節(jié)約空間的好辦法,但是也意味著沒(méi)有辦法再說(shuō)“給我精確匹配的原文詞元”(Give me an exact match on the original word)。包含去掉和不去掉變音符號(hào)的詞元,會(huì)導(dǎo)致不可靠的相關(guān)性評(píng)分。
所以,正如我們這一章做的,把每個(gè)字段的不同形式分開(kāi)到不同的字段會(huì)讓索引更清晰。
Unicode的世界
當(dāng)Elasticsearch在比較詞元(token)的時(shí)候,它是進(jìn)行字節(jié)(byte)級(jí)別的比較。 換句話說(shuō),如果兩個(gè)詞元(token)被判定為相同的話,他們必須是相同的字節(jié)(byte)組成的。然而,Unicode允許你用不同的字節(jié)來(lái)寫(xiě)相同的字符。
例如, é 和 e? 的不同是什么?這取決于你問(wèn)誰(shuí)。對(duì)于Elasticsearch,第一個(gè)是由 0xC3 0xA9 這兩個(gè)字節(jié)組成的,第二個(gè)是由 0x65
0xCC 0x81 這三個(gè)字節(jié)組成的。
對(duì)于Unicode,他們的差異和他們的怎么組成沒(méi)有關(guān)系,所以他們是相同的。第一個(gè)是單個(gè)單詞 é ,第二個(gè)是一個(gè)簡(jiǎn)單 e 和重音符 ′。
如果你的數(shù)據(jù)有多個(gè)來(lái)源,就會(huì)有可能發(fā)生這種狀況:因?yàn)橄嗤膯卧~使用了不同的編碼,導(dǎo)致一個(gè)形式的 déjà 不能和它的其他形式進(jìn)行匹配。
幸運(yùn)的是,這里就有解決辦法。這里有4種Unicode 歸一化形式 (normalization forms) : nfc, nfd, nfkc, nfkd,它們都把Unicode字符轉(zhuǎn)換成對(duì)應(yīng)標(biāo)準(zhǔn)格式,把所有的字符 進(jìn)行字節(jié)(byte)級(jí)別的比較。
Unicode歸一化形式 (Normalization Forms)
組合 (composed) 模式—nfc
和nfkc
—用盡可能少的字節(jié)(byte)來(lái)代表字符。 (((“composed forms (Unicode normalization)”))) 所以用é
來(lái)代表單個(gè)字母é
。 分解 (decomposed) 模式—nfd
andnfkd
—用字符的每一部分來(lái)代表字符。所以é
分解為e
和′
。 (((“decomposed forms (Unicode normalization)”)))
規(guī)范 (canonical) 模式—nfc 和 nfd&—把連字作為單個(gè)字符,例如 ? 或者 ? 。 兼容 (compatibility) 模式—nfkc 和 nfkd—將這些組合的字符分解成簡(jiǎn)單字符的等價(jià)物,例如: f + f + i 或者 o + e.
無(wú)論你選擇哪一個(gè)歸一化(normalization)模式,只要你的文本只用一種模式,那你的同一個(gè)詞元(token)就會(huì)由相同的字節(jié)(byte)組成。例如,兼容 (compatibility) 模式 可以用連詞 ? 的簡(jiǎn)化形式 ffi
來(lái)進(jìn)行對(duì)比。
你可以使用 icu_normalizer 語(yǔ)匯單元過(guò)濾器(token filters) 來(lái)保證你的所有詞元(token)是相同模式:
PUT /my_index
{
"settings": {
"analysis": {
"filter": {
"nfkc_normalizer": {
"type": "icu_normalizer",
"name": "nfkc"
}
},
"analyzer": {
"my_normalizer": {
"tokenizer": "icu_tokenizer",
"filter": [ "nfkc_normalizer" ]
}
}
}
}
}
包括剛才提到過(guò)的 icu_normalizer 語(yǔ)匯單元過(guò)濾器(token filters)在內(nèi),這里還有 icu_normalizer 字符 過(guò)濾器(character filters)。雖然它和語(yǔ)匯單元過(guò)濾器做相同的工作,但是會(huì)在文本到達(dá)過(guò)濾器之前做。到底是用
standard
過(guò)濾器,還是 icu_tokenizer 過(guò)濾器,其實(shí)并不重要。因?yàn)檫^(guò)濾器知道怎么來(lái)正確處理所有的模式。
但是,如果你使用不同的分詞器,例如: ngram, edge_ngram, 或者 pattern 分詞器,那么在語(yǔ)匯單元過(guò)濾器(token filters)之前使用 icu_normalizer 字符過(guò)濾器就變得有意義了。
通常來(lái)說(shuō),你不僅僅想要?dú)w一化(normalize)詞元(token)的字節(jié)(byte)規(guī)則,還需要把他們轉(zhuǎn)成小寫(xiě)字母。這個(gè)可以通過(guò) icu_normalizer 和定制的歸一化(normalization)的模式 nfkc_cf 來(lái)實(shí)現(xiàn)。下一節(jié)我們會(huì)具體講這個(gè)。
Unicode 大小寫(xiě)折疊
人類沒(méi)有創(chuàng)造力的話就不會(huì)是人類, 而人類的語(yǔ)言就恰恰反映了這一點(diǎn)。
處理一個(gè)單詞的大小寫(xiě)看起來(lái)是一個(gè)簡(jiǎn)單的任務(wù),除非遇到需要處理多語(yǔ)言的情況。
那就舉一個(gè)例子:轉(zhuǎn)換小寫(xiě)德國(guó)單詞 ?。把它轉(zhuǎn)換成大寫(xiě)是 SS,然后在轉(zhuǎn)換成小寫(xiě)就成了 ss。還有一個(gè)例子:轉(zhuǎn)換希臘字母 ? (sigma, 在單詞末尾使用)。把它轉(zhuǎn)換成大寫(xiě)是 Σ,然后再轉(zhuǎn)換成小寫(xiě)就成了 σ。
把詞條小寫(xiě)的核心是讓他們看起來(lái)更像,而不是更不像。在Unicode中,這個(gè)工作是大小寫(xiě)折疊(case folding)來(lái)完成的,而不是小寫(xiě)化(lowercasing)。 大小寫(xiě)折疊 (Case folding) 把單詞轉(zhuǎn)換到一種(通常是小寫(xiě))形式,是讓寫(xiě)法不會(huì)影響單詞的比較,所以拼寫(xiě)不需要完全正確。
例如:?jiǎn)卧~ ?,已經(jīng)是小寫(xiě)形式了,會(huì)被_折疊_(folded)成 ss。類似的小寫(xiě)的 ? 被折疊成 σ,這樣的話,無(wú)論 σ, ?, 和 Σ
出現(xiàn)在哪里, 他們就都可以比較了。
icu_normalizer
語(yǔ)匯單元過(guò)濾器默認(rèn)的歸一化(normalization)模式是 nfkc_cf
。它像 nfkc
模式一樣:
- 組合 (Composes) 字符用最短的字節(jié)來(lái)表示。
- 用 兼容 (compatibility)模式,把像 ? 的字符轉(zhuǎn)換成簡(jiǎn)單的 ffi
但是,也會(huì)這樣做:
- 大小寫(xiě)折疊 (Case-folds) 字符成一種適合比較的形式
換句話說(shuō), nfkc_cf等價(jià)于
lowercase 語(yǔ)匯單元過(guò)濾器(token filters),但是卻適用于所有的語(yǔ)言。 on-steroids 等價(jià)于 standard 分析器,例如:
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"my_lowercaser": {
"tokenizer": "icu_tokenizer",
"filter": [ "icu_normalizer" ]
}
}
}
}
}
- icu_normalizer 默認(rèn)是 nfkc_cf 模式.
我們來(lái)比較 Wei?kopfseeadler和
WEISSKOPFSEEADLER(大寫(xiě)形式) 分別通過(guò) standard
分析器和我們的Unicode自識(shí)別(Unicode-aware)分析器處理得到的結(jié)果:
GET my_index/_analyze
{
"text": "Wei?kopfseeadler WEISSKOPFSEEADLER",
"analyzer": "standard"
}
GET my_index/_analyze
{
"text": "Wei?kopfseeadler WEISSKOPFSEEADLER",
"analyzer": "my_lowercaser"
}
- 得到的詞元(token)是 wei?kopfseeadler, weisskopfseeadler
- 得到的詞元(token)是 weisskopfseeadler, weisskopfseeadler
standard
分析器得到了兩個(gè)不同且不可比較的詞元(token),而我們定制化的分析器得到了兩個(gè)相同但是不符合原意的詞元(token)。
Unicode 字符折疊
- 在多語(yǔ)言(((“Unicode”, “character folding”)))(((“tokens”, “normalizing”, “Unicode character folding”)))處理中,
lowercase
語(yǔ)匯單元過(guò)濾器(token filters)是一個(gè)很好的開(kāi)始。但是作為對(duì)比的話,也只是對(duì)于整個(gè)巴別塔的驚鴻一瞥。所以 <<asciifolding-token-filter,asciifolding
token filter>> 需要更有效的Unicode 字符折疊 (character-folding)工具來(lái)處理全世界的各種語(yǔ)言。(((“asciifolding token filter”))) -
icu_folding
語(yǔ)匯單元過(guò)濾器(token filters) (provided by the <<icu-plugin,icu
plug-in>>)的功能和asciifolding
過(guò)濾器一樣, (((“icu_folding token filter”)))但是它擴(kuò)展到了非ASCII編碼的語(yǔ)言,例如:希臘語(yǔ),希伯來(lái)語(yǔ),漢語(yǔ)。它把這些語(yǔ)言都轉(zhuǎn)換對(duì)應(yīng)拉丁文字,甚至包含它們的各種各樣的計(jì)數(shù)符號(hào),象形符號(hào)和標(biāo)點(diǎn)符號(hào)。 -
icu_folding
語(yǔ)匯單元過(guò)濾器(token filters)自動(dòng)使用nfkc_cf
模式來(lái)進(jìn)行大小寫(xiě)折疊和Unicode歸一化(normalization),所以不需要使用icu_normalizer
GET my_index/_analyze
{
"text": "????? ",
"analyzer": "my_folder"
}
- 阿拉伯?dāng)?shù)字 ????? 被折疊成等價(jià)的拉丁數(shù)字: 12345.
如果你有指定的字符不想被折疊,你可以使用 UnicodeSet(像字符的正則表達(dá)式) 來(lái)指定哪些Unicode才可以被折疊。例如:瑞典單詞 ?,?, ?, ?, ?, 和 ? 不能被折疊,你就可以設(shè)定為: [^??????] (^ 表示 不包含)。這樣就會(huì)對(duì)于所有的Unicode字符生效。
PUT /my_index
{
"settings": {
"analysis": {
"filter": {
"swedish_folding": {
"type": "icu_folding",
"unicodeSetFilter": "[^??????]"
}
},
"analyzer": {
"swedish_analyzer": {
"tokenizer": "icu_tokenizer",
"filter": [ "swedish_folding", "lowercase" ]
}
}
}
}
}
-
swedish_folding
語(yǔ)匯單元過(guò)濾器(token filters) 定制了icu_folding
語(yǔ)匯單元過(guò)濾器(token filters)來(lái)不處理那些大寫(xiě)和小寫(xiě)的瑞典單詞。 - swedish 分析器首先分詞,然后用
swedish_folding
語(yǔ)匯單元過(guò)濾器來(lái)折疊單詞,最后把他們走轉(zhuǎn)換為小寫(xiě),除了被排除在外的單詞: ?, ?, 或者 ?。
排序和整理
本章到目前為止,我們已經(jīng)了解了怎么以搜索為目的去規(guī)范化詞匯單元。 本章節(jié)中要考慮的最終用例是字符串排序。
在 字符串排序與多字段 (復(fù)數(shù)域)中,我們解釋了 Elasticsearch 為什么不能在 analyzed (分析過(guò))的字符串字段上排序,并演示了如何為同一個(gè)域創(chuàng)建 復(fù)數(shù)域索引 ,其中 analyzed 域用來(lái)搜索, keyword 域用來(lái)排序。
analyzed 域無(wú)法排序并不是因?yàn)槭褂昧朔治銎鳎且驗(yàn)榉治銎鲗⒆址鸱殖闪撕芏嘣~匯單元,就像一個(gè) 詞匯袋 ,所以 Elasticsearch 不知道使用那一個(gè)詞匯單元排序。
依賴于 not_analyzed 域來(lái)排序的話不是很靈活:這僅僅允許我們使用原始字符串這一確定的值排序。然而我們 可以 使用分析器來(lái)實(shí)現(xiàn)另外一種排序規(guī)則,只要你選擇的分析器總是為每個(gè)字符串輸出有且僅有一個(gè)的詞匯單元。
大小寫(xiě)敏感排序
想象下我們有三個(gè) 用戶 文檔,文檔的 姓名 域分別含有 Boffey 、 BROWN 和 bailey 。首先我們將使用在 字符串排序與多字段 中提到的技術(shù),使用 keyword 域來(lái)排序:
PUT /my_index
{
"mappings": {
"properties": {
"name": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
}
- text name 域用來(lái)搜索。
- keyword name.raw 域用來(lái)排序。
我們可以索引一些文檔用來(lái)測(cè)試排序:
PUT /my_index/_doc/1
{ "name": "Boffey" }
PUT /my_index/_doc/2
{ "name": "BROWN" }
PUT /my_index/_doc/3
{ "name": "bailey" }
GET /my_index/_search
{
"query": {
"match_all": {}
},
"sort": [
{"name.raw": {"order": "asc"}}
]
}
運(yùn)行這個(gè)搜索請(qǐng)求將會(huì)返回這樣的文檔排序: BROWN 、 Boffey 、 bailey 。 這個(gè)是 詞典排序 跟 字符串排序 相反。基本上就是大寫(xiě)字母開(kāi)頭的字節(jié)要比小寫(xiě)字母開(kāi)頭的字節(jié)權(quán)重低,所以這些姓名是按照最低值優(yōu)先排序。
這可能對(duì)計(jì)算機(jī)是合理的,但是對(duì)人來(lái)說(shuō)并不是那么合理,人們更期望這些姓名按照字母順序排序,忽略大小寫(xiě)。為了實(shí)現(xiàn)這個(gè),我們需要把每個(gè)姓名按照我們想要的排序的順序索引。
換句話來(lái)說(shuō),我們需要一個(gè)能輸出單個(gè)小寫(xiě)詞匯單元的分析器:
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"case_insensitive_sort_analyzer": {
"type": "custom",
"tokenizer": "keyword",
"filter": [
"lowercase"
]
}
}
}
}
}
- keyword 分詞器將輸入的字符串原封不動(dòng)的輸出。
- lowercase 分詞過(guò)濾器將詞匯單元轉(zhuǎn)化為小寫(xiě)字母。
使用 大小寫(xiě)不敏感排序 分析器替換后,現(xiàn)在我們可以將其用在我們的復(fù)數(shù)域:
PUT /my_index/_mapping
{
"properties": {
"name": {
"type": "text",
"fields": {
"lower_case_sort": {
"type": "text",
"analyzer": "case_insensitive_sort_analyzer",
"fielddata": true
}
}
}
}
}
PUT /my_index/_doc/1
{ "name": "Boffey" }
PUT /my_index/_doc/2
{ "name": "BROWN" }
PUT /my_index/_doc/3
{ "name": "bailey" }
GET /my_index/_search
{
"query": {
"match_all": {}
},
"sort": [
{"name.lower_case_sort": {"order": "asc"}}
]
}
- 啟用fielddata: 如果您堅(jiān)持要在文本字段上執(zhí)行排序,并且確定數(shù)據(jù)量不大,可以在字段映射中將fielddata設(shè)置為true,以啟用fielddata。但要注意,這可能會(huì)消耗大量?jī)?nèi)存,因此僅在數(shù)據(jù)量較小的情況下才建議這樣做。
- name.lower_case_sort 域?qū)?huì)為我們提供大小寫(xiě)不敏感排序。
- 默認(rèn)排序可以用 text.keyword
運(yùn)行這個(gè)搜索請(qǐng)求會(huì)得到我們想要的文檔排序: bailey 、 Boffey 、 BROWN 。
但是這個(gè)順序是正確的么?它符合我門(mén)的期望所以看起來(lái)像是正確的, 但我們的期望可能受到這個(gè)事實(shí)的影響:這本書(shū)是英文的,我們的例子中使用的所有字母都屬于到英語(yǔ)字母表。
如果我們添加一個(gè)德語(yǔ)姓名 B?hm 會(huì)怎樣呢?
現(xiàn)在我們的姓名會(huì)返回這樣的排序: bailey 、 Boffey 、 BROWN 、 B?hm 。 B?hm 會(huì)排在 BROWN 后面的原因是這些單詞依然是按照它們表現(xiàn)的字節(jié)值排序的。 r 所存儲(chǔ)的字節(jié)為 0x72 ,而 ? 存儲(chǔ)的字節(jié)值為 0xF6 ,所以 B?hm 排在最后。每個(gè)字符的字節(jié)值都是歷史的意外。
顯然,默認(rèn)排序順序?qū)τ诔?jiǎn)單英語(yǔ)之外的任何事物都是無(wú)意義的。事實(shí)上,沒(méi)有完全“正確”的排序規(guī)則。這完全取決于你使用的語(yǔ)言。
語(yǔ)言之間的區(qū)別
每門(mén)語(yǔ)言都有自己的排序規(guī)則,并且 有時(shí)候甚至有多種排序規(guī)則。 這里有幾個(gè)例子,我們前一小節(jié)中的四個(gè)名字在不同的上下文中是怎么排序的:
英語(yǔ): bailey 、 boffey 、 b?hm 、 brown
德語(yǔ): bailey 、 boffey 、 b?hm 、 brown
德語(yǔ)電話簿: bailey 、 b?hm 、 boffey 、 brown
瑞典語(yǔ): bailey, boffey, brown, b?hm
Unicode 歸類算法
歸類是將文本按預(yù)定義順序排序的過(guò)程。 Unicode 歸類算法 或稱為 UCA (參見(jiàn) www.unicode.org/reports/tr10 ) 定義了一種將字符串按照在歸類單元表中定義的順序排序的方法(通常稱為排序規(guī)則)。
UCA 還定義了 默認(rèn) Unicode 排序規(guī)則元素表 或稱為 DUCET , DUCET 為無(wú)論任何語(yǔ)言的所有 Unicode 字符定義了默認(rèn)排序。如你所見(jiàn),沒(méi)有惟一一個(gè)正確的排序規(guī)則,所以 DUCET 讓更少的人感到煩惱,且煩惱盡可能的小,但它還遠(yuǎn)不是解決所有排序煩惱的萬(wàn)能藥。
而且,明顯幾乎每種語(yǔ)言都有自己的排序規(guī)則。大多時(shí)候使用 DUCET 作為起點(diǎn)并且添加一些自定義規(guī)則用來(lái)處理每種語(yǔ)言的特性。
UCA 將字符串和排序規(guī)則作為輸入,并輸出二進(jìn)制排序鍵。 將根據(jù)指定的排序規(guī)則對(duì)字符串集合進(jìn)行排序轉(zhuǎn)化為對(duì)其二進(jìn)制排序鍵的簡(jiǎn)單比較。
Unicode 排序
icu_collation 分詞過(guò)濾器默認(rèn)使用 DUCET 排序規(guī)則。這已經(jīng)是對(duì)默認(rèn)排序的改進(jìn)了。想要使用 icu_collation 我們僅需要?jiǎng)?chuàng)建一個(gè)使用默認(rèn) icu_collation 過(guò)濾器的分析器:
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"ducet_sort": {
"tokenizer": "keyword",
"filter": [ "icu_collation" ]
}
}
}
}
}
- 使用默認(rèn) DUCET 歸類。
通常,我們想要排序的字段就是我們想要搜索的字段, 因此我們使用與在 大小寫(xiě)敏感排序 中使用的相同的復(fù)數(shù)域方法:
PUT /my_index/_mapping
{
"properties": {
"name": {
"type": "text",
"fields": {
"sort": {
"type": "text",
"analyzer": "ducet_sort",
"fielddata": true
}
}
}
}
}
使用這個(gè)映射, name.sort 域?qū)?huì)含有一個(gè)僅用來(lái)排序的鍵。我們沒(méi)有指定某種語(yǔ)言,所以它會(huì)默認(rèn)會(huì)使用 DUCET collation 。
現(xiàn)在,我們可以重新索引我們的案例文檔并測(cè)試排序:
PUT /my_index/_doc/1
{ "name": "Boffey" }
PUT /my_index/_doc/2
{ "name": "BROWN" }
PUT /my_index/_doc/3
{ "name": "bailey" }
PUT /my_index/_doc/4
{ "name": "B?hm" }
GET /my_index/_search
{
"query": {
"match_all": {}
},
"sort": [
{"name.sort": {"order": "asc"}}
]
}
指定語(yǔ)言
可以為特定的語(yǔ)言配置使用歸類表的 icu_collation 過(guò)濾器,例如一個(gè)國(guó)家特定版本的語(yǔ)言,或者像德語(yǔ)電話簿之類的子集。 這個(gè)可以按照如下所示通過(guò)使用 language 、 country 、 和 variant 參數(shù)來(lái)創(chuàng)建自定義版本的分詞過(guò)濾器:
英語(yǔ)
{ "language": "en" }
德語(yǔ)
{ "language": "de" }
奧地利德語(yǔ)
{ "language": "de", "country": "AT" }
德語(yǔ)電話簿
{ "language": "de", "variant": "@collation=phonebook" }
你可以在一下網(wǎng)址閱讀更多的 ICU 本地支持: http://userguide.icu-project.org/locale.
這個(gè)例子演示怎么創(chuàng)建德語(yǔ)電話簿排序規(guī)則:
PUT /my_index
{
"settings": {
"number_of_shards": 1,
"analysis": {
"filter": {
"german_phonebook": {
"type": "icu_collation",
"language": "de",
"country": "DE",
"variant": "@collation=phonebook"
}
},
"analyzer": {
"german_phonebook": {
"tokenizer": "keyword",
"filter": [
"german_phonebook"
]
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "text",
"fields": {
"sort": {
"type": "text",
"analyzer": "german_phonebook",
"fielddata": true
}
}
}
}
}
}
- 首先我們?yōu)榈抡Z(yǔ)電話薄創(chuàng)建一個(gè)自定義版本的 icu_collation 。
- 之后我們將其包裝在自定義的分析器中。
- 并且為我們的 name.sort 域配置它。
像我們之前那樣重新索引并重新搜索:
PUT /my_index/_doc/1
{ "name": "Boffey" }
PUT /my_index/_doc/2
{ "name": "BROWN" }
PUT /my_index/_doc/3
{ "name": "bailey" }
PUT /my_index/_doc/4
{ "name": "B?hm" }
GET /my_index/_search
{
"query": {
"match_all": {}
},
"sort": [
{"name.sort": {"order": "asc"}}
]
}
現(xiàn)在返回的文檔排序?yàn)椋?bailey 、 B?hm 、 Boffey 、 BROWN 。在德語(yǔ)電話簿歸類中, B?hm 等同于 Boehm ,所以排在 Boffey 前面。
多排序規(guī)則
每種語(yǔ)言都可以使用復(fù)數(shù)域來(lái)支持對(duì)同一個(gè)域進(jìn)行多規(guī)則排序:
PUT /my_index/_mapping
{
"properties": {
"name": {
"type": "text",
"fields": {
"default": {
"type": "text",
"analyzer": "ducet"
},
"french": {
"type": "text",
"analyzer": "french"
},
"german": {
"type": "text",
"analyzer": "german_phonebook"
},
"swedish": {
"type": "text",
"analyzer": "swedish"
}
}
}
}
}
- 我們需要為每個(gè)排序規(guī)則創(chuàng)建相應(yīng)的分析器。
使用這個(gè)映射,只要按照 name.french 、 name.german 或 name.swedish 域排序,就可以為法語(yǔ)、德語(yǔ)和瑞典語(yǔ)用戶正確的排序結(jié)果了。不支持的語(yǔ)言可以回退到使用 name.default 域,它使用 DUCET 排序順序。
自定義排序
icu_collation 分詞過(guò)濾器提供很多選項(xiàng),不止 language 、 country 、和 variant ,這些選項(xiàng)可以用于定制排序算法。可用的選項(xiàng)有以下作用:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-851140.html
- 忽略變音符號(hào)
- 順序大寫(xiě)排先或排后,或忽略大小寫(xiě)
- 考慮或忽略標(biāo)點(diǎn)符號(hào)和空白
- 將數(shù)字按字符串或數(shù)字值排序
- 自定義現(xiàn)有歸類或定義自己的歸類
這些選項(xiàng)的詳細(xì)信息超出了本書(shū)的范圍,更多的信息可以查詢 ICU plug-in documentation 和 ICU project collation documentation 。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-851140.html
到了這里,關(guān)于使用阿里云試用Elasticsearch學(xué)習(xí):3.3 處理人類語(yǔ)言——?dú)w一化詞元的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!