ES分词导致查询结果不准确

问题现象

索引里面有数据,而没有查询出来。

如下图所示,术语库(索引)中里面有一条数据的原文是“层”,而根据完整的原文来查询该原文中的术语,并未将该术语查询出来。

根据原文查询该原文中的术语,并未查询到上面的术语:

原因分析

这是由于es分词器导致的。由于es在执行查询时,会先将原文进行分词,再将分词后的结果拿去匹配。

查看分词结果:

由于待查询的字段采用的分词器是optimizeIK,所以这里使用该分词器查询原文分词后的结果

分词结果:

由于分词结果并没有“层”这条数据,所以也不会匹配出“层”这条术语。

解决方案

两种解决方案,分别是增加自定义词库和查询前分词

增加自定义词库

也就是自定义一个词库,让es遇到自定义词库中的词时也进行分词。比如将上述的“层”加入自定义词库当中,如何es分词时则会将“层”给分词出来。这需要改动es的配置文件IKAnalyzer.cfg.xml,修改文件里面的扩展配置指向自定义词库。

这种方案的缺点是不能一次性解决该问题,因为需要遇到此问题时,才能将未查询到的词组加入自定义词库,而且需要重启es,会影响线上功能。

查询前分词

所谓查询前分词,也就是不直接使用原文进行查询,而是先将原文通过多个分词器进行分词,再使用分词后的结果进行查询。比如将原文分别进行standard分词器分词和optimizeIK分词器分词,将两种分词器的分词结果进行合并,然后再进行查询。

先分词:

使用分词后的结果进行匹配:

es命令:

GET /tb/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "terms": {
            "dbId": [
              "19841159f25845008fad2512aa91f48b"
            ]
          }
        }
      ],
      "should": [
        {
          "match_phrase": {
            "original": "侏"
          }
        },
        {
          "match_phrase": {
            "original": "罗"
          }
        }
        ....
      ],
      "minimum_should_match": 1, 
      "filter": [
        {
          "script": {
            "script": {
              "lang": "painless",
              "source": "String str = params.val;String strLower = params.valLower;String val = doc['original.keyword'].value;return str.contains(val) || strLower.contains(val.toLowerCase());",
              "params": {
                "val": "侏罗层系主力区块年注采比变化",
                "valLower": "侏罗层系主力区块年注采比变化"
              }
            }
          }
        }
      ]
    }
  }
}

缺点是如果分词结果太多,会比较消耗性能

目前是采用第二种方案来解决该问题的。

<think>首先,用户的问题是:“我想查找在ElasticsearchES)中查询人名时分词的方法,请问Elasticsearch查询人名时分词的实现方法”。我需要基于提供的引用和系统级指令来回答。 系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确(但在这个上下文中,可能没有数学公式)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1]。 用户引用: - 引用[1]:关于中文分词器的作用和使用。 - 引用[2]:关于业务分词搜索,提到了ik_max_word分词器的缺陷和优化方法。 - 引用[3]:关于查询人名关键字拆词查询,使用了match_phrase短语匹配模式。 用户的核心需求是实现查询人名时分词,即在Elasticsearch查询整个人名而进行分词处理。 从引用[3]中直接相关:它描述了查询人名关键字拆词查询的实现方法,使用match_phrase短语匹配模式,并提供了DSL查询语言的例子。 具体方法: - 使用bool查询。 - 在must子句中包含match_phrase查询。 - 例子:查询字段afterContent为"张三丰",它会精确匹配整个短语而分词。 其他引用也相关: - 引用[1]讨论了中文分词器的必要性,因为内置分词器对中文支持好。在处理人名时,可能需要选择合适的分词器。 - 引用[2]提到使用分词器来优化查询,例如避免某些分词缺陷(如“承诺”未被正确分词)。这可能涉及到在索引时设置字段使用特定的分词器。 因此,实现查询人名时分词的方法可能有几种: 1. **使用match_phrase查询**:如引用[3]所示,这是一种精确匹配短语的方法,进行分词。 2. **使用term查询**:如果字段是keyword类型,term查询会精确匹配分词。但默认情况下,字符串字段可能会被分词。 3. **索引设置**:在索引映射中,设置字段为keyword类型或使用分词分词器(如keyword分词器)。 4. **优化分词器**:如引用[2]提到的,使用同的分词器配置,例如在索引时使用一个分词的字段版本。 结构化回答: - 因为系统要求回答结构清晰,逐步解决问题。 - 使用中文回答。 - 在引用结束添加标识:[^数字]。 步骤: 1. **介绍问题**:解释为什么人名查询需要分词(避免分词导致的问题)。 2. **实现方法**: - **方法1:使用match_phrase查询**:基于引用[3]。 - **方法2:设置字段为keyword类型**:索引时使用keyword分词器。 - **方法3:自定义分词器**:如引用[2]提到的,使用两个字段,一个分词一个分词。 3. **提供例子**:给出DSL查询的例子。 4. **总结建议**:基于需求推荐最佳方法。 5. **生成相关问题**:在最后添加相关问题部分。 生成相关问题: - 参考示例,添加3-4个相关问题,如:“如何优化Elasticsearch的中文分词?”、“match_phrase和term查询的区别是什么?”等。 确保回答真实可靠:基于提供的引用和一般Elasticsearch知识。 最终回答结构: 1. 引言:解释问题。 2. 方法概述。 3. 详细方法。 4. 例子。 5. 总结。 6. 相关问题。 添加引用: - 在引用来自的地方添加[^1], [^2], [^3]。 由于用户提到“上述引用仅供参考,并是用户真正的需求”,但在引用[3]中直接有方法,我应该优先参考它。 </think>### Elasticsearch查询人名时分词的实现方法 在Elasticsearch查询人名时如果分词,主要目的是避免分词器将人名拆分成单个字或词(如“张三丰”被拆成“张”“三”“丰”),导致查询结果准确(例如查询“张三丰”时返回包含“张三”的文档)。这在中文人名场景中尤为重要,因为默认的分词器(如ik_max_word)可能无法很好地处理专有名词[^1][^2]。实现方法主要包括查询时使用特定查询类型和索引时优化分词器设置。以下是基于Elasticsearch的最佳实践,逐步说明: #### 1. **查询时使用短语匹配(match_phrase)** - **原理**:match_phrase查询会作为一个整体短语进行匹配,进行分词处理。它要求查询关键字完全按照顺序出现在字段中,适用于精确匹配人名或短句[^3]。 - **实现步骤**: - 在查询DSL中使用bool查询的must子句嵌入match_phrase。 - 指定目标字段和查询关键字(如人名)。 - **示例DSL查询**: ```json GET your_index/_search { "query": { "bool": { "must": [ { "match_phrase": { "name_field": "张三丰" // name_field是存储人名的字段名 } } ] } } } ``` - 解释:此查询会精确匹配字段`name_field`中值为"张三丰"的文档,而分词。如果查询"张三丰",它会返回仅包含"张三"的文档[^3]。 - **优缺点**: - 优点:简单易用,无需修改索引结构,适用于临时查询。 - 缺点:如果字段在索引时已被分词,match_phrase可能无法完全避免分词干扰(推荐结合索引设置优化)。 #### 2. **索引时设置keyword类型字段** - **原理**:在索引映射中,将存储人名的字段设置为keyword类型。keyword类型的分词器(默认分词)会将整个字符串视为一个token,查询时使用term查询实现精确匹配[^2][^3]。 - **实现步骤**: 1. 创建索引映射时,定义字段类型为keyword。 2. 查询时使用term查询分词)。 - **示例索引映射**: ```json PUT your_index { "mappings": { "properties": { "name_field": { // 人名字段 "type": "keyword" // 设置为keyword类型,分词 } } } } ``` - **示例查询**: ```json GET your_index/_search { "query": { "term": { "name_field": "张三丰" // 精确匹配整个字符串 } } } ``` - 解释:keyword类型确保字段值分词,term查询直接匹配完整人名。例如,查询"张三丰"只会返回该人名的文档[^2]。 - **优缺点**: - 优点:性能高,适合高精度场景;可与match_phrase结合使用。 - 缺点:索引时需预先规划字段类型;如果字段需要同时支持分词分词查询,需创建冗余字段(如`name_keyword`和`name_text`)。 #### 3. **自定义分词器或使用多字段优化** - **原理**:如果人名字段需要保留分词功能(如支持模糊搜索),但又要避免查询时的分词需求,可以定义一个多字段(multi-field):一个使用keyword类型分词,另一个使用分词器(如ik_smart)。查询时根据需求选择字段[^1][^2]。 - **实现步骤**: 1. 在索引映射中创建多字段。 2. 查询使用分词的子字段(keyword类型)。 - **示例映射**: ```json PUT your_index { "mappings": { "properties": { "name": { // 主字段 "type": "text", "fields": { "keyword": { // 冗余子字段,分词 "type": "keyword" }, "text": { // 子字段,使用分词器 "type": "text", "analyzer": "ik_smart" // 可选中文分词器 } } } } } } ``` - **示例查询**: ```json GET your_index/_search { "query": { "term": { "name.keyword": "张三丰" // 使用keyword子字段查询分词 } } } ``` - 解释:此方法解决了分词器的缺陷(如ik_max_word可能无法正确分词“承诺”),通过冗余字段实现灵活查询[^2]。 - **优缺点**: - 优点:灵活性高,支持分词分词并行;适用于复杂业务场景。 - 缺点:索引存储开销稍大,需要在索引设计时做好准备。 #### 总结和建议 - **推荐方法**:对于纯人名查询,优先使用索引时设置keyword类型字段(方法2),因为它效率最高且简单。如果索引已存在且无法修改,则使用match_phrase查询(方法1)。对于需要分词的场景,多字段优化(方法3)是最佳方案[^1][^2][^3]。 - **注意事项**: - 测试分词效果:使用Elasticsearch的`analyze API`检查字段分词行为(如`GET your_index/_analyze`)。 - 性能考量:keyword类型查询速度快,但match_phrase在大型索引中可能稍慢。 - 实际应用中,结合引用[3]的match_phrase方法,能快速实现需求,同时引用[2]的多字段技巧可优化中文分词缺陷[^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值