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

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
        }
    ]
}

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

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

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

猜你喜欢

转载自blog.csdn.net/time_travel/article/details/80214472