Elasticsearch 常用的增删改查语句
- 新增
- 删除
- 修改
- 批量增删改
- 查询
- 批量查询
- scoll 滚动查询
- multi-index 和 multi-type 搜索模式
- 索引别名
- 校验查询语句是否合法
- ES其它一些常用语句
新增
PUT /index-name/type-name/id
{
"json数据"
}
PUT /consultant_company_list_test1/_doc/1
{
"polar_abbreviation": "企查查",
"consultant_company_position": "北京市",
"consultant_company_admin_id": 3,
"channel_operation_id": "",
"good_at_industry_ids": "",
"is_cancel": 0,
"recommend_count": 0,
"consultant_company_name": "测试公司",
"industry": [ "金融", "银行" ]
}
// 新增的时候是不需要先创建索引的,创建数据的时候,如果索引不存在也会同时把索引创建了。
// 但是这个时候默认创建出来的索引, mapping 是 ES 默认给设置的,有可能不合适,所以不建议用这种方式创建索引。
// 如果对同一个index/type/id 使用 PUT,哪怕字段不一致,则后面的数据会覆盖前面的数据。
// 创建成功后的返回结果说明:
{
"_index": "consultant_company_list_test1", // 创建的document所属的index
"_type": "_doc", // 创建的document所属的type
"_id": "1", // 创建的document的id
"_version": 1, // 创建的document的版本号
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
// 如果我们想要确保创建的 document 是新的,不会覆盖原来的 document
PUT /consultant_company_list_test1/_doc/1/_create
{
"polar_abbreviation": "企查查",
"consultant_company_position": "北京市",
"consultant_company_admin_id": 3,
"channel_operation_id": "",
"good_at_industry_ids": "",
"is_cancel": 0,
"recommend_count": 0,
"consultant_company_name": "测试公司",
"industry": [ "金融", "银行" ]
}
删除
// 删除索引中的单条数据
DELETE /index_one/product/1
// 删除单个索引
DELETE /index_one
// 删除多个索引
DELETE /index_one,index_two
DELETE /index_*
// 危险操作,删除全部索引
DELETE /_all
DELETE /*
删除全部索引操作非常危险,禁止措施
elasticsearch.yml 做如下配置:
action.destructive_requires_name: true
修改
POST /ecommerce/product/1/_update
{
"doc": {
"name": "jiaqiangban gaolujie yagao"
}
}
1. 修改的过程是 ES 获取某个文档中的所有字段,修改指定字段的内容,然后把老的 document 标记为deleted;
再重新创建一个新的document
2. update 内部会执行乐观锁的并发控制策略,每次更新时会更新版本号。
3. 如果遇到并发更新失败的时候,会有 retry(重试) 策略
(1)再次获取 document 数据和最新版本号;
(2)基于最新版本号再次去更新,如果成功那么就结束了;
如果失败,就重复 1 和 2 两个步骤,最多可以重复 retry_on_conflict 参数指定的次数
批量增删改
POST /_bulk
{"delete":{"_index":"test-index", "_type":"test-type", "_id":"1"}}
{"create":{"_index":"test-index", "_type":"test-type", "_id":"2"}}
{"test_field":"test2"}
{"index":{"_index":"test-index", "_type":"test-type", "_id":"1"}}
{"test_field":"test1"}
{"update":{"_index":"test-index", "_type":"test-type", "_id":"3", "_retry_on_conflict":"3"}}
{"doc":{"test_field":"bulk filed 3"}}
// 有哪些类型的操作可以执行呢?
(1)delete:删除一个文档,只要1个json串就可以了
(2)create:PUT /index/type/id/_create;只创建新文档
(3)index:普通的put操作,可以是创建文档,也可以是全量替换文档
(4)update:执行的partial update操作,即 post 更新
// bulk操作中,任意一个操作失败,是不会影响其他的操作的,但是在返回结果里,会告诉你异常日志
// bulk size最佳大小: 如果太大的话,性能反而会下降;
// 一般从1000~5000条数据开始,尝试逐渐增加。另外,如果看大小的话,最好是在5~15MB之间;
查询
查询指定索引下全部数据
GET /consultant_company_list_test1/_search
{
"took": 0, // 耗时
"timed_out": false, // 是否超时
"_shards": {
"total": 1, // 搜索了几个分片
"successful": 1, // 成功了几个
"skipped": 0, // 跳过了几个
"failed": 0 // 失败了几个
},
"hits": {
"total": {
"value": 1, // 查询到的数据的总量
"relation": "eq" // 平常索引数据量不大的情况下这个数值没问题,// 但是当超出 10000 条数据的时候,
// 这个 value 就固定为 10000。想要精确匹配数量,就需要通过 track-total-hits 参数来设置
},
"max_score": 1, // 查询到的数据中匹配度分数最大值
"hits": [ // 查询到的结果
{
"_index": "consultant_company_list_test1",
"_type": "_doc",
"_id": "1",
"_score": 1, // 匹配度分数
"_source": {
"polar_abbreviation": "企查查",
"consultant_company_position": "北京市",
"consultant_company_admin_id": 3,
"channel_operation_id": "",
"good_at_industry_ids": "",
"is_cancel": 0,
"recommend_count": 0,
"consultant_company_name": "测试公司"
}
}
]
}
}
"relation" : "gte"
"relation" : "eq"
gte 表示 total.value 是查询匹配总命中数的下限(大于等于)。eq 则表示 total.value 是准确计数。
查询指定索引下指定id的数据
GET /consultant_company_list_test1/_doc/1
{
"_index": "consultant_company_list_test1",
"_type": "_doc",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true, // 查找成功为true,不成功为false
"_source": { // 搜索到的数据
"polar_abbreviation": "企查查",
"consultant_company_position": "北京市",
"consultant_company_admin_id": 3,
"channel_operation_id": "",
"good_at_industry_ids": "",
"is_cancel": 0,
"recommend_count": 0,
"consultant_company_name": "测试公司",
"industry": [ "金融", "银行" ]
}
}
查询指定索引下全部数据
// 查询指定索引下全部数据
GET /consultant_company_list_test1/_doc/_search
{
"query": {
"match_all": {}
}
}
查询指定值(match)
// 查询指定值
GET /consultant_company_list_test1/_doc/_search
{
"query" :{
"match": {
"consultant_company_name": "测试公司"
}
}
}
在多个字段中查询指定值(multi_match)
GET /consultant_company_list_test1/_doc/_search
{
"query": {
"multi_match": {
"query": "测试公司1",
"fields": ["consultant_company_name", "polar_abbreviation"]
}
}
}
match、multi_match是模糊匹配,匹配时会对所查找的关键词进行分词,然后按分词匹配查找。
term 查询
GET /consultant_company_list_test1/_doc/_search
{
"query": {
"term": {
"consultant_company_name": "测试公司"
}
}
}
terms 查询
GET /consultant_company_list_test1/_doc/_search
{
"query": {
"terms": {
"consultant_company_name": [
"测试公司1",
"测试公司2"
]
}
}
}
term、terms 会直接对关键词进行查找
范围查询
GET /consultant_company_list_test1/_doc/_search
{
"query": {
"range": {
"price": {
"gte": 10,
"lte": 30
}
}
}
}
查询并排序
默认情况下,是按照_score降序排序的
GET /consultant_company_list_test1/_doc/_search
{
"query" :{
"match": {
"consultant_company_name": "测试公司1"
}
},
"sort": {
"id": {
"order": "desc"
}
}
}
分页查询
GET /consultant_company_list_test1/_doc/_search
{
"query": {
"match_all": {}
},
"from": 0, // 从第几条数据开始
"size": 1 // 查几条数据
}
查询指定字段
GET /consultant_company_list_test1/_doc/_search
{
"query": {
"match_all": {}
},
"_source": ["id", "consultant_company_name"]
}
// 可以使用通配符*,显示要的字段、去除不需要的字段
GET student/_search
{
"query":{
"match_all": {}
},
"_source":{
"includes": "addr*",
"excludes": ["name","bir*"]
}
}
全文检索
GET /consultant_company_list_test1/_doc/_search
{
"query": {
"match": {
"producer": "测试公司 1"
}
}
}
phrase search(短语搜索)
跟全文检索相相反,全文检索会将输入的搜索串拆解开来,去倒排索引里面去一一匹配,只要能匹配上任意一个拆解后的单词,就可以作为结果返回。phrase search,要求输入的搜索串,必须在指定的字段文本中,完全包含一模一样的,才可以算匹配,才能作为结果返回。
GET /consultant_company_list_test1/_doc/_search
{
"query": {
"match_phrase": {
"producer": "测试公司 1"
}
}
}
highlight search(高亮搜索结果)
GET /consultant_company_list_test1/_doc/_search
{
"query": {
"match": {
"consultant_company_name": "测试公司"
}
},
"highlight": {
"fields": {
"consultant_company_name":{} // 指定高亮的字段
}
}
}
通配符查询
* 代表0个或多个字符
? 代表任意一个字符
GET /consultant_company_list_test1/_doc/_search
{
"query":{
"wildcard":{
"consultant_company_name":"测试公司?"
}
}
}
正则表达式查询
GET /consultant_company_list_test1/_doc/_search
{
"query":{
"regex":{
"title":{
"consultant_company_name":"测试公司[12]"
}
}
}
}
聚合搜索(很多种聚合方式,举几个例子)
term 分组统计
// 统计各个品牌的商品数量(只要分组就好了,自带统计数量)
GET /goods/_search
{
"aggs": {
"brand_aggs" : {
"terms" : {
"field" : "brand"
}
}
}
}
// 统计各个品牌下的平均手机价格
GET /goods/_search
{
"size" : 0,
"aggs" : {
"brand_aggs" : {
"terms" : {
"field" : "brand"
},
"aggs":{
"avg_price": {
"avg": { // 还有其它聚合方式 sum,max,min
"field": "price"
}
}
}
}
}
}
// 统计各个品牌下的平均手机价格,并且按照平均价格降序排序
GET /goods/_search
{
"size" : 0,
"aggs" : {
"brand_aggs" : {
"terms" : {
"field" : "brand",
"order": {
"avg_price": "desc"
}
},
"aggs":{
"avg_price": {
"avg": { // 还有其它聚合方式 sum,max,min
"field": "price"
}
}
}
}
}
}
histogram 分组统计
// 按照500为一个阶梯统计不同价位手机数量
GET /goods/_search
{
"size":0,
"aggs":{
"price_histogram":{
"histogram": {
"field": "price",
"interval": 500 // 间隔
}
}
}
}
range 分组统计
// 统计价格在4000-6000手机的数量
GET /goods/_search
{
"size": 0,
"aggs": {
"price_range": {
"range": {
"field": "price",
"ranges": [
{
"from": 4000,
"to": 6000
}
]
}
}
}
}
// 统计价格在4000-6000手机的数量,然后在每组内再按照tag进行分组,最后再计算每组的平均价格
GET /goods/_search
{
"size": 0,
"aggs": {
"price_range": {
"range": {
"field": "price",
"ranges": [
{
"from": 4000,
"to": 6000
}
]
},
"aggs": {
"group_by_tag": {
"terms": {
"field": "tags"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
}
}
日期分组统计
GET /cars/_search
{
"size":0,
"aggs" : {
"date" : {
"date_histogram" : {
// 需要聚合分组的字段名称, 类型需要为date, 格式没有要求
"field": "sold",
// 按什么时间段聚合, 这里是5分钟;分钟 (1m)、小时 (1h)、天 (1d)、星期 (1w)、月 (1M)、季度 (1q)、年 (1y)
"interval": "5m",
// 设置时区, 这样就相当于东八区的时间
"time_zone":"+08:00",
// 返回值格式化,HH大写,不然不能区分上午、下午
"format": "yyyy-MM-dd HH",
// 为空的话则填充0
"min_doc_count": 0,
// 需要填充0的范围
"extended_bounds": {
"min": 1533556800000,
"max": 1533806520000
}
}
}
}
}
组合条件查询
bool:
must => 必须匹配 // 相当于MySQL的 and
must_not => 必须不匹配 // 相当于MySQL的 not
should => 应该匹配,既可以有,也可以没有 // 相当于MySQL的 or
filter => 过滤
// title必须包含elasticsearch,content可以包含elasticsearch也可以不包含,author_id必须不为111
GET /website/article/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "elasticsearch"
}
}
],
"should": [
{
"match": {
"content": "elasticsearch"
}
}
],
"must_not": [
{
"match": {
"author_id": 111
}
}
]
}
}
}
SELECT product
FROM products
WHERE (price = 20 OR productID = "XHDK-A-1293-#fJ3")
AND (price != 30)
GET /my_store/products/_search
{
"query" : {
"bool" : {
"should" : [
{ "term" : {"price" : 20}},
{ "term" : {"productID" : "XHDK-A-1293-#fJ3"}}
],
"must_not" : {
"term" : {"price" : 30}
}
}
}
}
filter 与 query
1. 默认情况下,Elasticsearch 根据 相关性评分(relevance score) 对匹配到的搜索结果进行排序,
这个分数用来衡量每个文档与查询的匹配程度。
2. 相关性分数是一个正浮点数,在 search API 返回结果的 _score 元字段中。 _score越高,文档的相关性就越强。
3. 在 query 上下文中, 除了决定文档是否匹配之外,查询子句还计算文档的相关性得分(在元字段 _score 中显示)。
4. 在 filter 上下文中, 结果是简单的"是"或"否"--不会计算分数。
5. Elasticearch 会自动缓存经常使用的 filter,以提高性能。因此,filter速度要快于query
6. 一般来说,如果你是在进行搜索,需要将最匹配搜索条件的数据先返回,那么用query;
如果你只是要根据一些条件筛选出一部分数据,不关注其排序,那么用filter。
GET /_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Search" }},
{ "match": { "content": "Elasticsearch" }}
],
"filter": [
{ "term": { "status": "published" }},
{ "range": { "publish_date": { "gte": "2015-01-01" }}}
]
}
}
}
对上面的例子分析下:
query 参数表示整个语句是处于 query context 中
bool 和 match 语句被用在 query context 中,也就是说它们会计算每个文档的匹配度(_score)
filter 参数则表示这个子查询处于 filter context 中
filter 语句中的 term 和 range 语句用在 filter context 中,它们只起到过滤的作用,并不会计算文档的得分。
批量查询
// 查询不同index下的数据
GET /_mget
{
"docs": [
{
"_index": "test-index",
"_type": "test-type",
"_id": 1
},
{
"_index": "ecommerce",
"_type": "product",
"_id": 2
}
]
}
// 查询的document是一个index下的不同type
GET /test_index/_mget
{
"docs" : [
{
"_type" : "test_type",
"_id" : 1
},
{
"_type" : "test_type",
"_id" : 2
}
]
}
// 查询的数据都在同一个index下的同一个type
GET /ecommerce/product/_mget
{
"ids": [1,2]
}
scoll 滚动查询
scoll滚动查询就是,一批一批的查,直到所有数据都查询完处理完。
例如:如果一次性要查出来比如10万条数据,那么性能会很差,此时使用scoll滚动搜索,可以先搜索一批数据,然后下次再搜索一批数据,以此类推,直到搜索出全部的数据来。
scoll搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的。
每次发送scroll请求,我们还需要指定一个scoll参数,指定一个时间窗口,每次搜索请求只要在这个时间窗口内能完成就可以了。
GET /test_index/test_type/_search?scroll=1m
{
"query": {
"match_all": {}
},
"sort": [ "_doc" ],
"size": 3
}
// 搜索结果会有一个scoll_id,下一次再发送scoll请求的时候,必须带上这个scoll_id,这样才能接着查询下一批数据。
{
"_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAACxeFjRvbnNUWVZaVGpHdklqOV9zcFd6MncAAAAAAAAsYBY0b25zVFlWWlRqR3ZJajlfc3BXejJ3AAAAAAAALF8WNG9uc1RZVlpUakd2SWo5X3NwV3oydwAAAAAAACxhFjRvbnNUWVZaVGpHdklqOV9zcFd6MncAAAAAAAAsYhY0b25zVFlWWlRqR3ZJajlfc3BXejJ3",
"took": 5,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 10,
"max_score": null,
"hits": [
{}
]
}
}
// 查询下一批数据
GET /_search/scroll
{
"scroll": "1m",
"scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAACxeFjRvbnNUWVZaVGpHdklqOV9zcFd6MncAAAAAAAAsYBY0b25zVFlWWlRqR3ZJajlfc3BXejJ3AAAAAAAALF8WNG9uc1RZVlpUakd2SWo5X3NwV3oydwAAAAAAACxhFjRvbnNUWVZaVGpHdklqOV9zcFd6MncAAAAAAAAsYhY0b25zVFlWWlRqR3ZJajlfc3BXejJ3"
}
scroll + bulk 可以实现快速重建索引
multi-index 和 multi-type 搜索模式
GET /_search:所有索引,所有type下的所有数据都搜索出来
GET /index1/_search:指定一个index,搜索其下所有type的数据
GET /index1,index2/_search:同时搜索两个index下的数据
GET /*1,*2/_search:按照通配符去匹配多个索引
GET /index1/type1/_search:搜索一个index下指定的type的数据
GET /index1/type1,type2/_search:可以搜索一个index下多个type的数据
GET /index1,index2/type1,type2/_search:搜索多个index下的多个type的数据
GET /_all/type1,type2/_search:_all,可以搜索所有index下的指定type的数据
索引别名
索引别名可以指向一个或多个索引,并且可以在任何需要索引名称的API中使用。
索引别名允许我们执行以下操作:
1)索引重建时,可以把正在运行的索引和新的索引进行无缝切换,无需停机更新;
2)对多个索引进行分组组合(例如,last_three_months的索引别名:是过去3个月索引 logstash_201903, logstash_201904, logstash_201905的组合);
单个设置索引别名的方式
PUT /index1/_alias/index
一次设置多个索引别名的方式
POST /_aliases
{
"actions": [
{
"add": {
"index": "index1",
"alias": "index",
"is_write_index": true // 控制是否可以通过索引别名写入数据
}
},
{
"add": {
"index": "index2",
"alias": "index"
}
}
]
}
删除别名
POST /_aliases
{
"actions": [
{
"remove": {
"index": "index2",
"alias": "index"
}
}
]
}
查询指定索引的别名
GET /index1/_alias
查询所有索引的别名
GET /_alias
假设没有别名,如何处理多索引检索?
GET /index1,index2/_search
有了别名后,多索引检索操作就变得简单了
1. 先为不同的索引设置相同的别名
POST /_aliases
{
"actions": [
{
"add": {
"index": "index1",
"alias": "index"
}
},
{
"add": {
"index": "index2",
"alias": "index"
}
}
]
}
2. 使用别名检索
GET /index/_search
校验查询语句是否合法
// 语法: GET /index/type/_validate/query?explain
// 一般用在那种特别复杂庞大的搜索下,比如你一下子写了上百行的搜索,这个时候可以先用validate api去验证一下,语法是否合法
GET /test_index/test_type/_validate/query?explain
{
"query": {
"math": {
"test_field": "test"
}
}
}
ES其它一些常用语句
查看索引
查看所有索引
GET /_cat/indices
查看某个索引的状态
GET /_cat/indices/index-name
查看某个索引的 mapping
GET /index-name/_mapping
查看、修改索引设置
GET index-name/_settings
PUT index-name/_settings
{
"number_of_replicas": 1
}
开启、关闭索引
关闭一个索引(关闭索引后不能在读写 索引中的文档)
POST index-name/_close
开启一个索引(将一个关闭的索引从新开启)
POST index-name/_open