tokenizer(标记生成器)接受字符流,负责将它们分割为独立的标记tokens(通常是一个个独立的单词),最后将这些tokens输出。比如空格标记生成器whitespace在分割文本时,只要遇到空格就会执行分割,比如whitespace标记生成器会将文本Quick brown fox!将分割为[Quick, brown, fox!]。标记生成器tokenizer除了分割字符流外,还负责记录每个标记(或术语,即term)的顺序和位置以及每个术语term的始、末字符在原始文本中的偏移量。ES有很多内置的标记生成器,这些标记生成器可以在自定义分析器中使用。

面向单词的标记生成器

 以下标记器通常用于将全文标记为单个单词(面向单词的标记生成器就是这个意思):

  • Standard Tokenizer:standard标记生成器将文本以单词边界分割为术语,就是Unicode文本分割算法所定义的分割方式,它可以去除大部分的标点符号,是大部分语言最好的选择;
  • Letter Tokenizer:letter标记生成器只要遇到非字母字符就会将文本分割为术语;
  • Lowercase Tokenizer:lowercase标记生成器和letter标记生成器一样,会在遇到非字母字符将文本分割为术语,但它还会将术语全部转成小写;
  • Whitespace Tokenizer:whitespace标记生成器只要遇到空格字符就会将文本分割为术语;
  • UAX URL Email Tokenizer:uax_url_email标记生成器和standard标记生成器类似,只是它将url和email地址识别为单个标记;
  • Classic Tokenizer:classic标记生成器是基于语法的英语标记器;
  • Thai Tokenizer:thai标记生成器将泰语分成单词;

部分单词标记生成器

 这些标记生成器将文本或者单词分割为更小的片段,用于部分单词匹配:

  • N-Gram Tokenizer:ngram标记生成器可以在遇到特殊字符(比如空格或标点)时将文本分割为单词,然后返回每个单词的 n-grams(其实就是一个连续字母的滑动窗口,比如 quick–>[qu, ui, ic, ck]);
  • Edge N-Gram Tokenizer:edge_ngram标记生成器在遇到特殊字符时(比如空格或标点),会将文本分割为单词,最后返回每个单词的 n-gram ,它们锚定在单词的开头(也是一个滑动窗口,只是锚头是固定在开始字符,然后逐渐往后滑,比如 quick → [q, qu, qui, quic, quick]);

结构化文本标记生成器

 以下标记生成器通常与结构化文本一起使用,如标识符、电子邮件地址、邮政编码和路径,而不是整个文本:

  • Keyword Tokenizer:keyword标记生成器是一个内部啥都没有的“空”标记生成器,无论输入什么文本,它都会输出完全一样的文本(作为一个单独的术语),它可以和标记过滤器(比如小写标记过滤器lowercase)组合以便输出标准化术语;
  • Pattern Tokenizer:pattern标记生成器使用Java正则表达式在文本与单词分隔符匹配时将文本分割为术语,或者将匹配的文本作为术语捕获;
  • Simple Pattern Tokenizer:simple_pattern标记生成器使用正则表达式捕获匹配的文本作为术语,它使用正则表达式特征的受限子集(可能是不能使用正则表达式的每个规则),并且通常比pattern标记化器更快;
  • Char Group Tokenizer:char_group标记生成器可以通过要分割的字符集进行配置,这通常比运行正则表达式消耗的资源要少得多;
  • Simple Pattern Split Tonkenizer:simple_pattern_split标记生成器和simple_pattern标记生成器同样使用正则表达式特征的受限子集,但是在匹配时会拆分输入而不是将匹配作为术语返回;
  • Path Tokenizer:path_hierarchy标记生成器采用类似文件系统路径的分层值,在路径分隔符上拆分,并为树级目录的每个组件输出一个术语(比如/foo/bar/baz[/foo, /foo/bar, /foo/bar/baz]);

1. 标准标记生成器(Standard Tokenizer)

standard标记生成器提供基于语法的标记化工作,是大部分语言的最好选择。比如:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": "standard",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog\u0027s bone."
}
'

结果为:[The, 2, QUICK, Brown, Foxes, jumped, over, the, lazy, dog's, bone]。和之前配置内置的分析时一样,有些标记生成器也是可配置的,这里standard标记生成器就是可配置的,它接受的参数如下:

  • max_token_length:表示标记的最大长度,如果某个标记超过了这个长度,则直接以max_token_length参数的数值大小分割文本,默认是255;

下面是配置standard标记生成器的小栗子:

curl -X PUT "localhost:9200/my_index" -H 'Content-Type: application/json' -d'
{
  // 如果想要配 tokenizer ,必须通过结构化数据的方式去配置
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "standard",
          "max_token_length": 5
        }
      }
    }
  }
}
'

上述自定义了一个分析器my_analyzer,引用一个自定义的标记生成器my_tokenizer,这个标记生成器是基于standard标记生成器配置得到的(指定配置类型为standard,最大标记长度为5),用这个分析器分析文本The 2 QUICK Brown-Foxes jumped over the lazy dog's bone.,最终得到的结果为:[The, 2, QUICK, Brown, Foxes, jumpe, d, over, the, lazy, dog's, bone],这里和之前的配置自定义分析器的功能有点类似。

2. 字母标记生成器(Letter Tokenizer)

letter标记生成器只要遇到非字母字符(数字是非字母)就会将文本分割成一个个的术语,这对大部分的欧洲语言是合理的,但对一些亚洲语言并不适用,因为有些语言并不是以空格作为单词间的边界。下面是一个letter标记生成器的栗子:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  // 如果不配置,直接使用 tokenizer,则不需要通过结构化数据配置
  "tokenizer": "letter",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}
'

这里直不是用分析器去搞了,直接上了一个必要的标记生成器(这也可以!),结果也很明显:[The, QUICK, Brown, Foxes, jumped, over, the, lazy, dog, s, bone]letter标记生成器是不可配置的。

3. 小写标记生成器(Lowercase Tokenizer)

lowercase标记生成器和letter标记生成器类似,只要遇到非字母字符就会将文本分割为术语,但它将所有术语转成小写。其实它的功能就是letter标记生成器和lowercase标记过滤器的组合,但是lowercase标记生成器更加高效,一步中执行了上述两步的工作。下面是一个栗子:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": "lowercase",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}
'

对文本The 2 QUICK Brown-Foxes jumped over the lazy dog's bone.进行分割结果为[the, quick, brown, foxes, jumped, over, the, lazy, dog, s, bone]lowercase标记生成器是不可配置的。

4. 空格标记生成器(Whitespace Tokenizer)

whitespace标记生成器只要遇到空格就会将文本分割成术语,和whitespace类似,下面是栗子:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": "whitespace",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}
'

结果也很明显:[The, 2, QUICK, Brown-Foxes, jumped, over, the, lazy, dog's, bone.]whitespace标记生成器是可配置的,可接受的参数有:

  • max_token_length:表示标记的最大长度,和之前一样,默认255;

5. UAX URL Email Tokenizer

uax_url_email标记生成器和standard生成器一样,提供基于语法的标记化工作,此外它还会将url和email地址识别为单个术语。下面是一个栗子:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": "uax_url_email",
  "text": "Email me at john.smith@global-international.com"
}
'

结果为:[Email, me, at, john.smith@global-international.com],这里是将john.smith@global-international.com识别为单个术语,如果使用standard标记生成器就会将john.smith@global-international.com分割为john.smith, global, international.com

uax_url_length标记生成器是可配置的,可接受的参数有:

  • max_token_length:表示标记的最大长度,和上面的定义一样,默认为255;

配置完还是和上面的一样,只不过生成标记时会有长度限制,超过限制直接分割。

6. 经典标记生成器(Classic Tokenizer)

classic标记生成器是基于语法的,对英文文档的分割很友好,具有启发式功能,可以对缩写词、公司名称、电子邮件地址和互联网主机名进行特殊处理,但这些规则并不总是有效,而且除了英语以外语言基本不太适用。主要作用有:

  • 在大多数标点字符处分割单词,删除标点符号,但没有空格的点被视为标记的一部分(如big. lang–>[big, lang],然后big.lang–>[big.lang]);
  • 在连字符处分割单词,除非在标记中有一个数字,整个token被解释为产品编号,不会被拆分;
  • 将email地址和网路主机名识别为一个术语;

classic标记生成器是可配置的,可接受的参数也是max_token_length,下面是一个栗子:

curl -X PUT "localhost:9200/my_index" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "classic",
          "max_token_length": 5
        }
      }
    }
  }
}
'

用来分析文本The 2 QUICK Brown-Foxes jumped over the lazy dog's bone.,结果为[The, 2, QUICK, Brown, Foxes, jumped, over, the, lazy, dog's, bone]

7. NGram Tokenizer(网上翻译为连词分词器)

ngram标记生成器遇到一系列特殊字符时会将文本分割为一个个的单词,然后输出一些固定长度的 N-Grams 单词,有点类似于滑动窗口,移动得到指定长度的连续字符串序列,对于查询不使用空格或具有长复合词的语言非常有用(例如德语),ngram标记生成器默认将初始文本视为单个标记,并生成最小长度为1且最大长度为2的N-gram。下面是一个栗子:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": "ngram",
  "text": "Quick Fox"
}
'

得到的结果为:[Q, Qu, u, ui, i, ic, c, ck, k, "k ", " ", " F", F, Fo, o, ox, x](用窗体宽度为1和2的窗口去滑动,注意是包含空格的)。

ngram标记生成器是可配置的,可接受的参数包含:

  • min_gram:在 gram 中的最小长度,其实就是滑动窗口的最小宽度,默认为1;
  • max_gram:在 gram 中的最大长度,其实就是滑动窗口的最大宽度,默认为2;
  • token_chars:表示应包含在token中的字符类,ES将分割不属于指定类的字符,默认是[](保留所有字符),指定的类可以是:
  • letter:字母,比如a, b, ï or 京
  • digit:数字,比如3或7;
  • whitespace:空格符,比如空格或者换行符\n
  • punctuation:标点,比如!"
  • symbol:符号,比如$

通常会将min_grammax_gram设置为同一个值(长度)会更有用一些,长度越小,文档匹配的越多,但匹配的质量越低,长度越长,匹配越具体。tri-gram(长度为3)是一个合适的初始长度;创建索引时,可以通过配置index.max_ngram_diff控制max_grammin_gram的差值(如index.max_ngram_diff配置为5,那么max_gram-min_gram必须小于等于5)。下面是一个小栗子:

curl -X PUT "localhost:9200/my_index" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "analysis": {
	  // 自定义分析器
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
		// 配置ngram标记生成器
        "my_tokenizer": {
		  // 指定类型
          "type": "ngram",
		  // 滑动窗口的最小宽度
          "min_gram": 3,
		  // 滑动窗口的最大宽度
          "max_gram": 3,
		  // 指定应包含在 token 中的字符,就是去除
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  }
}
'

使用my_analyzer分析文本2 Quick Foxes.得到的文本为:[Qui, uic, ick, Fox, oxe, xes](这里开始有点懵,不是说token中可包含字母和数字吗,然后反应过来token_chars含义是指定了token中可包含的内容,其他的都不可以,因为指定了窗口长度为3,如果想包含2这个数字,因为后面跟了一个空格,所以2是肯定无法包含在哪个token中的)。

8. Edge NGram Tokenizer(网上翻译为边界连词分词器)

edge_ngram标记生成器在遇到一系列特殊字符时会将文本分割为单词,然后锚定单词的首字符输出连词,Edge N-Grams对于搜索类型的查询非常有用。当需要按类型搜索具有广为人知的顺序的文本时,例如电影或歌曲标题,completion suggester 是比Edge N-gram 更有效的选择,Edge N-gram在尝试自动填充可以按任何顺序出现的单词时具有优势。edge_ngram标记生成器将初始文本视为单个标记,并生成最小长度为1且最大长度为2的 N-gram,如:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": "edge_ngram",
  "text": "Quick Fox"
}
'

输出为:[Q, Qu],因为锚定了首字母Q,最大长度为2,所以只有2个(通常默认的长度是无用的,需要使用edge_ngram配置这个数值)。

edge_ngram标记生成器是可配置的,可接受的参数有:

  • min_gram:在 gram 中的最小长度,其实就是滑动窗口的最小宽度,默认为1;
  • max_gram:在 gram 中的最大长度,其实就是滑动窗口的最大宽度,默认为2;
  • token_chars:表示应包含在token中的字符类,ES将分割不属于指定类的字符,默认是[](保留所有字符),指定的类可以是:
  • letter:字母,比如a, b, ï or 京
  • digit:数字,比如3或7;
  • whitespace:空格符,比如空格或者换行符\n
  • punctuation:标点,比如!"
  • symbol:符号,比如$

下面是一个栗子:

curl -X PUT "localhost:9200/my_index" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "edge_ngram",
          "min_gram": 2,
          "max_gram": 10,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  }
}
'

配置了edge_ngram标记生成器的最小连词长度为2、最大连词长度为10,token中可包含的字母为letterdigit(即字母和数字),然后使用这个自定义配置的edge_ngram标记生成器分析文本2 Quick Foxes.,得到的结果为:[Qu, Qui, Quic, Quick, Fo, Fox, Foxe, Foxes]。通常推荐在索引时使用和搜索时使用的analyzer一样,但是edge_ngram标记生成器不一样,处理也不一样,只有在索引时使用edge_ngram标记生成器才有意义,以确保部分单词可用于索引中的匹配。在搜索时,只需搜索用户输入的字词,比如:Quick Fo。下面是一个如何设置search-as-you-type字段的栗子(这个栗子有点懵):

curl -X PUT "localhost:9200/my_index" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "analysis": {
      "analyzer": {
        "autocomplete": {
          "tokenizer": "autocomplete",
          "filter": [
            "lowercase"
          ]
        },
        "autocomplete_search": {
          "tokenizer": "lowercase"
        }
      },
      "tokenizer": {
        "autocomplete": {
          "type": "edge_ngram",
          "min_gram": 2,
          "max_gram": 10,
          "token_chars": [
            "letter"
          ]
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "title": {
          "type": "text",
          "analyzer": "autocomplete",
          "search_analyzer": "autocomplete_search"
        }
      }
    }
  }
}
'

然后用下面的请求进行验证:

// 放入数据
curl -X PUT "localhost:9200/my_index/_doc/1" -H 'Content-Type: application/json' -d'
{
  // 自动完成分析器对[qu,qui,quic,quick,fo,fox,foxe,foxes]这些术语进行索引
  "title": "Quick Foxes"
}
'
curl -X POST "localhost:9200/my_index/_refresh"
curl -X GET "localhost:9200/my_index/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "title": {
		// autocomplete_search分析器搜索术语[quick,fo],两者都出现在索引中。
        "query": "Quick Fo",
        "operator": "and"
      }
    }
  }
}
'

9. 关键词标记生成器(Keyword Tokenizer)

keyword标记生成器是一个“空”标记生成器,无论给出什么文本,都输出与单个术语完全相同的文本。keyword标记生成器可以和标记过滤器组合使用标准化输出。下面是一个栗子:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": "keyword",
  "text": "New York"
}
'

结果很明显[New York]keyword标记生成器也是可配置的,接受的参数有:

  • buffer_size:表示单次传递中读入术语缓冲区的字符数,默认为256,术语缓冲区将按这个参数设置的值大小增长,直到消耗完所有文本,建议不要更改此设置;

10. 模式标记生成器(Pattern Tokenizer)

pattern标记生成器使用正则表达式在文本与单词分隔符匹配时将文本拆分为术语,或者将匹配的文本作为术语捕获,默认模式为\W+,只要遇到非单词字符(非单词就是除了字母和数字以及下划线外的所有字符,等价于[^A-Za-z0-9_])就会分割文本。比如:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": "pattern",
  "text": "The foo_bar_size's default is 5."
}
'

最后的分析结果也很明显[The, foo_bar_size, s, default, is, 5]

pattern标记生成器是可配置的,可接受的参数有:

  • pattern:Java正则表达式,默认为\W+
  • flags:Java表达式的标识符,标志应该是管道分开的,比如CASE_INSENSITIVE|COMMENTS
  • group:哪个捕获组提取为标记,默认为-1;

下面是一个小栗子:

curl -X PUT "localhost:9200/my_index" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "pattern",
          "pattern": ","
        }
      }
    }
  }
}
'

上述基于pattern标记生成器将正则表达式改为,,此时用my_analyzer分割文本comma,separated,values时,得到的结果就为[comma, separated, values]

11. 字符群标记生成器(Char Group Tokenizer)

char_group标记生成器会在遇到某一特定字符时(一个已定义好的集合中的任一字符),就会将文本分割为术语,它主要用于需要简单自定义标记的情况,并且不能接受使用pattern标记器的开销。char_group标记生成器是可配置的,可接受的参数有:

  • tokenize_on_chars:表示需要进行标记化的字符列表(即上述提到的已定义好的字符集合),只要遇到此列表中的字符,就会开始切割新标记,这个参数接受单个字符(如连字符-)或字符组(如whitespace, letter, digit, punctuation, symbol);

下面是一个栗子:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": {
    "type": "char_group",
    "tokenize_on_chars": [
      "whitespace",
      "-",
      "\n"
    ]
  },
  "text": "The QUICK brown-fox"
}
'

最后的结果为[The, QUICK, brown, fox]

12. 简单模式标记生成器(Simple Pattern Tokenizer)

注:Lucene中将Simple Pattern Tokenizer和Simple Pattern Split Tokenizer标记为实验性功能。

13. 路径层次的标记生成器(Path Hierarchy Tokenizer)

path_hierarchy采用类似文件系统路径的分层值,在路径分隔符上拆分,并为树中的每个组件输出一个术语。比如:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": "path_hierarchy",
  "text": "/one/two/three"
}
'

分析的结果为[/one, /one/two, /one/two/three],其实有点类似于N-gram,锚定首字符,只不过窗口的长度是动态变化的,永远是以下一个/分隔符为界定的。

path_hierarchy标记生成器是可配置的,可接受的参数有(以分析/one/two/three文本为例):

  • delimiter:表示路径分隔符,默认是\
  • replacement:表示用于分隔符的可选替换字符,默认是\(即输出标记时将原来的路径分隔符替换为这里的值,比如指定为|,结果就为[|one, |one|two, |one|two|three ]);
  • buffer_size:表示单次传递中读入术语缓冲区的字符数,默认为1024.术语缓冲区将这个参数的值增长,直到消耗完所有文本,建议不要更改此设置;
  • reverse:表示是否以相反的顺序输出标记,默认为false(正常输出顺序为[/one, /one/two, /one/two/three],当逆序就是[/One/two/Three, One/two/Three, two/Three, Three]);
  • skip:表示要跳过的初始标记数量,默认为0(比如1,那输出的标记就为[/one/two, /one/two/three]);

下面是栗子:

curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
  "tokenizer": {
    "type": "path_hierarchy",
    "delimiter": "/",
    "replacement": "|",
    "reverse": true,
    "skip": 1
  },
  "text": "/One/two/Three"
}
'

最后的结果为[|One|two|, One|two|, two|]

【注】

这里对reverse参数作一个说明(输入的文本为/One/two/Three):

  • false:即正序时,输出为[/One, /One/two, /One/two/Three]
  • true:即逆序时,输出为[/One/two/Three, One/two/Three, two/Three, Three]

总结一下,正序时就是从前往后依次添加/xx字符串(xx可以为空);逆序时,是从前向后依次删除xx/的字符串(xx可以为空)。