一、数据类型
1.1 核心数据类型
字符串型:text(分词)、keyword(不分词)
数值型:long、integer、short、byte、double、float、half_float、scaled_float
日期类型:date
布尔类型:boolean
二进制类型:binary
范围类型:integer_range、float_range、long_range、double_range、date_range
1.2 复杂数据类型
数组类型:array
对象类型:object
嵌套类型:nested object
1.3 地理位置数据类型
geo_point(点)、geo_shape(形状)
1.4 专用类型
记录IP地址ip
实现自动补全completion
记录分词数:token_count
记录字符串hash值murmur3
多字段特性multi-fields
ES常用的数据类型可分为3大类
- 核⼼数据类型
- 复杂数据类型
- 专⽤数据类型
核心数据类型
(1)字符串
- text ⽤于全⽂索引,搜索时会自动使用分词器进⾏分词再匹配
- keyword 不分词,搜索时需要匹配完整的值
(2)数值型
- 整型: byte,short,integer,long
- 浮点型: float, half_float, scaled_float,double
(3)日期类型
- date
json没有date类型,插入|更新文档|字段时怎么表示date类型?
#mapping,将字段类型设置为date
"type" : "date"#插入|更新此字段的值时,有3种表示方式
#使用固定格式的字符串
"2020-04-18"、"2020/04/18 09:00:00"#值使用长整型的时间戳,1970-01-01 00:00:00,s
1610350870#值使用长整型的时间戳,ms
1641886870000
(4)范围型
integer_range, long_range, float_range,double_range,date_range
比如招聘要求年龄在[20, 40]上,mapping:
age_limit :{
"type" : "integer_range"
}
插入|更新文档|字段时,值写成json对象的形式:
"age_limit" : {
"gte" : 20,
"lte" : 40
}
gt是大于,lt是小于,e是equals等于。
按此字段搜索时,值写常量:
"term" : {
"age_limit" : 30
}
age_limit的区间包含了此值的文档都算是匹配。
(5)布尔
- boolean #true、false
(6)⼆进制
- binary 会把值当做经过 base64 编码的字符串,默认不存储,且不可搜索
复杂数据类型
(1)对象
- object
#定义mapping
"user" : {
"type":"object"
}#插入|更新字段的值,值写成json对象的形式
"user" : {
"name":"chy",
"age":12
}#搜索时,字段名使用点号连接
"match":{
"user.name":"chy"
}
一个对象中可以嵌套对象。
(2)数组
#ES没有专门的数组类型,定义mapping,写成元素的类型
"arr" : {
"type":"integer"
}#插入|更新字段的值。元素可以是各种类型,但元素的类型要相同
"arr" : [1,3,4]
专用数据类型
- ip
#定义mapping
"ip_address" : {
"type":"ip"
}#插入|更新字段的值,值写成字符串形式
"ip" : "192.168.1.1"#搜索
"match":{
"ip_address":"192.168.1.1"
}#ip在192.168.0.0 ~ 192.168.255.255上的文档都匹配
"match":{
"ip_address":"192.168.0.0/16"
}
二、 Mapping
2.1 手动创建
1)创建操作
PUT stu
{
"mappings": {
"_doc":{
"properties":{
"id":{
"type":"keyword"
},
"name":{
"type":"text"
},
"sex":{
"type":"integer"
},
"birth":{
"type":"date"
}
}
}
}
}
—————————————华丽分割符————————————————
###############对上面命令做个简单解释
PUT stu (stu类似于MySQL中的数据库名)
{
"mappings": { ("mappings"可以理解为建表语句 create table关键字)
"_doc":{ ("_doc"相当于MySQL中表名)
"properties":{ ("properties"相当于建表语句中要指定字段及字段类型的关键字)"id":{ (" id "相当于具体某个字段)
"type":"keyword"("type"相当于要指定字段类型的关键字,"keyword "相当于字段的具体类型)
},
"name":{
"type":"text"
},
"sex":{
"type":"integer"
},
"birth":{
"type":"date"
}
}
}
}
}
2)创建文档
PUT stu/_doc/1001
{
"id":"001",
"name":"红红",
"sex":0,
"birth":"1999-01-01"
}
—————————————华丽分割符————————————————
###############对上面命令做个简单解释
PUT stu/_doc/1001 (相当于指定要插入到哪个库的哪个表的哪个id中,类似于hbase的rowkey)
{
"id":"001", (字段具体的值)
"name":"红红",
"sex":0,
"birth":"1999-01-01"
}
3)查询
GET stu/_search
输出结果如下:
{
"took" : 9,
"timed_out" : false,
"_shards" : {
"total" : 5, (切片总数)
"successful" :
"skipped" : 0,
"failed" : 0
},
"hits" : { (命中)
"total" : 1, (命中总数一条)
"max_score" : 1.0, (数据在里面存储的时候是有一个评分,这个评分是按照搜索的关键字跟文章的匹配度得来的,如果这个关键字在文章中出现频率较高,这个分数也相应的较大)
"hits" : [
{
"_index" : "stu",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.0,
"_source" : { (具体的数据)
"id" : "001",
"name" : "红红",
"sex" : 0,
"birth" : "1999-01-01"
}
}
]
}
}
2.2 自动创建
ES可以自动识别文档字段类型,从而降低用户使用成本
1)直接插入文档
PUT stu1/_doc/1001
{
"id":"002",
"name":"绿绿",
"sex":"male",
"birth":"1999-01-02"
}
2)查看mapping
GET stu1/_mapping
输出结果如下:
{
"stu1" : {
"mappings" : {
"_doc" : {
"properties" : {
"birth" : {
"type" : "date"
},
"id" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"sex" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
}
birth自动识别为long类型,name识别为text类型
3)日期类型的自动识别
日期的自动识别可以自行配置日期格式,以满足各种需求。
(1)自定义日期识别格式
PUT my_index
{
"mappings":{
"_doc":{
"dynamic_date_formats": ["yyyy-MM-dd","yyyy/MM/dd"]
}
}
}
(2)关闭日期自动识别
PUT my_index
{
"mappings": {
"_doc": {
"date_detection": false
}
}
}
4)字符串是数字时,默认不会自动识别为整形,因为字符串中出现数字时完全合理的
Numeric_datection可以开启字符串中数字的自动识别
PUT my_index
{
"mappings":{
"doc":{
"numeric_datection": true
}
}
}
三、IK分词器
3.1 为什么使用分词器
分词器主要应用在中文上,在ES中字符串类型有keyword和text两种。keyword默认不进行分词,而text是将每一个汉字拆开称为独立的词,这两种都是不适用于生产环境,所以我们需要有其他的分词器帮助我们完成这些事情,其中IK分词器是应用最为广泛的一个分词器。
1)keyword类型的分词
GET _analyze
{
"keyword":"我是程序员"
}
结果展示(报错),因为keyword不支持分词
{
"error": {
"root_cause": [
{
"type": "illegal_argument_exception",
"reason": "Unknown parameter [keyword] in request body or parameter is of the wrong type[VALUE_STRING] "
}
],
"type": "illegal_argument_exception",
"reason": "Unknown parameter [keyword] in request body or parameter is of the wrong type[VALUE_STRING] "
},
"status": 400
}
2)text类型的分词
GET _analyze
{
"text":"我是程序员"
}
结果展示:
{
"tokens": [
{
"token": "我",
"start_offset": 0,
"end_offset": 1,
"type": "<IDEOGRAPHIC>",
"position": 0
},
{
"token": "是",
"start_offset": 1,
"end_offset": 2,
"type": "<IDEOGRAPHIC>",
"position": 1
},
{
"token": "程",
"start_offset": 2,
"end_offset": 3,
"type": "<IDEOGRAPHIC>",
"position": 2
},
{
"token": "序",
"start_offset": 3,
"end_offset": 4,
"type": "<IDEOGRAPHIC>",
"position": 3
},
{
"token": "员",
"start_offset": 4,
"end_offset": 5,
"type": "<IDEOGRAPHIC>",
"position": 4
}
]
}
3.2 IK分词器安装
1)下载与安装的ES相对应的版本
2)解压elasticsearch-analysis-ik-6.6.0.zip,将解压后的IK文件夹拷贝到ES安装目录下的plugins目录下,并重命名文件夹为ik(什么名称都OK)
[hadoop@hadoop102 plugins]$ mkdir ik
[hadoop@hadoop102 software]$ unzip elasticsearch-analysis-ik-6.6.0.zip -d /opt/module/elasticsearch-6.6.0/plugins/ik/
3)分发分词器目录
[hadoop@hadoop102 elasticsearch-6.6.0]$ xsync plugins/
4)重新启动Elasticsearch,即可加载IK分词器
3.3 IK分词器测试
IK提供了两个分词算法ik_smart 和 ik_max_word,其中 ik_smart 为最少切分,ik_max_word为最细粒度划分。
1)最少划分ik_smart
GET _analyze
{
"analyzer": "ik_smart",
"text":"我是程序员"
}
结果展示
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "是",
"start_offset" : 1,
"end_offset" : 2,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "程序员",
"start_offset" : 2,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 2
}
]
}
2)最细切分ik_max_word
GET _analyze
{
"analyzer": "ik_max_word",
"text":"我是程序员"
}
输出的结果为:
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "是",
"start_offset" : 1,
"end_offset" : 2,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "程序员",
"start_offset" : 2,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "程序",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "员",
"start_offset" : 4,
"end_offset" : 5,
"type" : "CN_CHAR",
"position" : 4
}
]
}
四、检索文档
向Elasticsearch增加数据
PUT student
{
"mappings": {
"_doc":{
"properties":{
"class_id":{
"type":"keyword"
},
"stu_id":{
"type":"keyword"
},
"name":{
"type":"text"
},
"sex":{
"type":"keyword"
},
"age":{
"type":"integer"
},
"favo":{
"type":"text",
"analyzer":"ik_max_word"
}
}
}
}
}
PUT student/_doc/1001
{
"class_id":"0821",
"stu_id":"001",
"name":"小花",
"sex":"female",
"age":18,
"favo":"洗脚,海狗人参丸"
}
如果在关系型数据库Mysql中主键查询数据一般会执行下面的SQL语句
select * from student where id = 1001;
但在Elasticsearch中需要采用特殊的方式
# 协议方法 索引/类型/文档编号
GET student/_doc/1001
响应
{
"_index" : "student",
"_type" : "_doc",
"_id" : "1001",
"_score" : 1.0,
"_source" : {
"class_id" : "0821",
"stu_id" : "001",
"name" : "小花",
"sex" : "male",
"age" : 18,
"favo" : "洗脚,海狗人参丸"
}
}
我们通过HTTP方法GET来检索文档,同样的,我们可以使用DELETE方法删除文档,使用HEAD方法检查某文档是否存在。如果想更新已存在的文档,我们只需再PUT一次。
4.1 元数据查询
GET _cat/indices
GET _cat/indices?v
4.2 全文档检索
如果在关系型数据库Mysql中查询所有数据一般会执行下面的SQL语句
select * from stu;
但在Elasticsearch中需要采用特殊的方式
# 协议方法 索引/类型/_search
GET stu/_search
响应内容不仅会告诉我们哪些文档被匹配到,而且这些文档内容完整的被包含在其中—我们在给用户展示搜索结果时需要用到的所有信息都有了。
4.3 字段全值匹配检索
如果在关系型数据库Mysql中查询多字段匹配数据(字段检索)
一般会执行下面的SQL语句
select * from student where sex= 'male';
但在Elasticsearch中需要采用特殊的方式,并且全值匹配会把查询条件当做一个整体来查。
GET student/_search
{
"query": {
"bool": {
"filter": {
"term": {
"sex": "海狗人参丸"
}
}
}
}
}
查询结果:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : null,
"hits" : [ ]
}
}
没有对应的查询条件,因为ik_max_word分词器会把“海狗人参丸”分成以下词组
{
"tokens" : [
{
"token" : "海狗",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "人参",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "丸",
"start_offset" : 4,
"end_offset" : 5,
"type" : "CN_CHAR",
"position" : 2
}
]
}
我们会发现并没有“海狗人参丸”这几个字组合起来的词,因此查不到。
4.4 字段分词匹配检索
分词匹配会按照检索条件将其分词后再去匹配
GET student/_search
{
"query": {
"match": {
"favo": "海狗人参丸"
}
}
}
4.5 全值匹配和分词匹配结合使用
GET student/_search
{
"query": {
"bool": {
"filter": {
"term": {
"sex": "male"
}
}
, "must": [
{
"match": {
"favo": "海狗人参丸"
}
}
]
}
}
}
4.6 字段模糊匹配检索
如果在关系型数据库Mysql中模糊查询多字段数据
一般会执行下面的SQL语句
select * from student where name like '%haha%'
但在Elasticsearch中需要采用特殊的方式,查询出所有文档字段值分词后包含"我是程序"的文档
GET student/_search
{
"query": {
"fuzzy": {
"sex": "fmale"
}
}
}
4.7 聚合检索
4.7.1 单聚合条件
聚合查询相当于MySQL中的group by
GET student/_search
{
"aggs": {
"groupByClass": {
"terms": {
"field": "class_id",
"size": 10
}
}
}
}
4.7.2 多聚合条件
此时这两个聚合条件相互之间是没有关联的,比如同班级下的学生和年龄最大的学生
GET student/_search
{
"aggs": {
"groupByClass": {
"terms": {
"field": "class_id",
"size": 10
}
},
"groupByAge":{
"max": {
"field": "age"
}
}
}
}
查询结果:
"aggregations" : {
"groupByAge" : {
"value" : 60.0
},
"groupByClass" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "0821",
"doc_count" : 5
},
{
"key" : "0621",
"doc_count" : 1
}
]
}
}
4.7.3 聚合嵌套检索
此时这两个聚合条件相互之间是有关联的,比如同班级下年龄最大的学生
GET student/_search
{
"aggs": {
"groupByClass": {
"terms": {
"field": "class_id",
"size": 10
},
"aggs": {
"maxAge": {
"max": {
"field": "age"
}
}
}
}
}
}
查询结果:
"aggregations" : {
"groupByClass" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "0821",
"doc_count" : 5,
"maxAge" : {
"value" : 60.0
}
},
{
"key" : "0621",
"doc_count" : 1,
"maxAge" : {
"value" : 23.0
}
}
]
}
}
### 4.8 分页检索
GET student/_search
{
"from": 0, (从第几条开始)
"size": 1 (显示几条)
}
如果想实现看某一页的内容,或者跳转到某一页显示出其内容,可以将from的值设置为((页面数-1)*size)
4.9 查询时请求体中的关键字含义
五、索引别名 _aliases
索引别名就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何一个需要索引名的API来使用。别名带给我们极大的灵活性,允许我们做下面这些:
1)给多个索引分组 (例如, last_three_months)
2)给索引的一个子集创建视图
3)在运行的集群中可以无缝的从一个索引切换到另一个索引
5.1 创建索引别名
1)建索引时直接声明
PUT movie_index
{
"aliases": {
"movie_chn_2020-query": {}
},
"mappings": {
"movie_type":{
"properties": {
"id":{
"type": "long"
},
"name":{
"type": "text",
"analyzer": "ik_smart"
}
}
}
}
}
2)为已存在的索引增加别名
POST _aliases
{
"actions": [
{
"add": {
"index": "student",
"alias": "student_query"
}
}
]
}
也可以通过加过滤条件缩小查询范围,建立一个子集视图
POST _aliases
{
"actions": [
{
"add": {
"index": "student",
"alias": "student-query",
"filter": {
"term": {
"sex": "male"
}
}
}
}
]
}
5.2 查询别名
与使用普通索引没有区别
GET movie_chn_2020-query/_search
5.3 删除某个索引的别名
POST _aliases
{
"actions": [
{
"remove": {
"index": "test1",
"alias": "alias1"
}
}
]
}
5.4 为某个别名进行无缝切换
POST _aliases
{
"actions": [
{
"add": {
"index": "test1",
"alias": "alias1"
}
},
"remove": {
"index": "test1",
"alias": "alias2"
}
]
}
4.5.5 查询别名列表
GET _cat/aliases?v
六、索引模板
Index Template 索引模板,顾名思义,就是创建索引的模具,其中可以定义一系列规则来帮助我们构建符合特定业务需求的索引的mappings和 settings,通过使用 Index Template 可以让我们的索引具备可预知的一致性。
6.1 常见的场景: 分割索引
分割索引就是根据时间间隔把一个业务索引切分成多个索引。比如把order_info 变成 order_info_20200101,order_info_20200102 …..
这样做的好处有两个:
结构变化的灵活性:因为elasticsearch不允许对数据结构进行修改。但是实际使用中索引的结构和配置难免变化,那么只要对下一个间隔的索引进行修改,原来的索引位置原状。这样就有了一定的灵活性。
查询范围优化:因为一般情况并不会查询全部时间周期的数据,那么通过切分索引,物理上减少了扫描数据的范围,也是对性能的优化。
6.2 创建模板
PUT _template/template_movie2020
{
"index_patterns": ["movie_test*"],
"aliases" : {
"{index}-query": {},
"movie_test-query":{}
},
"mappings": {
"_doc": {
"properties": {
"id": {
"type": "keyword"
},
"movie_name": {
"type": "text",
"analyzer": "ik_smart"
}
}
}
}
}
其中 “index_patterns”: [“movie_test*”], 的含义就是凡是往movie_test开头的索引写入数据时,如果索引不存在,那么es会根据此模板自动建立索引。
在 “aliases” 中用{index}表示,获得真正的创建的索引名。
测试
POST movie_test_2020xxxx/_doc
{
"id":"333",
"name":"zhang3"
}
6.3 查看系统中已有的模板清单
GET _cat/templates
6.4 查看某个模板详情
GET _template/template_movie2020
或者
GET _template/template_movie*