ElasticSearch学习笔记五 映射和数据类型

映射(Mapping)

映射定义了文档和它所包含的属性被存储类型和索引方式等等

举例来说,我们用映射来定义以下信息:

  • 是否一个字符串应该被当成全文类型来存储和索引。
  • 属性是整形、字符型、日期类型还是地理位置类型。
  • 是否一个属性应该被包含在_all元数据中。
  • 日期类型属性的格式化。
  • 自定义动态添加的属性的映射。

类比于关系数据库,索引(index)相当于数据库,类型(type)相当于数据表,映射(Mapping)相当于数据表的表结构。ElasticSearch中的映射(Mapping)用来定义一个文档,可以定义所包含的字段以及字段的类型、分词器及属性等等。

在关系型数据库中,我们使用数据库必须优先创建数据库,创建并定义数据表结构,我们才可以逐行添加修改数据。而在Elasticsearch中我们不需要事先定义映射(Mapping),文档写入Elasticsearch时,Elasticsearch会根据文档字段自动识别类型创建映射,依据Elasticsearch是否自动创建映射,我们可以把映射分为动态映射和静态映射。

动态映射

字段的数据类型在加入索引之前不需要事先定义,Elasticsearch会根据文档字段自动识别类型创建映射。不仅首次加入数据到索引时Elasticsearch会自动创建映射,新的字段首次加入文档顶级对象或者内部对象Elasticsearch也会自动创建映射。

新建索引tweet

PUT tweet

创建结果

{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "tweet"
}

此时我们查看一下索引的映射

GET tweet/_mapping

映射如下

{
  "tweet": {
    "mappings": {}
  }
}

此时,我们插入一条tweet信息

PUT tweet/tweet/1
{
    "tweet":    "However did I manage before Elasticsearch?",
    "date":     "2014-09-14",
    "name":     "Mary Jones",
    "user_id":  1
}

插入结果如下:

{
  "_index": "tweet",
  "_type": "tweet",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 1,
  "_primary_term": 1
}

此时我们查看一下索引的映射

GET tweet/_mapping

映射如下

{
  "tweet": {
    "mappings": {
      "tweet": {
        "properties": {
          "date": {
            "type": "date"
          },
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "tweet": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "user_id": {
            "type": "long"
          }
        }
      }
    }
  }
}

从上面的映射结果我们可以看出来,tweet 和 name映射为text类型,发布日期date自动映射为date,而用户IDuser_id也自动识别为了long型。

此时,我们再为我们的tweet追加一条评论。

PUT tweet/tweet/1
{
    "tweet":    "However did I manage before Elasticsearch?",
    "date":     "2014-09-14",
    "name":     "Mary Jones",
    "user_id":  1,
    "comments": [
      {
        "content":  "very good",
        "date":  "2014-09-14",
        "user_id":  1
      }
    ]
}

追加结果如下

{
  "_index": "tweet",
  "_type": "tweet",
  "_id": "1",
  "_version": 2,
  "result": "updated",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 2,
  "_primary_term": 1
}

此时我们再查看一下索引的映射

GET tweet/_mapping

映射如下

{
  "tweet": {
    "mappings": {
      "tweet": {
        "properties": {
          "comments": {
            "properties": {
              "content": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "date": {
                "type": "date"
              },
              "user_id": {
                "type": "long"
              }
            }
          },
          "date": {
            "type": "date"
          },
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "tweet": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "user_id": {
            "type": "long"
          }
        }
      }
    }
  }
}

此时,我们新加的comments.content也自动映射为text类型,发布日期date自动映射为date,而用户IDuser_id也自动识别为了long型。

由此可见ElasticSearch的动态映射十分强大。

动态映射规则

由于Elasticsearch动态映射的存在,我们在创建索引后直接将文档数据写入Elasticsearch而不需要优先创建映射。在实际项目中,如果在导入数据前不能确定包含哪些字段或者不方便确定字段类型,可以使用动态映射。当向Elasticsearch写入一个文档时,文档中包含当前映射没有的字段,Elasticsearch会通过动态映射来推断该字段类型修改映射。

ElasticSearch动态映射规则如下:

JSON数据 自动推测的类型
null 没有字段被添加
true或false boolean型
小数 float型
数字 long型
日期 date或text
字符串 text
数组 由数组第一个非空值决定
JSON对象 object类型

静态映射

相对于Elasticsearch的自动映射,我们可以在ElasticSearch中事先为索引定义好映射,包含文档的各个字段及其类型等,并且考虑到动态映射的自动类型推测功能并不是100%正确的,这就需要静态映射机制。静态映射与关系数据库中创建表语句类型思想一致,我们需要事先指定字段类型。

相对于动态映射,静态映射可以添加更加详细字段类型、更精准的配置信息等。

Elasticsearch字段数据类型

一级分类 二级分类 具体类型
核心类型 字符串类型 string,text,keyword
整数类型 integer,long,short,byte
浮点类型 double,float,half_float,scaled_float
逻辑类型 boolean
日期类型 date
范围类型 range
二进制类型 binary
复合类型 数组类型 array
对象类型 object
嵌套类型 nested
地理类型 地理坐标类型 geo_point
地理地图 geo_shape
特殊类型 IP类型 ip
范围类型 completion
令牌计数类型 token_count
附件类型 attachment
抽取类型 percolator

字符串类型

  • string
    string类型在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,由text和keyword类型替代。
  • text
    当一个字段是要被全文搜索的,比如Email内容、产品描述,应该使用text类型。设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text类型的字段不用于排序,很少用于聚合。
  • keyword
    keyword类型适用于索引结构化的字段,比如email地址、主机名、状态码和标签。如果字段需要进行过滤(比如查找已发布博客中status属性为published的文章)、排序、聚合。keyword类型的字段只能通过精确值搜索到。

整数类型

类型 取值范围
byte -128~127
short -32768~32767
integer -231~231-1
long -263~263-1

在满足需求的情况下,尽可能选择范围小的数据类型。比如,某个字段的取值最大值不会超过100,那么选择byte类型即可。迄今为止吉尼斯记录的人类的年龄的最大值为134岁,对于年龄字段,short足矣。字段的长度越短,索引和搜索的效率越高。

浮点类型

类型 取值范围
doule 64位双精度IEEE 754浮点类型
float 32位单精度IEEE 754浮点类型
half_float 16位半精度IEEE 754浮点类型
scaled_float 缩放类型的的浮点数

对于float、half_float和scaled_float,-0.0和+0.0是不同的值,使用term查询查找-0.0不会匹配+0.0,同样range查询中上边界是-0.0不会匹配+0.0,下边界是+0.0不会匹配-0.0。

其中scaled_float,比如价格只需要精确到分,price为57.34的字段缩放因子为100,存起来就是5734
优先考虑使用带缩放因子的scaled_float浮点类型。

逻辑类型

逻辑类型(布尔类型)可以接受true/false/”true”/”false”值

date类型

我们人类使用的计时系统是相当复杂的:秒是基本单位, 60秒为1分钟, 60分钟为1小时, 24小时是一天……如果计算机也使用相同的方式来计时, 那显然就要用多个变量来分别存放年月日时分秒, 不停的进行进位运算, 而且还要处理偶尔的闰年和闰秒以及协调不同的时区. 基于”追求简单”的设计理念, UNIX在内部采用了一种最简单的计时方式:

  • 计算从UNIX诞生的UTC时间1970年1月1日0时0分0秒起, 流逝的秒数

  • UTC时间1970年1月1日0时0分0秒就是UNIX时间0, UTC时间1970年1月2日0时0分0秒就是UNIX时间86400.

  • 这个计时系统被所有的UNIX和类UNIX系统继承了下来, 而且影响了许多非UNIX系统.

日期类型表示格式可以是以下几种:

  1. 日期格式的字符串,比如 “2018-01-13” 或 “2018-01-13 12:10:30”
  2. long类型的毫秒数( milliseconds-since-the-epoch,epoch就是指UNIX诞生的UTC时间1970年1月1日0时0分0秒)
  3. integer的秒数(seconds-since-the-epoch)

ElasticSearch 内部会将日期数据转换为UTC,并存储为milliseconds-since-the-epoch的long型整数。

binary类型

二进制字段是指用base64来表示索引中存储的二进制数据,可用来存储二进制形式的数据,例如图像。默认情况下,该类型的字段只存储不索引。二进制类型只支持index_name属性。

array类型

在ElasticSearch中,没有专门的数组(Array)数据类型,但是,在默认情况下,任意一个字段都可以包含0或多个值,这意味着每个字段默认都是数组类型,只不过,数组类型的各个元素值的数据类型必须相同。在ElasticSearch中,数组是开箱即用的(out of box),不需要进行任何配置,就可以直接使用。(我们自定义数据类型映射时,一般可以指定类型为text类型(自动映射一般会映射首元素类型),涉及到elasticsearch的检索原理)。

在同一个数组中,数组元素的数据类型是相同的,ElasticSearch不支持元素为多个数据类型: [ 10, "some string" ],常用的数组类型是:

  1. 字符数组: [ "one", "two" ]
  2. 整数数组: productId:[ 1, 2 ]
  3. 对象(文档)数组:
 "user":[ 
 	{ "name": "Mary", "age": 12 },
 	{ "name": "John", "age": 10 }
 ]

ElasticSearch内部把对象数组展开为

 {"user.name": ["Mary", "John"],"user.age": [12,10]}

object类型

JSON天生具有层级关系,文档会包含嵌套的对象。

自定义映射

PUT test
{
  "mappings":{
    "test": {
       "properties": {
          "name": {"type": "keyword"},
          "age": {"type": "integer"},
          "birthday": {"type": "date"},
          "interests":{"type": "text"},
          "friends":{"type": "object"}
       }
    }
  }
}

映射定义成功

{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "test"
}

查看映射

{
  "test": {
    "mappings": {
      "test": {
        "properties": {
          "age": {
            "type": "integer"
          },
          "birthday": {
            "type": "date"
          },
          "friends": {
            "type": "object"
          },
          "interests": {
            "type": "text"
          },
          "name": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

插入数据

PUT test/test/1
{
  "name":"test",
  "age":28,
  "birthday":"1986-10-18",
  "interests":["reading","sing"],
  "friends":{
    "name":"jack"
  }
}

数据插入成功

{
  "_index": "test",
  "_type": "test",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

再次查看映射

{
  "test": {
    "mappings": {
      "test": {
        "properties": {
          "age": {
            "type": "integer"
          },
          "birthday": {
            "type": "date"
          },
          "friends": {
            "properties": {
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          },
          "interests": {
            "type": "text"
          },
          "name": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

我们新加的字段也自动映射了。

猜你喜欢

转载自blog.csdn.net/weixin_43430036/article/details/83146599