文章目录
- 1.简介
- 2.增加
- 2.删除
- 3.修改
- 3.1 更新 mapping
- 3.1.1 增加字段
- 3.1.2 删除字段
- 3.1.3 添加 multi-fields
- 3.2 重命名 index
- 3.1 更新 mapping
- 4.查询
- 5.小结
- 参考文献
1.简介
Elasticsearch 的索引(index)是文档(document)的集合,类似 MySQL 的表。文档是 ES 中存储的一条 JSON 格式的数据。
index 是一个逻辑空间的概念,index 中的文档会分散放在不同的 shard 中,而 shard 在 ES 中则是个物理空间的概念。
index 由以下几个部份组成:
- data:由 document metadata 所組成;
- mapping:用来定义文档结构,如字段名称 & 类型;
- setting:定义数据是如何存放,如 shard 和 replica 数量。
下面设计一个 index 用来描述我们要存储的用户信息,index 也用一个 JSON 串来表示。
代码语言:javascript复制index = `{
"mappings":{
"dynamic": "strict",
"properties":{
"id": { "type": "long" },
"username": { "type": "keyword" },
"nickname": { "type": "text" },
"phone": { "type": "keyword" },
"age": { "type": "long" },
"ancestral": { "type": "text" },
"identity": { "type": "text" },
"update_time": { "type": "long" },
"create_time": { "type": "long" }
}
},
"settings" : {
"index" : {
"number_of_shards" : "1",
"number_of_replicas" : "1"
}
}
}`
其中 dynamic 是 mapping 一个很重要的属性 ,来控制是否动态添加新字段,并接受以下参数:
参数 | 说明 |
---|---|
true | New fields are added to the mapping (default). |
runtime | New fields are added to the mapping as runtime fields. These fields are not indexed, and are loaded from _source at query time. |
false | New fields are ignored. These fields will not be indexed or searchable, but will still appear in the _source field of returned hits. These fields will not be added to the mapping, and new fields must be added explicitly. |
strict | If new fields are detected, an exception is thrown and the document is rejected. New fields must be explicitly added to the mapping. |
- dynamic 为 true 表示动态映射(dynamic mapping)。
true 为缺省值。添加的文档中如果有新增的字段,则 ES 会自动把新的字段添加到映射中。新增的字段可以被索引,也就是这个字段可以被搜索,mapping 同时也被更新。
- dynamic 为 false 表示静态(显式)映射(explicit mapping)。
当 ES 察觉到有新增字段时,会写入新字段,但不会索引新字段,即无法通过新字段进行查询。在有些情况下,静态映射依然不够,所以还需要更严谨的策略来进一步做限制。
- dynamic 为 strict 表示精确(严格)映射(strict mapping)。
字段需要严格匹配,新增字段写入将会报错。
一般静态映射用的较多。就像 HTML 的 img 标签一样,src 为自带的属性,你可以在需要的时候添加 id 或者 class 属性。当然,如果你非常了解你的数据,并且未来很长一段时间不会改变,strict 不失为一个好选择。
注意: 动态映射很方便,但是实际业务中,对于关键字段类型,通常预先定义好,这样可以避免 ES 自动生成不是你想要的字段类型。
2.增加
设计好了 index 及 mapping 后,我们开始编写代码进行创建。
代码语言:javascript复制// ESIndexExists 索引是否存在。
func ESIndexExists(ctx context.Context, index string) (bool, error) {
return GetESClient().IndexExists(index).Do(ctx)
}
// CrtESIndex 创建 ES 索引。
func CrtESIndex(ctx context.Context, index, desc string) error {
exist, err := ESIndexExists(ctx, index)
if err != nil {
return err
}
// 已经创建
if exist {
return nil
}
// 重复创建会报错
_, err = GetESClient().CreateIndex(index).BodyString(desc).Do(ctx)
return err
}
因为重复创建 index,ES 会报错,所以创建前先判断一下是否已经创建。
创建成功后,我们在 Kibana 上通过 Restful API 可以查看到刚刚创建的 index。
代码语言:javascript复制GET /es_index_userinfo
{
"es_index_userinfo" : {
"aliases" : { },
"mappings" : {
"dynamic" : "strict",
"properties" : {
"age" : {
"type" : "long"
},
"ancestral" : {
"type" : "text"
},
"create_time" : {
"type" : "long"
},
"id" : {
"type" : "long"
},
"nickname" : {
"type" : "text"
},
"phone" : {
"type" : "keyword"
},
"update_time" : {
"type" : "long"
},
"username" : {
"type" : "keyword"
}
}
},
"settings" : {
"index" : {
"creation_date" : "1627546052453",
"number_of_shards" : "1",
"number_of_replicas" : "1",
"uuid" : "_FIlQz-uQD2ynzITVPFpRw",
"version" : {
"created" : "7070099"
},
"provided_name" : "es_index_userinfo"
}
}
}
}
其中 number_of_shards 为主分片数,缺省为 1,只能在创建索引时指定,后期无法修改。number_of_replicas 是指每个分片有多少个副本,后期可以动态修改。
对应的 RESTful API 为:
代码语言:javascript复制PUT /es_index_userinfo
{
"mappings":{
"dynamic": "strict",
"properties":{
"id": { "type": "long" },
"username": { "type": "keyword" },
"nickname": { "type": "text" },
"phone": { "type": "keyword" },
"age": { "type": "long" },
"ancestral": { "type": "text" },
"update_time": { "type": "long" },
"create_time": { "type": "long" }
}
}
}
2.删除
通过 RESTful API 删除 index 很简单,格式如下:
代码语言:javascript复制DELETE /<index>
比如删除上面创建的 es_index_userinfo。
代码语言:javascript复制DELETE /es_index_userinfo
3.修改
对于一个已经存在的 index,我们可以修改 index 的相关设置。
3.1 更新 mapping
3.1.1 增加字段
比如修改文档结构,即修改 index 的 mapping,我们想其中再增加一个字段爱好 hobby。
代码语言:javascript复制PUT /es_index_userinfo/_mapping
{
"properties": {
"hobby": {
"type":"text"
}
}
}
3.1.2 删除字段
ES 中已经添加成功的字段是没法直接删除的,因为这会导致数据不可用。我们可以通过间接的方式来完成字段的删除。操作步骤如下: (1)创建一个新的 index,不包含要删除的字段; (2)删除原 index 中待删除字段的数据。只删除数据,不删除字段。因为如果不清空字段值的话,在下面的 reindex 会出现问题,如果新 index 的 mapping 的 dynamic 属性为 strict,会出错。
代码语言:javascript复制POST es_index_userinfo/_update_by_query
{
"script" : "ctx._source.remove('hobby')",
"query": {
"match_all": {}
}
}
(3)通过 redindex 将数据拷贝至新的 index;
代码语言:javascript复制POST /_reindex
{
"source": {
"index": "my-index"
},
"dest": {
"index": "my-new-index"
}
}
(4)删除旧的 index; (5)给新 index 添加别名,别名是旧 index。
代码语言:javascript复制POST <target>/_alias/<alias>
注意:完成第四步后,才能进行第五步的操作,期间会导致依赖该 index 的服务短暂不可用,所以尽量在业务低峰时间段操作。最安全的做法,是完成上面前三部操作后,将服务通过配置的方式把使用的 index 切换到新的 index。再进行后面的两步操作。
3.1.3 添加 multi-fields
一个字段可以存在多个类型,通过添加 multi-fields 来实现。
比如一个字段既要支持分词匹配,又要支持全词匹配,那么需要为其添加两个类型,分别是 text 和 keyword。
代码语言:javascript复制PUT /<index>/_mapping
{
"properties": {
"city": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
引用 keyword 的类型字段方式为city.raw
。
这里需要注意的是,新增的字段 raw 只对新加入的 document 生效,旧的数据无法通过新字段 raw 来检索。如果想要对旧数据生效,一般有两个做法: (1)更新旧字段。读取旧字段的值重新覆盖一次。
代码语言:javascript复制POST /<index>/_update_by_query?refresh=true
{
"query": {
"match_all":{}
},
"script": {
"source": "ctx._source['city'] = ctx._source.city"
}
}
(2)reindex。新建一个 index,将旧 index 数据拷贝到新的 index,再删除旧 index,再给新 index 添加一个别名为旧 index。
3.2 重命名 index
ES 中不能直接重命名 index,因为这会造成旧 index 不可用。我们可以给 index 添加别名,达到重命名的效果。
代码语言:javascript复制POST <target>/_alias/<alias>
4.查询
查询一个 index 也很简单,接口调用格式如下:
代码语言:javascript复制GET /<index>
5.小结
本文只是简单的介绍了 index 的概念,组成和相关操作,关于 index 和 mapping 的更多操作