注意
1、index.mapping.single_type: true在索引上 设置将启用按索引的单一类型行为,该行为将在6.0后强制执行。
原 {type} 要改为 _doc,格式如下:
PUT {index}/_doc/{id}和 POST {index}/_doc
2、_mapping方法表示修改mapping属性。已存在的字段,一旦数据被写入,就不再支持修改字段定义。若要修改已存在的字段属性,需指定字段属性重建索引,然后通过Reindex api接口,将数据复制到新索引。在数据较多的时候,开销较大。
索引操作
增
创建索引(可指定 settings 或 mapping)
PUT 索引名
{
"settings": {
"index": {
"number_of_shards": "3",
"number_of_replicas": "0"
}
},
"mappings": {
"properties": {
"age": {
"type": "long",
"index": false
},
"name": {
"type": "text"
},
"sex": {
"type": "text",
"index": false
}
}
}
}
删
删除索引
DELETE 索引名
改
索引数据迁移
source索引必须存在,dest索引没有会自动创建
将 source 索引中的数据迁移至 dest 索引
POST _reindex
{
"source": {
"index": "test01"
},
"dest": {
"index": "test02"
}
}
查
查看所有的索引
GET _cat/indices
查看索引结构
#查看索引结构
GET 索引名
#查询索引 mappings 结构
GET 索引名/_mapping
#查询索引 settings结构
GET 索引名/_settings
索引数据操作
增
往索引中添加数据
//id不填会默认生成,填的话,原id的数据会被覆盖
POST 索引名/_doc/文档唯一id
{
"key":"value"
...
}
//不填默认生成,填的话,该id已存在会提示
POST 索引名/_create/文档唯一id
{
"key":"value"
...
}
删
//根据 id 删除
DELETE 索引名/_doc/id
//根据条件删除,无条件时,默认全部删除
POST 索引或索引模板名/_delete_by_query
{
"query":{
"match_all":{}
}
}
//根据条件删除,删除等于的数据
POST 索引或索引模板名/_delete_by_query
{
"query": {
"term": {
"day": "2023-07-06T00:00:00.000+08"
}
}
}
//根据条件删除,删除不等于的数据
POST 索引或索引模板名/_delete_by_query
{
"query": {
"bool" : {
"must_not" : {
"term" : {
"day": "2023-07-06T00:00:00.000+08"
}
}
}
}
}
改
//根据 id 更新,(存在的key会更新,不存在的则添加)
POST 索引名/_update/id
{
"doc": {
"key1": "v1",
"key2": "v2",
"key3": "v3"
}
}
//根据 条件 更新
POST 索引名/_update_by_query
{
}
查
//根据 id 查询
GET 索引名或索引模板名/_doc/id
//批量查询
GET /_mget
{
"docs": [
{
"_index": "索引名",
"_id": 1
},
{
"_index": "索引名",
"_id": 2
}
]
}
//_search查询所有索引的所有数据,请求体可不携带
GET _search
{
"query": {
"match_all": {}
}
}
_search查询某个索引或索引模板的所有数据
#查询所有数据,[按照id进行排序]
GET 索引或索引模板名/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_id": {
"order": "desc"
}
}
]
}
//_search泛查询
//查询所有的文档,只要属性值包含zero的文档
GET 索引名或索引模板名/?q=zero
//查询fisrtname属性包含russ的文档
GET 索引名或索引模板名/_search?q=russ&df=firstname
GET 索引名或索引模板名/_search?q=firstname:russ
//_count查询某个索引或索引模板的数据条数
GET 索引名或索引模板名/_count
GET merge_*/_count
其他
查看集群健康情况
GET /_cat/health?v
ES7常见查询
(term、match、bool、filter、match)
一、精准查询term
term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇。
1、term单值
字段只有一个值时候,用term关键词查询
查询biz_id值为1909190023901225的记录
curl -XGET http://192.168.1.73:9200/xyerp/order/_search -H 'Content-Type: application/json' -d '
{
"query": {
"term": {
"biz_id": "1909190023901225"
}
}
}
进一步优化查询,因为是精准查询,不需要查询进行评分计算,只希望对文档进行包括或排除的计算,所以我们会使用 constant_score 查询以非评分模式来执行 term 查询并以一作为统一评分。推荐如下查询
{
"query" : {
"constant_score" : {
"filter" : {
"term" : {
"biz_id" : "1909190023901225"
}
}
}
}
}'
2、terms多值
字段有一多个值时候,用terms关键词查询,后跟数组
{
"query":{
"terms":{
"biz_id":["1909190023901225"]
}
}
}'
constant_score 以非评分模式查询,推荐如下查询
{
"query" : {
"constant_score" : {
"filter" : {
"terms" : {
"biz_id" : ["1909190023901225","e1909190111365113"]
}
}
}
}
}'
3、term多个字段
{
"query": [{
"term": {
"biz_id": "1909190023901225"
}
}, {
"term": {
"name": "zhangsan"
}
}]
}
二、匹配查询match
match和term的区别是,match查询的时候,elasticsearch会根据你给定的字段提供合适的分析器,而term查询不会有分析器分析的过程,match查询相当于模糊匹配,只包含其中一部分关键词就行,同时还要注意match系列匹配时,datatype要设置为text,否则不会开启分词。
1、match
进行full text search或者exact value(非string字段或not_analyzed的字段),进行匹配,会对要查询的内容按照某种规则进行分词。如es中存的merchant_id的值为"2500,2501,2502",按照逗号分词。match匹配时查询参数值param="2500,2502",会对param进行分词,分为2500和2502,对merchant_id的值进行匹配,默认是是or,即或者的关系,匹配任意一个分词,就返回数据结果。
{
"query": {
"match": {
"merchant_id": "2500,2502"
}
},
"sort": [
{
"trade_finished_time": {
"order": "desc"
}
}
]
}'
2、match_all
{ "match_all": {}} 匹配所有的, 当不给查询条件时,默认全查,匹配所有字段。
{
"query": {
"match_all": {}
}
}
3、multi_match
同时对查询的关键词,多个字段同时进行匹配,只要其中一个字段匹配到值就返回结果
只要查询的字段merchant_id,_id字段值中任何一个包含2501,就返回对应结果
{
"query":{
"multi_match":{
"query":"2501",
"fields":["merchant_id","_id"]
}
}
}
同时field还支持更为丰富的查询在在fields中,按brandName(品牌名)、sortName(分类名)、productName(商品名)productKeyword(商品关键字),搜索“牛仔 弹力”关键词,brandName源值、拼音值、关键字值都是100分,sortName源值、拼音值80分,productName源值60分,productKeyword值20分,分值由高到低优先级搜索。
{
"query": {
"multi_match": {
"query": "牛仔 弹力",
"fields": [
"brandName^100",
"brandName.brandName_pinyin^100",
"brandName.brandName_keyword^100",
"sortName^80",
"sortName.sortName_pinyin^80",
"productName^60",
"productKeyword^20"
],
"type": <multi-match-type>,
"operator": "AND"
}
}
}
4、match_phrase
match_phrase查询分析文本,并从分析文本中创建短语查询。
类似 match 查询, match_phrase 查询首先将查询字符串解析成一个词项列表,然后对这些词项进行搜索,但只保留那些包含全部搜索词项,且位置与搜索词项相同的文档。
即对给定的短语完整查询匹配,搜索到的结果集都必须包含给定的查询词组如下,查询 quick brown、quick brown fox、 brown fox可以查询到,quick fox 查询不到
{
"query": {
"match_phrase": {
"title": "quick brown fox"
}
}
}
如下, 查询 a,b,a和b之间隔3个字符可以查询到,隔不是3个查询不到
{
"query":{
"match_phrase" :{
"query":"a,b",
"slop":3
}
}
}
5、match_phrase_prefix
左前缀匹配,类似sql中的 like 'zhang%'
如查询姓张的同学有哪些,zhang san,zhang san feng,都能返回结果集
{
"query": {
"match_phrase_prefix": {
"name": "zhang"
}
}
}
6、wildcard模糊查询
?(只一个)、*(可多个)匹配任意字符,不用于match分词要求的text,这里模糊的字段类型需要是keyword。类似于SQL LIKE,通配符性能自然也会差一些
如下:模糊匹配包含“海淀”的学校名字
{
"size": 20,
"from": 0,
"query": {
"bool": {
"must": [
{
"wildcard": {
"school_name": "*海淀*"
}
}
]
}
}
}
三、bool查询
bool查询包含四种操作符,分别是must,should,must_not,filter。它们均是一种数组,数组里面是对应的判断条件
must: 必须匹配,与and等价。贡献算分
must_not:必须不匹配,与not等价,常过滤子句用,但不贡献算分
should: 选择性匹配,至少满足一条,与 OR 等价。贡献算分
filter: 过滤子句,必须匹配,但不贡献算分需要注意的是must与should同时使用时候,不可平级出现,必须是一个嵌套在另一个其中
{
"query": {
"bool": {
"must": [{
"term": {
"merchant_id": 100
}
},
{
"bool": {
"should": [{
"term": {
"creator": "zhangsan"
}
}, {
"term": {
"id": 40
}
}]
}
}
],
"filter": {
"term": {
"trade_type": 2
}
}
}
}
}
四、filter查询
过滤器,会查询对结果进行缓存,不会计算相关度,避免计算分值,执行速度非常快。我们可以把经常被查询且不频繁变更的条件放到filter中
如下, 查询出12月份已付款的订单,付款状态不会经常变动,所以放在filter中
{
"query": {
"bool": {
"must": [{
"term": {
"month": "2022-12"
}
}],
"filter": {
"term": {
"trade_status": 2
}
}
}
}
}
filter也常和range范围查询一起结合使用,range范围可供组合的选项
gt : 大于
lt : 小于
gte : 大于等于
lte :小于等于
如下,查询merchant_id值为2501下的交易数据
{
"query": {
"bool": {
"must": {
"term": {
"merchant_id": "2501"
}
},
"filter": {
"range": {
"trade_finished_time": {
"from": "2019-09-01T00:00:00",
"to": "2019-09-30T23:59:59"
}
}
}
}
}
}
如下查询,must下匹配,filter进行过滤,range定义范围
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "Search"
}
},
{
"match": {
"content": "Elasticsearch"
}
}
],
"filter": [
{
"term": {
"status": "1"
}
},
{
"range": {
"publish_date": {
"gte": "2015-01-01"
}
}
}
]
}
}
}
五、常见组合查询
查询商户ID为3582,订单号为360102199003072618,按时间范围过滤,按下单时间倒序,每次查询100条
{
"query": {
"bool": {
"must": [{
"term": {
"merchant_id": "3582"
}
}, {
"term": {
"order_num": "360102199003072618"
}
}],
"filter": [{
"range": {
"order_time": {
"from": "2019-11-01T17:00:00+08:00",
"to": "2019-11-01T20:00:00+08:00"
}
}
}]
}
},
"size": 100,
"sort": [{
"order_time": "desc"
}]
}
2、bool-must-should-match
查询venderId值为1234,taskId为1234,字段itemCodes和templateCodes的值至少有一个match匹配到结果,才返回对应数据集。即must下两个terms同时满足,should下两个match至少满足一条
{
"bool": {
"must": [{
"terms": {
"venderId": [
"1234"
]
}
},
{
"terms": {
"taskId": [
"1234"
]
}
},
{
"should": [{
"match": {
"itemCodes": {
"query": "12,124"
}
}
},
{
"match": {
"templateCodes": {
"query": "t123,t124,t125"
}
}
}
]
}
]
}
}
3、bool-must-wildcard-range
根据促销ID和促销名称查询某个时间段的促销,并时间排序
{
"from": 0,
"size": 10,
"query": {
"bool": {
"must": [{
"term": {
"promt_id": {
"value": 200352052277
}
}
}, {
"wildcard": {
"promt_name": {
"wildcard": "*业务部*"
}
}
}, {
"range": {
"promt_end_time": {
"from": "2022-10-01 00:00:00"
}
}
}, {
"range": {
"promt_begin_time": {
"to": "2022-10-31 23:59:59"
}
}
}]
}
},
"sort": [{
"created_time": {
"order": "desc",
"unmapped_type": "keyword"
}
}]
}
ES 7.6 获取查询结果集count
分页插件需要{count}这个参数,类似MYSQL中的select count(*) from user where userType=1,获取结果集的count数量。
JAVA有两种方式可以获得
1、将SearchSourceBuilder的“size”参数设置为0,获取结果集的“hits.total.value”得到count
2、查询语句不变,使用RestHighLevelClient.count()方法获取,创建CountRequest传入QueryBuilder(与SearchSourceBuilder使用相同)
代码:
public class FNewsImpl implements FNewsService {
@Autowired
ElasticService elasticService;
/**
* 查找标题和内容
* @param pageRequest
* @return
*/
@Override
public ReturnESList getNewsES(PageRequest pageRequest)
{
// 创建BoolQueryBuilder
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
// 设置条件
MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders
.matchPhraseQuery("ftitle", pageRequest.getKeywords());
MatchPhraseQueryBuilder matchPhraseQueryBuilder2 = QueryBuilders
.matchPhraseQuery("fbastan", pageRequest.getKeywords());
// 子查询“或”关系
BoolQueryBuilder childBoolQueryBuilder = new BoolQueryBuilder()
.should(matchPhraseQueryBuilder)
.should(matchPhraseQueryBuilder2);
boolQueryBuilder.must(childBoolQueryBuilder);
// 创建SearchSourceBuilder
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 查询条件生成DSL语句
searchSourceBuilder.query(boolQueryBuilder);
// 从多少
searchSourceBuilder.from(pageRequest.getPageNum());
// 查多少条数据,如果设置“0”返回count数量
searchSourceBuilder.size(pageRequest.getPageSize());
// 排序规则
searchSourceBuilder.sort("fcreatetime", SortOrder.DESC);
// 设置超时
TimeValue t=new TimeValue(3000);
searchSourceBuilder.timeout(t);
Class<?> clazz = ElasticUtil.getClazz(FquestionES.class.getName());
//查询结果集
List<?> data = elasticService.search(EsEnum.ES_INDEX,searchSourceBuilder,clazz);
//查询count
long count=elasticService.count(EsEnum.ES_INDEX,boolQueryBuilder);
ReturnESList list=new ReturnESList();
list.setSize(count);
list.setData((List<FquestionES>) data);
return list;
}
}
@Service
public class ElasticService {
@Autowired
RestHighLevelClient restHighLevelClient;
/**
* 根据条件查询
* @param idxName index
* @param builder 查询参数
* @param c 结果类对象
*/
public <T> List<T> search(String idxName, SearchSourceBuilder builder, Class<T> c) {
SearchRequest request = new SearchRequest(idxName);
request.source(builder);
try {
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
SearchHit[] hits = response.getHits().getHits();
List<T> res = new ArrayList<>(hits.length);
for (SearchHit hit : hits) {
res.add(JSON.parseObject(hit.getSourceAsString(), c));
}
return res;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 查询条件下共有多少条
* @param idxName index
* @param builder 查询参数
*/
public long count(String idxName, QueryBuilder builder) {
CountRequest countRequest=new CountRequest(idxName);
countRequest.query(builder);
try {
CountResponse response=restHighLevelClient.count(countRequest,RequestOptions.DEFAULT);
long length = response.getCount();
return length;
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
}
ES搜索结果中各个字段介绍
空搜索:
搜索API的最基础的形式是没有指定任何查询的空搜索,它简单地返回集群中所有索引下的所有文档:
GET /_search
返回结果示例:
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 42,
"relation": "eq"
},
"max_score": 0.0,
"hits": [
{
"_index": "mmcp-control-log-p1-20231115",
"_id": "VpEo0osBRx0w9Gx3EH0-",
"_score": 0.0,
"_ignored": [
"request.keyword"
],
"_source": {
"departmentName": "安徽省",
"responseTime": 1700037925509,
"departmentId": "626b51e2f0b611eab12756c242eb4dac",
"msgId": "93",
"distributionStatus": 380
}
}
]
},
"aggregations": {
"date_histogram#group_by_timeStamp": {
"buckets": [
{
"key_as_string": "2023-11-14",
"key": 1699977600000,
"doc_count": 20,
"lterms#group_by_distributionStatus": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": 300,
"doc_count": 3
},
{
"key": 340,
"doc_count": 3
}
]
}
}
]
}
}
}
搜索结果分析:
hits
返回结果中最重要的部分是 hits ,它包含 total 字段来表示匹配到的文档总数,并且一个 hits 数组包含所查询结果的前十个文档。
在 hits 数组中每个结果包含文档的 _index 、 _type 、 _id ,加上 _source 字段。这意味着我们可以直接从返回的搜索结果中使用整个文档。这不像其他的搜索引擎,仅仅返回文档的ID,需要你单独去获取文档。
每个结果还有一个 _score ,它衡量了文档与查询的匹配程度。默认情况下,首先返回最相关的文档结果,就是说,返回的文档是按照 _score 降序排列的。在这个例子中,我们没有指定任何查询,故所有的文档具有相同的相关性,因此对所有的结果而言 1 是中性的 _score 。
max_score 值是与查询所匹配文档的 _score 的最大值。
took
took 值告诉我们执行整个搜索请求耗费了多少毫秒。
shard
_shards 部分告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。正常情况下我们不希望分片失败,但是分片失败是可能发生的。如果我们遭遇到一种灾难级别的故障,在这个故障中丢失了相同分片的原始数据和副本,那么对这个分片将没有可用副本来对搜索请求作出响应。假若这样,Elasticsearch 将报告这个分片是失败的,但是会继续返回剩余分片的结果。
timeout
timed_out 值告诉我们查询是否超时。默认情况下,搜索请求不会超时。如果低响应时间比完成结果更重要,你可以指定 timeout 为 10 或者 10ms(10毫秒),或者 1s(1秒):
GET /_search?timeout=10ms
在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。应当注意的是 timeout 不是停止执行查询,它仅仅是告知正在协调的节点返回到目前为止收集的结果并且关闭连接。在后台,其他的分片可能仍在执行查询即使是结果已经被发送了。
使用超时是因为 SLA(服务等级协议)对你是很重要的,而不是因为想去中止长时间运行的查询。