25

Elasticsearch能检索出来,但不能正确高亮怎么办?

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzI2NDY1MTA3OQ%3D%3D&%3Bmid=2247484758&%3Bidx=1&%3Bsn=1fa663c5f8b85a82ef25f8453af88394
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1、问题引出

微信群里的线上实战问题:

诸位大哥,es中:

keyword类型的字段进行高亮查询,值为 123asd456,查询 sd4,高亮结果是 em 123asd456 em

有没有办法只对我查询的sd4高亮?

明明查询id的一部分,却高亮结果是整个id串,怎么办?

死磕Elasticsearch技术微信群

2、一个Demo描述清楚问题

注:本文示例DSL在7.2版本运行ok,6.X之前早期版本可能需要微调。

PUT findex
{
  "mappings": {
    "properties": {
      "aname":{
        "type":"text"
      },
      "acode":{
        "type":"keyword"
      }
    }
  }
}

POST findex/_bulk
{"index":{"_id":1}}
{"acode":"160213.OF","aname":"X泰纳斯达克100"}
{"index":{"_id":2}}
{"acode":"160218.OF","aname":"X泰国证房地产"}

POST findex/_search
{
  "highlight": {
    "fields": {
      "acode": {}
    }
  },
  "query": {
    "bool": {
      "should": [
        {
          "wildcard": {
            "acode": "*1602*"
          }
        }
      ]
    }
  }
}

高亮检索结果,

"highlight" : {
          "acode" : [
            "<em>160213.OF</em>"
          ]
        }

也就是说整个串都被高亮了,没有达到预期。

实际需求:搜索 1602 ,相关数据:160213.O、160218.OF都能召回,且仅高亮搜索字段 1602

3、问题拆解

  • 检索选型wildcard是为了解决子串能匹配的问题,wildcard的实现类似mysql的“like”模糊匹配。

  • 传统的text标准分词器,包括中文分词器ik、英文分词器english、standard等都不能解决上述子串匹配问题。

而实际业务需求:

一方面:要求输入子串召回全串;

另一方面:要求高亮检索的子串。

只能更换一种分词Ngram来实现了!

4、什么是Ngram?

4.1 Ngram定义

Ngram是一种基于统计语言模型的算法。

Ngram 基本思想 :是将文本里面的内容按照字节进行大小为N的滑动窗口操作,形成了长度是N的字节片段序列。每一个字节片段称为 gram ,对所有gram的出现频度进行统计,并且按照事先设定好的阈值进行过滤,形成关键gram列表,也就是这个文本的向量特征空间,列表中的每一种gram就是一个特征向量维度。

该模型基于这样一种假设,第N个词的出现只与前面N-1个词相关,而与其它任何词都不相关,整句的概率就是各个词出现概率的乘积。

这些概率可以通过直接从语料中统计N个词同时出现的次数得到。常用的是二元的Bi-Gram(二元语法)和三元的Tri-Gram(三元语法)。

4.2 Ngram举例

中文句子:“你今天吃饭了吗”,它的Bi-Gram(二元语法)分词结果为:

你今
今天
天吃
吃饭
饭了
了吗

4.3 Ngram 应用场景

  • 场景1:文本压缩、检查拼写错误、加速字符串查找、文献语种识别。

  • 场景2:自然语言处理自动化领域得到新的应用,如自动分类、自动索引、超链的自动生成、文献检索、 无分隔符语言文本的切分 等。
  • 场景3:自然语言的自动分类功能。对应到Elasticsearch检索,应用场景就更加明确:无分隔符语言文本的切分分词,提高检索效率(相比:wildcard 查询和正则查询)。

5、实践一把

PUT findex_ext
{
  "settings": {
    "index.max_ngram_diff": 10,
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "ngram",
          "min_gram": 4,
          "max_gram": 10,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "aname": {
        "type": "text"
      },
      "acode": {
        "type": "text",
        "analyzer": "my_analyzer",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

POST findex_ext/_bulk
{"index":{"_id":1}}
{"acode":"160213.OF","aname":"X泰纳斯达克100"}
{"index":{"_id":2}}
{"acode":"160218.OF","aname":"X泰国证房地产"}

# 查看分词结果
POST findex_ext/_analyze
{
  "analyzer": "my_analyzer",
  "text":"160213.OF"
}

POST findex_ext/_search
{
  "highlight": {
    "fields": {
      "acode": {}
    }
  },
  "query": {
    "bool": {
      "should": [
        {
          "match_phrase": {
            "acode": {
              "query": "1602"
            }
          }
        }
      ]
    }
  }
}

注意:三个核心参数

  • min_gram:最小字符长度(切分),默认为1

  • max_gram:最大字符长度(切分),默认为2

  • token_chars:生成的分词结果中包含的字符类型,默认是全部类型。如上的示例中代表:保留数字、字母。若上述示例中,只指定  "letter",则数字就会被过滤掉,分词结果只剩下串中的字符如:"OF"。

返回结果截取片段如下:

"highlight" : {
          "acode" : [
            "<em>1602</em>13.OF"
          ]
        }

已经能满足检索和 高亮 的双重需求。

5、选型注意

  • Ngram的 本质 :用空间换时间。其能匹配的前提是写入的时候已经按照:min_gram、max_gram切词。

  • 数据量非常少且不要求子串高亮,可以考虑keyword。

  • 数据量大且要求子串高亮,推荐使用:Ngram分词结合match或者match_phrase检索实现。

  • 数据量大,切记不要使用wildcard前缀匹配!

    原因 :带有通配符的pattern构造出来的DFA(Deterministic Finite Automaton)可能会很复杂,开销很大!甚至可能导致线上环境宕机。

    Wood大叔也 多次强调: wildcard query应杜绝使用通配符打头 ,实在不得已要这么做,就一定需要限制用户输入的字符串长度。

6、小结

为讨论解决线上问题,引申出Ngram的原理和使用逻辑,并指出了wildcard和Ngram的适用业务场景。希望对实战中的你有所启发和帮助!

你在业务中遇到子串匹配和高亮的情况吗?你是如何分词和检索的?欢迎留言讨论。

参考:

1、https://zhuanlan.zhihu.com/p/32829048

2、http://blog.sciencenet.cn/blog-713101-797384.html

3、https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-ngram-tokenizer.html

4、https://elasticsearch.cn/article/171

更多推荐:

1、Elasticsearch实战|如何从数千万手机号中识别出情侣号?

2、干货|Elasticsearch开发人员最佳实战指南

3、 95后运维小哥20天+通过Elastic认证考试经验分享

4、潜心一技、做到极致!——Elastic认证工程师之路

5、“金三银四“,敢不敢“试”?

VZBneeI.jpg!web

中国通过Elastic认证工程师考试人数最多的圈子!

更短时间更快习得更多干货!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK