第三章 ES基础操作

1 索引操作

1.1 创建索引

使用ES构建搜索引擎的第一步是创建索引。在创建索引时,可以按照实际需求对索引进行主分片和副分片设置。ES创建索引的请求类型为PUT,其请求形式如下

put /index_name
{
   "settings":{
   },
   "mappings":{
   }
}

变量index_name就是创建的目标索引名称;可以在settings子句内部填写索引相关的设置项,如主分片个数和副分片个数等(主分片个数使用的是系统默认值(默认值为5),并且没有使用副分片个数(默认值为0));可以在mappings子句内部填写数据组织结构,即数据映射。

  1. 指定主分片和副分片个数
PUT /hotel
{
  "settings": {
    "number_of_shards": 2,//主分片
    "number_of_replicas": 1//副分片
  },
  "mappings": {
    "properties": {
      
    }
  }
}
  1. 删除索引
DELETE /hotel
  1. 关闭索引

在有些场景下,某个索引暂时不使用,但是后期可能又会使用,这里的使用是指数据写入和数据搜索。这个索引在某一时间段内属于冷数据或者归档数据,这时可以使用索引的关闭功能。索引关闭时,只能通过ES的API或者监控工具看到索引的元数据信息,但是此时该索引不能写入和搜索数据,待该索引被打开后,才能写入和搜索数据。

POST /hotel/_close
  1. 打开索引

索引关闭后,需要开启读写服务时可以将其设置为打开状态。

POST /hotel/_open

1.2 索引别名

别名是指给一个或者多个索引定义另外一个名称,使索引别名和索引之间可以建立某种逻辑关系。 可以用别名表示别名和索引之间的包含关系。

  1. 例如,我们建立了1月、2月、3月的用户入住酒店的日志索引,假设当前日期是4月1日,需要搜索过去的3个月的日志索引,如果分别去3个索引中进行搜索,这种编码方案比较低效。此时可以创建一个别名first_quarter,设置前面的3个索引的别名为first_quarter,然后在first_quarter中进行搜索即可
  • 首先依次建立january_log、february_log和march_log3个索引
PUT /january_log
{
  "mappings":{
    "properties":{
      "uid":{
        "type":"keyword"
      },
      "hotel_id":{
        "type":"keyword"
      },
      "check_in_date":{
        "type":"keyword"
      }
    }
  }
}
  • 在3个索引中写入同一用户在不同月份的入住记录
POST /january_log/_doc/001
{
  "uid":"001",
  "hotel_id":"1000",
  "check_in_date":"2023-01-15"
}
  • 建立别名last_three_month,设置上面3个索引的别名为first_quarter
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "january_log",
        "alias": "first_quarter"
      }
    },{
      "add": {
        "index": "february_log",
        "alias": "first_quarter"
      }
    },{
      "add": {
        "index": "march_log",
        "alias": "first_quarter"
      }
    }
  ]
}
  • 请求在索引first_quarter中搜索uid为001的用户的入住记录,搜索的DSL
GET /first_quarter/_search
{
  "query": {
    "term": {
      "uid": {
        "value": "001"
      }
    }
  }
}

由上面的结果可知,当请求搜索first_quarter的数据时,ES将请求转发到了january_log、february_log和march_log3个索引中; 当一个别名只指向一个索引时,写入数据的请求可以指向这个别名,如果这个别名指向多个索引(就像上面的例子),则写入数据的请求是不可以指向这个别名的

  1. 别名中的is_write_index属性

在默认情况下,ES不能确定向first_quarter写入数据时的转发对象。这种情况需要在别名设置时,将目标索引的is_write_index属性值设置为true来指定该索引可用于执行数据写入操作。

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "january_log",
        "alias": "first_quarter",
        "is_write_index":true
      }
    }
  ]
}

将索引别名first_quarter的数据写入转发对象设置为索引january_log之后,再向first_quarter发起写入数据的请求时,ES会将该请求转发到索引january_log中

  1. 用别名表示索引之间的替代关系

一般在某个索引被创建后,有些参数是不能更改的(如主分片的个数),但随着业务发展,索引中的数据增多,需要更改索引参数进行优化。我们需要平滑地解决该问题,既要更改索引的设置,又不能改变索引名称,这时就可以使用索引别名

  • 首先建立索引hotel_1,设置其主分片个数为5,副分片个数为2
PUT /hotol_1
{
  "settings":{
      "number_of_shards":5,
      "number_of_replicas":2
   },
  "mappings":{
    "properties":{
      "title":{
        "type":"text"
      },
      "city":{
        "type":"keyword"
      },
      "price":{
        "type":"double"
      }
    }
  }
}
  • 在数据写入端向hotel_1写入搜索数据,请求的DSL
POST /hotol_1/_doc/001
{
  "title":"儒家酒店",
  "city":"沈阳",
  "price":380.00
}
  • 建立别名hotel,请求的DSL
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "hotol_1",
        "alias": "hotol"
      }
    }
  ]
}
  • 假设过一段时间后酒店索引的分片需要扩展。通过变更索引的方式可以完成扩展。建立索引hotel_2,并设置主分片个数为10,设置副分片个数为2,请求的DSL
PUT /hotol_2
{
  "settings":{
      "number_of_shards":10,
      "number_of_replicas":2
   },
  "mappings":{
    "properties":{
      "title":{
        "type":"text"
      },
      "city":{
        "type":"keyword"
      },
      "price":{
        "type":"double"
      }
    }
  }
}
  • 此时hotel_2中的数据已经准备完毕,现在变更别名设置,删除hotel_1的索引别名,设置索引hotel_2的别名为hotel2
POST /_aliases
{
  "actions": [
    {
      "remove": {
        "index": "hotol_1",
        "alias": "hotol"
      }
    },
    {
      "add": {
        "index": "hotol_2",
        "alias": "hotol"
      }
    }
  ]
}

2 映射操作

2.1 查看映射

在使用数据之前,需要构建数据的组织结构。这种组织结构在关系型数据库中叫作表结构,在ES中叫作映射。作为无模式搜索引擎,ES可以在数据写入时猜测数据类型,从而自动创建映射。但有时ES创建的映射中的数据类型和目标类型可能不一致。当需要严格控制数据类型时,还是需要用户手动创建映射。

GET /hotol_1/_mapping
# 返回结果
{
  "hotol_1" : {
    "mappings" : {
      "properties" : {
        "city" : {
          "type" : "keyword"
        },
        "price" : {
          "type" : "double"
        },
        "title" : {
          "type" : "text"
        }
      }
    }
  }
}

2.2 扩展映射

映射中的字段类型是不可以修改的,但是字段可以扩展。最常见的扩展方式是增加字段和为object(对象)类型的数据新增属性。下面的DSL示例为扩展hotel索引,并增加tag字段

POST /hotol_1/_mapping
{
  "properties":{
    "tag":{
      "type":"keyword"
    }
  }
}

2.3 基本的数据类型

  1. keyword类型

keyword类型是不进行切分的字符串类型。这里的“不进行切分”指的是:在索引时,对keyword类型的数据不进行切分,直接构建倒排索引;在搜索时,对该类型的查询字符串不进行切分后的部分匹配。keyword类型数据一般用于对文档的过滤、排序和聚合。 在现实场景中,keyword经常用于描述姓名、产品类型、用户ID、URL和状态码等。keyword类型数据一般用于比较字符串是否相等,不对数据进行部分匹配,因此一般查询这种类型的数据时使用term查询。 注意: 对keyword类型使用match搜索进行匹配是不会命中文档,而且返回400错误

GET /hotol_1/_search
{
  "query": {
    "term": {
      "city": {
        "value": "阳"
      }
    }
  }
}
GET /hotol_1/_search
{
  "query": {
    "match": {
      "city": {
        "value": "阳"
      }
    }
  }
}
# 返回
{
  "error" : {
    "root_cause" : [
      {
        "type" : "parsing_exception",
        "reason" : "[match] query does not support [value]",
        "line" : 5,
        "col" : 18
      }
    ],
    "type" : "parsing_exception",
    "reason" : "[match] query does not support [value]",
    "line" : 5,
    "col" : 18
  },
  "status" : 400
}
  1. text类型

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

  • 模糊查询text字段
GET /hotol_1/_search
{
  "query": {
    "match": {
      "title": "酒"
    }
  }
}
  1. 数值类型 ES支持的数值类型有long、integer、short、byte、double、float、half_float、scaled_float和unsigned_long等。数值类型的数据也可用于对文档进行过滤、排序和聚合
  • 例如:价格范围查询
GET /hotol_1/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 350,
        "lte": 400
      }
    }
  }
}
  1. 布尔类型 布尔类型使用boolean定义,写入或者查询该类型的数据时,其值可以使用true和false,或者使用字符串形式的"true"和"false"。
  2. 日期类型 在ES中,日期类型的名称为date。ES中存储的日期是标准的UTC格式(世界协调时,Universal Time Coordinated的缩写,中国大陆、中国香港、中国澳门、中国台湾、蒙古国、新加坡、马来西亚、菲律宾、西澳大利亚州的时间与UTC的时差均为+8,也就是UTC+8)。

一般使用如下形式表示日期类型数据:·格式化的日期字符串。·毫秒级的长整型,表示从1970年1月1日0点到现在的毫秒数。·秒级别的整型,表示从1970年1月1日0点到现在的秒数。日期类型的默认格式为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和yyyy-MM-ddTHH:mm:ss.SSSZ等格式,epoch_millis的含义是从1970年1月1日0点到现在的毫秒数。 日期类型默认不支持yyyy-MM-dd HH:mm:ss格式,如果经常使用这种格式,可以在索引的mapping中设置日期字段的format属性为自定义格式。下面的示例将设置create_time字段的格式为yyyy-MM-dd HH:mm:ss

PUT /hotol_1/_mapping
{
  "properties":{
    "create_time":{
      "type":"date",
      "format":"yyyy-MM-dd HH:mm:ss"
    }
  }
}
  • 插入记录
POST /hotol_1/_doc/002
{
  "title":"喜来登酒店",
  "city":"沈阳",
  "price":686.5,
  "create_time":"2023-03-01 18:56:18"
}

2.4 复杂类型

  1. 数组类型

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

  • 扩展tag字段
PUT /hotol_1/_mapping
{
  "properties":{
    "tag":{
      "type":"keyword"
    }
  }
}
  • 插入数据
POST /hotol_1/_doc/003
{
  "title":"玫瑰大酒店",
  "city":"沈阳",
  "price":700,
  "create_time":"2023-03-01 17:56:18",
  "tag":["提供免费早餐","有Wifi"]
}
  • 对数组数据进行搜索
GET /hotol_1/_search
{
  "query": {
    "term": {
      "tag": {
        "value": "有Wifi"
      }
    }
  }
}
  1. 对象类型

对象类型也不用事先定义,在写入文档的时候ES会自动识别并转换为对象类型。

POST /hotol_1/_doc/004
{
  "title":"玫瑰大酒店",
  "city":"沈阳",
  "price":700,
  "create_time":"2023-03-01 17:56:18",
  "tag":["提供免费早餐","有Wifi"],
  "comment_info":{
    "fav_num": 18,
    "neg_num":20
  }
}
  • 查询索引结构
GET /hotol_1
# 返回
{
  "hotol_1" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "city" : {
          "type" : "keyword"
        },
        "comment_info" : { // 对插入的对象类型自动识别
          "properties" : {
            "fav_num" : {
              "type" : "long"
            },
            "neg_num" : {
              "type" : "long"
            }
          }
        },
        "create_time" : {
          "type" : "date",
          "format" : "yyyy-MM-dd HH:mm:ss"
        },
        "price" : {
          "type" : "double"
        },
        "tag" : {
          "type" : "keyword"
        },
        "title" : {
          "type" : "text"
        }
      }
    },
    "settings" : {
      "index" : {
        "routing" : {
          "allocation" : {
            "include" : {
              "_tier_preference" : "data_content"
            }
          }
        },
        "number_of_shards" : "5",
        "provided_name" : "hotol_1",
        "creation_date" : "1680327849676",
        "number_of_replicas" : "2",
        "uuid" : "5owTZuhWQJOI9_rl71E2eg",
        "version" : {
          "created" : "7160399"
        }
      }
    }
  }
}
  • 对对象类型属性进行查询
GET /hotol_1/_search
{
  "query": {
    "range": {
      "comment_info.neg_num": {
        "gte": 10
      }
    }
  }
}
  1. 地理类型

mapping中指定目标字段的数据类型为geo_point类型

  • 扩展属性location为地理类型
PUT /hotol_1/_mapping
{
  "properties":{
    "location":{
      "type":"geo_point"
    }
  }
}
  • 插入数据
POST /hotol_1/_doc/005
{
  "title":"商贸饭店",
  "city":"沈阳",
  "price":720,
  "create_time":"2023-03-02 17:56:18",
  "tag":["提供免费早餐","有Wifi"],
  "comment_info":{
    "fav_num": 18,
    "neg_num":20
  },
  "location":{
    "lat":41.48,
    "lon":123.58
  }
}

2.5 动态映射

当字段没有定义时,ES可以根据写入的数据自动定义该字段的类型,这种机制叫作动态映射

2.6 多字段

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

  • 创建索引
PUT /hotel_order
{
  "mappings": {
    "properties": {
      "order_id":{
        "type": "keyword"
      },
      "user_name":{
        "type": "text",
        "fields": { //多字段定义
          "user_name_key":{
            "type":"keyword"
          }
        }
      }
    }
  }
}
  • 批量插入多条记录
POST /_bulk
{"index":{"_index":"hotel_order","_id":"001"}}
{"order_id":"a001","user_name":"刘琦"}
{"index":{"_index":"hotel_order","_id":"002"}}
{"order_id":"a002", "user_name":"刘邦"}
{"index":{"_index":"hotel_order", "_id":"003"}}
{"order_id":"a003","user_name":"刘少奇"}
  • 对多字段进行查询并排序
GET /hotel_order/_search
{
  "query": {
    "match": {
      "user_name": "刘"
    }
  },
  "sort": [
    {
      "user_name.user_name_key": {
        "order": "asc"
      }
    }
  ]
}

3 文档操作

  1. 单条数据写入

在ES中写入文档请求的类型是POST,其请求形式如下

# 创建索引名为mydata,创建映射
PUT /mydata
{
  "mappings": {
    "properties": {
      "mname":{
        "type": "text"
      }
    }
  }
}
  • 使用post写入单体数据
POST /mydata/_doc/001
{
  "mname":"张三"
}
  • 不指定id,插入数据id由ES系统生成
POST /mydata/_doc
{
  "mname":"李四"
}
  1. 批量写入
  • 语法
POST /_bulk
{"index":{"_index","索引名"[,"_id":id值]}}
{单条插入数据}
{"index":{"_index","索引名"[,"_id":id值]}}
{单条插入数据}
  • 批量插入多条数据
POST /_bulk
{"index":{"_index":"mydata"}}
{"mname":"王五"}
{"index":{"_index":"mydata"}}
{"mname":"赵六"}
  1. 更新单条记录
  • 语法
POST /索引名/_update/id值
{
  需要更新的数据
}
  • 根据id值更新单体记录
POST /mydata/_update/-7-kQIcBX-mbhp5AZ5XW
{
  "doc": {
    "mname":"巨无霸"
  }
}
  • 除了普通的update功能,ES还提供了upsert。upsert即是update和insert的合体字,表示更新/插入数据。如果目标文档存在,则执行更新逻辑;否则执行插入逻辑。
POST /mydata/_update/-7-kQIcBX-mbhp5AZ5XW
{
  "doc": {
    "mname":"巨无霸"
  },
  "upsert": {
    "mname":"玩大锤"
  }
}

文档-7-kQIcBX-mbhp5AZ5XW存在,则执行更新逻辑,将doc内容更新到文档中;否则执行插入逻辑,将upsert的内容写入文档中

  1. 批量更新文档

注意,与批量写入文档不同的是,批量更新文档必须在元数据中填写需要更新的文档_id

POST /_bulk
{"index":{"_index":"mydata","_id":"-b-gQIcBX-mbhp5AzZW8"}}
{"mname":"张艺谋"}
{"index":{"_index":"mydata","_id":"8"}}
{"mname":"张一鸣"}
  1. 根据条件更新文档
POST /mydata/_update_by_query
{
  "query": {
    "match": {
      "mname": "王"
    }
  },
  "script": {
    "source": "ctx._source['mname']='西门'",
    "lang": "painless"
  }
}
  1. 根据id值删除单条记录
DELETE /mydata/_doc/-r-kQIcBX-mbhp5AZ5XW
  1. 批量删除文档
POST /_bulk
{"delete":{"_index":"mydata","_id":"-L-gQIcBX-mbhp5AzZW8"}}
{"delete":{"_index":"mydata","_id":"8"}}
  1. 根据条件删除文档
POST /mydata/_delete_by_query
{
  "query":{
    "match":{
      "mname":"张"
    }
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值