1# @Time    : 2020-05-27
2# @Language: Markdown
3# @Software: VS Code
4# @Author  : Di Wang
5# @Email   : [email protected]

把EPICS PV存入ES后,为了方便搜索,需要自定义analyzer。

ES 文本分析

基于https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis.html完成本文。

ES默认使用standard analyzer对一段文本进行操作,如分词(根据空格拆分),单词还原(foxes还原为fox),去除停用词(移除the,a等冠词)等。除默认分析器还有一系列内置分析器(built-in analyzer),用户也可以自定义分析器。

Analyzer概述

文本分析器结构

character filters, tokenizers, and token filters,顾名思义,第一个是去除那些不想要的character,第二个是根据分隔符得到一组token,第三个是设置token的过滤和处理,下面详细讲。

使用时机

  1. 索引一个text的field时进行分析,称为 index analyzer
  2. 调用search API进行检索时对要搜索的文本进行分析search analyzer

大多数情况,使用同一个分析器。

如何设置分析器

对于index analyzer,可以直接设置index的默认分词器,也可以在mapping里设置field的分词器。

对于search analyzer,可以在查询语句里指定分析器,也可以设置indexfield的搜索分析器,这其中有优先级,按顺序:

  1. 查询语句中指定的分析器
  2. field中指定的搜索分析器
  3. index中指定的搜索分析器
  4. field中指定的索引分析器
  5. 默认的standard analyzer
 1PUT my_index
 2{
 3  "mappings": {
 4    "properties": {
 5      "title": {
 6        "type": "text",
 7        "analyzer": "whitespace",
 8        "search_analyzer": "simple"
 9      }
10    }
11  }
12}
13
14PUT my_index
15{
16  "settings": {
17    "analysis": {
18      "analyzer": {
19        "default": {
20          "type": "simple"
21        },
22        "default_search": {
23          "type": "whitespace"
24        }
25      }
26    }
27  }
28}
29GET my_index/_search
30{
31  "query": {
32    "match": {
33      "message": {
34        "query": "Quick foxes",
35        "analyzer": "stop"
36      }
37    }
38  }
39}

Stemming

把单词还原到其词根,be, are, and ammouse and micefoot and feet有很多细节,不展开了。

如何测试分析器

1POST _analyze
2{
3  "analyzer": "whitespace",
4  "text":     "The quick brown fox."
5}

如何配置ES自带的分析器

 1PUT my_index
 2{
 3  "settings": {
 4    "analysis": {
 5      "analyzer": {
 6        "std_english": { 
 7          "type":      "standard",
 8          "stopwords": "_english_"
 9        }
10      }
11    }
12  },
13  "mappings": {
14    "properties": {
15      "my_text": {
16        "type":     "text",
17        "analyzer": "standard", 
18        "fields": {
19          "english": {
20            "type":     "text",
21            "analyzer": "std_english" 
22          }
23        }
24      }
25    }
26  }
27}
28
29POST my_index/_analyze
30{
31  "field": "my_text", 
32  "text": "The old brown cow"
33}
34
35POST my_index/_analyze
36{
37  "field": "my_text.english", 
38  "text": "The old brown cow"
39}

输出结果一个去除了the,一个保留。

如何自定义分析器

 1PUT my_index
 2{
 3  "settings": {
 4    "analysis": {
 5      "analyzer": {
 6        "my_custom_analyzer": { 
 7          "type": "custom",
 8          "char_filter": [
 9            "emoticons"
10          ],
11          "tokenizer": "punctuation",
12          "filter": [
13            "lowercase",
14            "english_stop"
15          ]
16        }
17      },
18      "tokenizer": {
19        "punctuation": { 
20          "type": "pattern",
21          "pattern": "[ .,!?]"
22        }
23      },
24      "char_filter": {
25        "emoticons": { 
26          "type": "mapping",
27          "mappings": [
28            ":) => _happy_",
29            ":( => _sad_"
30          ]
31        }
32      },
33      "filter": {
34        "english_stop": { 
35          "type": "stop",
36          "stopwords": "_english_"
37        }
38      }
39    }
40  }
41}
42
43POST my_index/_analyze
44{
45  "analyzer": "my_custom_analyzer",
46  "text": "I'm a :) person, and you?"
47}

自定义内容:

  • 字符过滤器,把颜文字替换掉
  • 分词器:根据标点符号分词
  • 过滤器:大写字母变小写,然后去除英语停用词

输出结果: [ i'm, _happy_, person, you ]

看完这个其实就可以自定义了,毕竟EPICS PV一般都长这样:

LIiEV:evg0-EvtClk:FracSynFreq-SP

似乎设置:作为分词器,然后大写转小写就搞定了。

内置分析器

  • Standard Analyzer

    • 根据一个叫Unicode Text Segmentation algorithm的算法分隔单词,转小写,支持移除停用词。
  • Simple Analyzer

    • 不可配置,遇到不是字母的字符就分隔,转小写
  • Whitespace Analyzer

    • 不可配置,遇到空格就分隔。
  • Stop Analyzer

    • 比Simple Analyzer 多了移除停用词
  • Keyword Analyzer

    • 什么也不干,把整段文字当作一个keyword,不可配置。就和keyword field一样。
  • Pattern Analyzer

    • 基于正则表达式,默认用的Java \W+,匹配任何non-word的字符,也就是[^a-zA-Z0-9_],用别的正则官方说可能导致速度很慢。支持转小写,设置停用词,设置停用词文件路径。
  • Language Analyzer

    • 支持不同的语言,懒得看细节了。
  • Fingerprint Analyzer

    • 支持小写,去重,排序,移除停用词,以及concatenated into a single token(没懂)。

Character filter

  • HTML Strip Char Filter
    • 处理HTML的,把<p>I&apos;m so <b>happy</b>!</p> —> I'm, so, happy
  • Mapping Character Filter
    • 看上面自定义分析器的例子
  • Pattern Replace Character Filter
    • 用正则表达式进行替换,可能很慢

Tokenizer

分词器分为三大类:面向单词的,面向单词内的,面向结构型文本的。

Word Oriented Tokenizers

  • Standard Tokenizer
    • Unicode Text Segmentation algorithm去分隔字符,官方说它是最佳之选。
  • Letter Tokenizer
    • 遇到不是字母的就分隔
  • Lowercase Tokenizer
    • 比letter tokenizer 多了转小写
  • Whtespace Tokenizer
    • 遇到空格就分隔
  • UAX URL Email Tokenizer
    • 把URL和Email地址当成一个token
  • Classic Tokenizer
    • 针对英语语法的分词器
  • Thai Tokenizer
    • 泰语分词(那么多语言,ES居然专门把泰语分词器列出来,why)

Partial Word Tokenizers

  • N-Gram Tokenizer
    • 默认使用2-gram,quick —> [qu, ui, ic, ck]
  • Edge N-gram Tokenizer
    • 在单词头设置anchor,如果5-gram的话,quick —> [q, qu, qui, quic, quick]

Structured Text Tokenizers

  • Keyword Tokenizer
    • 啥也不干
  • Pattern Tokenizer
    • 正则
  • Simple Pattern Tokenizer
    • 支持简单的正则,更快,返回匹配的项
  • Char Group Tokenizer
    • 设置一组字符进行分隔,比正则快
  • Simple Pattern Split Tokenizer
    • 遇到正则匹配的项就分隔,和上面那个的区别跑一下下面的例子就明白了
  • Path_hierarchy Tokenizer
    • 根据路径分隔,/one/two/three —> [ /one, /one/two, /one/two/three ]
 1POST _analyze
 2{
 3  "tokenizer": {
 4    "type": "simple_pattern_split",
 5    "pattern": "[0123456789]{3}"
 6  },
 7  "text": "fd-786-335-514-x"
 8}
 9
10POST _analyze
11{
12  "tokenizer": {
13    "type": "simple_pattern",
14    "pattern": "[0123456789]{3}"
15  },
16  "text": "fd-786-335-514-x"
17}

Token filter

太多了,常用的有:

  • ASCII folding token filter
    • 把一些拉丁字母转成ASCII字符
  • Lowercase token filter
    • 转小写
  • Length token filter
    • 移除长度超过设定值的,可以设置最小或者最大。
  • Truncate token filter
    • 超过设定长度的就截短
  • Stop token filter
    • 移除停用词

EPICS PV

直接上代码,添加了epics_pv_analyzer,并在PVNAME这个field上使用,在mapping里设置了几个常见的EPICS PV field的映射。把PVTYPE设置为keyword就可以做聚合了,看看哪种类型的PV最多(好像没什么用)。

  1PUT linac-epics-pv
  2{
  3  "settings": {
  4    "number_of_shards": 1,
  5    "number_of_replicas": 0,
  6    "analysis": {
  7      "analyzer": {
  8        "epics_pv_analyzer": {
  9          "type": "custom",
 10          "tokenizer": "pv_tokenizer",
 11          "filter": [
 12            "lowercase"
 13          ]
 14        }
 15      },
 16      "tokenizer": {
 17        "pv_tokenizer": {
 18          "type": "char_group",
 19          "tokenize_on_chars": [
 20            ":",
 21            "-",
 22            "_",
 23            " "
 24          ]
 25        }
 26      }
 27    }
 28  },
 29  "mappings": {
 30    "properties": {
 31      "PVNAME": {
 32        "type": "text",
 33        "analyzer": "epics_pv_analyzer",
 34        "search_analyzer": "epics_pv_analyzer"
 35      },
 36      "PVTYPE": {
 37        "type": "keyword",
 38        "ignore_above": 256
 39      },
 40      "DTYP": {
 41        "type": "text"
 42      },
 43      "NELM": {
 44        "type": "integer"
 45      },
 46      "PINI": {
 47        "type": "keyword"
 48      },
 49      "FLNK": {
 50        "type": "text",
 51        "analyzer": "epics_pv_analyzer",
 52        "search_analyzer": "epics_pv_analyzer"
 53      },
 54      "FTVL": {
 55        "type": "keyword"
 56      }
 57    }
 58  }
 59}
 60# 测试分析器
 61POST linac-epics-pv/_analyze
 62{
 63  "analyzer": "epics_pv_analyzer",
 64  "text": "LIiBM:SP_61_F5_1:Y1PK:ZRE:1S:INP"
 65}
 66# 修改mapping
 67PUT linac-epics-pv/_mapping
 68{
 69  "properties": {
 70    "PVNAME": {
 71      "type": "text",
 72      "analyzer": "epics_pv_analyzer",
 73      "search_analyzer": "epics_pv_analyzer"
 74    },
 75    "PVTYPE": {
 76      "type": "keyword",
 77      "ignore_above": 256
 78    },
 79    "DTYP": {
 80      "type": "text",
 81    },
 82    "NELM": {
 83      "type": "integer",
 84    },
 85    "PINI": {
 86      "type": "keyword",
 87    },
 88    "FLNK": {
 89      "type": "text",
 90      "analyzer": "epics_pv_analyzer",
 91      "search_analyzer": "epics_pv_analyzer"
 92    },
 93    "FTVL": {
 94      "type": "keyword",
 95    },
 96  }
 97}
 98# 搜索
 99GET linac-epics-pv/_search
100{
101  "size": 20, 
102  "query": {
103    "match": {
104      "PVNAME": {
105        "query": "liibm zre inp",
106        "operator": "and"
107      } 
108    }
109  }
110}