1 索引操作
1.1 创建索引
使用ES构建搜索引擎的第一步是创建索引。在创建索引时,可以按照实际需求对索引进行主分片和副分片设置。ES创建索引的请求类型为PUT,其请求形式如下
put /index_name
{
"settings":{
},
"mappings":{
}
}
变量index_name就是创建的目标索引名称;可以在settings子句内部填写索引相关的设置项,如主分片个数和副分片个数等(主分片个数使用的是系统默认值(默认值为5),并且没有使用副分片个数(默认值为0));可以在mappings子句内部填写数据组织结构,即数据映射。
- 指定主分片和副分片个数
PUT /hotel
{
"settings": {
"number_of_shards": 2,//主分片
"number_of_replicas": 1//副分片
},
"mappings": {
"properties": {
}
}
}
- 删除索引
DELETE /hotel
- 关闭索引
在有些场景下,某个索引暂时不使用,但是后期可能又会使用,这里的使用是指数据写入和数据搜索。这个索引在某一时间段内属于冷数据或者归档数据,这时可以使用索引的关闭功能。索引关闭时,只能通过ES的API或者监控工具看到索引的元数据信息,但是此时该索引不能写入和搜索数据,待该索引被打开后,才能写入和搜索数据。
POST /hotel/_close
- 打开索引
索引关闭后,需要开启读写服务时可以将其设置为打开状态。
POST /hotel/_open
1.2 索引别名
别名是指给一个或者多个索引定义另外一个名称,使索引别名和索引之间可以建立某种逻辑关系。
可以用别名表示别名和索引之间的包含关系。
- 例如,我们建立了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个索引中;
当一个别名只指向一个索引时,写入数据的请求可以指向这个别名,如果这个别名指向多个索引(就像上面的例子),则写入数据的请求是不可以指向这个别名的
- 别名中的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中
- 用别名表示索引之间的替代关系
一般在某个索引被创建后,有些参数是不能更改的(如主分片的个数),但随着业务发展,索引中的数据增多,需要更改索引参数进行优化。我们需要平滑地解决该问题,既要更改索引的设置,又不能改变索引名称,这时就可以使用索引别名
- 首先建立索引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 基本的数据类型
- 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
}
- text类型
text类型是可进行切分的字符串类型。这里的“可切分”指的是:在索引时,可按照相应的切词算法对文本内容进行切分,然后构建倒排索引;在搜索时,对该类型的查询字符串按照用户的切词算法进行切分,然后对切分后的部分匹配打分;
- 模糊查询text字段
GET /hotol_1/_search
{
"query": {
"match": {
"title": "酒"
}
}
}
- 数值类型
ES支持的数值类型有long、integer、short、byte、double、float、half_float、scaled_float和unsigned_long等。数值类型的数据也可用于对文档进行过滤、排序和聚合
- 例如:价格范围查询
GET /hotol_1/_search
{
"query": {
"range": {
"price": {
"gte": 350,
"lte": 400
}
}
}
}
- 布尔类型
布尔类型使用boolean定义,写入或者查询该类型的数据时,其值可以使用true和false,或者使用字符串形式的"true"和"false"。 - 日期类型
在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 复杂类型
- 数组类型
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"
}
}
}
}
- 对象类型
对象类型也不用事先定义,在写入文档的时候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
}
}
}
}
- 地理类型
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 文档操作
- 单条数据写入
在ES中写入文档请求的类型是POST,其请求形式如下
# 创建索引名为mydata,创建映射
PUT /mydata
{
"mappings": {
"properties": {
"mname":{
"type": "text"
}
}
}
}
- 使用post写入单体数据
POST /mydata/_doc/001
{
"mname":"张三"
}
- 不指定id,插入数据id由ES系统生成
POST /mydata/_doc
{
"mname":"李四"
}
- 批量写入
- 语法
POST /_bulk
{"index":{"_index","索引名"[,"_id":id值]}}
{单条插入数据}
{"index":{"_index","索引名"[,"_id":id值]}}
{单条插入数据}
- 批量插入多条数据
POST /_bulk
{"index":{"_index":"mydata"}}
{"mname":"王五"}
{"index":{"_index":"mydata"}}
{"mname":"赵六"}
- 更新单条记录
- 语法
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的内容写入文档中
- 批量更新文档
注意,与批量写入文档不同的是,批量更新文档必须在元数据中填写需要更新的文档_id
POST /_bulk
{"index":{"_index":"mydata","_id":"-b-gQIcBX-mbhp5AzZW8"}}
{"mname":"张艺谋"}
{"index":{"_index":"mydata","_id":"8"}}
{"mname":"张一鸣"}
- 根据条件更新文档
POST /mydata/_update_by_query
{
"query": {
"match": {
"mname": "王"
}
},
"script": {
"source": "ctx._source['mname']='西门'",
"lang": "painless"
}
}
- 根据id值删除单条记录
DELETE /mydata/_doc/-r-kQIcBX-mbhp5AZ5XW
- 批量删除文档
POST /_bulk
{"delete":{"_index":"mydata","_id":"-L-gQIcBX-mbhp5AzZW8"}}
{"delete":{"_index":"mydata","_id":"8"}}
- 根据条件删除文档
POST /mydata/_delete_by_query
{
"query":{
"match":{
"mname":"张"
}
}
}
网友评论