本文来看一下ES的多字段特性,以及如何配置一个自定义的分词器。

一、多字段类型

多字段特性:
	可以实现精确匹配。
	可以使用不同的analyzer,就是搜索的时候是一个分词器,插入的时候是另一个分词器。

1、Exact Values && Full Text

精确值和全文检索值。精确值的意思就是不分词,不全文检索。当成mysql中的那种等值查询。全文文本值意思就是查询的时候走的是分词的路子,全文文本的匹配。

1.1、Exact Values

包括数字类型,日期类型,具体字符串类型(keyword类型),这几个类型在ES中是不分词的。

因为精确值不需要做分词的处理,所以ES为每一个精确值类型做索引的时候,不分词处理,就是简单的存进去一个倒排索引。不分词,存的是完整的词。

es 自定义分词词库 es有哪些分词器_分词器

1.2、Full Text

ES中的text类型走的就是Full Text的路子,是分词检索和索引的。

二、自定义分词器

当ES自带的分词器无法满足的时候,可以自定义分词器,通过自己组合不同的组件来实现不同的分词效果。
我们前面讲分词器的时候说过,ES中分词效果的实现是由三个组件实现的。分别是:

Character Filters
它是在Tokenizer之前对文本进行处理,例如增加删除以及替换文本字符,可以配置多个Character Filters 串行做处理,因为他处理了文本的内容,所以会影响Tokenizer的position和offset信息。
ES中内置了一些Character Filters分别是:
HTML strip:去除html标签
Mapping:字符串替换
Pattern replace:正则匹配替换
Tokenizer:
他的作用是讲原始的文本按照一定的规则,切分为分词,
ES内置的Tokenizer有:whitespace/standard/uax_url_email/pattern/keyword/path hierarchy 其中keyword不做任何处理,就是一个原样的。
也可以自己使用java开发插件,实现自己的Tokenizer。
Token Filter:
是将Tokenizer输出的单词(term)进行增加,修改,删除。
做一些后续处理,ES内置的Token Filter有
Lowercase /stop/synonym 分别是把大写字母转小写,增加停用词,增加近义词。
基于ES提供的这些内置组件,我们就可以在开发的时候自己定义自己的分析器实现对应你想要的效果。

下面我们就针对这些概念,自己实现一个自己想要的分词器。

es 自定义分词词库 es有哪些分词器_es 自定义分词词库_02

1、去除html标签的分词组合

# 自己组装一个去除html标签的分词器效果
POST _analyze
{
  "tokenizer": "keyword",
  "char_filter": ["html_strip"],
  "text": "<b>hello es</b>"
}
注意我们这里就是看看测试效果,具体真的定义分词器不是在这里。类似做个单元测试。
keyword表示我们这个词hello es在索引的时候不做分词,插进去原样,取出来原样。
结果就是去掉了html标签。
{
  "tokens" : [
    {
      "token" : "hello es",  去掉了html标签
      "start_offset" : 3,
      "end_offset" : 15,
      "type" : "word",
      "position" : 0
    }
  ]
}

当我们做一些爬虫数据的时候,爬下来的数据可以做一个html标签的剥离。

2、使用char filter进行替换

#使用char filter进行替换
POST _analyze
{
  "tokenizer": "standard",
  "char_filter": [
      {
        "type" : "mapping",
        "mappings" : [ "- => _"]
      }
    ],
  "text": "123-456, I-test! test-990 650-555-1234"
}
这个分词我们使用的是标准分词,char_filter我们用的是mapping类型,其中的映射关系指定为把中划线转为下划线。注意对文本还是会分词的,标准的就是按照字母一个一个词分出来的。
{
  "tokens" : [
    {
      "token" : "123_456",
      "start_offset" : 0,
      "end_offset" : 7,
      "type" : "<NUM>",
      "position" : 0
    },
    {
      "token" : "I_test",
      "start_offset" : 9,
      "end_offset" : 15,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "test_990",
      "start_offset" : 17,
      "end_offset" : 25,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "650_555_1234",
      "start_offset" : 26,
      "end_offset" : 38,
      "type" : "<NUM>",
      "position" : 3
    }
  ]
}
我们看到他把所有的中划线,转为了下划线,并且进行了分词。而且感叹号也被去掉了。

还可以把一些表情转为字符,我们把笑脸转为happy 把哭脸转为sad

//char filter 替换表情符号
POST _analyze
{
  "tokenizer": "standard",
  "char_filter": [
      {
        "type" : "mapping",
        "mappings" : [ ":) => happy", ":( => sad"]
      }
    ],
    "text": ["I am felling :)", "Feeling :( today"]
}

3、正则表达式

#正则表达式
GET _analyze
{
  "tokenizer": "standard",
  "char_filter": [
      {
        "type" : "pattern_replace",   
        "pattern" : "http://(.*)",
        "replacement" : "$1"
      }
    ],
    "text" : "http://www.elastic.co"
}
效果就是replace掉了http那块的正则表达式。
{
  "tokens" : [
    {
      "token" : "www.elastic.co",
      "start_offset" : 0,
      "end_offset" : 21,
      "type" : "<ALPHANUM>",
      "position" : 0
    }
  ]
}

4、指定分词组件tokenizer

4.1、按文件路径切分词汇

POST _analyze
{
  "tokenizer":"path_hierarchy",
  "text":"/user/ymruan/a/b/c/d/e"
}
这个分词组件会把你的路径按照级别全部拆分出来,你能查到所有的路径和子路径

  "tokens" : [
    {
      "token" : "/user",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "/user/ymruan",
      "start_offset" : 0,
      "end_offset" : 12,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "/user/ymruan/a",
      "start_offset" : 0,
      "end_offset" : 14,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "/user/ymruan/a/b",
      "start_offset" : 0,
      "end_offset" : 16,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "/user/ymruan/a/b/c",
      "start_offset" : 0,
      "end_offset" : 18,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "/user/ymruan/a/b/c/d",
      "start_offset" : 0,
      "end_offset" : 20,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "/user/ymruan/a/b/c/d/e",
      "start_offset" : 0,
      "end_offset" : 22,
      "type" : "word",
      "position" : 0
    }
  ]
}

5、定义filter

1、停用词的操作

# whitespace与stop
POST _analyze
{
  "tokenizer": "whitespace",
  "filter": ["stop"],
  "text": ["The rain in Spain falls mainly on the plain."]
}
我们的分词是按空格分的,同时指定了过滤掉一些停用词,前面说过诸如像介词之类的,a an the什么的。但是注意,the会被过滤,但是一旦有了大写字符就不会被过率了,所以第一个The是不会被过滤掉的。
{
  "tokens" : [
    {
      "token" : "The",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "rain",
      "start_offset" : 4,
      "end_offset" : 8,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "Spain",
      "start_offset" : 12,
      "end_offset" : 17,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "falls",
      "start_offset" : 18,
      "end_offset" : 23,
      "type" : "word",
      "position" : 4
    },
    {
      "token" : "mainly",
      "start_offset" : 24,
      "end_offset" : 30,
      "type" : "word",
      "position" : 5
    },
    {
      "token" : "plain.",
      "start_offset" : 38,
      "end_offset" : 44,
      "type" : "word",
      "position" : 8
    }
  ]
}
我们看到那些介词都没了,但是大写的Theh还在。其余的也是同理。

但是我们可以这样去掉The

#/remove 加入lowercase后,The被当成 stopword删除
POST _analyze
{
  "tokenizer": "whitespace",
  "filter": ["lowercase","stop"],
  "text": ["The gilrs in China are playing this game!"]
}
因为我们说filter是可以多个的,所以你在stop之前加一个转小写,The被转为the就能去掉了

  "tokens" : [
    {
      "token" : "gilrs",
      "start_offset" : 4,
      "end_offset" : 9,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "china",
      "start_offset" : 13,
      "end_offset" : 18,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "playing",
      "start_offset" : 23,
      "end_offset" : 30,
      "type" : "word",
      "position" : 5
    },
    {
      "token" : "game!",
      "start_offset" : 36,
      "end_offset" : 41,
      "type" : "word",
      "position" : 7
    }
  ]
}
注意一定要在前面转小写,你放到stop后面他先看停用发现你是大写The就被保留了,然后走转小写的过滤,最后就是the被保留了。

6、自己实现真的分词器

前面都是测试效果用的,现在就真的开始搞一个分词器给索引用。

#自定义分词器
PUT myindex    自己定义一个索引
{
  "settings": {      # 在setting里面配置分词配置
    "analysis": {
      "analyzer": {
        "my_div_analyzer":{   # 分词器的名字叫my_div_analyzer
          "type":"custom",  # 类型是自定义的
          "char_filter":["emoticons"],  # 过滤器是emoticons,下面自定义的
          "tokenizer": "punctuation",   # 分词组件是punctuation,下面自定义的
          "filter":[                    # 过滤器是大写转小写的,还有english_stop,这个english_stop是自己下面定义的
            "lowercase",
            "english_stop"
          ]
        }
      },
      "tokenizer": {
        "punctuation":{  # 自己定义的,名字自取。类型就是正则匹配,正则表达式自己写就行,按照逗号分词
          "type":"pattern",
          "pattern":"[.,!?]"
        }
      },
      "char_filter": {
        "emoticons":{ # 自己定义的,名字自取,类型是mapping的,笑脸转为happy,哭脸是sad
          "type" : "mapping",
          "mappings" : [
            ":) => _happy_",
            ":( => _sad_"
          ]
        }
      },
      "filter": {
        "english_stop":{ # 自己定义的,名字自取,类型就是stop,禁用词类型是_english_,前面有说是默认的
          "type":"stop",
          "stopwords":"_english_"
        }
      }
    }
  }
}

测试一下:

POST myindex/_analyze
{
  "analyzer": "my_div_analyzer",
  "text": "I  am a :) person,and you?"
}
分词结果是:
{
  "tokens" : [
    {
      "token" : "i  am a _happy_ person",
      "start_offset" : 0,
      "end_offset" : 17,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "and you",
      "start_offset" : 18,
      "end_offset" : 25,
      "type" : "word",
      "position" : 1
    }
  ]
}
我们看到大写被转了小写,笑脸被转了happy,而且分词分开的也是按逗号分开的,这就是我们定义分词器的效果。

三、总结

我们既然知道了分词器的三个组成部件,也知道了它能随意组合,是一个类似插拔式的操作。所以我们就能根据ES内置的一些基础的组件随意组合。当然了进阶的你也可以自己以JAVA语言定义组件然后拿来组合。