【ES】Elasticsearch之数据类型

1、Mapping

1.1 Mapping的作用

ES中的Mapping 类似于数据库中的表结构定义 schema,它有以下几个作用:

  • 定义索引中的字段的名称
  • 定义字段的数据类型,比如字符串、数字、布尔等
  • 定义字段的属性,比如设置某个字段为要不要被分词,要不要被索引等

在 ES 早期版本,一个索引下是可以有多个 Type ,从 7.0 开始,一个索引只有一个 Type,也就是说不需要在 Mapping 指定 type 信息。

一个简单的例子如下:

{
    "mappings":{
        "type_name":{                           //type名称
            "dynamic": "strict",      //是否可以动态添加字段
            "properties":{                      
                "name":{                            //字段名
                    "type":"keyword"    //字段的数据类型
                },
                "message":{
                    "type":"text"
                },
                "age":{
                    "type":"integer"
                }
            }
        }
    }
}

mapping中字段类型一旦设定后禁止直接修改。因为Lucene实现的倒排索引生成后不允许修改,除非重建索引映射,然后做reindex操作。

1.2 Dynamic Mapping

Dynamic Mapping 机制使我们不需要手动定义 Mapping,ES 会自动根据文档信息来判断字段合适的类型,但是有时候也会推算的不对,比如地理位置信息有可能会判断为 Text,当类型如果设置不对时,会导致一些功能无法正常工作,比如 Range 查询。

ES 类型的自动识别是基于 JSON 的格式,如果输入的是 JSON 是字符串且格式为日期格式,ES 会自动设置成 Date 类型;当输入的字符串是数字的时候,ES 默认会当成字符串来处理,可以通过设置来转换成合适的类型;如果输入的是 Text 字段的时候,ES 会自动增加 keyword 子字段,还有一些自动识别如下表所示:

JSON 类型Elasticsearch 类型
字符串匹配日期格式设置成 Date;设置数字设置为 float 或者 long,该选项默认关闭;设置为 Text, 并增加 keyword 子字
布尔值boolean
浮点数float
整数long
对象Object
数组由第一个非空数值的类型所决定
空值忽略
//写入文档
PUT mapping_test/_doc/1
{
  "firstName":"Lee",
  "lastName":"Crazy",
  "loginDate":"2020-08-26T21:08:48"
}

//查看Mapping 文件
GET mapping_test/_mapping
{
  "mapping_test" : {
    "mappings" : {
      "properties" : {
        "firstName" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "lastName" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "loginDate" : {
          "type" : "date"
        }
      }
    }
  }
}
//dynamic mapping 推断字符的类型
PUT mapping_test/_doc/1
{
  "uid":"123",
  "isVip": false,
  "isAdmin":"true",
  "age": 18,
  "heigh" : 180
}

//返回结果
{
  "mapping_test" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "heigh" : {
          "type" : "long"
        },
        "isAdmin" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "isVip" : {
          "type" : "boolean"
        },
        "uid" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

Dynamic Mapping 机制由参数dynamic控制,的可选值有三个:

  • true:允许自动将检测到的新字段加到映射中(默认的)
  • false: 不允许自动新增字段,文档可以写入,但无法对字段进行搜索等操作,不会添加在映射中。
  • strict:文档不能写入,写入会报错

1.3 字段控制参数

在字段层面也有很多可以设置的参数,下面只列举几个重要的,其余可以参考官网的说明。

1.3.1 index

控制当前字段是否被索引。默认为 true。如果设置成 false,该字段不可被搜索。

{
  "mappings" : {
    "properties" : {
      "firstName" : {
        "type" : "text"
      },
      "lastName" : {
        "type" : "text"
      },
      "mobile" : {
        "type" : "text",
        "index": false
      }
    }
  }
}

1.3.2 Index Options

四种不同级别的 Index Options 配置,可以控制倒排索引记录的内容

  • docs: 记录 doc id
  • freqs: 记录 doc id 和 term frequencies
  • positions: 记录 doc id /term frequencies /term position
  • offsets: 记录doc id / term frequencies / term posistion / character offects

Text 类型默认记录 postions,其他默认为 docs。

记录内容越多,占用存储空间越大。

1.3.3 null_value

需要对 NULL 值实现搜索,只有 Keyword 类型支持设定 Null_Value。

//设置Mapping
PUT users
{
  "mappings" : {
    "properties" : {
      "firstName" : {
        "type" : "text"
      },
      "lastName" : {
        "type" : "text"
      },
      "mobile" : {
        "type" : "keyword", //这个如果是text 无法设置为空
        "null_value": "NULL"
      }
    }
  }
}

//添加记录
PUT users/_doc/2
{
"firstName":"Li",
"lastName": "Sunke",
"mobile": null
}

//搜索空值
GET users/_search?q=mobile:NULL
"_source" : {
        "firstName" : "Li",
        "lastName" : "Sunke",
        "mobile" : null
      }

1.3.4 copy_to

_all 在ES 7 中已经被 copy_to 所替代。

copy_to 将字段的数值拷贝到目标字段,实现类似 _all 的作用,用于满足一些特定的搜索需求,类似于数据库 title like “%a%” or title2 like “%a%”。

//设置Mapping
PUT users
{
"mappings": {
  "properties": {
    "firstName":{
      "type": "text",
      "copy_to": "fullName"
    },
    "lastName":{
      "type": "text",
      "copy_to": "fullName"
    }
  }
}
}

//添加记录
PUT users/_doc/1
{
"firstName":"Kobe",
"lastName": "Bryant"
}

//使用fullName查询
GET users/_search?q=fullName:(Kobe Bryant)

//_source中不会有fullName字段
{
"_index" : "users",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
  "firstName" : "Kobe",
  "lastName" : "Bryant"
}
}

2、数据类型

ES支持的数据类型可以做如下分类:

1.png

2.1 核心数据类型

2.1.1 字符串类型

2.1.1.1 text
当一个字段需要用于全文搜索(会被分词),比如产品名称、产品描述信息,就应该使用text类型。

text类型的字段不能用于排序, 也很少用于聚合。

{
    "mappings":{
        "blog":{
            "properties":{
                "summary":{
                    "type":"text",
                    "index":"true"
                }
            }
        }
    }
}

2.1.1.2 keyword
当一个字段需要按照精确值进行过滤、排序、聚合等操作时,就应该使用keyword类型。

keyword与text最大的区别就是不会被分词,而是当做一个整体来索引。

{
    "mappings":{
        "blog":{
            "properties":{
                "tags":{
                    "type":"keyword",
                    "index":"true"
                }
            }
        }
    }
}

2.1.2 数字类型

类型说明
byte有符号的8位整数, 范围: [-128 ~ 127]
short有符号的16位整数, 范围: [-32768 ~ 32767]
integer有符号的32位整数, 范围: [−2^31 ~ 2^31-1]
long有符号的64位整数, 范围: [−2^63 ~ 2^63-1]
float32位单精度浮点数
double64位双精度浮点数
half_float16位半精度IEEE 754浮点类型
scaled_float缩放类型的的浮点数, 比如price字段只需精确到分, 57.34缩放因子为100, 存储结果为5734
  • 尽可能选择范围小的数据类型,字段的长度越短,索引和搜索的效率越高
  • 优先考虑使用带缩放因子的浮点类型
{
    "mappings": {
        "book": {
            "properties": {
                "name": {"type": "text"},
                "quantity": {"type": "integer"},  // integer类型
                "price": {
                    "type": "scaled_float",       // scaled_float类型
                    "scaling_factor": 100
                }
            }
        }
    }
}

2.1.3 日期类型

2.1.3.1 date

JSON没有日期数据类型, 所以在ES中, 日期可以是:

  • 包含格式化日期的字符串, “2020-08-26”, 或"2020/08/26 12:00:00"
  • 代表时间毫秒数的长整型数字
  • 代表时间秒数的整数
PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "date": {
          "type": "date" 
        }
      }
    }
  }
}

PUT my_index/_doc/1
{ "date": "2015-01-01" } 

PUT my_index/_doc/2
{ "date": "2015-01-01T12:10:30Z" } 

PUT my_index/_doc/3
{ "date": 1420070400001 } 

同时ES的date类型允许我们规定格式:

# 规定格式如下: || 表示或者
PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "date": {
          "type":   "date",
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        }
      }
    }
  }
}

一旦我们规定了格式,如果新增数据不符合这个格式,ES将会报错。

2.1.3.2 date_nanos

ES 7新增的时间类型,可精确到纳秒,用法类似date。

2.1.4 布尔类型

可以接受表示真、假的字符串或数字:

  • 真值: true, “true”, “on”, “yes”, “1”…
  • 假值: false, “false”, “off”, “no”, “0”, “”(空字符串), 0.0, 0

2.1.5 二进制类型

二进制类型是Base64编码字符串的二进制值,不以默认的方式存储,且不能被搜索。

有2个设置项:

  • doc_values: 该字段是否需要存储到磁盘上,方便以后用来排序、聚合或脚本查询。接受true和false(默认)
  • store: 该字段的值是否要和_source分开存储、检索,意思是除了_source中, 是否要单独再存储一份。接受true或false(默认).

使用示例:

// 添加映射
PUT website
{
    "mappings": {
        "blog": {
            "properties": {
                "blob": {"type": "binary"}   // 二进制
            }
        }
    }
}

// 添加数据
PUT website/blog/1
{
    "title": "Some binary blog",
    "blob": "hED903KSrA084fRiD5JLgY=="
}

注意: Base64编码的二进制值不能嵌入换行符\n, 逗号(0x2c)等符号。

2.1.6 范围类型

range类型支持以下几种:

类型范围
integer_range−2^31 ~ 2^31−1
long_range−2^63 ~ 2^63−1
float_range32位单精度浮点型
double_range64位双精度浮点型
date_range64位整数, 毫秒计时
ip_rangeIP值的范围, 支持IPV4和IPV6, 或者这两种同时存在

添加映射:

PUT company
{
    "mappings": {
        "department": {
            "properties": {
                "expected_number": {  // 预期员工数
                    "type": "integer_range"
                },
                "time_frame": {       // 发展时间线
                    "type": "date_range", 
                    "format": "yyyy-MM-dd HH:mm:ss"
                },
                "ip_whitelist": {     // ip白名单
                    "type": "ip_range"
                }
            }
        }
    }
}

添加数据:

PUT company/department/1
{
    "expected_number" : {
        "gte" : 10,
        "lte" : 20
    },
    "time_frame" : { 
        "gte" : "2020-08-01 12:00:00", 
        "lte" : "2020-09-01 12:00:00"
    }, 
    "ip_whitelist": "192.168.0.0/16"
}

查询数据:

GET company/department/_search
{
    "query": {
        "term": {
            "expected_number": {
                "value": 12
            }
        }
    }
}

GET company/department/_search
{
    "query": {
        "range": {
            "time_frame": {
                "gte": "2020-08-10 12:00:00",
                "lte": "2020-08-20 12:00:00",
                "relation": "within" 
            }
        }
    }
}

2.2 复杂数据类型

2.2.1 数组类型

在Elasticsearch中,数组不需要专用的字段数据类型。默认情况下,任何字段都可以包含零个或多个值。

数组中所有的值必须是同一种数据类型, 不支持混合数据类型的数组:

  • 字符串数组: [“one”, “two”]
  • 整数数组: [1, 2]
  • 由数组组成的数组: [1, [2, 3]], 等价于[1, 2, 3]
  • 对象数组: [{“name”: “Tom”, “age”: 20}, {“name”: “Jerry”, “age”: 18}]

2.2.2 对象类型

JSON文档是分层的:文档可以包含内部对象,内部对象也可以包含内部对象。

添加示例:

PUT employee/developer/1
{
    "name": "Winner",
    "address": {
        "region": "China",
        "location": {"province": "ZheJiang", "city": "HuangZhou"}
    }
}

存储方式:

{
    "name":                       "Winner",
    "address.region":             "China",
    "address.location.province":  "ZheJiang", 
    "address.location.city":      "HuangZhou"
}

文档的映射结构类似为:

PUT employee
{
    "mappings":{
        "developer":{
            "properties":{
                "name":{
                    "type":"text",
                    "index":"true"
                },
                "address":{
                    "properties":{
                        "region":{
                            "type":"keyword",
                            "index":"true"
                        },
                        "location":{
                            "properties":{
                                "province":{
                                    "type":"keyword",
                                    "index":"true"
                                },
                                "city":{
                                    "type":"keyword",
                                    "index":"true"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

2.2.3 嵌套类型

嵌套类型是对象数据类型的一个特例,可以让array类型的对象被独立索引和搜索。

先来看下对象数据类型是怎么存储的。

添加数据:

PUT game_of_thrones/role/1
{
    "group":"stark",
    "performer":[
        {
            "first":"John",
            "last":"Snow"
        },
        {
            "first":"Sansa",
            "last":"Stark"
        }
    ]
}

内部存储结构:

{
    "group":"stark",
    "performer.first":[
        "john",
        "sansa"
    ],
    "performer.last":[
        "snow",
        "stark"
    ]
}

可以看出,user.first和user.last会被平铺为多值字段,这样一来,John和Snow之间的关联性丢失了,在查询时, 可能出现John Stark的结果。

嵌套数据类型可以解决关联性丢失的问题。嵌套对象实质是将每个对象分离出来,作为隐藏文档进行索引。

创建映射:

PUT game_of_thrones
{
    "mappings":{
        "role":{
            "properties":{
                "performer":{
                    "type":"nested"
                }
            }
        }
    }
}

添加数据:

PUT game_of_thrones/role/1
{
    "group":"stark",
    "performer":[
        {
            "first":"John",
            "last":"Snow"
        },
        {
            "first":"Sansa",
            "last":"Stark"
        }
    ]
}

检索数据:

GET game_of_thrones/_search
{
    "query":{
        "nested":{
            "path":"performer",
            "query":{
                "bool":{
                    "must":[
                        {
                            "match":{
                                "performer.first":"John"
                            }
                        },
                        {
                            "match":{
                                "performer.last":"Snow"
                            }
                        }
                    ]
                }
            }
        }
    }
}

2.3 空间数据类型

2.3.1 地理点类型

地理点类型用于存储地理位置的经纬度对,可用于:

  • 查找一定范围内的地理点;
  • 通过地理位置或相对某个中心点的距离聚合文档;
  • 将距离整合到文档的相关性评分中;
  • 通过距离对文档进行排序.

添加映射:

PUT employee
{
    "mappings": {
        "developer": {
            "properties": {
                "location": {"type": "geo_point"}
            }
        }
    }
}

存储地理位置:

// 方式一: 纬度 + 经度键值对
PUT employee/developer/1
{
    "location": {
        "lat": 23.11, "lon": 113.33     // 纬度: latitude, 经度: longitude
    }
}

// 方式二: "纬度, 经度"的字符串参数
PUT employee/developer/2
{
  "location": "23.11, 113.33"           // 纬度, 经度
}

// 方式三: ["经度, 纬度"] 数组地理点参数
PUT employee/developer/3
{
  "location": [ 113.33, 23.11 ]         // 经度, 纬度
}

查询示例:

GET employee/_search
{
    "query": { 
        "geo_bounding_box": { 
            "location": {
                "top_left": { "lat": 24, "lon": 113 },      // 地理盒子模型的上-左边
                "bottom_right": { "lat": 22, "lon": 114 }   // 地理盒子模型的下-右边
            }
        }
    }
}

除此之外,还用于多边形的geo_shape类型、用于笛卡尔点的point类型、用于笛卡尔几何的shpe类型,使用很少,这里省略。

2.4 专用数据类型

2.4.1 IP类型

IP类型的字段用于存储IPv4或IPv6的地址,本质上是一个长整型字段。

添加映射:

PUT employee
{
    "mappings":{
        "customer":{
            "properties":{
                "ip_addr":{
                    "type":"ip"
                }
            }
        }
    }
}

添加数据:

PUT employee/customer/1
{
    "ip_addr":"192.168.1.1"
}

查询数据:

GET employee/customer/_search
{
    "query": {
        "term": { "ip_addr": "192.168.0.0/16" }
    }
}

2.4.2 计数数据类型

token_count类型用于统计字符串中的单词数量。

本质上是一个整数型字段,接受并分析字符串值,然后索引字符串中单词的个数。

添加映射:

PUT employee
{
    "mappings":{
        "customer":{
            "properties":{
                "name":{
                    "type":"text",
                    "fields":{
                        "length":{
                            "type":"token_count",
                            "analyzer":"standard"
                        }
                    }
                }
            }
        }
    }
}

添加数据:

PUT employee/customer/1
{ "name": "John Snow" }
PUT employee/customer/2
{ "name": "Tyrion Lannister" }

查询数据:

GET employee/customer/_search
{
    "query":{
        "term":{
            "name.length":2
        }
    }
}

除此之外,还有十余种其他的专门数据类型,具体可以参考官方文档,此处不再一一列举。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值