bigywater.tech
Elasticsearch · 25

Character Filter、Tokenizer、Token Filter 的区别

真实对比有无html_strip char_filter时HTML文本的分词结果差异,并用tokenizer参数拒绝数组的真实报错,证明三种组件在Analyzer链路里结构性不同的角色。

不加char_filter分词HTML
真实标签名污染了分词结果
html_strip char_filter
真实在分词前干净清除了结构
tokenizer参数
真实结构上只接受单值,不支持数组

做的事:真实对比有没有char_filter时,HTML标签对分词结果的真实影响

代码POST /_analyze {"tokenizer": "standard", "text": "<p>Hello <b>World</b></p>"} POST /_analyze {"char_filter": ["html_strip"], "tokenizer": "standard", "text": "<p>Hello <b>World</b></p>"} POST /_analyze {"tokenizer": ["standard", "whitespace"], "text": "hello world"} # 能传两个tokenizer吗

验证:没有char_filter清理,HTML标签残片真实混进了分词结果

实测 · 本机 Elasticsearch 8.15.0 执行输出
不加char_filter直接分词:
→ ['p', 'Hello', 'b', 'World', 'b', 'p']    ← 真实把标签名p和b当成了独立单词

加了html_strip char_filter:
→ ['Hello', 'World']                        ← 真实干净了,标签全部被在分词前清除

这组对比真实说明了char_filter必须在分词之前工作的原因:标准分词器真实只会按字符类型(字母、数字、标点)切词,它完全不认识"这是个HTML标签"这种结构化概念,<p>里的尖括号被当成分隔符丢掉之后,剩下的字母p真实会被当成一个合法的独立单词保留下来,直接污染了分词结果。html_strip这个char_filter真实必须抢在分词之前,在原始字符串这个层面就把整个标签结构识别出来并连标签名一起清除,等轮到tokenizer介入的时候,它看到的已经真实是干净的纯文本。

真实的结构性证据:tokenizer在API层面压根不接受数组,只能是单个值

实测 · 本机执行输出
POST /_analyze {"tokenizer": ["standard", "whitespace"], ...}
→ 400 x_content_parse_exception:
  "tokenizer doesn't support values of type: START_ARRAY"
这条报错真实从API参数校验的层面,直接证明了tokenizer跟char_filter/filter(token filter)在设计上的本质区别:char_filter和filter这两个参数真实都接受一个数组,意味着它们可以真实链式叠加多个(本系列上一篇验证过的两层token_filter依次生效就是这个道理),而tokenizer这个字段真实在参数解析那一层就只接受单个字符串值,连尝试传数组都会被直接拒绝,根本走不到"这两个分词器该怎么组合"这一步逻辑判断。这揭示了整条Analyzer链路真实的架构:char_filter是分词前可以叠加任意多层的字符串预处理,tokenizer是整条链路里唯一、必须恰好出现一次的"将字符串切成词"的核心步骤,token_filter是分词后同样可以叠加任意多层的token级后处理——三者不是地位平等、可以随意互换顺序或者数量的组件,tokenizer真实是整条链路里唯一的、不可重复也不可省略的枢纽。

分步演示:从HTML标签污染分词结果到tokenizer参数的结构性限制

每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。

char_filter清理结构在前,tokenizer是唯一不可重复的枢纽
这一步新验证的组件角色

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的 _analyze API 与 html_strip char_filter
    京ICP备2026067502号-1京公网安备11010502063487号