ES101系列06 | 中文分词和Suggester

67 阅读6分钟

本篇文章主要讲解中文分词和 ElasticSearch 中的重要 API —— Suggester,同时也会提到 Search Template、Index Alias 和 Function Score Query 等高级搜索功能。

自然语言与查询

当处理人类自然语言时,有时尽管搜索和原文不完全匹配,但是希望搜到一些内容。

可以采取的措施:

  • 归一化词元:例如消除变音符号(西语,拼音)。
  • 抽取词根:消除单复数等。
  • 包含同义词。
  • 拼写错误处理。

混合多语言的挑战

不同的索引使用不同的语言;同一个索引中,不同的字段使用不同的语言;一个文档的一个字段内混合不同的语言。

  • 词干提取:以色列文档,包含了希伯来语,阿拉伯语,俄语和英文。
  • 不正确的文档频率:英文为主的文章中,德文算分高(稀有)。
  • 需要判断用户搜索时使用的语言。

中文分词(IK)

由于生产环境中最常见的中文分词器为 IK,因此本篇也以 IK 为主。

安装

仓库地址:L2ncE/es101

克隆仓库后进入到 docker-compose 文件 目录,将 docker.elastic.co/elasticsearch/elasticsearch:7.8.0 改为 zingimmick/elasticsearch-ik:7.8.0。运行 docker-compose up

示例

POST _analyze
{
  "analyzer": "ik_smart",
  "text": ["剑桥分析公司多位高管对卧底记者说,他们确保了唐纳德·特朗普在总统大选中获胜"]
}

Search Template

用于解耦程序和搜索 DSL。

示例

POST _scripts/tmdb
{
  "script": {
    "lang": "mustache",
    "source": {
      "_source": [
        "title",
        "overview"
      ],
      "size": 20,
      "query": {
        "multi_match": {
          "query": "{{q}}",
          "fields": [
            "title",
            "overview"
          ]
        }
      }
    }
  }
}

POST tmdb/_search/template
{
    "id":"tmdb",
    "params": {
        "q": "basketball with cartoon aliens"
}
}

上游可以不感知模版的变化,避免耦合。

Index Alias

实现零停机运维。比如在进行索引重建、版本升级、滚动更新等操作时,无需中断服务。

示例

1、插入数据

PUT movies-2019/_doc/1
{
  "name":"the matrix",
  "rating":5
}

PUT movies-2019/_doc/2
{
  "name":"Speed",
  "rating":3
}

2、设置别名

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "movies-2019",
        "alias": "movies-latest"
      }
    }
  ]
}

POST movies-latest/_search
{
  "query": {
    "match_all": {}
  }
}

Function Score Query

可以在查询结束后对每一个匹配的文档进行一系列的重新算分,根据新生成的分数进行排序。

  • Weight:为每一个文档设置一个简单而不被规范化的权重。
  • Field Value Factor:使用该数值来修改 \_score,例如将「热度」和「点赞数」作为算分的参考因素。
  • Random Score: 为每一个用户使用一个不同的,随机算分结果。
  • 衰减函数:以某个字段的值为标准,距离某个值越近,得分越高。
  • Script Score:自定义脚本完全控制所需逻辑。

示例

1、插入数据

DELETE blogs
PUT /blogs/_doc/1
{
  "title":   "About popularity",
  "content": "In this post we will talk about...",
  "votes":   0
}

PUT /blogs/_doc/2
{
  "title":   "About popularity",
  "content": "In this post we will talk about...",
  "votes":   100
}

PUT /blogs/_doc/3
{
  "title":   "About popularity",
  "content": "In this post we will talk about...",
  "votes":   1000000
}

2、使用多个参数测试

POST /blogs/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {
        "field": "votes",
        "modifier": "log1p" ,
        "factor": 0.1
      }
    }
  }
}
  • 初始逻辑:新的算分=老的算分投票数新的算分 = 老的算分 * 投票数
  • 使用 modifier 平滑参数后:新的算分=老的算分log(1+投票数)新的算分 = 老的算分 * log(1 + 投票数)
  • Factor 参数:新的算分=老的算分log(1+factor投票数)新的算分 = 老的算分 * log(1 + factor * 投票数)

3、一致性随机函数

POST /blogs/_search
{
  "query": {
    "function_score": {
      "random_score": {
        "seed": 911119
      }
    }
  }
}
  • 使用场景:网站的广告需要提高展现率。
  • 具体需求:让每个用户能看到不同的随机排名,但是也希望同一个用户访问时,结果的相对顺序保持一致。

4、Boost Mode 和 Max Boost

POST /blogs/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {
        "field": "votes",
        "modifier": "log1p" ,
        "factor": 0.1
      },
      "boost_mode": "sum",
      "max_boost": 3
    }
  }
}

Max Boost 可以将算分控制在一个最大值。Boost Mode 用于进行算分的数学运算。

Suggester

实现搜索引擎中纠错的功能。原理是将文本分解为 Token 然后在索引的字典中查找相似的 Term 并返回。

四种 Suggester 的不同之处:

Suggester 类型功能与特点适用场景
Term Suggester对输入文本的每个词条进行纠错或建议,基于索引中的词典查找相似 Term。单个词条级别的纠错和建议(如拼写错误修正)。
Phrase Suggester在 Term Suggester 基础上,考虑多个词条之间的关系(如是否共同出现、相邻程度等)。多个词组成的短语级别的纠错和建议(如句子片段的修正)。
Completion Suggester提供前缀匹配的自动补全功能,支持快速搜索建议(如用户输入时的提示)。快速自动补全(如搜索框输入提示)。
Context Suggester基于 Completion Suggester,增加了上下文信息的支持(如地理位置、类别等过滤条件)。需要结合上下文信息的自动补全(如特定分类下的搜索提示)。

示例

1、插入数据

DELETE articles

POST articles/_bulk
{ "index" : { } }
{ "body": "lucene is very cool"}
{ "index" : { } }
{ "body": "Elasticsearch builds on top of lucene"}
{ "index" : { } }
{ "body": "Elasticsearch rocks"}
{ "index" : { } }
{ "body": "elastic is the company behind ELK stack"}
{ "index" : { } }
{ "body": "Elk stack rocks"}
{ "index" : {} }
{  "body": "elasticsearch is rock solid"}

2、Term Suggester

几种 Suggestion Mode

  • Missing:如果索引中已经存在,就不提供建议。
  • Popular:推荐出现频率更加高的词。
  • Always:无论是否存在,都提供建议。
POST /articles/_search
{
  "size": 1,
  "query": {
    "match": {
      "body": "lucen rock"
    }
  },
  "suggest": {
    "term-suggestion": {
      "text": "lucen rock",
      "term": {
        "suggest_mode": "missing",
        "field": "body"
      }
    }
  }
}

3、Phrase Suggester

在 Term Suggester 的基础上增加了一些额外的逻辑。

  • Max Errors:最多可以拼错的 Terms 数。
  • Confidence:控制返回建议的置信度阈值。只有当建议短语的原始得分加上长度归一化后 ≥confidence 时才会被返回,默认为 1。
POST /articles/_search
{
  "suggest": {
    "my-suggestion": {
      "text": "lucne and elasticsear rock hello world ",
      "phrase": {
        "field": "body",
        "max_errors":2,
        "confidence":2,
        "direct_generator":[{
          "field":"body",
          "suggest_mode":"always"
        }],
        "highlight": {
          "pre_tag": "<em>",
          "post_tag": "</em>"
        }
      }
    }
  }
}

4、Completion Suggester

提供了自动补全功能。对性能要求比较严苛,采用了非倒排索引的数据结构,将 Analyze 数据编码成 FST 和索引一起存放。FST 会整个加载进内存,速度很快。同时 FST 仅支持前缀查找。

DELETE articles
PUT articles
{
  "mappings": {
    "properties": {
      "title_completion":{
        "type": "completion"
      }
    }
  }
}

POST articles/_bulk
{ "index" : { } }
{ "title_completion": "lucene is very cool"}
{ "index" : { } }
{ "title_completion": "Elasticsearch builds on top of lucene"}
{ "index" : { } }
{ "title_completion": "Elasticsearch rocks"}
{ "index" : { } }
{ "title_completion": "elastic is the company behind ELK stack"}
{ "index" : { } }
{ "title_completion": "Elk stack rocks"}
{ "index" : {} }
POST articles/_search?pretty
{
  "size": 0,
  "suggest": {
    "article-suggester": {
      "prefix": "elk ",
      "completion": {
        "field": "title_completion"
      }
    }
  }
}

5、Context Suggester

是 Completion Suggester 的拓展,能够在搜索中加入更多的上下文信息。

可以定义两种类型的 Context:

  • Category 一任意的字符串。
  • Geo—地理位置信息。

实现 Context Suggester 的具体步骤:

  • 定制一个 Mapping。
  • 索引数据,并且为每个文档加入 Context 信息。
  • 结合 Context 进行 Suggestion 查询。
DELETE comments
PUT comments
PUT comments/_mapping
{
  "properties": {
    "comment_autocomplete":{
      "type": "completion",
      "contexts":[{
        "type":"category",
        "name":"comment_category"
      }]
    }
  }
}

POST comments/_doc
{
  "comment":"I love the star war movies",
  "comment_autocomplete":{
    "input":["star wars"],
    "contexts":{
      "comment_category":"movies"
    }
  }
}

POST comments/_doc
{
  "comment":"Where can I find a Starbucks",
  "comment_autocomplete":{
    "input":["starbucks"],
    "contexts":{
      "comment_category":"coffee"
    }
  }
}
POST comments/_search
{
  "suggest": {
    "MY_SUGGESTION": {
      "prefix": "sta",
      "completion":{
        "field":"comment_autocomplete",
        "contexts":{
          "comment_category":"coffee"
        }
      }
    }
  }
}

写在最后

这是该系列的第六篇,主要讲解中文分词和 ElasticSearch 中的重要 API —— Suggester,同时也提到 Search Template、Index Alias 和 Function Score Query 等高级搜索功能。可以自己去到 Kibana 的 Dev Tool 实战操作,未来会持续更新该系列,欢迎关注👏🏻。

同时欢迎关注公众号:LanTech指南。不定时分享职场思考、独立开发日志、大厂方法论和后端经验❤️

参考

  1. github.com/onebirdrock…
  2. www.elastic.co/elasticsear…