Mapping 类似于数据库中的表结构定义schema,它的主要作用是:用来定义索引中的字段的名称、定义字段的数据类型、定义字段类型的一些其它参数,比如字符串、数字、布尔字段,倒排索引的相关配置,设置某个字段为不被索引、记录 position 等。每一种数据类型都有对应的使用场景,并且每个文档都有映射,但是在大多数使用场景中,我们并不需要显示的创建映射,因为ES中实现了动态映射。我们在索引中写入一个下面的JSON文档:
- {
- "name":"jack",
- "age":18,
- "birthDate": "1991-10-05"
- }
在动态映射的作用下,name会映射成text类型,age会映射成long类型,birthDate会被映射为date类型,映射的索引信息如下。
- {
- "mappings": {
- "_doc": {
- "properties": {
- "age": {
- "type": "long"
- },
- "birthDate": {
- "type": "date"
- },
- "name": {
- "type": "text",
- "fields": {
- "keyword": {
- "type": "keyword",
- "ignore_above": 256
- }
- }
- }
- }
- }
- }
- }
ES 字段类型类似于 MySQL 中的字段类型,ES 字段类型主要有:核心类型、复杂类型、地理类型、特殊类型,常见的ELasticSearch数据类型如下:
一级分类 | 二级分类 | 具体类型 |
---|---|---|
核心类型 | 字符串类型 | |
整数类型 | integer,long,short,byte | |
浮点类型 | double,float,half_float,scaled_float | |
逻辑类型 | boolean | |
日期类型 | date | |
范围类型 | range(Integer_range,long_range,date_range...) | |
二进制类型 | binary (BASE64 的二进制) | |
复合类型 | 数组类型 | array |
对象类型 | object | |
嵌套类型 | nested | |
地理类型 | 地理坐标类型 | geo_point |
地理地图 | geo_shape | |
特殊类型 | IP类型 | ip |
... | ... | ... |
下面简单介绍一下常用的类型
从ElasticSearch 5.x开始不再支持string,由text和keyword类型替代:
【text 类型】:
适用于需要被全文检索的字段,因为它会分词,例如新闻正文、邮件内容等比较长的文字,text 类型会被 Lucene 分词器(Analyzer)处理为一个个词项,并使用 Lucene 倒排索引存储,text 字段不能被用于排序。如果需要使用该类型的字段只需要在定义映射时指定 JSON 中对应字段的 type 为 text。
【keyword】:
只能通过精确值搜索到,适合简短、结构化字符串,例如主机名、姓名、商品名称等,可以用于过滤、排序、聚合检索,也可以用于精确查询。
总结:
对text类型的字段,会先使用分词器分词,生成倒排索引,用于之后的搜索。
对keyword类型的字段,不会分词,搜索时只能精确查找。
ES支持的数字类型有:
整型数字类型:integer类型、long类型、short类型、byte类型。
浮点型数字类型: double类型、 float类型、 half_float类型、 scaled_float类型。
这类数据类型都是以确切值索引的,可以使用term查询精确匹配。数字类型的字段在满足需求的前提下应当尽量选择范围较小的数据类型,字段长度越短,搜索效率越高,对于浮点数,可以优先考虑使用 scaled_float 类型,该类型可以通过缩放因子来精确浮点数,例如 12.34 可以转换为 1234 来存储。
整数类型:byte(8位 1字节), short(16位 2字节), int(32位 4字节), long(4位 8字节)
浮点数:float(32位 4字节),double(64位 8字节)
注意:1个字节等于4bit,1bit等2位)
在 ES 中日期可以为以下形式:格式化的日期字符串,例如 2020-03-17 00:00、2020/03/17时间戳(和 1970-01-01 00:00:00 UTC 的差值),单位毫秒或者秒即使是格式化的日期字符串,ES 底层依然采用的是时间戳的形式存储。
日期类型一般会结合一个mapping参数来使用:
format :自定义的日期格式,默认:strict_date_optional_time || epoch_millis。
- -- 创建索引并设置结构
-
- -- 7.X写法
- PUT test_index
- {
- "mappings": {
- "properties": {
- "date1": {
- "type": "date"
- },
- "date2": {
- "type": "date",
- "format": "yyyy-MM-dd HH:mm:ss"
- },
- "date3": {
- "type": "date",
- "format": "yyyy-MM-dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
- }
- }
- }
- }
-
- 说明:es7.x前的版本需要在 properties 外添加 "_doc": {}
-
-
- #添加2条数据
- POST test_index/_doc
- {
- "dateTimeFormat": "2015-01-01 12:10:30"
- }
-
-
- PUT test_index/_doc/1
- {
- "date1": 1577808000000,
- "date2": "2020-01-01 00:00:00",
- "date3": "2020/01/01"
- }
- PUT test_index/_doc/2
- {
- "date1": 1577808000001,
- "date2": "2020-01-01 00:00:01",
- "date3": "2020/01/01"
- }
- #查询测试
- GET test_index/_search
- {
- "sort": [
- {
- "date2": {
- "order": "desc"
- }
- }
- ]
- }
JSON 文档中同样存在布尔类型,不过 JSON 字符串类型也可以被 ES 转换为布尔类型存储,前提是字符串的取值为 true 或者 false,布尔类型常用于检索中的过滤条件。
二进制类型 binary 接受 BASE64 编码的字符串,默认 store 属性为 false,并且不可以被搜索。
范围类型可以用来表达一个数据的区间,所以会用到gt、gte、lt、lte…等逻辑表示符。可以分为6种:integer_range、float_range、long_range、double_range、date_range以及ip_range。
- -- 7.X写法
- PUT test_index
- {
- "mappings": {
- "properties": {
- "data1": {
- "type": "integer_range"
- },
- "data2": {
- "type": "float_range"
- },
- "data3": {
- "type": "date_range",
- "format": "yyyy-MM-dd HH:mm:ss"
- },
- "data4": {
- "type": "ip_range"
- }
- }
- }
- }
- 说明:es7.x前的版本需要在 properties 外添加 "_doc": {}
-
-
- PUT test_index/_doc/1
- {
- "date1": {
- "gte": 100,
- "lte": 200
- },
- "date2": {
- "gte": 21.21,
- "lte": 22
- },
- "date3": {
- "gte": "2020-01-01 00:00:00",
- "lte": "2020-01-02 00:00:00"
- },
- "date4": {
- "gte": "192.168.192.10",
- "lte": "192.168.192.11"
- }
- }
对象类型即一个JSON对象,JSON 字符串允许嵌套对象,所以一个文档可以嵌套多个、多层对象。可以通过对象类型来存储二级文档,不过由于 Lucene 并没有内部对象的概念,所以ES 会将原 JSON 文档扁平化,例如有下面这样的文档:
- PUT test_index
- {
- "mappings": {
- "properties": {
- "user": {
- "type": "object"
- }
- }
- }
- }
-
- PUT test_index/_doc/1
- {
- "username": {
- "first": "zhang",
- "last": "san"
- }
- }
上面我们看到都是一个个分开的字段,而实际上 ES 会将其转换为以下格式,并通过 Lucene 存储,即使 name 是 object 类型:
- {
- "username.first": "zhang",
- "username.last": "san"
- }
所以我们在进行条件搜索时也必须使用这种方式:
- GET test_index/_search
- {
- "query": {
- "bool": {
- "must": [
- {
- "match": {
- "username.first": "zhang"
- }
- }
- ]
- }
- }
- }
bool查询的使用
must
查询必须匹配某些条件才可以返回must_not
查询必须不匹配某些条件should
当查询满足此条件时,会增加其_score
值filter
必须匹配,但是结果不会计算分值。嵌套类型可以看成是一个特殊的对象类型,在文档属性是一个对象数组时使用,它允许对象数组彼此独立地编制索引和查询,例如文档:
- {
- "address": [
- {
- "country": "CN",
- "city": "BJ"
- },
- {
- "country": "US",
- "city": "NY"
- }
- ]
- }
示例中的文档在实际存储时,会被拆解为两个数组字段:
- {
- "adress.country" : ["CN,"US"],
- "address.city" : ["BJ","NY"]
- }
这样一来,单个对象内部,country字段和city字段之间的匹配关系就丢失了。换句话说,使用CN与NY作为共同条件检索的文档时,上述文档也会被检索出来,这在逻辑上就出现了错误;
在示例中使用了bool组合查询,要求country字段为CN而city字段为NY。这样的文档显然并不存在,但由于数组中的对象被平铺为两个独立的数组字段,文档仍然会被检索出来。
为了解决对象类型在数组中丢失内部字段之间匹配关系的问题,Elasticsearch提供了一种特殊的对象类型nested。这种类型会为数组中的每一个对象创建一个单独的文档, 以保存对象的字段信息并使它们可检索。由于这类文档并不直接可见,而是藏置在父文档之中,所以这类文档可以称为为隐式文档 或 嵌入文档。
测试nested查询:
nested查询只能针对nested类型字段,需要通过path参数指定nested类型字段的路径,而在query参数中则包含了针对隐式文档的具体查询条件。
- -- 删除索引
- DELETE test_index
-
- -- 创建索引
- PUT test_index
- {
- "mappings": {
- "properties": {
- "address": {
- "type": "nested"
- }
- }
- }
- }
-
- -- 索引添加文档,id设置为1
- PUT test_index/_doc/1
- {
- "address": [
- {
- "country": "CN",
- "city": "BJ"
- },
- {
- "country": "US",
- "city": "NY"
- }
- ]
- }
-
-
- #查询测试
- GET test_index/_search
- {
- "query": {
- "nested": {
- "path": "address",
- "query": {
- "bool": {
- "must": [
- {
- "match": {
- "address.country": "CN"
- }
- },
- {
- "match": {
- "address.city": "NY"
- }
- }
- ]
- }
- }
- }
- }
- }
-
在示例中再次使用CN与NY共同作为查询条件,但由于使用nested类型后会将数组中的对象转换成隐式文档,所以在 nested查询中将不会有文档返回了。将条件更换为CN和BJ,则有文档返回。
一个mapping主要有两部分组成:metadata和mapping:
metadata元数据字段用于自定义如何处理文档关联的数据。例如:
mapping中包含字段的类型和参数。本文主要介绍的mapping参数就需要在field中去定义。例如:
主要参数如下: