📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

提到 Easysearch(或 Elasticsearch)的 分词器(Analyzer),很多开发者的第一反应是:“我就用默认的 standard,或者装个 ik_max_word,够用就行。”

但当你遇到下面这些场景时,标准分词器往往会让你抓狂:

  • 用户搜邮箱 john.doe@example.com,搜 john 能出,搜 gmail 却出不来?
  • 搜商品型号 X-2000-Pro,搜 X2000 居然搜不到?
  • 想让搜索忽略大小写,但又不想忽略某些特定的专有名词?

这时候,你需要的不是寻找更强大的插件,而是自己动手组装一个 Analyzer

别被“自定义”这三个字吓倒。在 Easysearch 中,自定义分词器就像搭乐高积木一样简单且有趣。

分词器的“乐高”结构 #

一个完整的 Analyzer 由三个核心组件按顺序流水线组成。我们可以把它们想象成三个不同的积木桶,你可以从每个桶里挑出你需要的积木,拼成一个专属的分词器。

  1. 字符过滤器(Character Filters) —— 预处理
    • 作用:在分词前对原始字符串进行增删改。
    • 常见积木html_strip(去 HTML 标签)、mapping(字符替换,如把 & 换成 and)。
    • 数量:0 个或多个。
  2. 分词器(Tokenizer) —— 切分
    • 作用:把字符串切成一个个的 Token(词)。
    • 常见积木whitespace(按空格切)、standard(按语法切)、pattern(按正则切)。
    • 数量必须且只能有 1 个
  3. Token 过滤器(Token Filters) —— 加工
    • 作用:对切好的 Token 进行处理(转小写、去停用词、加同义词)。
    • 常见积木lowercasestopsynonymunique
    • 数量:0 个或多个。

实战:打造一个“商品型号”专用分词器 #

假设我们电商网站有一批商品,型号格式如 CODE-2024-PRO
需求:

  1. 忽略大小写(搜 code 能出)。
  2. 支持部分匹配(搜 2024 能出)。
  3. 支持兼容匹配(搜 CODE2024 也能出)。

如果用默认的 standard,它会把 CODE-2024-PRO 切分成 [code, 2024, pro]。但这满足不了“兼容匹配”的需求(搜 CODE2024 没法匹配到单独的词)。

我们来 DIY 一个。

第一步:设计蓝图 #

  • Tokenizer: 使用 pattern 分词器。我们可以定义一个正则,把非字母数字的字符(如 -)作为分隔符,或者反过来,只保留字母数字。
  • Token Filter:
    1. lowercase:统一转小写。
    2. ngram(可选):如果我们想支持极强的模糊搜索。

但为了解决 CODE2024 这种连写搜 CODE-2024 的问题,我们换个思路:保留原始符号,同时生成不带符号的版本。或者更简单,在索引时就把标点符号去掉。

让我们尝试一个更通用的配置:Mapping Char Filter + Whitespace Tokenizer

第二步:编写 DSL #

我们在创建索引(Index)时,通过 settings 来定义这个 Analyzer。

PUT /product_index
{
  "settings": {
    "analysis": {
      "char_filter": {
        "my_hyphen_remover": {
          "type": "pattern_replace",
          "pattern": "-",
          "replacement": ""
        }
      },
      "analyzer": {
        "my_product_analyzer": {
          "type": "custom",
          "char_filter": [
            "html_strip",        // 1. 先去 HTML
            "my_hyphen_remover"  // 2. 把横杠去掉 (CODE-2024 -> CODE2024)
          ],
          "tokenizer": "standard", // 3. 标准切分
          "filter": [
            "lowercase"          // 4. 转小写
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "product_code": {
        "type": "text",
        "analyzer": "my_product_analyzer"
      }
    }
  }
}

注意:上面的配置是一个激进的例子。它会把 CODE-2024 变成 code2024。这样用户搜 code2024 完美匹配。但搜 2024 可能就挂了。

更优的方案:保留分隔符,但支持多种切分

通常我们会定义一个 Analyzer,只负责简单的切分和归一化:

PUT /product_index_v2
{
  "settings": {
    "analysis": {
      "analyzer": {
        "code_analyzer": {
          "type": "custom",
          "tokenizer": "whitespace",  // 按空格切,保留 CODE-2024-PRO 作为一个整体(假设没空格)
          "filter": [
            "lowercase",              // 转小写
            "word_delimiter_graph"    // 神器!智能拆分单词
          ]
        }
      }
    }
  }
}

这里用到了 word_delimiter_graph,它是处理结构化文本(如 ID、型号、MAC 地址)的神器。它能配置成:

  • WiFi 拆成 Wi, Fi
  • CODE-2024 拆成 CODE, 2024
  • 甚至保留原始词 CODE-2024

调试:不确定的事,测一下 #

配置好了不敢用?Easysearch 提供了 _analyze API,让你在写入数据前就能看到效果。

POST /product_index_v2/_analyze
{
  "analyzer": "code_analyzer",
  "text": "CODE-2024-PRO"
}

观察返回的 Token 列表,看看是否包含了你期望的所有搜索词。

总结 #

自定义 Analyzer 并不是黑魔法,它只是对 Easysearch 现有能力的重新排列组合

  1. 不要迷信默认配置:默认配置是为了通用性,而你的业务往往有特殊性。
  2. **善用 **_analyze:所见即所得,调试是自定义分词器的核心步骤。
  3. 从需求倒推:先想好用户会怎么搜(搜 2024 还是 code),再决定怎么切。

当你开始尝试自定义 Analyzer 时,你就已经从一个 Easysearch 的“使用者”进阶为了“掌控者”。