ElasticSearch 内置分析器的一些问题 第一篇

本文分析了ElasticSearch内置的standard分析器在处理中文时导致的问题,如将中文拆分为单字,影响搜索结果的准确性。讨论了搜索评分机制,并提出通过使用shingle来优化文档,以提高‘内科’等连字搜索的精确性。然而,这可能导致‘肿瘤内科’得分高于‘内科’,需要进一步调整查询权重。

1. 内置分析器(不是分词器)standard

es自带四种内置分析器,分别为标准分析器、简单分析器、空格分析器、语言分析器。

1.1 standard标准分析器

1.1.1 分析器

分析的过程:

  • 首先,将一块文本分成适合于倒排索引的独立的 词条
  • 之后,将这些词条统一化为标准格式以提高它们的“可搜索性”,或者 recall

包含三个过程:
- 字符过滤器(如过滤空格)

  • 分词器(简单的进行分词)

  • Token 过滤器(通过设置的token过滤器,对词条进行处理,如大小写转换)

1.1.2 场景
  • 假设索引里面有两个数据:
内科
内一科
内二科
普通内科
肿瘤内科
  • 我们的预期目标:输入“内科”——得到——>“内科”(且内科应得分最高)以及包含“内科”的结果
  • 实际上的到的结果:
"hits": [
    {
        "_index": "studydemo",
        "_type": "doc",
        "_id": "TicbAWMB1wKYJm5vNAws",
        "_score": 1.5603871,
        "_source": {
            "text": "肿瘤内科"
        }
    },
    {
        "_index": "studydemo",
        "_type": "doc",
        "_id": "SycaAWMB1wKYJm5v0gz9",
        "_score": 0.5753642,
        "_source": {
            "text": "内一科"
        }
    },
    {
        "_index": "studydemo",
        "_type": "doc",
        "_id": "TScbAWMB1wKYJm5vEwyj",
        "_score": 0.5753642,
        "_source": {
            "text": "普通内科"
        }
    },
    {
        "_index": "studydemo",
        "_type": "doc",
        "_id": "SicaAWMB1wKYJm5vvQzv",
        "_score": 0.3971361,
        "_source": {
            "text": "内科"
        }
    },
    {
        "_index": "studydemo",
        "_type": "doc",
        "_id": "TCcaAWMB1wKYJm5v8gzZ",
        "_score": 0.33706507,
        "_source": {
            "text": "内二科"
        }
    }
]

以上结果中有两处是我们不希望得到的:

  • “内一科”和“内二科”

  • 最佳匹配结果“内科”得分很低

1.1.3 原因分析

标准分析器(standard)会将中文拆分为单字,如:内科—–>内,科;内一科—–>内,一,科
同时,查询的时候并非直观的拿着“内科”(连在一起)进行匹配,而是首先会对搜索条件进行一下操作:
- 使用搜索的目标field配置的analyzer进行analysis 得到对应的词条
- 将第一步得到的词条逐个在文档内进行匹配

现在再看文档中的数据:
- “内科”其实保存的是“内”,“科”
- “内一科”其实保存的是“内”,“一”,“科”
- “肿瘤内科”其实保存的是“肿”,“瘤”,“内”,“科”

所以输入内科,进行搜索的是“内”和“科”的单子匹配,这是为什么会出现“内一科”这类结果的原因。

理解为什么“肿瘤内科”的得分为什么比内科高之前,需要了解es内部对于相关度的评分,“一个文档的相关度评分部分取决于每个查询词在文档中的 权重
而影响权重的因素包含:
- 检索词频率: 检索词在该字段出现的频率?出现频率越高,相关性也越高。 字段中出现过 5 次要比只出现过 1 次的相关性高。
- 反向文档频率:每个检索词在索引中每个文档的对应字段中出现的频率?频率越高,相关性越低。检索词出现在多数文档中会比出现在少数文档中的权重更低。
- 字段长度准则:字段的长度是多少?长度越长,相关性越低。 检索词出现在一个短的 title 要比同样的词出现在一个长的 content 字段权重更大。

与此同时还需要理解一点:

{
  "query": {
    "bool": {
      "should": [
        {"match": { "text": "内科" }}
      ]
    }
  }
}

会被重写为:

{
  "query": {
    "bool": {
      "should": [
        {"match": { "text": "内" }},
        {"match": { "text": "科" }}
      ]
    }
  }
}

只要一个文档与查询匹配,Lucene 就会为查询计算评分,然后合并每个匹配词的评分结果,
所以对于“肿瘤内科”,其评分等于“内”,“科”,“肿”,“瘤”四个单自结合以上的评分规则得到的分值,所以高于内科
具体评分规则参考相关度评分

1.1.4 解决方案
方案一:

索引时优化文档,使用“shingle”将目标field分解为单字和连字的倒排索引,如”肿瘤内科”分析为:


{
    "tokens": [
        {
            "token": "肿",
            "start_offset": 0,
            "end_offset": 1,
            "type": "<IDEOGRAPHIC>",
            "position": 0
        },
        {
            "token": "肿 瘤",
            "start_offset": 0,
            "end_offset": 2,
            "type": "shingle",
            "position": 0,
            "positionLength": 2
        },
        {
            "token": "肿 瘤 内",
            "start_offset": 0,
            "end_offset": 3,
            "type": "shingle",
            "position": 0,
            "positionLength": 3
        },
        {
            "token": "肿 瘤 内 科",
            "start_offset": 0,
            "end_offset": 4,
            "type": "shingle",
            "position": 0,
            "positionLength": 4
        },
        {
            "token": "瘤",
            "start_offset": 1,
            "end_offset": 2,
            "type": "<IDEOGRAPHIC>",
            "position": 1
        },
        {
            "token": "瘤 内",
            "start_offset": 1,
            "end_offset": 3,
            "type": "shingle",
            "position": 1,
            "positionLength": 2
        },
        {
            "token": "瘤 内 科",
            "start_offset": 1,
            "end_offset": 4,
            "type": "shingle",
            "position": 1,
            "positionLength": 3
        },
        {
            "token": "内",
            "start_offset": 2,
            "end_offset": 3,
            "type": "<IDEOGRAPHIC>",
            "position": 2
        },
        {
            "token": "内 科",
            "start_offset": 2,
            "end_offset": 4,
            "type": "shingle",
            "position": 2,
            "positionLength": 2
        },
        {
            "token": "科",
            "start_offset": 3,
            "end_offset": 4,
            "type": "<IDEOGRAPHIC>",
            "position": 3
        }
    ]
}

此时输入上述任一字符,均可得到“肿瘤内科”,如搜索“瘤内科”。以上字符中的空格是分析器为了方便查阅,实质是上没有空格。

到这时,会发现输入“内科”,”内一科”将不会出现在搜索结果中,也就是说这时候搜索结果只会是包含“内科”(连字)的数据集,而不是包含“内“或”科”(单字)的结果集。但是新的问题,“肿瘤内科”的得分高于“内科”。

解决方案查询时使用正则提升权重

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值