ElasticSearch 模糊搜索方案

ㅤㅤㅤ
ㅤㅤㅤ
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ(获取信用是要付出很高代价的。——杰罗尔德)
ㅤㅤㅤ
ㅤㅤㅤ
ㅤㅤㅤㅤㅤㅤㅤㅤㅤ在这里插入图片描述
ElasticSearch 数据迁移方案
ElasticSearch 常用api

ElasticSearch 模糊搜索方案
问题场景

使用es检索文本时,有时无法得到预期结果,检索带中文的文本时,只有中文部分可以模糊搜索,但英文和数字却不行,比如全英文,全数字,英文+数字。

优化方案

使用新的索引结构解决,配置了ngram分词器,limit,unique分词过滤器

配置环境

es可视化工具链接

  • 仓储地址:https://github.com/mobz/elasticsearch-head.git
  • 配置连接地址
  • 在这里插入图片描述
索引结构
{
    "settings": {
        "number_of_shards": 7,
        "number_of_replicas": 1,
        "analysis": {
            "analyzer": {
                "default": {
                    "tokenizer": "1_25_tokenizer_grams",
                    "filter": [
                        "unique",
                        "500_limit_filter"
                    ]
                }
            },
            "tokenizer": {
                "1_25_tokenizer_grams": {
                    "type": "ngram",
                    "min_gram": 1,
                    "max_gram": 25
                }
            },
            "filter": {
                "500_limit_filter": {
                    "type": "limit",
                    "max_token_count": 500
                }
            }
        }
    },
    "mappings": {
        "business": {
            "numeric_detection": false,
            "date_detection": false,
            "properties": {
                "account": {
                    "type": "string",
                    "index": "not_analyzed"
                },
                "number": {
                    "type": "string",
                    "index": "not_analyzed"
                },
                "flow": {
                    "type": "string",
                    "index": "not_analyzed"
                },
                "createTime": {
                    "type": "string",
                    "index": "not_analyzed"
                }
            }
        }
    }
}
插入索引数据的索引结构
{
    "state":"open",
    "settings":{
        "index":{
            "creation_date":"1646724159602",
            "analysis":{
                "filter":{
                    "500_limit_filter":{
                        "type":"limit",
                        "max_token_count":"500"
                    }
                },
                "analyzer":{
                    "default":{
                        "filter":[
                            "unique",
                            "500_limit_filter"
                        ],
                        "tokenizer":"1_25_tokenizer_grams"
                    }
                },
                "tokenizer":{
                    "1_25_tokenizer_grams":{
                        "type":"ngram",
                        "min_gram":"1",
                        "max_gram":"25"
                    }
                }
            },
            "number_of_shards":"7",
            "number_of_replicas":"1",
            "uuid":"xehbLr2-TH-8WEt3Ptn9CA",
            "version":{
                "created":"2040199"
            }
        }
    },
    "mappings":{
        "business":{
            "date_detection":false,
            "properties":{
                "2aadb33c-de2e-11ea-9a63-210593f52d68":{
                    "type":"string"
                },
                "0eae0e80-5f81-11eb-a75b-b3919f758643":{
                    "type":"string"
                },
                "dbf32100-019d-11ec-b3b4-d9a8f9085a2e":{
                    "type":"string"
                },
                "d78430c0-9f31-11ea-83ed-a34974eec164":{
                    "type":"string"
                },
                "e5ca8500-fbd6-11eb-9b56-fb75f73c50dc":{
                    "type":"string"
                },
                "42f94240-959b-11e6-9917-2d95033366e1":{
                    "type":"string"
                },
                "2aadda43-de2e-11ea-9a63-210593f52d68":{
                    "type":"string"
                },
                "3baaae30-1e93-11ec-b7ca-9d88fef23a2a":{
                    "type":"string"
                },
                "80f268d0-fbd8-11eb-9b56-fb75f73c50dc":{
                    "type":"string"
                },
                "number":{
                    "index":"not_analyzed",
                    "type":"string"
                },
                "createTime":{
                    "index":"not_analyzed",
                    "type":"string"
                },
                "d115eb70-9f31-11ea-bb50-8b404e7d32bf":{
                    "type":"string"
                },
                "3da8d4b0-e38d-11ea-bba7-b5a306bc2148":{
                    "type":"string"
                },
                "def45310-019d-11ec-8372-1f0ef8ecd3d5":{
                    "type":"string"
                },
                "account":{
                    "index":"not_analyzed",
                    "type":"string"
                },
                "flow":{
                    "index":"not_analyzed",
                    "type":"string"
                },
                "2aadda44-de2e-11ea-9a63-210593f52d68":{
                    "type":"string"
                },
                "43030560-1e93-11ec-b7ca-9d88fef23a2a":{
                    "type":"string"
                }
            }
        }
    },
    "aliases":[

    ]
}
索引结构说明

官方文档:
settings https://www.elastic.co/guide/en/elasticsearch/reference/8.0/index-modules.html
mappings https://www.elastic.co/guide/en/elasticsearch/reference/8.0/mapping.html

settings //索引设置
settings.number_of_shards //索引的主分片数
settings.number_of_replicas //每个主分片具有的副本数
settings.analysis //索引分析器
settings.analysis.analyzer //索引分析器设置
settings.analysis.analyzer.default //索引分析器默认设置,除指定配置外的字段全部使用该配置
settings.analysis.analyzer.default.tokenizer //索引分词器  1_25_tokenizer_grams(自定义的分词器,逐字分词)
settings.analysis.analyzer.default.filter //索引过滤器,可以同时使用多个  unique(重复分词过滤器) limit_filter(自定义的分词结果限制器,限制返回结果数量)
settings.analysis.tokenizer //索引分词器设置
settings.analysis.tokenizer.1_25_tokenizer_grams //索引分词器自定义名称key
settings.analysis.tokenizer.1_25_tokenizer_grams.type // 自定义分词的类型
settings.analysis.tokenizer.1_25_tokenizer_grams.min_gram // ngram分词器的属性,最小分词数
settings.analysis.tokenizer.1_25_tokenizer_grams.max_gram // ngram分词器的属性,最大分词数
settings.analysis.filter //索引分词过滤器设置
settings.analysis.filter.limit_filter //索引分词过滤器自定义名称key
settings.analysis.filter.limit_filter.type //自定义分词过滤器的类型
settings.analysis.filter.limit_filter.max_token_count //limit分词过滤器的属性,最多分词结果数

mappings //索引映射设置
mappings.business //自定义的索引映射名称
mappings.business.numeric_detection //数字自动转换 比如字符串1.0会被转换成1
mappings.business.date_detection //日期自动转换 会被转换成"yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"格式
mappings.business.account //自定义索引字段
mappings.business.account.type //自定义索引字段类型 string(字符串)

mappings.business.account.index //自定义索引字段类型 not_analyzed(不会分词,精确搜索,存储的时候存的是一个) 默认是analyzed(根据配置的分词器规则进行存储和检索)
相关文档:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_exact_values_versus_full_text.html#_exact_values_versus_full_text
https://www.elastic.co/guide/cn/elasticsearch/guide/current/mapping-intro.html

minimum_should_match:should语句中的最少匹配数,类型为数字,用于或的查询
排查过程
  • 先查看原索引结构,发现对account,number还有flow等字段没有进行分词,对自定义字段"80f268d0-fbd8-11eb-9b56-fb75f73c50dc"等没有设置,所以默认是分词的
{
    "settings":{
        "number_of_shards":7,
        "number_of_replicas":1
    },
    "mappings":{
        "business":{
            "properties":{
                "account":{
                    "type":"string",
                    "index":"not_analyzed"
                },
                "number":{
                    "type":"string",
                    "index":"not_analyzed"
                },
                "flow":{
                    "type":"string",
                    "index":"not_analyzed"
                },
                "createTime":{
                    "type":"string",
                    "index":"not_analyzed"
                },
                "80f268d0-fbd8-11eb-9b56-fb75f73c50dc":{
                    "type":"string"
                }
            }
        }
    }
}
  • 使用es的api _analyze来分别对account(不分词)和80f268d0-fbd8-11eb-9b56-fb75f73c50dc(分词)进行分析
    • 分析"account",只有一个词组,因为没有使用分词,所以是符合预期的
    • 在这里插入图片描述
    • 分析"def45310-019d-11ec-8372-1f0ef8ecd3d5",发现对于中文能正常分词,但对于数字+英文,没有空格和标点符号的文本,分词的结果只有一个,还是全文的
    • 在这里插入图片描述
    • 这就导致后续的搜索,对于英文和数字部分只能匹配全文而不能模糊搜索
  • 于是再查看该索引结构的映射关系,发现没有指定任何的分词器,分词过滤器和字符过滤器
    • 在这里插入图片描述
  • 搜集资料和测试,发现当没有指定分词器时,es默认的分词器为standard分词器
    • 官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/8.0/analysis-standard-tokenizer.html
    • 该分词器基于Unicode 文本分段算法,去除空格和标点符号等,但对于连续的英文,则不会分词,从而导致对于没有符号的文本不能模糊搜索
    • 在这里插入图片描述
  • 于是开始从分词器着手,搜集大量的资料并测试,选择了以下分词器 ngram
    • 官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/8.0/analysis-ngram-tokenizer.html
    • 该分词器基于n-ngram,将文本逐字拆分,比如张3w5,会被拆分成张,张3,张3w,张三w5,3,3w等
    • 可以很好的应对客户模糊搜索的需求
      • 在这里插入图片描述
      • 默认分词最小长度为min_gram1,最大为max_gram2,但针对不同需求场景,可以设置不同的分词长度。
      • 比如针对工单自定义字段来说,25个长度的字符模糊搜索已经基本满足绝大部分客户的需求,所以在这里我们设置为min_gram1,最大为max_gram25,那么最大的分词长度就是25个
      • 在这里插入图片描述
  • 现在已知ngram可以解决模糊搜索问题,但发现当设置最小为1,最大为25时出现了性能问题。比如文本长度过长(几千个字符时),分词耗时长,导致查询也耗时长,es索引存储空间占用大
    • 但针对工单自定义字段,大多都是短文本,长文本较少,所以可以使用limit分词过滤器,对分析的结果数量进行限制,达到性能优化和节省空间的目的
    • 官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/8.0/analysis-limit-token-count-tokenfilter.html
    • 限制分词的结果集数量,max_token_count默认为1
    • 在这里插入图片描述
    • 但很明显只有一个分词结果是不合适的,所以我们设置max_token_count为500,可以满足30个字符的模糊搜索,具体的索引结构,在上面有提到
    • 在这里插入图片描述
    • 在这里插入图片描述
  • 现在虽然解决了模糊搜索,也优化了性能,但发现分词结果中有很多重复的结果
    • 在这里插入图片描述
    • 这些重复的结果对于检索来说不但浪费性能,而且浪费存储空间,所以需要一个对结果进行去重的过滤器
  • 查看官方文档,发现有一个unique去重过滤器可以满足需求
    • 官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/8.0/analysis-unique-tokenfilter.html
    • 在这里插入图片描述
  • 完结
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值