掌握 analyze API,一舉搞定 Elasticsearch 分詞難題

初次接觸 Elasticsearch 的同學(xué)經(jīng)常會遇到分詞相關(guān)的難題豹缀,比如如下這些場景:

  1. 為什么命名有包含搜索關(guān)鍵詞的文檔坟漱,但結(jié)果里面就沒有相關(guān)文檔呢鼠次?
  2. 我存進去的文檔到底被分成哪些詞(term)了?
  3. 我得自定義分詞規(guī)則,但感覺好麻煩呢腥寇,無從下手

如果你遇到過類似的問題成翩,希望本文可以解決你的疑惑。

1. 上手

讓我們從一個實例出發(fā)赦役,如下創(chuàng)建一個文檔:

PUT test/doc/1
{
  "msg":"Eating an apple a day keeps doctor away"
}

然后我們做一個查詢麻敌,我們試圖通過搜索 eat這個關(guān)鍵詞來搜索這個文檔

POST test/_search
{
  "query":{
    "match":{
      "msg":"eat"
    }
  }
}

ES的返回結(jié)果為0。這不太對啊掂摔,我們用最基本的字符串查找也應(yīng)該能匹配到上面新建的文檔才對笆醺帷!

各位不要急棒呛,我們先來看看什么是分詞聂示。

2. 分詞

搜索引擎的核心是倒排索引(這里不展開講),而倒排索引的基礎(chǔ)就是分詞簇秒。所謂分詞可以簡單理解為將一個完整的句子切割為一個個單詞的過程鱼喉。在 es 中單詞對應(yīng)英文為 term。我們簡單看個例子:

image

ES 的倒排索引即是根據(jù)分詞后的單詞創(chuàng)建趋观,即 扛禽、北京皱坛、天安門這4個單詞编曼。這也意味著你在搜索的時候也只能搜索這4個單詞才能命中該文檔。

實際上 ES 的分詞不僅僅發(fā)生在文檔創(chuàng)建的時候剩辟,也發(fā)生在搜索的時候掐场,如下圖所示:

image

讀時分詞發(fā)生在用戶查詢時,ES 會即時地對用戶輸入的關(guān)鍵詞進行分詞贩猎,分詞結(jié)果只存在內(nèi)存中熊户,當(dāng)查詢結(jié)束時,分詞結(jié)果也會隨即消失吭服。而寫時分詞發(fā)生在文檔寫入時嚷堡,ES 會對文檔進行分詞后,將結(jié)果存入倒排索引艇棕,該部分最終會以文件的形式存儲于磁盤上蝌戒,不會因查詢結(jié)束或者 ES 重啟而丟失。

ES 中處理分詞的部分被稱作分詞器沼琉,英文是Analyzer北苟,它決定了分詞的規(guī)則。ES 自帶了很多默認的分詞器刺桃,比如Standard粹淋、 Keyword吸祟、Whitespace等等,默認是 Standard桃移。當(dāng)我們在讀時或者寫時分詞時可以指定要使用的分詞器屋匕。

3. 寫時分詞結(jié)果

回到上手階段,我們來看下寫入的文檔最終分詞結(jié)果是什么借杰。通過如下 api 可以查看:

POST test/_analyze
{
  "field": "msg",
  "text": "Eating an apple a day keeps doctor away"
}

其中 test為索引名过吻,_analyze 為查看分詞結(jié)果的 endpoint,請求體中 field 為要查看的字段名蔗衡,text為具體值纤虽。該 api 的作用就是請告訴我在 test 索引使用 msg 字段存儲一段文本時,es 會如何分詞绞惦。

返回結(jié)果如下:

{
  "tokens": [
    {
      "token": "eating",
      "start_offset": 0,
      "end_offset": 6,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "an",
      "start_offset": 7,
      "end_offset": 9,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "apple",
      "start_offset": 10,
      "end_offset": 15,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "a",
      "start_offset": 16,
      "end_offset": 17,
      "type": "<ALPHANUM>",
      "position": 3
    },
    {
      "token": "day",
      "start_offset": 18,
      "end_offset": 21,
      "type": "<ALPHANUM>",
      "position": 4
    },
    {
      "token": "keeps",
      "start_offset": 22,
      "end_offset": 27,
      "type": "<ALPHANUM>",
      "position": 5
    },
    {
      "token": "doctor",
      "start_offset": 28,
      "end_offset": 34,
      "type": "<ALPHANUM>",
      "position": 6
    },
    {
      "token": "away",
      "start_offset": 35,
      "end_offset": 39,
      "type": "<ALPHANUM>",
      "position": 7
    }
  ]
}

返回結(jié)果中的每一個 token即為分詞后的每一個單詞逼纸,我們可以看到這里是沒有 eat 這個單詞的,這也解釋了在上手中我們搜索 eat 沒有結(jié)果的情況济蝉。如果你去搜索 eating 杰刽,會有結(jié)果返回。

寫時分詞器需要在 mapping 中指定王滤,而且一經(jīng)指定就不能再修改贺嫂,若要修改必須新建索引。如下所示我們新建一個名為ms_english 的字段雁乡,指定其分詞器為 english

PUT test/_mapping/doc
{
  "properties": {
    "msg_english":{
      "type":"text",
      "analyzer": "english"
    }
  }
}

4. 讀時分詞結(jié)果

由于讀時分詞器默認與寫時分詞器默認保持一致第喳,拿 上手 中的例子,你搜索 msg 字段踱稍,那么讀時分詞器為 Standard 曲饱,搜索 msg_english 時分詞器則為 english。這種默認設(shè)定也是非常容易理解的珠月,讀寫采用一致的分詞器渔工,才能盡最大可能保證分詞的結(jié)果是可以匹配的。

然后 ES 允許讀時分詞器單獨設(shè)置桥温,如下所示:

POST test/_search
  {
    "query":{
      "match":{
        "msg":{
          "query": "eating",
          "analyzer": "english"
        }
      }
    }
  }

如上 analyzer 字段即可以自定義讀時分詞器,一般來講不需要特別指定讀時分詞器梁丘。

如果不單獨設(shè)置分詞器侵浸,那么讀時分詞器的驗證方法與寫時一致;如果是自定義分詞器氛谜,那么可以使用如下的 api 來自行驗證結(jié)果掏觉。

POST _analyze
  {
    "text":"eating",
    "analyzer":"english"
  }

返回結(jié)果如下:

{
  "tokens": [
    {
      "token": "eat",
      "start_offset": 0,
      "end_offset": 6,
      "type": "<ALPHANUM>",
      "position": 0
    }
  ]
}

由上可知 english分詞器會將 eating處理為 eat,大家可以再測試下默認的 standard分詞器值漫,它沒有做任何處理澳腹。

5. 解釋問題

現(xiàn)在我們再來看下 上手 中所遇問題的解決思路。

  1. 查看文檔寫時分詞結(jié)果
  2. 查看查詢關(guān)鍵詞的讀時分詞結(jié)果
  3. 匹對兩者是否有命中

我們簡單分析如下:

image

由上圖可以定位問題的原因了。

6. 解決需求

由于 eating只是 eat的一個變形酱塔,我們依然希望輸入 eat時可以匹配包含 eating的文檔沥邻,那么該如何解決呢?

答案很簡單羊娃,既然原因是在分詞結(jié)果不匹配唐全,那么我們就換一個分詞器唄~ 我們可以先試下 ES 自帶的 english分詞器,如下:

# 增加字段 msg_english蕊玷,與 msg 做對比
PUT test/_mapping/doc
{
  "properties": {
    "msg_english":{
      "type":"text",
      "analyzer": "english"
    }
  }
}

# 寫入相同文檔
PUT test/doc/1
{
  "msg":"Eating an apple a day keeps doctor away",
  "msg_english":"Eating an apple a day keeps doctor away"
}

# 搜索 msg_english 字段
POST test/_search
{
  "query": {
    "match": {
      "msg_english": "eat"
    }
  }
}

執(zhí)行上面的內(nèi)容邮利,我們會發(fā)現(xiàn)結(jié)果有內(nèi)容了,原因也很簡單垃帅,如下圖所示:

image

由上圖可見 english分詞器會將 eating分詞為 eat延届,此時我們搜索 eat或者 eating肯定都可以匹配對應(yīng)的文檔了。至此贸诚,需求解決方庭。

7. 深入分析

最后我們來看下為什么english分詞器可以解決我們遇到的問題。一個分詞器由三部分組成:char filter赦颇、tokenizer 和 token filter二鳄。各部分的作用我們這里就不展開了,我們來看下 standardenglish分詞器的區(qū)別媒怯。

image

從上圖可以看出订讼,english分詞器在 Token Filter 中和 Standard不同,而發(fā)揮主要作用的就是 stemmer扇苞,感興趣的同學(xué)可以自行去看起它的作用欺殿。

8. 自定義分詞

如果我們不使用 english分詞器,自定義一個分詞器來實現(xiàn)上述需求也是完全可行的鳖敷,這里不詳細講解了脖苏,只給大家講一個快速驗證自定義分詞器效果的方法,如下:

POST _analyze
{
  "char_filter": [], 
  "tokenizer": "standard",
  "filter": [
    "stop",
    "lowercase",
    "stemmer"
  ],
  "text": "Eating an apple a day keeps doctor away"
}

通過上面的 api 你可以快速驗證自己要定制的分詞器定踱,當(dāng)達到自己需求后棍潘,再將這一部分配置加入索引的配置。

至此崖媚,我們再看開篇的三個問題亦歉,相信你已經(jīng)心里有答案了,趕緊上手去自行測試下吧畅哑!

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肴楷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子荠呐,更是在濱河造成了極大的恐慌赛蔫,老刑警劉巖砂客,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異呵恢,居然都是意外死亡鞠值,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門瑰剃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來齿诉,“玉大人,你說我怎么就攤上這事晌姚≡辆纾” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵挥唠,是天一觀的道長抵恋。 經(jīng)常有香客問我,道長宝磨,這世上最難降的妖魔是什么弧关? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮唤锉,結(jié)果婚禮上世囊,老公的妹妹穿的比我還像新娘。我一直安慰自己窿祥,他們只是感情好株憾,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晒衩,像睡著了一般嗤瞎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上听系,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天贝奇,我揣著相機與錄音,去河邊找鬼靠胜。 笑死掉瞳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浪漠。 我是一名探鬼主播菠赚,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼郑藏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瘩欺,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤必盖,失蹤者是張志新(化名)和其女友劉穎拌牲,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歌粥,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡塌忽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了失驶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片土居。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嬉探,靈堂內(nèi)的尸體忽然破棺而出擦耀,到底是詐尸還是另有隱情,我是刑警寧澤涩堤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布眷蜓,位于F島的核電站,受9級特大地震影響胎围,放射性物質(zhì)發(fā)生泄漏吁系。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一白魂、第九天 我趴在偏房一處隱蔽的房頂上張望汽纤。 院中可真熱鬧,春花似錦福荸、人聲如沸蕴坪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辞嗡。三九已至,卻和暖如春滞造,著一層夾襖步出監(jiān)牢的瞬間续室,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工谒养, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挺狰,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓买窟,卻偏偏與公主長得像丰泊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子始绍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Solr&ElasticSearch原理及應(yīng)用 一瞳购、綜述 搜索 http://baike.baidu.com/it...
    樓外樓V閱讀 7,254評論 1 17
  • 倒排索引 正排索引:文檔id到單詞的關(guān)聯(lián)關(guān)系 倒排索引:單詞到文檔id的關(guān)聯(lián)關(guān)系 示例:對以下三個文檔去除停用詞后...
    小旋鋒的簡書閱讀 4,614評論 1 11
  • 表面的你可能就是一根草,但不為人知的另一面亏推,卻隱藏著巨大的寶藏学赛。所以不要忘記生長年堆,當(dāng)你被發(fā)現(xiàn)的那一刻,驚喜與努力將...
    sunnyinkfish閱讀 116評論 0 0
  • 真相并沒有什么可怕的盏浇。 理解变丧、接受并且知道應(yīng)該如何處理現(xiàn)實問題是至關(guān)重要的。 為什么會害怕真相绢掰,恐懼事實痒蓬? 因為事...
    lamanda閱讀 503評論 0 0
  • 生活就是一直摸索前進,慢慢的撥開云霧滴劲,發(fā)現(xiàn)自己想要的攻晒,從前我不知道目標(biāo)是什么,突然的哑芹,就發(fā)現(xiàn)了自己是個什么樣的人炎辨,...
    LU小Y閱讀 168評論 0 0