Restful-API使用
接口文档可参考上一边ES文章8.x版本的文档,下文都以8.x语法为例
https://www.elastic.co/guide/en/elasticsearch/reference/8.18/search-with-elasticsearch.html
RestfulAPI使用
操作索引
新增
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| #创建一个索引 PUT music_V1 { "mappings": { "properties": { "singers": { "type": "text", "fields": { "keyword": { "type": "keyword" }, "pinyin": { "type": "text", "analyzer": "pinyin" }, "zh": { "type": "text", "analyzer": "smartcn" } } }, "song_name": { "type": "text", "fields": { "keyword": { "type": "keyword" }, "pinyin": { "type": "text", "analyzer": "pinyin" }, "zh": { "type": "text", "analyzer": "smartcn" } } }, "lyrics": { "type": "text", "fields": { "keyword": { "type": "keyword" }, "pinyin": { "type": "text", "analyzer": "pinyin" }, "zh": { "type": "text", "analyzer": "smartcn" } } }, "md5": { "type": "keyword" }, "music_score_data": { "type": "text" }, "origin": { "type": "keyword" }, "chord_item_id": { "type": "keyword" }, "song_item_id": { "type": "keyword" } } } }
POST /_aliases { "actions" : [ { "add" : { "index" : "music_V1", "alias" : "music" } } ] }
|
注意song_name原本是text类型,但是创建索引时通过fields字段定义了多个类型数据,比如keyword和不同分词效果的text,在全文检索中便于用这些field参与搜索
查询
删除
属性类型
字符串类型:
text: 一把被用于全文检索。将当前Field进行分词。
keyword: 当前Field不会被分词。
数值类型 :
long: 取值范围为-9223372036854774808~922337203685477480(-2的63次方到2的63次方-1),占用8个字节
integer: 取值范围为-2147483648~2147483647(-2的31次方到2的31次方-1),占用4个字节
short: 取值范围为-32768~32767(-2的15次方到2的15次方-1),占用2个字节
byte: 取值范围为-128~127(-2的7次方到2的7次方-1),占用1个字节
double:1.797693e+308~4.9000000e-324(e+308表示是乘以10的308次方,e-324表示乘以10的负324次方)占用8个字节
float:3.402823e+38~1.401298e-45(e+38表示是乘以10的38次方,e-45表示乘以10的负45次方),占用4个字节
half_float: 精 度 比float小 一 半 。
scaled_float: 根据一个long 和scaled来表达一个浮点型,long-345,scaled-100->3.45
时间类型 :
date类型,针对时间类型指定具体的格式
布尔类型 :
boolean类型,表达true和false
二进制类型:
binary类型暂时支持Base64 encode string
范围类型 :
long_range: 赋值时,无需指定具体的内容,只需要存储一个范围即可,指定gt,It,gte,Ite
integer_range: 同 上
double_range: 同上
float_range: 同上
date_range: 同上
ip_range: 同上
经纬度类型:
geo_point: 用来存储经纬度的
ip类型:
ip: 可以存储IPV4或者IPV6
操作文档
新建文档
自动生成_id
1 2 3 4 5 6 7 8 9
| #添加文档,自动生成id POST /novel { "name": “盘龙”, "author": "我吃西红柿", "count":100000, "on-sale": "2000-01-01", "descr": “山重水复疑无路,柳暗花明又一村” }
|
手动指定_id
1 2 3 4 5 6 7 8 9
| #添加文档,手动指定id PUT /book/novel/1 { "name":" 红楼梦", "author": “ 曹雪芹”, "count":10000000 , "on-sale":" 1985-01-01", "descr": “一个是阆苑仙葩,一个是美玉无瑕” }
|
修改文档
覆盖式修改
1 2 3 4 5 6 7 8
| #添加文档,手动指定id PUT /book/novel/1 { "name" :" 红楼梦", "author": "曹雪芹", "count" :4353453, "on-sale": "1985-01-01", "descr": “一个是阆苑仙葩,一个是美玉无瑕” }
|
部分更新
1 2 3 4 5 6
| POST test/_update/1 { "doc": { "name": "new_name" } }
|
删除文档
根据id删除
1 2
| #根据id删除文档 DELETE /book/novel/_id
|
查询文档Search API
term&terms查询
term查询:term 的查询是代表完全匹配,搜索之前不会对你搜索的关键字进行分词,对你的关键字去文档分词库中去匹配内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| POST /sms-logs-index/_search { "query": { "term": { "user.id": "kimchy" } } }
POST /sms-logs-index/_search { "query": { "terms": { "user.id": ["kimchy","kimchy2"] } } }
|
id&ids
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #id 查询 GET /sms-logs-index/1 #ids 查询 POST /sms-logs-index/_search { "query": { "ids": { "values": [ "1", "2", "3" ] } } }
|
范围查询
range查询
范围查询,只针对数值类型,对某一个Field进行大于或者小于的范围指定
支持的运算符:
- gt (>) - 大于
- gte (>=) - 大于等于
- lt (<) - 小于
- lte (<=) - 小于等于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| POST /sms-logs-index/_search { "query": { "range": { "fee": { "gt": 5, "lte": 10 } } } }
常用表达式: now - 当前时间 +1h - 加1小时 -1d - 减1天 /d - 舍入到天 # 日期范围查询 GET /orders/_search { "query": { "range": { "order_date": { "gte": "now-30d/d", "lte": "now/d" } } } }
{ "range": { "timestamp": { "gte": "now-1h", "lte": "now" } } }
|
date_range 时间
1 2 3 4 5 6 7 8 9 10 11 12 13
| GET /logs/_search { "query": { "date_range": { "field": "timestamp", "format": "yyyy-MM-dd", "ranges": [ { "from": "2023-01-01", "to": "2023-01-31" }, { "from": "2023-03-01" } ] } } }
|
numeric_range 数值
1 2 3 4 5 6 7 8 9 10 11 12
| GET /products/_search { "query": { "numeric_range": { "field": "price", "ranges": [ { "from": 100, "to": 200 }, { "from": 500 } ] } } }
|
ip_range IP地址
1 2 3 4 5 6 7 8 9 10 11
| GET /servers/_search { "query": { "ip_range": { "field": "ip_address", "ranges": [ { "from": "192.168.1.1", "to": "192.168.1.254" } ] } } }
|
geo_distance 地理距离
查找距离某点特定范围内的文档
1 2 3 4 5 6 7 8 9 10 11 12
| GET /locations/_search { "query": { "geo_distance": { "distance": "10km", "location": { "lat": 40.715, "lon": -73.988 } } } }
|
模糊匹配
prefix 前缀匹配
前缀查询,可以通过一个关键字去指定一个Field的前缀,从而查询到指定的文档。
一般用于keyword类型字段
区分大小写
1 2 3 4 5 6 7 8
| POST /sms-logs-index/_search { "query": { "prefix": { "corpName": "途虎" } } }
|
wildscard 通配查询
通配查询,和MySQL 中的like是一个套路,可以在查询时,在字符串中指定通配符*和占位符?
1 2 3 4 5 6 7 8 9 10 11 12
| # wildcard 查询 POST /sms-logs-index/_search { "query": { "wildcard": { "corpName": { "value": "中国*" # 可以使用*和?指定通配符和占位符 } } } }
|
fuzzy 近似匹配
模糊查询,我们输入字符的大概,ES就可以去根据输入的内容大概去匹配一下结果
核心参数说明
fuzziness(模糊度):
可设置为0, 1, 2或"AUTO"
AUTO
会根据词项长度自动选择(推荐):
1-2个字符:必须精确匹配
3-5个字符:允许1个编辑距离
5个字符:允许2个编辑距离
max_expansions(最大扩展数):
prefix_length(前缀长度):
- 开头多少个字符必须精确匹配(默认0)
- 可显著提高性能
transpositions(相邻字母互换):
- 是否将相邻字母互换视为1次编辑(默认true)
- 如”ab”→”ba”计为1次编辑
1 2 3 4 5 6 7 8 9 10 11 12
| # fuzzy查询 POST /sms-logs-index/_search { "query": { "fuzzy": { "corpName": { "value": "盒马先生", "prefix_length": 2 # 指定前面2个字符不允许出现错误 } } } }
|
正则匹配regexp
正则查询,通过你编写的正则表达式去匹配内容
prefix/fuzzy/wildcard/regexp查询属于低效查询,在以下场景应避免使用:
1 2 3 4 5 6 7 8 9
| # regexp 查询 POST /sms-logs-index/_search { "query": { "regexp": { "mobile": "180[0-9]{8}" # 匹配以180开头+8位数字的手机号 } } }
|
分词查询
match是全文查询的一种,他会使用分词器分析输入字符串成单独的词条,再去倒排索引中匹配计算这些词条,返回命中的索引文档。
过程如下:
1、分词 2、匹配计算 3、结果倒序返回
查询的是日期或者是数值的话,他会将你基于的字符串查询内容转换为日期或者数值对待。
查询的内容是一个不能被分词的内容 (keyword),match查询不会对你指定的查询关键字进行分词。
查询的内容时 一个可以被分词的内容 (text),match会将你指定的查询内容根据一定的方式去分词,去分词库中匹配指定的内容
match 查询,实际底层就是多个term查询,将多个term查询的结果给你封装到了一起
分词器
1 2 3 4 5
| GET /_analyze { "text": "中华人民共和国", "analyzer": "ik_smart" }
|
match_all
查询全部内容,不指定任何查询条件
1 2 3 4 5 6 7 8
| POST /sms-logs-index/ _search { "query":{ "match_all":{ } } }
|
match
制定一个field进行查询字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| POST /sms-logs-index/ _search { "query": { "match": { "smsContent": "收货安装" } } }
POST /sms-logs-index/ _search { "query": { "match": { "smsContent": { "query":"中国健康", "operator":"and" } } } }
POST /sms-logs-index/ _search { "query": { "match": { "smsContent": { "query":"中国健康", "operator":"or" } } } }
|
multi_match
multi_match 针对多个field进行检索,多个field对应一个text
1 2 3 4 5 6 7 8 9 10 11 12
| POST /sms-logs-index/ _search { "query": { "multi_match": { "query": " 北京", "fields": [ "province", "smsContent" ] } } }
|
match_parse
用于精确匹配短语的查询方式,强调查询词项的顺序和位置完全匹配
特别适合中文短句搜索场景。使用时需根据实际需求调整slop参数,并在性能和精度之间取得平衡。
slop:词项间最大间隔距离
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| GET /products/_search { "query": { "match_phrase": { "name": "iPhone 13 Pro" } } }
GET /address/_search { "query": { "match_phrase": { "full_address": { "query": "北京 海淀区", "slop": 3 } } } } # 能搜索出的文本:
# 不能搜索出的文本: 海淀区 北京市 //词序颠倒 上海海淀区 //缺少关键字 北京 北京作为中国首都,其西北郊区的海淀区 // 间隔超过3个词
"北京市最核心的教育区域海淀区" 分词位置: 0:北京市 1:最 2:核心 3:的 4:教育 5:区域 6:海淀区 从"北京市"(位置0)到"海淀区"(位置6)需要跳过5个位置(6-0-1=5跳),超过slop=3,因此不会匹配
|
分页查询
普通分页from + size
深分页scoll
ES对from+size 是有限制的,from + size 不能超过 index.max_result_window(默认 10000) 原理:
● from+size 在ES查询数据的方式:
o 第一步现将用户指定的关键进行分词。
o 第二步将词汇去分词库中进行检索,得到多个文档的id。
o 第三步去各个分片中去拉取指定的数据。耗时较长。
o 第四步将数据根据score进行排序。耗时较长。
o 第五步根据from 的值,将查询到的数据舍弃一部分。
o 第六步返回结果。
● scroll+size在ES查询数据的方式:
o 第一步现将用户指定的关键进行分词。
o 第二步将词汇去分词库中进行检索,得到多个文档的id。
o 第三步将文档的id存放在一个ES的上下文中。
o 第四步根据你指定的size的个数去ES中检索指定个数的数据,拿完数据的文档id, 会从上下文中移除。 。
o 第五步如果需要下一页数据,直接去ES的上下文中,找后续内容。
o 第六步循环第四步和第五步
Scroll查询方式,不适合做实时的查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| # 执行scroll查询,返回第一页数据,并且将文档id信息存放在ES上下文中,指定生存时间1m POST /sms-logs-index/_search?scroll=1m { "query": { "match_all": {} }, "sort": [ { "fee": { "order": "desc" } } ], "size": 10 #每批获取10条 }
# 根据scroll查询下一页数据,查询结果和排序规则与第一步的一致 POST /_search/scroll { "scroll_id": "<根据上一步得到的scorll_id,每次查询会返回新的scroll_id>", "scroll": "<scorll信息的生存时间>" }
# 删除scroll在ES上下文中的数据 DELETE /_search/scroll/scroll_id
|
-RestfulAPI%E4%BD%BF%E7%94%A8/image-20250615163806243.png)
| Scroll分页 |
from+size分页 |
|
| 数据获取方式 |
首次查询建立数据快照,后续基于游标读取 |
每次请求重新计算所有匹配文档 |
| 排序开销 |
仅首次查询需要全局排序 |
每次请求都需全局排序(深度分页时O(n)复杂度) |
| 内存消耗 |
维护固定大小的scroll上下文 |
每次请求需在协调节点缓存全量结果 |
| 适用场景 |
大数据量导出/非实时遍历(如日志分析) |
实时用户查询(前1000条) |
高亮查询
高亮查询就是你用户输入的关键字,以一定的特殊样式展示给用户,让用户知道为什么这个结果被检索出来。
高亮展示的数据,本身就是文档中的一个Field,单独将Field以highlight的形式返回给你。
ES提供了一个highlight属性,和query同级别的。
● fragment_size:指定高亮数据展示多少个字符回来。
● pre_tags:指定前缀标签,举个栗子
● post_tags:指定后缀标签,举个栗子
● fields:指定哪几个Field以高亮形式返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| # highlight查询 POST /sms-logs-index/_search { "query": { "match": { "smsContent": "盒马" } }, "highlight": { "fields": { "smsContent": { #这里也能写pre_tags这些 } }, "pre_tags": "<font color='red'>", "post_tags": "</font>", "fragment_size": 10 } } # 预期结果 "hits": { "hits": [ { "_source": { "smsContent": "盒马鲜生配送员已出发" }, "highlight": { "smsContent": [ "<font color='red'>盒马</font>鲜生配" ] } } ] }
|
目前高亮web交互的方案主要由3种:
1、后端直接把ES查询时定好高亮的规则,把hightlight内容直接以带HTML的格式给到前端给前端显示
2、后端解析查询highlight字段,按照定好的标志位解析高亮的起始和结束位置,把连续高亮字段也做合并处理,返回合适的结构体给前端,这样前端就能做一些酷炫的高亮展示
1 2 3 4 5
| { "content:":"盒马鲜生配送员已出发", "start":0, "end":1 }
|
3、第三种就是后端不做高亮的任何处理,前端全文检索高亮文本内容(极其不推荐,效果不好且性能差)
全文检索案例
需求
某个全文检索需要支持搜索歌曲,检索匹配歌曲的歌名、歌手名、歌词,并且权重以此递减
分析
1、新建索引,歌名、歌手名、歌词(都是多fields属性字段,即便type是keyword,子field也包含text方便分词搜索)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| { "mappings": { "properties": { "song_name": { "type": "text", "fields": { "keyword": { "type": "keyword" }, "pinyin": { "type": "text", "analyzer": "pinyin" }, "zh": { "type": "text", "analyzer": "smartcn" } } }, ...... } } }
|
2、歌名、歌手名、歌词权重递减,匹配查询时对不同的field查询时需要指定“boost”属性,通过该值的大小来设置权重
3、为了相关度高的排前面,采用should查询 + boost,组合使用,多次命中则分数相加,得分靠前
4、为了提升数据召回率 采用 [term查询keyword] + [march + march_parse 分词搜索text,中文分词field,拼音分词field] 同时匹配某一属性的方式。
最终查询举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| { "size": 1500, "from": 0, "query": { "bool": { "should": [ { "term": { "song_name.keyword": { "value": "搜索词", "boost": "权重分数" } } }, { "match_phrase": { "song_name.zh": { "query": "搜索词", "boost": "权重分数" } } }, { "match_phrase": { "song_name.pinyin": { "query": "搜索词", "boost": "权重分数" } } }, { "match": { "song_name.zh": { "query": "搜索词", "boost": "权重分数" } } }, { "match": { "song_name.pinyin": { "query": "搜索词", "boost": "权重分数" } } }, { "match": { "song_name": { "query": "搜索词", "boost": "权重分数" } } }, ...... ], "minimum_should_match": 1 } } }
|