Elasticsearch基础使用介绍(数据类型,索引操作)

发布于:2024-05-17 ⋅ 阅读:(58) ⋅ 点赞:(0)

环境准备

  • Elasticsearch 服务(单机或集群)
  • Kibana 服务

如果对ES不了解或没有上述环境,可以看下我之前的博客。

Elasticsearch入门基础和集群部署

Elasticsearch查看集群信息,设置ES密码,Kibana部署

进入 Kibana 开发工具
在这里插入图片描述

数据类型

官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.10/mapping-types.html

基础数据类型

keyword

keyword类型是不进行切分的字符串类型。


这里的“不进行切分”指的是:在索引时,对keyword类型的数据不进行切分,直接构建倒排索引;在搜索时,对该类型的查询字符串不进行切分后的部分匹配。


keyword类型数据一般用于对文档的过滤、排序和聚合在现实场景中。
keyword经常用于描述姓名、产品类型、用户ID、URL和状态码等。keyword类型数据一般用于比较字符串是否相等,不对数据进行部分匹配,因此一般查询这种类型的数据时使用term查询

text

text类型是可进行切分的字符串类型。


这里的“可切分”指的是: 在索引时,可按照相应的切词算法对文本内容进行切分,然后构建倒排索引;
在搜索时,对该类型的查询字符串按照用户的切词算法进行切分,然后对切分后的部分匹配打分。一般查询这种类型的数据时使用match查询

数值类型

官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.10/number.html

ES支持的数值类型有long、integer、short、byte、double、float、half float.scaled float和unsigned long等。


为节约存储空间并提升搜索和索引的效率,在实际应用中,在满足需求的情况下应尽可能选择范围小的数据类型。


数值类型的数据也可用于对文档进行过滤、排序和聚合。

boolean

用来表示 true 和 false

日期类型(date)

在ES中,日期类型的名称为date。ES中存储的日期是标准的UTC格式。

UTC(Universal Time Coordinated)叫做世界统一时间,中国大陆和 UTC 的时差是+8,也就是 UTC+8

在elasticsearch中的date类型可以是下面的形式:

  • 格式化好的字符串,比如"2015-01-01",“2015/01/01 12:10:30”
  • 一个long类型整数,代表从纪元以来的毫秒数
  • 一个integer类型整数,表示从纪元开始的秒数

实际上不管日期以何种格式写入,在 ES 内部都会先换成 UTC 时间并存储为 long 类型
注意:

  • long和integer必须是非负数
  • 使用带格式的日期表示1970年之前的日期
  • 日期将始终对外呈现为字符串

可以自定义日期格式(format),多个日期格式 用 || 分割

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "date": {
        "type":   "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}

如果不指定使用默认格式strict_date_optional_time||epoch_millis

其中strict _date_optional time的含义是严格的时间类型,支持
yyyy-MM-dd、
yyyyMMdd.yyyyMMddHHmmss、
yyyy-MM-ddTHH:mm:ss、
yyyy-MM-ddTHH:mm:ss.SSS
yyyyMM-ddTHH:mm:ss.SSSZ等格式
epoch milis的含义是从1970年1月1日0点到现在的毫秒

注意strict_date_optional_time 并不支持 yyyy-MM-dd HH:mm:ss 格式
如果需要,需要指定formatyyyy-MM-dd HH:mm:ss

复杂数据类型

数组类型

官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.10/array.html

ES数组没有定义方式,其使用方式是开箱即用的,即无须事先声明。
在写入时把数据用中括号 [] 括起来,由ES对该字段完成定义。

PUT my-index-000001/_doc/1
{
  "message": "some arrays in this document...",
  "tags":  [ "elasticsearch", "wow" ], 
  "lists": [ 
    {
      "name": "prog_list",
      "description": "programming list"
    },
    {
      "name": "cool_list",
      "description": "cool stuff list"
    }
  ]
}

当然,如果事先已经定义了字段类型,在写数据时以数组形式写入,ES也会将该类型转为数组。

数组应该不算一个纯粹的数据类型,而是一种逻辑存储方式
数组中的所有值都必须是 相同的数据类型。

数组类型的字段适用于元素类型的搜索方式,也就是说,数组元素适用于什么搜索,数组字段就适用于什么搜索。

例如,数组元素类型是keyword,该类型可以适用于term搜索。

对象类型

官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.10/object.html

对象类型 也不用事先定义,在写入文档时 ES 会自动识别转化为对象类型。
在写入数据时 使用花括号 {} 括起来,ES将自动识别为 对象类型

PUT my-index-000001/_doc/1
{ 
  "region": "US",
  "manager": { 
    "age":     30,
    "name": { 
      "first": "John",
      "last":  "Smith"
    }
  }
}

在内部,此文档被索引为键值的对应关系如下:

{
“region”: “US”,
“manager.age”: 30,
“manager.name.first”: “John”,
“manager.name.last”: “Smith”
}

上述文档索引的主动创建方式可以为:

PUT /my-index-000001
{
  "mappings": {
    "properties": { 
      "region": {
        "type": "keyword"
      },
      "manager": { 
        "properties": {
          "age":  { "type": "integer" },
          "name": { 
            "properties": {
              "first": { "type": "text" },
              "last":  { "type": "text" }
            }
          }
        }
      }
    }
  }
}

地理位置类型

官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.10/geo-point.html#geo-point

geo_point类型,用来存储经纬度数据

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_point"
      }
    }
  }
}
# 五种方式指定经纬度
# 用 和 键表示为对象的地理点。latlon
PUT my-index-000001/_doc/1
{
  "text": "Geo-point as an object",
  "location": { 
    "lat": 41.12,
    "lon": -71.34
  }
}
# 地理点表示为格式为:的字符串。"lat,lon"
PUT my-index-000001/_doc/2
{
  "text": "Geo-point as a string",
  "location": "41.12,-71.34" 
}
# 以地理哈希表示的地理点。
PUT my-index-000001/_doc/3
{
  "text": "Geo-point as a geohash",
  "location": "drm3btev3e86" 
}
# 以数组形式表示的地理点,格式为:[ ,lonlat]
PUT my-index-000001/_doc/4
{
  "text": "Geo-point as an array",
  "location": [ -71.34, 41.12 ] 
}

# 地理点表示为已知文本点,格式为:"POINT(lon lat)"
PUT my-index-000001/_doc/5
{
  "text": "Geo-point as a WKT POINT primitive",
  "location" : "POINT (-71.34 41.12)" 
}

# 	一个地理边界框查询,用于查找框内的所有地理点。
GET my-index-000001/_search
{
  "query": {
    "geo_bounding_box": { 
      "location": {
        "top_left": {
          "lat": 42,
          "lon": -72
        },
        "bottom_right": {
          "lat": 40,
          "lon": -74
        }
      }
    }
  }
}

多字段(子字段)

针对同一个字段,有时需要不同的数据类型,这通常表现在为了不同的目的以不同的方式索引相同的字段。

例如,按照用户姓名进行搜索,又希望按照姓氏进行排列,可以在mapping定义中将姓名字段先后定义为text类型和keyword类型。
其中,keyword类型的字段叫作子字段,这样ES在建立索引时会将姓名字段建立两份索引,即text类型的索引和keyword类型的索引。

索引的定义如下:

PUT /user-0001
{
  "mappings": {
    "properties": {
      "user_name": {
        "type": "text",
        "fields": {
          "user_name_keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

添加数据

POST /_bulk
{"index":{"_index":"user-0001","_id":"001"}}
{"user_name":"Mic John"}
{"index":{"_index":"user-0001","_id":"002"}}
{"user_name":"Tim John"}
{"index":{"_index":"user-0001","_id":"003"}}
{"user_name":"Bobe John"}

查询数据

GET /user-0001/_search
{
  "query": {
    "match": {
      "user_name": "John"
    }
  }
  , "sort": [
    {
      "user_name.user_name_keyword": {
        "order": "desc"
      }
    }
  ]
}

数据类型动态映射

S支持根据写入的数据自动创建mapping中对应的字段类型(生产不建议使用)
在这里插入图片描述
提前定义好数据类型并将索引创建语句纳入SVN或Git管理范围是良好的编程习惯,同时还能增强项目代码的连贯性和可读性。

索引

增删查

# 创建索引user
PUT /user
{
  // 指定别名user_alias ,可以多个
  "aliases": {
    "user_alias": {
      // 过滤哪些文档 设置别名
      "filter": {},
      // 指定读写路由值,用来限定操作的分片,
      // 指定了routing 就无需指定index_routing 和 search_routing ,否则会无效
      "routing": "1",
      // 指定写操作路由值,用来限定操作的分片
      "index_routing": "1",
      // 指定读操作路由值,用来限定操作的分片
      "search_routing": "1,2"
    }
  }, 
  "settings": {
    "number_of_shards": 4,  // 主分片数
    "number_of_replicas": 2 // 副分片数
  }, 
  //映射
  "mappings": {
    // 指定属性和类型
    "properties": {
      // name 属性为 text类型
      "name":{
        "type": "text"
      },
      // age 属性为 integer类型
      "age":{
        "type": "integer"
      }
    }
  }
}

# 查看索引
GET /user
GET /user/_settings
GET /user/_mappings
GET /user/_alias

# 删除
DELETE /user

修改索引

官方文档 :https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#index-modules-settings

索引的修改 分为 静态部分动态部分

静态部分 即 索引建立后 不能修改
动态部分 即 索引建立后 可以修改

动态可修改部分

# 关闭索引,一般用于冷备份数据归档,索引关闭后无法读写数据
POST /user/_close

# 打开索引
POST /user/_open

# 修改映射(不能修改已有的字段类型,因为会破坏倒排索引)
PUT /user/_mappings
{
  //新增id字段
  "properties": {
    "id": {
      "type": "long"
    }
  }
}
# 修改设置(不能修改主分片数,因为会导致找到文档的存储位置)
PUT /user/_settings
{
  //修改副分片数
  "settings": {
    "number_of_replicas": 3 // 副分片数
  }
}

静态不可修改部分

  1. 主分片数不能修改

ES 的路由规则是 根据 主分片数取余来确定 需要操作的分片,一旦修改了主分片数,会导致找不到对应的文档位置。所以索引一旦创建,主分片数就不能修改
路由规则 在之前文章中有介绍: Elasticsearch入门基础和集群部署

  1. 已经确定了的字段类型不能修改

ES 采用倒排索引来增加 搜索的性能,倒排索引是根据分词器 ,字段类型等规则创建的。所以索引创建后,也不能直接修改分词器,字段类型。

但是 随着业务系统的发展,不可避免的需要修改索引 静态内容

例如:
随着数据的增加,需要增加索引的主分片数
数据类型建立不合适,程序运行一段时间后,发现需要修改数据类型

ES 对于 静态索引内容的修改 提供了 索引别名索引重建 两个功能来实现静态内容的 修改

索引重建

索引重建 可以将 旧索引的数据 拷贝到新索引
具体步骤如下:

  1. 创建新索引: 创建一个新的索引(修改索引静态内容)
  2. 重新索引(Reindex): 使用Elasticsearch的Reindex API将旧索引中的数据重新索引到新创建的索引中。这一步会消耗一些计算和存储资源,因此在生产环境中可能需要谨慎操作。
  3. 切换到新索引: 在完成重新索引后,将应用中的写操作切换到新的索引,确保新数据写入新的索引。
  4. 删除旧索引(可选): 如果确认新索引运行正常且数据完整,可以选择删除旧索引释放资源。
# 创建新索引(示例中主分片数量为6PUT new_test_index
{
  "settings": {
    "index": {
      "number_of_shards": "6",
      "number_of_replicas": "2"
    }
  }
}

# 使用Reindex API将旧索引数据重新索引到新索引
POST /_reindex
{
  "source": {
    "index": "test_index"
  },
  "dest": {
    "index": "new_test_index"
  }
}

# 切换应用中的写操作到新索引

# 可选:删除旧索引
DELETE /old_index

索引别名

类似 关系数据库中的视图

es可以对一个或者多个索引指定别名,通过别名可以查到一个或者多个索引的内容

# 给已经创建的索引指定别名
POST /_aliases
{
  "actions": [
    //增加索引别名
    {
      "add": {
        "index": "user",
        "alias": "alias1"
      }
    },
    //删除索引别名
    {
      "remove": {
        "index": "user",
        "alias": "alias1"
      }
    }
  ]
}

特别注意:当一个别名指向多个索引时,针对别名的写入操作会失败(除非指定哪个索引用来写入)

针对这一特性,可以平滑的完成新旧索引的替换
在这里插入图片描述

如图:
初始状态索引别名hotel 指向 索引hotel_1
如果随着业务的增长,需要修改 hotel_1的分片数或字段类型,这时必须要重建一个索引
这里重建一个新的索引hotel_2
然后将hotel_1 的数据 reindex 到 hotel_2,同时将新数据都写入到hotel_2
最后指定 hotel 别名指向 hotel_2, 删除hotel_1 的别名
这样 就完成了搜索端 平滑的过度