这里写自定义目录标题
- 准备工作
- 新增API
- 修改文档
- 删除文档
- Bulk API
- 查询API
- SQL DSL
- Query DSL
- 复合查询
- 聚合函数
- suggest 自动补全
- 数据关系
- 索引重建 || 复制数据 || 拷贝数据>_reindex
- 索引别名 _aliases
- Snapshot 快照备份
准备工作
GET recipes/_search
DELETE recipes
PUT recipes
{
"mappings": {
"properties": {
"name":{//菜名
"type": "text",
"analyzer": "ik_smart"
},
"rating":{//菜品等级
"type":"float"
},
"types":{//菜品类型
"type": "text",
"fields": {
"keyword":{
"type":"keyword"
}
}
},
"price":{//价格
"type":"double"
},
"sales":{//销量
"type":"long"
},
"end_time":{//结束日期
"type": "date"
},
"remark":{//备注
"type": "nested",
"properties": {
"id":{//备注id
"type":"integer"
},
"context":{ //备注文本
"type":"text",
"analyzer": "ik_smart"
}
}
}
}
}
}
POST recipes/_bulk
{ "index": { "_index": "recipes"}}
{"name":"清蒸鱼头","rating":1,"types":"湘菜","price":89,"sales":3000,"end_time":"2020-01-01","remark":[{"id":1,"context":"吃的很好"},{"id":2,"context":"味道还不错"}]}
{ "index": { "_index": "recipes"}}
{"name":"剁椒鱼头","rating":2,"types":"湘菜","price":189,"sales":24,"end_time":"2020-02-01","remark":[{"id":1,"context":"吃的很好"},{"id":2,"context":"头不大"}]}
{ "index": { "_index": "recipes"}}
{"name":"红烧鲫鱼","rating":3,"types":"湘菜","price":189,"sales":563,"end_time":"2020-03-01"}
{ "index": { "_index": "recipes"}}
{"name":"鲫鱼汤(辣)","rating":3,"types":"湘菜","price":29,"sales":24,"end_time":"2020-04-01"}
{ "index": { "_index": "recipes"}}
{"name":"鲫鱼汤(微辣)","rating":4,"types":"湘菜","price":29,"sales":3632,"end_time":"2020-05-01"}
{ "index": { "_index": "recipes"}}
{"name":"鲫鱼汤(变态辣)","rating":5,"types":"湘菜","price":29,"sales":343,"end_time":"2020-06-01"}
{ "index": { "_index": "recipes"}}
{"name":"广式鲫鱼汤","rating":5,"types":"粤菜","price":47,"sales":2,"end_time":"2020-07-01"}
{ "index": { "_index": "recipes"}}
{"name":"鱼香肉丝","rating":2,"types":"川菜","price":48,"sales":464,"end_time":"2020-08-01"}
{ "index": { "_index": "recipes"}}
{"name":"奶油鲍鱼汤","rating":2,"types":"西菜","price":150,"sales":131,"end_time":"2020-09-01"}
{ "index": { "_index": "recipes"}}
{"name":"红烧肉","rating":2,"types":"东北菜","price":45,"sales":33,"end_time":"2020-01-03"}
{ "index": { "_index": "recipes"}}
{"name":"回锅肉","rating":3,"types":"东北菜","price":45,"sales":55,"end_time":"2020-01-21"}
{ "index": { "_index": "recipes"}}
{"name":"川辣鱼头","rating":1,"types":"川菜","price":89,"sales":3000,"end_time":"2020-01-12"}
{ "index": { "_index": "recipes"}}
{"name":"西菜鲍鱼汤","rating":1,"types":"粤菜","price":99,"sales":3000,"end_time":"2020-01-24"}
@Value("${spring.elasticsearch.host}")
private String host;
@Value("${spring.elasticsearch.port}")
private String port;
@Bean
public RestHighLevelClient createRestHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost(host, Integer.valueOf(port), "http")));
return client;
}
新增API
POST 索引
/_doc/文档id
POST recipes/_doc/id1
{"name":"八宝鱼","rating":1,"types":"粤菜","price":168,"sales":3050,"end_time":"2020-01-24"}
javaAPI
String index="product";//索引
Product pro = Product.builder().id("1").name("小明").price(12).build();
IndexRequest<Product> product = new IndexRequest.Builder<Product>().id(pro.getId())
.document(pro).index(index).refresh(Refresh.True).build();
IndexResponse response = client.index(product);
Result result = response.result();
System.out.println(result.jsonValue());
Product pro2 = Product.builder().id("2").name("小红").price(13).build();
client.index(idxR->idxR.index(index).id(pro2.getId()).document(pro2).refresh(Refresh.True));
修改文档
POST 索引/_update/文档id
POST /<index>/_update/<_id>
POST test/_update/1
{
"doc": {
"name": "new_name"
},
"doc_as_upsert": true
}
String index="product";//索引
Product pro = Product.builder().id("1").name("小明s").price(1233333).build();
UpdateRequest productBuilder = new UpdateRequest.Builder().index(index)
.id(pro.getId()).doc(pro).refresh(Refresh.True).build();
UpdateResponse update = client.update(u->u.doc(pro).id(pro.getId()).index(index).refresh(Refresh.True), Product.class);
System.out.println(update.result().jsonValue());
{
"script" : {
"source": "ctx._source.字段名称 += params.count",
"lang": "painless",
"params" : {
"count" : 4
}
}
}
对list的处理
"source": "ctx._source.字段.add(params.tag)",
//删除list
"source": """
if(ctx._source.字段.contains(params.tag)){
ctx._source.字段.remove(ctx._source.字段.indexOf(params.tag))
}
""",
添加删除字段
"script" : "ctx._source.new_field = 'value_of_new_field'"
"script" : "ctx._source.remove('new_field')"
如果文档不存在,则将upsert元素的内容作为新文档插入。如果文档存在,则执行脚本:
"scripted_upsert": true
:如果文档id不存在的话,会根据当前搜索的id
创建这个文档
POST 索引/_update/文档id?refresh
{
//"scripted_upsert": true,
"script": {
"source": "ctx._source.字段A= params.count",
"lang": "painless",
"params": {
"count": 4
}
},
"upsert": {
"字段A": 1
}
}
_update_by_query
`POST /索引/_update_by_query`
{
"script": {//通过脚本修改
"source": "ctx._source.字段A= params.count",
"lang": "painless",
"params": {
"count": 4
}
},
"query":{
查询体
}
}
String index="product";//索引
Query.Builder builder = new Query.Builder();
builder.term(new TermQuery.Builder().field("name").value("小明").build());
String source="ctx._source.name=params.name";
InlineScript.Builder inline = new InlineScript.Builder().
lang(ScriptLanguage.Painless.jsonValue())
.source(source).params("name", JsonData.of("天猫精灵"));
UpdateByQueryRequest request=new UpdateByQueryRequest.Builder()
.index(index).query(builder.build())
.script(s->s.inline(inline.build())).build();
UpdateByQueryResponse updateByQueryResponse= client.updateByQuery(request);
System.out.println(updateByQueryResponse);
updateByQueryResponse = client.updateByQuery(uq -> uq.index(index)
.query(q -> q.term(t->t.field("name").value("小爱同学")))
.refresh(true)
.script(s -> s.inline(
i -> i.lang(ScriptLanguage.Painless.jsonValue())
.source("ctx._source.name=params.name")
.params("name", JsonData.of("小爱同学"))
)));
System.out.println(updateByQueryResponse);
删除文档
DELETE /索引
/_doc/文档id
查询删除
String index="product";//索引
Product pro = Product.builder().id("1").name("小红").price(12).build();
DeleteRequest request = new DeleteRequest.Builder().id(pro.getId()).index(index)
.refresh(Refresh.True).build();
DeleteResponse response = client.delete(request);
Result result = response.result();
System.out.println(result.jsonValue());
response = client.delete(del -> del.id(pro.getId()).index(index).refresh(Refresh.True));
System.out.println(result.jsonValue());
POST /索引/_delete_by_query
{
"query": {
"match": {
"name": "elasric"
}
}
}
String index = "product";//索引
Query.Builder builder = new Query.Builder();
//可使用if判断参数
builder.matchAll(new MatchAllQuery.Builder().build());
builder.matchAll(m->m);
builder.term(t->t.field("name").value("111"));
DeleteByQueryRequest request = new DeleteByQueryRequest.Builder()
.query(builder.build()).index(index).build();
DeleteByQueryResponse response = client.deleteByQuery(request);
System.out.println(response.deleted());
//lombda 方式
DeleteByQueryResponse deleteByQueryResponse = client.deleteByQuery(delQuery -> delQuery.index(index).query(q->q.bool(b->b.must(m->m.matchAll(m1->m1)))));
System.out.println(deleteByQueryResponse.deleted());
Bulk API
POST /<索引可以没有需要设置_index
>/_bulk
POST /索引/_bulk
{ "index" : { "_index" : "索引", "_id" : "1" } }
{ "字段名称" : "val" }
POST /索引/_bulk
{ "delete" : { "_index" : "索引", "_id" : "2" } }
POST /索引/_bulk
{ "create" : { "_index" : "索引", "_id" : "3" } }
{ "字段" : "val" }
POST /索引/_bulk
{ "update" : {"_id" : "1", "_index" : "索引"} }
{ "doc" : {"字段" : "val"} }
void test1() throws Exception {
String index="product";//索引
List<Product> list=new ArrayList<>();
list.add(Product.builder().id("1").name("小明1").price(123).build());
list.add(Product.builder().id("2").name("小红1").price(456).build());
list.add(Product.builder().id("3").name("小绿1").price(789).build());
list.add(Product.builder().id("4").name("小蓝1").price(246).build());
List<BulkOperation> bkList=new ArrayList<>();
//批量添加
for (Product p : list) {
bkList.add(new BulkOperation.Builder().create(c->c.document(p).id(p.getId()).index(index)).build());
}
BulkRequest request=new BulkRequest.Builder().operations(bkList).build();
BulkResponse response=client.bulk(request);
bulkItemSout(response.items());
//批量修改
bkList=new ArrayList<>();
for (Product p : list) {
bkList.add(new BulkOperation.Builder().update(uo -> uo.index(index).id(p.getId()).action(ua -> ua.doc(p))).build());
}
request=new BulkRequest.Builder().operations(bkList).build();
response=client.bulk(request);
bulkItemSout(response.items());
//批量删除
bkList=new ArrayList<>();
for (Product p : list) {
bkList.add(new BulkOperation.Builder().delete(d->d.index(index).id(p.getId())).build());
}
request=new BulkRequest.Builder().operations(bkList).build();
response=client.bulk(request);
bulkItemSout(response.items());
}
public void bulkItemSout(List<BulkResponseItem> items){
items.forEach(item->{
System.out.println(item.toString());
System.out.println(item.operationType().jsonValue());
});
}
查询API
格式
GET 索引表/_search
{
}
分页-size-from-search_after
注意:搜索请求耗用的堆内存和时间与 from + size 大小成正比。分页越深耗用越大,为了不因分页导致OOM或严重影响性能,ES中规定from + size 不能大于索引setting参数 index.max_result_window 的值,默认值为 10,000。
可归纳为亮点:
- from size,深度分页或者size特别大的情况,会出deep
pagination问题;且es的自保机制max_result_window也会阻预设的查询。 2、scroll虽然能够解决from - size带来的问题,但是由于它代表的是某个时刻的snapshot,不适合做实时查询;且由于scroll后接超时时间,频繁地发起scroll请求,也会出现一系列问题。
此时,search_after恰巧能够解决scroll的非实时取值问题。
折叠字段-collapse
根据types(菜系)做折叠(去重),
GET recipes/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "鱼"
}
}
]
}
}
,"collapse": {
"field": "types.keyword", //折叠的字段
"inner_hits": { //折叠参数
"name": "top_rated", //折叠的名字(任意)
"ignore_unmapped": true, //默认false,数据没有折叠字段的会报错,true避免类似的报错
//from和size控制返回折叠列表
"from":0,
"size":2,
"sort": [
{
"rating": "desc"
}
]
}
},
"from": 0,
"size": 3
}
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("recipes");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(
QueryBuilders.matchQuery("name","鱼")
);
CollapseBuilder collapseBuilder = new CollapseBuilder("types.keyword");
InnerHitBuilder collapseHitBuilder = new InnerHitBuilder("collapse_inner_hit");
collapseHitBuilder.setSize(2);
collapseBuilder.setInnerHits(collapseHitBuilder);
sourceBuilder.collapse(collapseBuilder);
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(result);
SearchRequest request=new SearchRequest();
request.indices("recipes");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(
new BoolQueryBuilder().must( QueryBuilders.matchQuery("name","鱼"))
);
CollapseBuilder collapseBuilders=new CollapseBuilder("types.keyword");
InnerHitBuilder innerHitBuilder=new InnerHitBuilder("top_rated");
innerHitBuilder.setSize(3);
collapseBuilders.setInnerHits(innerHitBuilder);
searchSourceBuilder.collapse(collapseBuilder);
request.source(searchSourceBuilder);
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
System.out.println(search);
search.getHits();
_source(取出字段)
"_source":false/["name"]/["obj.*"]
"_source": {
"includes": [ "obj.*" ],//包含
"excludes": [ "*.field" ]//不包含
}
stored_fields
version & explain
version 来指定返回文档的版本字段
explain 返回文档的评分解释
GET recipes/_search
{
"query":{
"match_all": {}
},
"explain": true,
"version":true
}
GET recipes/_search
{
"query":{
"match_all": {}
},
"explain": false,
"version":false
}
Script Field
用脚本来对命中的每个文档的字段进行运算后返回
params[‘_source’]取得是_source 内容中的字段
GET recipes/_search
{
"query":{
"match_all": {}
},
"script_fields": {
"price": {
"script": {
"lang": "painless",
"source": """
if(doc['rating'].value==3){
return doc['price'].value + 5 ;
}else{
return doc['price'].value;
}
"""
}
},
"name": {
"script": {
"lang": "painless",
"source": """
if(doc['rating'].value==3){
'<推荐>'+params['_source'].name
}else{
params['_source'].name
}
"""
}
}
}
}
min_score 限制最低评分得分
GET recipes/_search
{
"query": {
"match": {
"name": "鱼头"
}
},
"min_score": 1
}
sort 排序
order 值:asc、desc。如果不给定,默认是asc,_score默认是desc
可多字段排序
GET recipes/_search
{
"sort": [
{
"price": {
"order": "asc"
},
"sales": {
"order": "desc"
}
},
"_score"
]
}
根据评分排序
GET recipes/_search
{
"sort": [
"_score"
]
}
多值字段排序
对于值是数组或多值的字段,也可进行排序,通过mode参数指定按多值的:
高亮 highlight
示例
GET recipes/_search
{
"query": {
"match": {
"name": "川"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}
多字段高亮
包含川字高亮两个字段需要添加 “require_field_match”: “false”,
GET recipes/_search
{
"query": {
"match": {
"name": "川"
}
},
"highlight": {
"require_field_match": "false",
"fields": {
"name": {},
"types": {}
}
}
}
添加高亮标签
GET recipes/_search
{
"query": {
"match": {
"name": "川"
}
},
"highlight": {
"require_field_match": "false",
"fields": {
"name": {
"pre_tags": ["<strong>"],
"post_tags": ["</strong>"]
},
"types": {}
}
}
}
Profile 调试优化
GET recipes/_search
{
"profile": true,
"query": {
SQL DSL
DELETE myindex
PUT myindex
{
"mappings": {
"properties": {
"name":{
"type": "text",
"fields": {
"keyword":{
"type":"keyword"
}
}
},
"age":{
"type":"long"
},
"birthday_date":{
"type":"date",
"format": "yyyy-MM-dd"
},
"score":{
"type":"long"
}
}
}
}
PUT myindex/_bulk?refresh
{"index":{}}
{"name": "小明", "age": "18", "birthday_date": "2011-06-02", "score": 561}
{"index":{}}
{"name": "小红", "age": "40", "birthday_date": "1989-05-26", "score": 482}
{"index":{}}
{"name": "小鹿", "age": "80", "birthday_date": "1965-06-01", "score": 604}
响应格式
csv
GET /_sql?format=csv
{
"query": """
SELECT * FROM "myindex" ORDER BY age DESC
"""
}
age,birthday_date,name,score
80,1965-06-01T00:00:00.000Z,小鹿,604
40,1989-05-26T00:00:00.000Z,小红,482
18,2011-06-02T00:00:00.000Z,小明,561
?format=json
{
"columns" : [
{"name" : "age","type" : "text"},
{"name" : "birthday_date", "type" : "datetime"},
{"name" : "name", "type" : "text"},
{"name" : "score","type" : "long"}
],
"rows" : [
[
"80",
"1965-06-01T00:00:00.000Z",
"小鹿",
604
]
}
?format=yaml
---
columns:
- name: "age"
type: "text"
- name: "birthday_date"
type: "datetime"
- name: "name"
type: "text"
- name: "score"
type: "long"
rows:
- - "80"
- "1965-06-01T00:00:00.000Z"
- "小鹿"
- 604
- - "40"
- "1989-05-26T00:00:00.000Z"
- "小红"
- 482
- - "18"
- "2011-06-02T00:00:00.000Z"
- "小明"
- 561
?format=tsv
age birthday_date name score
80 1965-06-01T00:00:00.000Z 小鹿 604
40 1989-05-26T00:00:00.000Z 小红 482
18 2011-06-02T00:00:00.000Z 小明 561
?format=txt
age | birthday_date | name | score
---------------+------------------------+---------------+---------------
80 |1965-06-01T00:00:00.000Z|小鹿 |604
40 |1989-05-26T00:00:00.000Z|小红 |482
18 |2011-06-02T00:00:00.000Z|小明 |561
分页
GET /_sql?format=json
{
"query": """
SELECT * FROM "myindex" ORDER BY age DESC
""",
"fetch_size": 2
}
取出
POST /_sql?format=json
{
"cursor": "r8PpA0RGTACEkEtuwjAQhj0mfYiq4gi9QQUCpG66MGpShERazCM0m8hJHAgYO3IsHrfpIXq/1iFClbrpL1n+Z8bj0TcQIlgjDKjSt1Xr7BACgGaWc5FGpdLmjq3445afDkqn6DrKcl0aAAQ3l1zd8wUNhLE1GOFe25o6XR2nYb9A+D7OtVmn7BSlzHB4KI3OE3MOIlWYXEkmIpPveCSZVCV2JNvZtqsyUdre8An66b0gB3cYL0UR76jgg/BAA/f4EVA97XibZN4X8dwrZy6l6TLskkG45ZvR65vb6QYLbxzOfDXuFiMW+NvpxqPkon574slFMX5ZnSZtXy48XwTDdY8Fo/18mE5ny4QQ+s8sQp5Rs96jA1mFDI7hR2MXkP1Bv62YK9Kqdsb8fVrjgiOUXNlyC/0AAAD//wMA"
}
每次请求置换一次
cursor 直到rows为=[]
filter设置 Dsl
字符串要keyword ,filter 只能有1个 不理解
GET /_sql?format=json
{
"query": """
SELECT * FROM "myindex" ORDER BY age DESC
""",
"filter":{
"term": {
"name.keyword": "小鹿"
}
}
}
“columnar”:字段所有值按arr返回
GET /_sql?format=json
{
"query": """
SELECT * FROM "myindex" ORDER BY age DESC
""",
"columnar": true
}
{
"columns":[
{"name":"age","type":"long"},
{"name":"birthday_date","type":"datetime"},
{"name":"name","type":"text"},
{"name":"score","type":"long"}
],
"values":[
[80,80,40,18],
["1965-06-01T00:00:00.000Z","1965-06-01T00:00:00.000Z","1989-05-26T00:00:00.000Z","2011-06-02T00:00:00.000Z"],
["小鹿",null,"小红","小明"],
[604,604,482,561]
]
}
params 传参
GET /_sql?format=json
{
"query": """
SELECT YEAR(birthday_date) AS year,name AS name FROM "myindex" WHERE age>?
""",
"params":[20]
}
异步执行
默认情况下,SQL 搜索是同步的
GET /_sql?format=json
{
"wait_for_completion_timeout": "2s",
"query": """
SELECT YEAR(birthday_date) AS year,name AS name FROM "myindex" WHERE age>?
""",
"params":[20]
}
- id用于搜索 。
- is_partial值为 ,true表示搜索结果不完整。
- is_running值true,表示搜索仍在后台运行。
{
"id": "FnR0TDhyWUVmUmVtWXRWZER4MXZiNFEad2F5UDk2ZVdTVHV1S0xDUy00SklUdzozMTU=",
"is_partial": true,
"is_running": true,
"rows": [ ]
}
sql转换DSL
POST /_sql/translate
{
"query": """
SELECT YEAR(birthday_date) AS year,name AS name FROM "myindex" WHERE age>? ORDER BY age LIMIT 10
""",
"params":[20]
}
{
"size" : 10,
"query" : {
"range" : {
"age" : {
"gt" : 20,
"boost" : 1.0
}
}
},
"_source" : false,
"fields" : [
{
"field" : "birthday_date",
"format" : "strict_date_optional_time_nanos"
},
{
"field" : "name"
}
],
"sort" : [
{
"age" : {
"order" : "asc",
"missing" : "_last",
"unmapped_type" : "long"
}
}
]
}
函数
-
""AS 字段 ''意思是字符串
-
<=> null 对null的比较
-
<> 和 != 对没有存储的字段搜索无效(!=null)
-
IS NOT NULL/IS NULL 可以在where 中使用
-
‘123’::long 将123 转为long 类型
-
MATCH(name,‘鹿’) = like ‘%鹿%’
-
QUERY(‘name: 鹿~’)
-
MATCH(‘字段12,字段25’,‘字段1参 字段2参’,‘operator=and/OR’) ^是权重 通过 select SCORE() 获取
可以使用 analyzer, auto_generate_synonyms_phrase_query, lenient, fuzziness, fuzzy_transpositions, fuzzy_rewrite, minimum_should_match, operator, max_expansions, prefix_length -
COUNT(DISTINCT field_name)
-
FIRST(name) 第一个
-
LAST(name) 最后一个
-
HISTOGRAM(字段,数字间隔) AS aggs from X group by aggs 直方图分组
可以是日期字段 HISTOGRAM(birth_date, INTERVAL 1 YEAR/MONTH/DAY) -
SELECT CURRENT_DATE() AS result 获取当天 SELECT TODAY() AS result
-
CURRENT_TIMESTAMP() 获取当前时间
-
DATE_ADD(‘years’, 10, ‘2019-09-04T11:22:33.000Z’::datetime) 添加10年
year,month,day,week,hour,minute,second,millisecond -
DATE_DIFF(‘years’, ‘2019-09-04T11:22:33.000Z’::datetime, ‘2032-09-04T22:33:11.000Z’::datetime) 计算时间差值
-
DATE_PARSE(‘2022-01-01’,‘yyyy-MM-dd’) 日期转换
-
DATETIME_FORMAT(CAST(‘2020-04-07’ AS DATE), ‘yyyy-MM-dd’) 格式化
-
SELECT CBRT(10) 立方根
-
CEIL(125.01), CEILING(-125.99) 向上取政
-
FLOOR(125.01), FLOOR(-125.99) 向下取证
-
POWER(3, 2) 几次方
-
ROUND(-345.153, 1) 四舍五入
-
SIGN(-123), SIGN(0), SIGN(415); >0=1 ==0=0 <0=-1
-
SQRT(25) 根号
-
CHAR_LENGTH(‘Elastic’) str长度
-
CONCAT(‘Elasticsearch’, ’ SQL’) str拼接
-
INSERT( str, 开始位置,长度, 替换) INSERT('Elastic ', 8, 1, ‘search’)
-
LCASE(‘Elastic’) 小写 UCASE(‘Elastic’) 大写
-
LOCATE(‘a’, ‘Elasticsearch’) 出现的位置
-
LTRIM(’ Elastic’) 去空格 RTRIM('Elastic ‘) TRIM(’ Elastic ')
-
REPLACE(‘Elastic’,‘El’,‘Fant’) 替换
-
SPACE(3) 几个空格
-
STARTS_WITH(‘Elasticsearch’, ‘Elastic’) 判断开头匹配
-
SUBSTRING(‘Elasticsearch’, 0, 7) 字符串截取
-
类型转换 CAST(‘123’ AS INT) CAST(123 AS VARCHAR);YEAR(CAST(‘2018-05-19T11:23:45Z’ AS TIMESTAMP));
-
支持 case when then else end
-
COALESCE(null, ‘elastic’, ‘search’) 返回第一个不为空的
-
GREATEST(null, 1, 2,5,null) 返回最后一个不为空
-
IFNULL(null, ‘search’) 如果为空返回
-
ISNULL(null, ‘search’) 如果为空返回
-
IIF(1 < 2, ‘TRUE’, ‘FALSE’) 参1判断 参2为true执行 参3为false
-
NULLIF(‘elastic’, ‘last’) 参1==参2 =null 参1!=参2 =can1
-
NVL(‘elastic’, null) 参1=null,则为第二个表达式,否则为第一个表达式。
Query DSL
Match all query 查询所有
GET recipes/_search
{
"query": {
"match_all": {}
}
}
Query.Builder builder = new Query.Builder();
builder.matchAll(MatchAllQuery.of(m->m));
SearchRequest build = new SearchRequest.Builder()
.index(index)//索引
.query(builder.build()).build();
Full text querys 全文查询,用于对分词的字段进行搜索
match query
operator 逻辑符
or有数据
GET recipes/_search
{
"query": {
"match": {
"name":{
"query": "鱼身",
"operator": "or"
}
}
}
}
无数据
GET recipes/_search
{
"query": {
"match": {
"name":{
"query": "鱼身",
"operator": "and"
}
}
}
}
String index="product";//索引
Query.Builder builder = new Query.Builder();
builder.match(m->m.field("name").query("小 红")
.operator(Operator.And)//逻辑条件
.fuzziness("1")//模糊数
.boost(2F)//权重
.minimumShouldMatch("1"));//最小匹配词量
SearchRequest build = new SearchRequest.Builder()
.index(index)//索引
.query(builder.build()).build();
fuzziness 模糊查询
GET recipes/_search
{
"query": {
"match": {
"name":{
"query": "鱼鲫",
"operator": "and",
"fuzziness": 1
}
}
}
}
minimum_should_match 词匹配数
指定最少需满足两个词匹配
GET recipes/_search
{
"query": {
"match": {
"name":{
"query": "鲫鱼",
"minimum_should_match":3
}
}
}
}
match_phrase 短语匹配
指定前缀匹配选用的最大词项数量
GET /_search
{
"query": {
"match_phrase_prefix" : {
"message" : {
"query" : "quick brown f",
"max_expansions" : 10
}
}
}
}
multi_match 多字段查询
GET ftq/_search
{
"query": {
"multi_match" : {
"query": "lucene java",
"fields": [ "title", "content" ]
}
}
}
给字段的相关性评分加权重
“fields”: [ “title^5”, “content” ]
query_string 多字段通配符查询
GET recipes/_search
{
"query": {
"query_string":{
"query": "川",
"fields":["name","types"]
}
}
}
simple_query_string 查同 query_string
Term level querys
term & terms 精准查询
boot 权重
GET recipes/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"types.keyword": {
"value": "湘菜",
"boost": 2
}
}
},
{
"terms": {
"rating": [ 1, 2,3,4,5 ],
"boost": 1
}
}
]
}
}
}
String index="product";//索引
Query.Builder builder = new Query.Builder();
// builder.term(new TermQuery.Builder().field("name.keyword").value("小明1").build());
builder.term(t->t.field("name.keyword").value("小明1"));
List<FieldValue> fieldValues = Arrays.asList(new FieldValue.Builder().stringValue("小明1").build(), new FieldValue.Builder().stringValue("小红1").build());
// builder.terms(new TermsQuery.Builder().field("name.keyword").terms(new TermsQueryField.Builder().value(fieldValues).build()).build());
builder.terms(ts-> ts.boost(2F).field("name.keyword").terms(t->t.value(fieldValues)));
SearchRequest build = new SearchRequest.Builder()
.index(index)//索引
.query(builder.build()).build();
range query
"range":{
"rating":{
"from":1, //起始
"to":6, //结束
"include_lower":false,//是否包含
"include_upper":true,//是否包含
"format":"yyyy-MM",//日期核实的转换
"boost":1 //权重
}
}
String index="product";//索引
Query.Builder builder = new Query.Builder();
RangeQuery.Builder rang = new RangeQuery.Builder().field("price");
rang.gt(JsonData.of(1));rang.lt(JsonData.of(1000));
// rang.from();rang.to();
// rang.format("yyyy-MM-dd");
// builder.range(rang.build());
builder.range(r-> r.from("1").to("1000").field("price"));
SearchRequest build = new SearchRequest.Builder()
.index(index)//索引
.query(builder.build()).build();
exists query 不为空
查询为空
GET /_search
{
"query": {
"bool": {
"must_not": {
"exists": {
"field": "user"
}
}
}
}
}
String index="product";//索引
Query.Builder builder = new Query.Builder();
builder.exists(new ExistsQuery.Builder().field("name").build());
builder.exists(e->e.field("name"));
SearchRequest build = new SearchRequest.Builder()
.index(index)//索引
.query(builder.build()).build();
wildcard query 通配符查询
实测 types(text) 类型不可匹配<东北*> types.keyword(keyword) 可查 <东北*>
wildcard进行默认进行分词 所以“东北”可能就被分词成了2个字 但是却没有连起来的分词这就导致你查询不出来的原因。可以使用keyword
GET recipes/_search
{
"query": {
"wildcard": {
"types": {
"value": "东"
}
}
}
}
GET recipes/_search
{
"query": {
"bool": {
"must": [
{
"wildcard": {
"types.keyword": {
"value": "东北*"
}
}
}
]
}
}
}
String index="product";//索引
Query.Builder builder = new Query.Builder();
builder.wildcard(new WildcardQuery.Builder()
.field("name.keyword").value("*明*")
.caseInsensitive(true)//不区分大小写
.wildcard("*")//规定通配符
.build());
SearchRequest build = new SearchRequest.Builder()
.index(index)//索引
.query(builder.build()).build();
regexp query 正则查询
中文分词会有影响
- 句号 “.” 用于代表任何1个字符。
- .* 标识所有字符 相当于sql的 like ‘%Str%’
- 用括号括起来的一系列字符,例如 [a-z],是一个字符类。 字符类表示字符范围; 在此示例中,它充当任何字母的替代。
- 加号 “+” 用于表示重复的字符; 例如,“Mississippi” 中的 “pp”。
GET recipes/_search
{
"query": {
"bool": {
"must": [
{
"regexp": {
"name.keyword": ".*鲍.汤"
}
}
]
}
}
}
XX开头 | “红烧.*” |
XX结尾 | “.*汤” |
fuzzy query 模糊查询
fuzziness 值
0 、1或2个字符的字符串
1 、3、4或5个字符的字符串
2 、多于5个字符的字符串
GET recipes/_search
{
"query": {
"bool": {
"must": [
{
"fuzzy": {
"name.keyword": {
"value": "奶油鲍汤鱼",
"boost": 1,
"fuzziness": 1,
"prefix_length": 0
}
}
}
]
}
}
}
ids query 根据文档id查询
GET recipes/_search
{
"query": {
"bool": {
"must": [
{
"ids": {
"type": "_doc"
, "values": ["gONF4XYBUR-ts-YkVhIB"]
}
}
]
}
}
}
String index="product";//索引
Query.Builder builder = new Query.Builder();
builder.ids(new IdsQuery.Builder().values(Arrays.asList("1","2")).build());
SearchRequest build = new SearchRequest.Builder()
.index(index)//索引
.query(builder.build()).build();
复合查询
Bool query
String index="product";//索引
Query.Builder builder = new Query.Builder();
BoolQuery.Builder boolBuilder = new BoolQuery.Builder();
boolBuilder.must(f->f.term(t->t.field("name.keyword").value("小明1")));
// boolBuilder.must(new Query.Builder().term(new TermQuery.Builder().field("name.keyword").value("小明1").build()).build());
boolBuilder.mustNot(f->f.term(t->t.field("name.keyword").value("小红1")));
// boolBuilder.mustNot(new Query.Builder().term(new TermQuery.Builder().field("name.keyword").value("小红1").build()).build());
boolBuilder.should(f->f.term(t->t.field("name.keyword").value("小绿1")));
boolBuilder.filter(f->f.exists(new ExistsQuery.Builder().field("name").build()));
SearchRequest build = new SearchRequest.Builder()
.index(index)//索引
.query(Query.of(q -> q.bool(boolBuilder.build()))).build();
boosting 增强查询
positive
查询的条件
negative
过滤的条件
negative_boost
过滤权重分值 _sourse * negative_boost(取值0-1) 查询匹配的分值 * 过滤的分值
GET recipes/_search
{
"query": {
"bool": {
"must": [
{
"match": { "name": "鲫鱼"}
}
]
}
}
}
GET recipes/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"name": "鲫鱼"
}
},
"negative": {
"match": {
"name": "微辣"
}
},
"negative_boost": 0.1
}
}
}
constant_score 指定评分
GET recipes/_search
{
"query": {
"bool": {
"should": [
{
"constant_score": {
"filter": {"term": { "name.keyword": "鲫鱼汤(微辣)"}},
"boost": 666
}
},
{
"constant_score": {
"filter": {"term": { "name.keyword": "回锅肉"}},
"boost": 999
}
}
]
}
}
}
dis_max 最佳字段查询
tie_breaker是⼀个介于0-1之间的浮点数。0代表使⽤最佳匹配;1代表所有语句同等重要。
tie_breaker 参数提供了一种 dis_max 和 bool 之间的折中选择,它的评分方式如下:
获得最佳匹配语句的评分 _score 。
将其他匹配语句的评分结果与 tie_breaker 相乘。
tie_breaker 可以是 0 到 1 之间的浮点数,其中 0 代表使用 dis_max 最佳匹配语句的普通逻辑, 1 表示所有匹配语句同等重要。最佳的精确值需要根据数据与查询调试得出,但是合理值应该与零接近(处于 0.1 - 0.4 之间),这样就不会颠覆 dis_max 最佳匹配性质的根本
GET recipes/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"name": "鲍鱼 西菜"
}
},
{
"match": {
"types": "鲍鱼 西菜"
}
}
]
}
}
}
GET recipes/_search
{
"query": {
"dis_max": {
"tie_breaker": 0.1,
"boost": 1.2,
"queries": [
{"match": { "name": "鲍鱼 西菜"}},
{"match": { "types": "鲍鱼 西菜"}}
]
}
}
}
function_score
允许修改查询检索到的文档的得分。例如,如果一个评分函数在计算上很昂贵,并且它足以在一组过滤后的文档上计算分数,那么它就很有用。
要使用function_score,用户必须定义一个查询和一个或多个函数,为查询返回的每个文档计算一个新分数。
整体
GET /_search
{
"query": {
"function_score": {
"query": { "match_all": {} }, //查询体
"boost": "2",// _score * 2 =分值
"random_score": {},
"boost_mode": "multiply",//分数算法
"score_mode": "max",//分数算法
"max_boost":10 //最大分值限制
}
}
}
query
查询体
boost
_score * boot=当前文档分值
score_mode
-
multiply 分数相乘(默认)
-
sum 分数是总结
-
avg 平均分数
-
first 应用第一个具有匹配过滤器的函数
-
max 使用最高分数
-
min 使用最低分数
max_boost
- multiply 查询分数与功能分数相乘(默认)
- replace 只使用函数分数,忽略查询分数
- sum 新增查询评分和功能评分
- avg 平均
- max 最大查询分数和功能分数
- min 最小查询分数和功能分数
script_score
语法查询
String str= doc[‘name.keyword’].value;
获取字符串需要keyword类型
GET recipes/_search
{
"query": {
"function_score": {
"query": {
"bool": {
"must": [
{
"match": {
"name": "鲍鱼汤"
}
}
]
}
},
"script_score": {
"script": """
String str= doc['name.keyword'].value;
if(str.contains('西菜')){
_score * 999
}
"""
}
}
}
}
GET recipes/_search
{
"query": {
"function_score": {
"query": {
"bool": {
"must": [
{
"match": {
"name": "鲍鱼汤"
}
}
]
}
},
"script_score": {
"script":{
"source": """
String str= doc['name.keyword'].value;
if(str.contains('西菜')){
_score * params.a
}
""",
"params": {
"a":999
}
}
}
}
}
}
聚合函数
- Metric(指标): 指标分析类型,如计算最大值、最小值、平均值等等 (对桶内的文档进行聚合分析的操作)
- Bucket(桶): 分桶类型,类似SQL中的GROUP BY语法 (满足特定条件的文档的集合)
- Pipeline(管道): 管道分析类型,基于上一级的聚合分析结果进行在分析
- Matrix(矩阵): 矩阵分析类型(聚合是一种面向数值型的聚合,用于计算一组文档字段中的统计信息)
GET recipes/_search
{
"size": 0,
"aggs": {
"任意名字": {
"聚合方法": {}
}
}
}
metric聚合查询
Avg(平均值)Max(最大值)Min(最小值)Sum(总和)
GET recipes/_search
{
"size": 0,
"aggs": {
"testname": {
"max": {
"field": "price"
}
}
}
}
GET recipes/_search
{
"size": 0,
"aggs": {
"testname": {
"max": {
"script": {
"source": "doc.price.value"
}
}
}
}
}
GET recipes/_search
{
"size": 0,
"aggs": {
"testname": {
"max": {
"field": "price",
"missing": 10 ,//处理缺少值的文档
"script": {
"source": "_value * params.a",
"params": {"a":10}
}
}
}
}
}
stats 统计,
请求后会直接显示多种聚合结果
GET recipes/_search
{
"size": 0,
"aggs": {
"testname": {
"stats": {
"field": "price"
}
}
}
}
{
...
"aggregations" : {
"testname" : {
"count" : 13,
"min" : 29.0,
"max" : 189.0,
"avg" : 82.07692307692308,
"sum" : 1067.0
}
}
}
cardinality 求唯一值
即不重复的字段有多少(相当于mysql中的distinct)
计算具有一定的错误率.
precision_threshold 定义去除重复数量个数 默认100 取值范围0-40000
当值为100 对应数量为数百万时 错误率在5%
GET recipes/_search
{
"size": 0,
"aggs": {
"testname": {
"cardinality": {
"field": "price",
"precision_threshold": 100
}
}
}
}
Percentiles 从小到大累计
对指定字段的值按从小到大累计每个值对应的文档数的占比,返回指定占比比例对应的值。
默认按照[ 1, 5, 25, 50, 75, 95, 99 ]来统计
GET recipes/_search
{
"size": 0,
"aggs": {
"testname": {
"percentiles": {
"field": "price"
}
}
}
}
"aggregations" : {
"testname" : {
"values" : {
"1.0" : 29.0,
"5.0" : 29.0,
"25.0" : 41.0,//价格小于41的占比25%
"50.0" : 48.0,
"75.0" : 104.25,
"95.0" : 189.0,
"99.0" : 189.0
}
}
}
GET recipes/_search
{
"size": 0,
"aggs": {
"testname": {
"percentiles": {
"field": "price",
"missing": 0, //省却处理
"keyed":false,//展示方式
"percents" : [95, 99, 99.9] //指定范围
}
}
}
}
percentile_ranks
上面是通过百分比求文档值,这里通过文档值求百分比。
GET recipes/_search
{
"size": 0,
"aggs": {
"testname": {
"percentile_ranks": {
"field": "price",
"values": [
89, //0-89
45
]
}
}
}
}
"aggregations" : {
"testname" : {
"values" : {
"45.0" : 30.76923076923077,
"89.0" : 69.23076923076923
}
}
}
Top hits
一般用于分桶后获取该桶内匹配前n的文档列表
GET recipes/_search
{
"size": 0,
"aggs": {
"testname": {
"terms": {
"field": "price" //根据价格 分组
},
"aggs": {
"top_sales_hits": {
"top_hits": {//该桶内匹配前n的文档列表
"size": 2,
"sort": [ //排序
{
"sales": {
"order": "desc","missing": 0
}
}
],
"_source": {
"includes": ["name","price","sales"]//展示字段
}
}
}
}
}
}
}
"aggregations" : {
"testname" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 29.0,
"doc_count" : 3,
"top_sales_hits" : {
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "--KM9XYBNlT5OLvXYyDV",
"_score" : null,
"_source" : {
"price" : 29,
"name" : "鲫鱼汤(微辣)",
"sales" : 3632
},
"sort" : [
3632
]
},
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "_OKM9XYBNlT5OLvXYyDV",
"_score" : null,
"_source" : {
"price" : 29,
"name" : "鲫鱼汤(变态辣)",
"sales" : 343
},
"sort" : [
343
]
}
]
}
}
},
{
"key" : 89.0,
"doc_count" : 3,
"top_sales_hits" : {
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "9-KM9XYBNlT5OLvXYyDU",
"_score" : null,
"_source" : {
"price" : 89,
"name" : "清蒸鱼头",
"sales" : 3000
},
"sort" : [
3000
]
},
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "AuKM9XYBNlT5OLvXYyHV",
"_score" : null,
"_source" : {
"price" : 89,
"name" : "川辣鱼头",
"sales" : 3000
},
"sort" : [
3000
]
}
]
}
}
},
{
"key" : 45.0,
"doc_count" : 2,
"top_sales_hits" : {
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "AeKM9XYBNlT5OLvXYyHV",
"_score" : null,
"_source" : {
"price" : 45,
"name" : "回锅肉",
"sales" : 55
},
"sort" : [
55
]
},
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "AOKM9XYBNlT5OLvXYyHV",
"_score" : null,
"_source" : {
"price" : 45,
"name" : "红烧肉",
"sales" : 33
},
"sort" : [
33
]
}
]
}
}
},
{
"key" : 189.0,
"doc_count" : 2,
"top_sales_hits" : {
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "-eKM9XYBNlT5OLvXYyDV",
"_score" : null,
"_source" : {
"price" : 189,
"name" : "红烧鲫鱼",
"sales" : 563
},
"sort" : [
563
]
},
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "-OKM9XYBNlT5OLvXYyDV",
"_score" : null,
"_source" : {
"price" : 189,
"name" : "剁椒鱼头",
"sales" : 24
},
"sort" : [
24
]
}
]
}
}
},
{
"key" : 47.0,
"doc_count" : 1,
"top_sales_hits" : {
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "_eKM9XYBNlT5OLvXYyDV",
"_score" : null,
"_source" : {
"price" : 47,
"name" : "广式鲫鱼汤",
"sales" : 2
},
"sort" : [
2
]
}
]
}
}
},
{
"key" : 48.0,
"doc_count" : 1,
"top_sales_hits" : {
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "_uKM9XYBNlT5OLvXYyDV",
"_score" : null,
"_source" : {
"price" : 48,
"name" : "鱼香肉丝",
"sales" : 464
},
"sort" : [
464
]
}
]
}
}
},
{
"key" : 150.0,
"doc_count" : 1,
"top_sales_hits" : {
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "_-KM9XYBNlT5OLvXYyDV",
"_score" : null,
"_source" : {
"price" : 150,
"name" : "奶油鲍鱼汤",
"sales" : 131
},
"sort" : [
131
]
}
]
}
}
}
]
}
}
Value Count 文档数量
可按字段统计文档数量。text 类型的字段不能做排序和聚合
GET recipes/_search
{
"size": 0,
"aggs": {
"qweqwe": {
"value_count": {
"field": "price"
}
}
}
}
Bucket
terms 用于分组聚合
“collect_mode”: “depth_first”//深度优先 , "breadth_first"广度优先
GET recipes/_search
{
"size": 0,
"aggs": {
"group": {
"terms": {
"field": "types.keyword",
"size": 10,
"collect_mode": "depth_first"//深度优先 , "breadth_first"广度优先
}
}
}
}
下钻
GET recipes/_search
{
"size": 0,
"aggs": {
"group": {
"terms": {
"field": "types.keyword",
"size": 10
},
"aggs": {
"max": {
"max": {
"field": "price"
}
}
}
}
}
}
"aggregations" : {
"group" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "湘菜",
"doc_count" : 6,
"max" : {
"value" : 189.0
}
},
{
"key" : "东北菜",
"doc_count" : 2,
"max" : {
"value" : 45.0
}
},
{
"key" : "川菜",
"doc_count" : 2,
"max" : {
"value" : 89.0
}
},
histogram 范围分组(类似terms)
interval:50 每50自增1次 0-50(不含) 50-100 100-150
extended_bounds:桶展示范围指定 默认是按照最小值-最大值 price=29-189
如果min=0 max=300 那么各个档位都会去有分组的计算(不存在为0)
GET recipes/_search
{
"size": 0,
"aggs": {
"NAME": {
"histogram": {
"field": "price",//字段
"interval": 50,//自增单位
"keyed": true,//key
"extended_bounds": {//范围限制
"min": 0,
"max": 300
},
"min_doc_count": 0,//桶最低值
"missing": -99999
}
}
}
}
date_histogram 日期分组
interval: year quarter(季度) month week day hour minute second
GET recipes/_search
{
"size": 0,
"aggs": {
"NAME": {
"date_histogram": {
"field": "end_time",//字段
"interval": "month",//分组日期类型
"format": "yyyy-MM-dd",//格式化
"min_doc_count": 0,//最小文档值
"extended_bounds": {//桶展示范围指定 默认是按照最小值-最大值
"min": "2019-01-01",
"max": "now/d"
}
}
}
}
}
DateHistogramInterval dataTypeObj=null;
String format="";
if("mon".equals(dataType)){
dataTypeObj = DateHistogramInterval.MONTH;
format="yyyy-MM";
}else{
dataTypeObj = DateHistogramInterval.DAY;
format="yyyy-MM-dd";
}
builder=AggregationBuilders.dateHistogram(prefix+param1).field(param1).dateHistogramInterval(dataTypeObj).format(format);
Range Aggregation 是范围聚合
“-9999999”
AggregationBuilder builder = null;
builder = AggregationBuilders.range("聚合名字").field("字段")
.addUnboundedFrom(100) // 范围配置, 0 - 100
.addRange(100.0, 200.0) // 范围配置, 100 - 200
.addUnboundedTo(200.0); // 范围配置,> 200的值
filter
- 过滤所有为湘菜的文档
GET recipes/_search
{
"size": 0,
"aggs": {
"NAME1": {
"filter": {"term": { "types.keyword": "湘菜"}},
"aggs": {
"NAME2": {
"top_hits": {
"size": 10,
"_source": "types"
}
}
}
}
}
}
"aggregations" : {
"NAME1" : {
"doc_count" : 6,
"NAME2" : {
"hits" : {
"total" : {
"value" : 6,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "9-KM9XYBNlT5OLvXYyDU",
"_score" : 1.0,
"_source" : {
"types" : "湘菜"
}
},
{
"_index" : "recipes",
"_type" : "_doc",
"_id" : "-OKM9XYBNlT5OLvXYyDV",
"_score" : 1.0,
"_source" : {
"types" : "湘菜"
}
},
filters
GET recipes/_search
{
"size": 0,
"aggs": {
"NAME": {
"filters": {
"other_bucket": true,
"other_bucket_key": "otherName",
"filters": {
"types": { "match":{"types":"湘菜"}},
"name":{"match":{"name":"鲫鱼汤"}
}
}
}
}
}
}
suggest 自动补全
term 词建议
## phrase 短语建议
## completion 自动补全
自动不全类型 completion
```javascript
GET recipes/_search
{
"_source": "name",
"suggest": {
"mysu1": {
"prefix":"鲫鱼", //用于搜索的前缀
"completion":{
"field":"name.suggestion_name",
"size":2 //返回数量
,"skip_duplicates":true //去重 默认false
}
}
}
}
数据关系
1对多
"remark":{
"type": "nested",//创建时使用nested类型
"properties": {
"id":{
"type":"int"
},
"context":{
"type":"text"
}
}
}
}
使用nested对象类型是一定要使用nested搜素
GET recipes/_search
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "remark",
"query": {
"bool": {
"must": [
{
"match": {
"remark.id": 2
}
},
{
"match": {
"remark.context": "味道"
}
}
]
}
}
}
}
]
}
}
}
索引重建 || 复制数据 || 拷贝数据>_reindex
可以从同一服务
||服务与服务之间
来复制数据
A 服务 有5013条数据 向
---->
B服务做数据迁移
POST _reindex?refresh&wait_for_completion=true
{
"conflicts": "proceed",
"source": {
"index": "A服务索引名称",
"size": 1000,//分批条数
"remote": {
"host": "ip:9200",//A服务地址
"username": "AAAA",//用户名
"password": "BBBB"//密码
}
},
"dest": {
"index": "es_product_dczcnew_cs",//B服务索引名称
"version_type": "internal"//版本类型为覆盖
}
}
参数
-
conflicts: (可选,enum)设置为即使发生冲突也继续重构。默认为
abort
。 -
max_docs: (可选,整数)
同步到新索引中的最大数量;
官方解释:要重新索引的最大文档数。如果冲突等于继续,则reindex可以尝试重新索引源文档中多于max_docs的文档,直到将max_docs文档成功索引到目标文档中,或者遍历源查询中的每个文档。 -
source: 源说明-嵌套
- index : (
必须
, string/str数组[“A”,“B”])源服务中的索引名称 - query: (可选,查询对象)查询数据的DSL。
- size: (可选,整数)
相当于分页同步因为请求缓存有限吧大白
;官方:每批索引的文档数量。当从远程进行索引时使用,以确保批适合堆上缓冲区(默认值为最大100 MB)。 - sort :
弃用在7.6
- _source : (可选,string/Strs数组)同步的字段[“字段A”,“字段B”]
- slice : 分片设置
- id:手动切片的切片ID
- max:总切片数
- remote
- host :(可选,string) 数据源es服务IP地址;
- username:(可选,string) 进行身份验证的用户名。
- password:(可选,string) 进行身份验证的密码。
- socket_timeout:(可选,时间单位) 远端socket读取超时时间。默认为30秒。
- connect_timeout:(可选,时间单位) 远程连接超时时间。默认为30秒。
- index : (
-
dest:设置目标参数
- index:
必填
新服务索引名称- op_type : (可选,enum)设置为create只索引不存在的文档(如果不存在则放置)。有效值:index、create。默认index。
- type:
6.0
弃用 - version_type : (可选)更新方式internal 、、external_gt、external_gte。有关更多信息,请参阅版本类型。链接
- index:
internal | 覆盖更新(即使ID相同) |
external | id不存在的文档会直接更新;id存在的文档会先判断版本号,只会更新版本号旧的文档。 |
- script:
- source:(可选,string)在索引时更新文档源或元数据的脚本。
- lang:(可选,enum)脚本语言:pain-less、expression、mustache、java。有关更多信息,请参见脚本编制。
将旧(源)数据中的name字段赋值到新数据中的newName字段,他会为你创建新的字段
"script": {
"source": """
ctx._source.newName=ctx._source.remove("name")
"""
}
ctx.op操作同步文档 A为旧索引 B为新索引
//B包含A这个文档时删除B的这个文档
if(ctx._source.name=="红烧鲫鱼"){ctx.op="delete"}
//B同步A,不创建这个文档,B存在时不处理
if(ctx._source.name=="红烧鲫鱼"){ctx.op="noop"}
请求参数
-
refresh:请求刷新受影响的碎片,使此操作对搜索可见。
-
timeout:超时时间 默认1m 1分钟
-
wait_for_active_shards:在继续操作之前必须处于活动状态的分片副本的数量。设置为索引中shard的总数(number_of_replicas+1)的全部或任意正整数。默认值:1,主分片。
-
wait_for_completion:如果为true,则请求阻塞直到操作完成。默认值为true。
-
requests_per_second:此请求的节流(以每秒子请求数为单位)。默认为-1(没有节流阀)。
-
require_alias:如果为真,目标必须是索引别名。默认值为false。
-
scroll:指定应为滚动搜索维护一致的索引视图的时间长度。
-
slices:这个任务应该被划分的片数。默认值为1,表示该任务不被分割为子任务。
-
max_docs:要处理的文件的最大数量。默认为所有文档。
返回格式
{
"took" : 192,//花费时间
"timed_out" : false,//请求超时
"total" : 2,//成功条数
"updated" : 0,//成功更新数,也就是覆盖相同id
"created" : 0,//创建成功的文档
"deleted" : 0,//删除成功的额数量
"batches" : 1,
"version_conflicts" : 2,//命中的版本冲突次数
"noops" : 0,
"retries" : {// reindex重试次数。
"bulk" : 0,//批量操作重试次数
"search" : 0//搜索操作重试次数
},
"throttled_millis" : 0,//请求休眠的毫秒数
"requests_per_second" : -1.0,
"throttled_until_millis" : 0,
"failures" : [ ]
}
代码
- 1.在B服务yml中配置白名单
reindex.remote.whitelist: "esIP1:9200,esIP1:9200"
- 2.在B服务中新建 A服务的索引
(推荐做法 也可以不创建,在新的服务中会自动创建)
在这里插入代码片
注意事项
新索引可以不创建,reindex会自动生成(
必须是旧索引中字段不为空的
)
新旧索引中字段的Type类型不同时会报错
{
"index" : "recipes",
"type" : "_doc",
"id" : "AZIP6n0Bidl6EOhx1fh4",
"cause" : {
"type" : "mapper_parsing_exception",
"reason" : "failed to parse field [name] of type [integer] in document with id 'AZIP6n0Bidl6EOhx1fh4'. Preview of field's value: '清蒸鱼头'",
"caused_by" : {
"type" : "number_format_exception",
"reason" : "For input string: \"清蒸鱼头\""
}
},
"status" : 400
},
在旧服务中字段A,B(无数据null),新服务中字段只有A,
复制后新服务是没有B字段的
性能优化
参考的博主文章
Reindex 支持 Sliced Scroll 来并行化重新索引过程。 这种并行化可以提高效率并提供一种将请求分解为更小的部分的便捷方式。
每个Scroll请求,可以分成多个Slice请求,可以理解为切片,各Slice独立并行,利用Scroll重建或者遍历要快很多倍。
slicing的设定分为两种方式:手动设置分片、自动设置分片。
自动设置分片如下:
POST _reindex?slices=5&refresh
slices大小设置注意事项:
1)slices大小的设置可以手动指定,或者设置slices设置为auto,auto的含义是:针对单索引,slices大小=分片数;针对多索引,slices=分片的最小值。
2)当slices的数量等于索引中的分片数量时,查询性能最高效。slices大小大于分片数,非但不会提升效率,反而会增加开销。
3)如果这个slices数字很大(例如500),建议选择一个较低的数字,因为过大的slices 会影响性能。
效果
实践证明,比默认设置reindex速度能提升10倍+。
异步请求
POST _reindex?refresh&wait_for_completion=false
返回taskId
{
"task" : "mkgxDVUjSfqK5Fwo5j59Tw:91491"
}
获取任务列表
GET _tasks?detailed=true&actions=*reindex
查看任务Id
GET /_tasks/任务ID
{
"completed" : true,
"task" : {
"node" : "mkgxDVUjSfqK5Fwo5j59Tw",
"id" : 93235,
"type" : "transport",
"action" : "indices:data/write/reindex",
"status" : {
"total" : 2,
"updated" : 2,
"created" : 0,
"deleted" : 0,
"batches" : 1,
"version_conflicts" : 0,
"noops" : 0,
"retries" : {
"bulk" : 0,
"search" : 0
},
"throttled_millis" : 0,
"requests_per_second" : -1.0,
"throttled_until_millis" : 0
},
"description" : """reindex from [host=1 port=9200 query={
"match_all" : {
"boost" : 1.0
}
}][recipes] to [recipes][_doc]""",
"start_time_in_millis" : 1640438102443,
"running_time_in_nanos" : 255119400,
"cancellable" : true,
"cancelled" : false,
"headers" : { }
},
"response" : {
"took" : 251,
"timed_out" : false,
"total" : 2,
"updated" : 2,
"created" : 0,
"deleted" : 0,
"batches" : 1,
"version_conflicts" : 0,
"noops" : 0,
"retries" : {
"bulk" : 0,
"search" : 0
},
"throttled" : "0s",
"throttled_millis" : 0,
"requests_per_second" : -1.0,
"throttled_until" : "0s",
"throttled_until_millis" : 0,
"failures" : [ ]
}
}
取消任务Id
POST _tasks/任务Id/_cancel
索引别名 _aliases
在我们数据同步,数据拷贝,正式,测试环境使用可以对
同一索引使用不同名称去搜索
POST _aliases
{
"actions": [
{
"remove": {
"index": "原索引",
"alias": "别名索引"
}
},
{
"add": {
"index": "原索引",
"alias": "移除索引名称"
}
}
]
}
查看 别名关系
GET _alias
Snapshot 快照备份
在yml中配置信息
"path.repo: "D:\workSoftware\elasticsearch-7.16.2\elasticsearch-7.16.2\backup"
创建快照库
注册快照存储库(即设置存储路径)
my_repository
是快照库名称
请求参数:
-
master_timeout:指定等待连接到主节点的时间。如果在超时时间内没有收到响应,则请求失败并返回一个错误。默认为30s
-
timeout:指定等待响应的时间范围。如果在超时时间内没有收到响应,则请求失败并返回一个错误。默认为 30s
-
type 存储类型
- fs::共享文件系统存储库。这种类型的存储库使用共享文件系统来存储快照。集群中的所有主节点和数据节点都必须可以访问该文件系统,要注册共享文件系统存储库,必须将相同的共享文件系统挂载到所有主节点和数据节点上的相同位置。此位置必须在路径中注册。集群中所有主节点和数据节点的回购设置。
- source::单一存储库。您可以使用纯源存储库创建最小的纯源快照,最多占用50%的磁盘空间。只有启用了_source字段且不进行源过滤时,才支持源快照。仅源快照包含存储字段和索引元数据。它们不包括索引或文档值结构,并且在恢复时不可搜索。恢复仅源快照后,需要将数据重新建立索引。
- url::URL存储库。这种类型的存储库对集群来说是只读的。这意味着集群可以从存储库中检索或恢复快照,但不能在存储库中写入或创建快照。您可以使用URL存储库作为另一种方式,让集群对共享文件系统存储库进行只读访问
-
settings 包含存储库的设置。
(fs类型适用)
location:(source类型适用)
delegate_type:委派存储库类型。(url类型适用)
url:(Required, string)共享文件系统存储库根的URL位置。支持以下协议:file,ftp,http,https,jar全部类型适用
- chunk_size:将快照分解成指定大小。如1G,10m,5K。默认值为null(不受文件大小限制)。
- compress: true表示快照压缩元数据文件,默认值为true。
- max_number_of_snapshots:(可选,整型)存储库支持的最大快照个数。默认为500。
- max_restore_bytes_per_sec:(可选,字节值)每个节点最大快照恢复速率。默认为无限。请注意,恢复也会通过恢复设置进行调节。
- max_snapshot_bytes_per_sec:(可选,字节值)每个节点最大快照创建速率。默认值为每秒40mb。
- readonly::(可选,Boolean)如果为true,则存储库为只读。集群可以从存储库中检索快照和恢复快照,但不能写入存储库或在存储库中创建快照。
false表示集群可以向存储库写入数据,并在存储库中创建快照。默认值为false。
-
verify::(可选,Boolean)如果为true,则请求验证存储库在集群中的所有主节点和数据节点上都有效。如果为false,则跳过此验证。默认值为true。
您可以使用验证快照存储库API手动执行此验证。
#删除 快照库
DELETE /_snapshot/my_repository
#创建 快照库
PUT /_snapshot/my_repository
{
"type": "fs",
"settings": {
"location": "backup",//linux 用路径最后文件夹
"compress":true,
"chunk_size":"1mb" //快照按size分块
}
}
# 查看快照库
GET /_snapshot/my_repository
#验证快照库
POST /_snapshot/my_repository/_verify
#清理快照库
POST /_snapshot/my_repository/_cleanup
创建快照
格式 PUT /_snapshot/<快照库>/<快照名称>
?master_timeout=100s&wait_for_completion=false
参数:
- indices:(可选,字符串或字符串数组)逗号分隔的数据流列表和索引包含在快照中。支持多索引的语法。默认为空数组([]),它包括所有数据流和索引,包括系统索引。若要排除所有数据流和索引,请使用-*或none
- expand_wildcards:(可选,string)确定indices参数中的通配符模式如何匹配数据流和索引。支持逗号分隔值,如closed、hidden。默认为开放的、隐藏的。有效值:
- all::匹配任何数据流或索引,包括隐藏的数据流或索引。
*open::匹配开放索引和数据流。
*closed::匹配闭合索引和数据流
*hidden::匹配隐藏的数据流和索引。必须与开、闭或两者结合。
*none::不要扩展通配符模式
- all::匹配任何数据流或索引,包括隐藏的数据流或索引。
- ignore_unavailable:(可选,Boolean)如果为false,表示缺少或关闭数据流或索引中的索引,则快照失败。如果为true,快照将忽略缺失或关闭的数据流和索引。默认值为false。
- include_global_state:(可选,Boolean)若为true,则快照中包含集群状态。默认值为true。
- feature_states:(可选,字符串数组)快照中包含的特性状态。要获取可能值及其描述的列表,请使用get特性API。如果include_global_state为true,快照默认包含所有特性状态。如果include_global_state为false,默认情况下快照不包含特性状态。要排除所有特性状态,无论include_global_state的值是多少,请指定一个空数组([])或不指定。
- metadata:(可选,对象)附加任意元数据到快照,如谁拍摄了快照,为什么拍摄,或任何其他有用的数据的记录。元数据必须小于1024字节。
- partial::(可选,Boolean)如果为false,则快照中包含的一个或多个索引没有所有可用的主shard,则整个快照将失败。默认值为false。如果为true,允许使用不可用的shards索引的部分快照。
#创建快照 _all(全部快照) recipes_*(通配符) 快照1,快照2(逗号分割)
PUT /_snapshot/my_repository/recipes_snp
{
"indices": "recipes",
"ignore_unavailable": true,
"include_global_state": false,
"metadata":{
"create_user":"李四",
"remarks":"测试备份",
"obj":"这个metadata对象里面存储了个map对象,可以随意创建对象"
}
}
#查看快照库全部快照
GET /_snapshot/my_repository/*
GET /_snapshot/my_repository/recipes_snp
#查看快照状态
GET /_snapshot/my_repository/recipes_snp/_status
#删除快照
DELETE /_snapshot/my_repository/recipes_snp
恢复快照
快照版本恢复关系
POST /_snapshot/<repository>/<snapshot>/_restore
参数
- indices:(可选,字符串或字符串数组)逗号分隔的索引列表和要恢复的数据流。支持多目标语法。默认为快照中的所有索引和数据流,包括系统索引。
- rename_pattern:(可选,string)原始名称的规则,支持正则
- rename_replacement:(可选,string)重命名替换字符串。
- ignore_unavailable:(可选,Boolean)如果为true,则请求忽略快照中缺失的任何索引或索引中的数据流。如果为false,则对任何缺少的索引或数据流返回一个错误。默认值为false。
- ignore_index_settings: (可选,字符串或字符串数组)不从快照恢复的索引设置。你不能使用这个选项忽略index.number_of_shards。
- include_aliases:(可选,Boolean)如果为false,不恢复别名设置,默认值为true。
- include_global_state:(可选,Boolean)若为true,则恢复集群状态。默认值为false。如果include_global_state为true,则恢复操作将集群中的遗留索引模板与快照中包含的模板合并,替换快照中名称匹配的任何现有索引模板。它完全删除集群中存在的所有持久设置、非遗留索引模板、摄取管道和ILM生命周期策略,并用快照中的相应项替换它们。
- feature_states:如果include_global_state为true,则默认恢复快照中的所有特性状态。如果include_global_state为false,默认情况下请求不恢复任何特性状态。无论include_global_state的值如何,要恢复任何特性状态,请指定一个空数组([])。
- index_settings:(可选,对象)添加或更改恢复索引(包括备份索引)的索引设置。你不能使用这个选项来改变index.number_of_shards。
对于数据流,此选项仅适用于已恢复的备份索引。使用数据流的匹配索引模板配置新的后台索引。 - partial:(可选,Boolean)如果为false,则如果快照中包含的一个或多个索引没有所有可用的主shard,则整个恢复操作将失败。默认值为false。如果为true,允许用不可用的shard恢复索引的部分快照。只有成功包含在快照中的分片才会被恢复。所有缺失的碎片将被重新创建为空
#恢复快照 将recipes_snp快照(索引名称recipes,更名为recipes_index)
POST _snapshot/my_repository/recipes_snp/_restore
{
"indices": "recipes",//快照内索引名称
"ignore_unavailable": true,//
"include_global_state": false,
"include_aliases":false,//取消别名的恢复
"rename_pattern": "recipes",//重命名设置
"rename_replacement": "recipes_index"
}
#删除快照
DELETE _snapshot/my_repository/recipes_snp