拜托,面试官再问我一次字节过滤器吧

拜托,面试官再问我一次字节过滤器吧

写在前面

在开始介绍字符过滤器,我们还是先回顾一下上一节,聊到的分词器结构。

分词器.png

分词器有三个主要的需求:记号化标准化自定义分词器,在Elasticsearch中,为了处理这样的三个需求,首先定义了一个Analyzer接口,通过模板方法的模式,来自定义相关功能的处理流程和方式,之后对于记号化和标准化,通过三种类型的过滤器,依次进行处理。

今天,就让我们聊下三种类型的过滤器的字符过滤器

字符过滤器

在es官网上,是这么介绍字符过滤器的

Character filters are used to preprocess the stream of characters before it is passed to the tokenizer.

在字节流进入分词之前,对字节流进行处理。

A character filter receives the original text as a stream of characters and can transform the stream by adding, removing, or changing characters.

因此,字节过滤器接收的是源文本,使用上吗,它可以对字节流进行增删改。

Elasticsearch has a number of built in character filters which can be used to build custom analyzers

es提供一些内置的字节过滤器,用来提供给用户自定义文本的分词器。

过滤器标识功能作用
HTML Strip Character Filterhtml_stripHTML文本处理,去除HTML标签,或者对标签进行decode
Mapping Character Filtermapping对文本进行字符串的替换
Pattern Replace Character Filterpattern_replace正则表达式方式对字符串文本进行替换

Character Filter用法演示

首先,我们先看一下Character Filter的使用方式,之后我们从源码角度看一下Character Filter是如何工作的。

验证分词过滤器效果

GET /_analyze
{
  "tokenizer": "keyword",
  "char_filter": [
    "html_strip"
  ],
  "text": "<p>I&apos;m so <b>happy</b>!</p>"
}
复制代码

返回结果:

I'm so happy!
复制代码

通过验证,html_strip将文本中的&apos;编码成了单引号',并且去除p标签b标签

分词器中使用

在使用中,我们可以通过索引的settings配置,为索引配置对应的分词器

PUT /my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "keyword",
          "char_filter": [
            "html_strip"
          ]
        }
      }
    }
  }
}
复制代码

自定义标签

至此我们知道字节过滤器的作用和使用方式,如果你担心有些html标签你想保留,例如b标签,但是使用html_strip会把所有的html标签进行无差别去除。那么是不是有其他过滤器可以达成这种效果?或者说html_strip过滤器能不能让我们指定某些参数呢?

PUT my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "keyword",
          "char_filter": [
            "my_custom_html_strip_char_filter"
          ]
        }
      },
      "char_filter": {
        "my_custom_html_strip_char_filter": {
          "type": "html_strip",
          "escaped_tags": [
            "b"
          ]
        }
      }
    }
  }
}
复制代码

通过上述的代码,我们可以得出两个信息。

我们可以对字符过滤器进行自定义。如上,我们自定义了名称为my_custom_html_strip_char_filter的过滤器,这个过滤器在html_strip过滤器的基础上,把b标签排除在外了。并且分词器能够包含很多个字节过滤器。

字节过滤器可以接收参数。或者说过滤器指定了一些参数,例如html_strip过滤器的参数escaped_tags,用来处理特殊的需求。

那么自定义中的参数是怎么生效的?字符过滤器的原理是什么样的,让我们通过源码一点一点来揭开这个面纱。

源码

下载源码:elasticsearch github

项目结构如下,过滤器等代码在modules下,有时间我们分析一下es的项目目录结构,方便你更容易阅读源码。今天我们先把中心放在html_strip字符过滤器是怎么工作的。

分词器code目录.png

进入modules有一个通用分词器目录,如图

分词器模块code.png

在这个module下找到HtmlStripCharFilterFactory终于主角出现了:

解析HtmlStripCharFilterFactory

HTMLstripFactory.png

我们终于看到了我们熟悉的参数,escaped_tags

通过create方法,可以看到最终es的HTMLStripCharFilter使用的是LuceneHTMLStripCharFilter类,因此,我们还要下载Lucene源码。

如下是Lucene中HTMLStripCharFilter构造器

htmlstrip构造器.png

把escapedTag加入到集合java private CharArraySet escapedTags = null;

html_strip流程

在上面,我们已经把escaped_tags传入了escapedTags,并且已经生成了HTMLStripCharFilter对象,进行html标签处理。那么具体是如何使用的呢,我们继续看。

htmlread.png HTMLStripCharFilter重写了Reader的方法,用来读取索引文本的字节流,其中重点在nextChar()

htmlnextchar.png

这七百多行代码,就是读取的流程,也是处理标签的流程。

nextchar-detail.png

在这个方法里是一个while(true)循环,对读取的字节流进行处理。

nextcharescaped.png

这里通过改变字节的状态来标识是否需要排除。状态包含如下

lexicalstate.png

具体如何通过标识,来达成标签的移除和排除,我们下节再介绍,点关注,有更多精彩。

分类:
后端