赞
踩
如下截图来自《一本书讲透 Elasticsearch》读者群里的问题,数十条交流信息,讨论得非常热烈。
个人建议非常有必要和大家一起探讨一下技术方案。
场景:电商创业公司(非传统巨头)
读者描述需求:
content是一个text类型,用的 ik_max_word 分的词,需要根据关键词做精准匹配,并且按照发布时间倒序。
比如我搜:小米6s,搜出来的结果要精确匹配到:小米6s,并且按照用户的发布时间倒序排序。
现在的问题是用 match_pharse 搜索的时候,有时候文档里明明有这个关键词,但是搜不出来,尝试了好几种手段......
熟悉咱们公众号推文的同学应该知道,咱们分别在 2018年、2020年、2022年 都做过多次类似问题的讨论。
Elasticsearch能检索出来,但不能正确高亮怎么办?
由 Elasticsearch 空间换时间的线上问题说开去......
数据索引化的过程是借助分词器完成的,如读者的分词器是 IK 中文分词器。
问题来了?IK 中文分词器能包含全部的词汇吗?
大家看 medcl 大佬开源的 IK 分词器的源码中能找到 main.dic 大小是 2.92 MB。并且这个词典 8 年+ 没有更新过。
显然:IK 默认词典覆盖不了全部词汇,尤其诸如“显眼包”、“小米14”、“奥利给”、“叶氏那拉”等的新词。如下截图是我自定义的词典的词库检索截图。
再来一波举例看看:
- PUT my_index_0512
- {
- "mappings": {
- "properties": {
- "title": {
- "type": "text",
- "analyzer": "ik_smart",
- "fields": {
- "keyword": {
- "type": "keyword"
- }
- }
- }
- }
- }
- }

- POST my_index_0512/_bulk
- {"index":{"_id":1}}
- {"title":"奥利给是一个网络流行词,第一次出现在一名快手主播直播时说的正能量语录里。"}
-
-
- ## 分词为:“奥利” 和 “给” 两个词
- POST my_index_0512/_analyze
- {
- "text":"奥利给是一个网络流行词,第一次出现在一名快手主播直播时说的正能量语录里。",
- "analyzer":"ik_smart"
- }
-
- ## 检索不能召回结果,这里用 term 主要说明问题,合理性待商榷!
- POST my_index_0512/_search
- {
- "profile": true,
- "query": {
- "term": {
- "title": "奥利给"
- }
- }
- }

结论:词典决定分词,词典里没有的词,极大可能(有一定概率,比如:match_phrase 词+词组合的情况)检索会检索不到。
ps: 关于 term、match、match_phrase 区别等,推荐阅读:检索选型。
全文检索的本质是查询待检索的关键词在写入所创建的索引中是否存在的过程。
存在,则召回;不存在,则返回空。
表面上可以看出,之前咱们2018年、2020年、2022年讨论的方案用 match、match_phrase、match_phrase_prefix 等再结合 slop,貌似能解决一些问题,好像有些不召回的情况,可以召回了。
但,依然治标不治本。依然会存在一些“新词”、“词典里没有的词”等看似明明一段话里存在的词,就是检索不到的原因。
答案:不完全能!
但,可以尝试空间换时间,借助 Ngram 能解决 99% 以上场景的问题。
针对读者的问题,借助 Ngram 分词实操一下:
- ### 3.1 创建索引
- DELETE new_spy_uat2
- PUT new_spy_uat2
- {
- "settings": {
- "index.max_ngram_diff": 10,
- "analysis": {
- "analyzer": {
- "my_analyzer": {
- "tokenizer": "my_tokenizer",
- "char_filter": ["my_char_filter"]
- }
- },
- "char_filter": {
- "my_char_filter": {
- "type": "pattern_replace",
- "pattern": "[^\\p{L}\\p{N}\\s]+",
- "replacement": ""
- }
- },
- "tokenizer": {
- "my_tokenizer": {
- "type": "ngram",
- "min_gram": 2,
- "max_gram": 10
- }
- }
- }
- },
- "mappings": {
- "properties": {
- "content": {
- "type": "text",
- "analyzer": "my_analyzer",
- "fields": {
- "keyword": {
- "type": "keyword"
- }
- }
- }
- }
- }
- }

在提供的 Elasticsearch 配置中,my_tokenizer 是一个基于 n-gram 的分词器,配置了从最小 2 个字符到最大 10 个字符的 n-gram。
在《一本书讲透 Elasticsearch》第6.4 章节 P111 解读了自定义分词器的三大核心组成:
character filter
tokenizer
token filter
咱们上面的“my_char_filter”定义了文本在分词前进行预处理的字符过滤规则。实际是使用正则表达式删除所有非字母、非数字、非空格字符,只保留字母、数字和空白字符,中文字符是可以保留的。
N-gram 是一种分词方法,通过从文本中提取 n 个连续字符的滑动窗口来创建词元(tokens)。这种方法在处理需要部分匹配和模糊搜索的应用中非常有用,比如搜索建议和拼写错误的容错处理。
在这种配置下,文本会被分解成所有可能的 2 到 10 个字符的组合。
例如,要执行如下检索:
- POST new_spy_uat2/_analyze
- {
- "analyzer":"my_analyzer",
- "text":"奥利给这几年才流行"
- }
分词结果如下:
这种方法可以大大增加索引的大小因为每个词都被分解成多个子词,但同时也提高了搜索的灵活性和准确性,尤其是在搜索短文本或关键词片段时。
这样的分词器尤其适合于搜索引擎的自动补全功能和处理用户可能的输入错误,因为它能够在用户输入部分信息时就开始匹配相关的词条。
- POST new_spy_uat2/_bulk
- { "index" : { "_index" : "new_spy_uat2", "_id" : "1" } }
- { "content" : "新品豪车❗️限1000单食物链巴氏小仙包犬湿粮360g声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/942969推荐阅读
相关标签
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。