这篇博文的主题是es的查询,因此我整理了尽可能齐全的es查询场景,形成下面的图: 本文基于elasticsearch 7.13.2版本,es从7.0以后,发生了很大的更新。7.3以后,已经不推荐使用transportclient这个client,取而代之的是java high level rest client。
测试使用的数据示例 首先是,mysql中的部分测试数据:
mysql中的一行数据在es中以一个文档形式存在:
{ _index : person, _type : _doc, _id : 4, _score : 1.0, _source : { address : 峨眉山, modifytime : 2021-06-29 1925, createtime : 2021-05-14 1107, sect : 峨嵋派, sex : 男, skill : 降龙十八掌, name : 宋青书, id : 4, power : 50, age : 21 }} 简单梳理了一下es javaapi的相关体系,感兴趣的可以自己研读一下源码。
接下来,我们用十几个实例,迅速上手es的查询操作,每个示例将提供sql语句、es语句和java代码。
1 词条查询 所谓词条查询,也就是es不会对查询条件进行分词处理,只有当词条和查询字符串完全匹配时,才会被查询到。
1.1 等值查询-term 等值查询,即筛选出一个字段等于特定值的所有记录。
sql:
select * from person where name = '张无忌'; 而使用es查询语句却很不一样(注意查询字段带上keyword):
get /person/_search{ query: { term: { name.keyword: { value: 张无忌, boost: 1.0 } } }} elasticsearch 5.0以后,string类型有重大变更,移除了string类型,string字段被拆分成两种新的数据类型: text用于全文搜索的,而keyword用于关键词搜索。
查询结果:
{ took : 0, timed_out : false, _shards : { // 分片信息 total : 1, // 总计分片数 successful : 1, // 查询成功的分片数 skipped : 0, // 跳过查询的分片数 failed : 0 // 查询失败的分片数 }, hits : { // 命中结果 total : { value : 1, // 数量 relation : eq // 关系:等于 }, max_score : 2.8526313, // 最高分数 hits : [ { _index : person, // 索引 _type : _doc, // 类型 _id : 1, _score : 2.8526313, _source : { address : 光明顶, modifytime : 2021-06-29 1656, createtime : 2021-05-14 1633, sect : 明教, sex : 男, skill : 九阳神功, name : 张无忌, id : 1, power : 99, age : 18 } } ] }} java中构造es请求的方式:(后续例子中只保留searchsourcebuilder的构建语句)
/** * term精确查询 * * @throws ioexception */@autowiredprivate resthighlevelclient client;@testpublic void queryterm() throws ioexception { // 根据索引创建查询请求 searchrequest searchrequest = new searchrequest(person); searchsourcebuilder searchsourcebuilder = new searchsourcebuilder(); // 构建查询语句 searchsourcebuilder.query(querybuilders.termquery(name.keyword, 张无忌)); system.out.println(searchsourcebuilder===================== + searchsourcebuilder); searchrequest.source(searchsourcebuilder); searchresponse response = client.search(searchrequest, requestoptions.default); system.out.println(jsonobject.tojson(response));} 仔细观察查询结果,会发现es查询结果中会带有_score这一项,es会根据结果匹配程度进行评分。打分是会耗费性能的,如果确认自己的查询不需要评分,就设置查询语句关闭评分:
get /person/_search{ query: { constant_score: { filter: { term: { sect.keyword: { value: 张无忌, boost: 1.0 } } }, boost: 1.0 } }} java构建查询语句:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 这样构造的查询条件,将不进行score计算,从而提高查询效率searchsourcebuilder.query(querybuilders.constantscorequery(querybuilders.termquery(sect.keyword, 明教))); 1.2 多值查询-terms 多条件查询类似mysql里的in查询,例如:
select * from persons where sect in('明教','武当派'); es查询语句:
get /person/_search{ query: { terms: { sect.keyword: [ 明教, 武当派 ], boost: 1.0 } }} java实现:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 构建查询语句searchsourcebuilder.query(querybuilders.termsquery(sect.keyword, arrays.aslist(明教, 武当派)));} 1.3 范围查询-range 范围查询,即查询某字段在特定区间的记录。
sql:
select * from pesons where age between 18 and 22; es查询语句:
get /person/_search{ query: { range: { age: { from: 10, to: 20, include_lower: true, include_upper: true, boost: 1.0 } } }} java构建查询条件:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 构建查询语句searchsourcebuilder.query(querybuilders.rangequery(age).gte(10).lte(30));} 1.4 前缀查询-prefix 前缀查询类似于sql中的模糊查询。
sql:
select * from persons where sect like '武当%'; es查询语句:
{ query: { prefix: { sect.keyword: { value: 武当, boost: 1.0 } } }} java构建查询条件:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 构建查询语句searchsourcebuilder.query(querybuilders.prefixquery(sect.keyword,武当)); 1.5 通配符查询-wildcard 通配符查询,与前缀查询类似,都属于模糊查询的范畴,但通配符显然功能更强。
sql:
select * from persons where name like '张%忌'; es查询语句:
{ query: { wildcard: { sect.keyword: { wildcard: 张*忌, boost: 1.0 } } }} java构建查询条件:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 构建查询语句searchsourcebuilder.query(querybuilders.wildcardquery(sect.keyword,张*忌)); 2 复合查询 前面的例子都是单个条件查询,在实际应用中,我们很有可能会过滤多个值或字段。先看一个简单的例子:
select * from persons where sex = '女' and sect = '明教'; 这样的多条件等值查询,就要借用到组合过滤器了,其查询语句是:
{ query: { bool: { must: [ { term: { sex: { value: 女, boost: 1.0 } } }, { term: { sect.keywords: { value: 明教, boost: 1.0 } } } ], adjust_pure_negative: true, boost: 1.0 } }} java构造查询语句:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 构建查询语句searchsourcebuilder.query(querybuilders.boolquery() .must(querybuilders.termquery(sex, 女)) .must(querybuilders.termquery(sect.keyword, 明教))); 2.1 布尔查询 布尔过滤器(bool filter)属于复合过滤器(compound filter)的一种 ,可以接受多个其他过滤器作为参数,并将这些过滤器结合成各式各样的布尔(逻辑)组合。
bool 过滤器下可以有4种子条件,可以任选其中任意一个或多个。filter是比较特殊的,这里先不说。
{ bool : { must : [], should : [], must_not : [], }} must:所有的语句都必须匹配,与 ‘=’ 等价。 must_not:所有的语句都不能匹配,与 ‘!=’ 或 not in 等价。 should:至少有n个语句要匹配,n由参数控制。 精度控制:
所有 must 语句必须匹配,所有 must_not 语句都必须不匹配,但有多少 should 语句应该匹配呢?默认情况下,没有 should 语句是必须匹配的,只有一个例外:那就是当没有 must 语句的时候,至少有一个 should 语句必须匹配。
我们可以通过 minimum_should_match 参数控制需要匹配的 should 语句的数量,它既可以是一个绝对的数字,又可以是个百分比:
get /person/_search{ query: { bool: { must: [ { term: { sex: { value: 女, boost: 1.0 } } } ], should: [ { term: { address.keyword: { value: 峨眉山, boost: 1.0 } } }, { term: { sect.keyword: { value: 明教, boost: 1.0 } } } ], adjust_pure_negative: true, minimum_should_match: 1, boost: 1.0 } }} java构建查询语句:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 构建查询语句searchsourcebuilder.query(querybuilders.boolquery() .must(querybuilders.termquery(sex, 女)) .should(querybuilders.termquery(address.word, 峨眉山)) .should(querybuilders.termquery(sect.keyword, 明教)) .minimumshouldmatch(1)); 最后,看一个复杂些的例子,将bool的各子句联合使用:
select *from personswhere sex = '女'and age between 30 and 40and sect != '明教'and (address = '峨眉山' or skill = '暗器') 用 elasticsearch 来表示上面的 sql 例子:
get /person/_search{ query: { bool: { must: [ { term: { sex: { value: 女, boost: 1.0 } } }, { range: { age: { from: 30, to: 40, include_lower: true, include_upper: true, boost: 1.0 } } } ], must_not: [ { term: { sect.keyword: { value: 明教, boost: 1.0 } } } ], should: [ { term: { address.keyword: { value: 峨眉山, boost: 1.0 } } }, { term: { skill.keyword: { value: 暗器, boost: 1.0 } } } ], adjust_pure_negative: true, minimum_should_match: 1, boost: 1.0 } }} 用java构建这个查询条件:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 构建查询语句boolquerybuilder boolquerybuilder = querybuilders.boolquery() .must(querybuilders.termquery(sex, 女)) .must(querybuilders.rangequery(age).gte(30).lte(40)) .mustnot(querybuilders.termquery(sect.keyword, 明教)) .should(querybuilders.termquery(address.keyword, 峨眉山)) .should(querybuilders.rangequery(power.keyword).gte(50).lte(80)) .minimumshouldmatch(1); // 设置should至少需要满足几个条件// 将boolquerybuilder构建到searchsourcebuilder中searchsourcebuilder.query(boolquerybuilder); 2.2 filter查询 query和filter的区别:query查询的时候,会先比较查询条件,然后计算分值,最后返回文档结果;而filter是先判断是否满足查询条件,如果不满足会缓存查询结果(记录该文档不满足结果),满足的话,就直接缓存结果,filter不会对结果进行评分,能够提高查询效率。
filter的使用方式比较多样,下面用几个例子演示一下。
方式一,单独使用:
{ query: { bool: { filter: [ { term: { sex: { value: 男, boost: 1.0 } } } ], adjust_pure_negative: true, boost: 1.0 } }} 单独使用时,filter与must基本一样,不同的是filter不计算评分,效率更高。
java构建查询语句:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 构建查询语句searchsourcebuilder.query(querybuilders.boolquery() .filter(querybuilders.termquery(sex, 男))); 方式二,和must、must_not同级,相当于子查询:
select * from (select * from persons where sect = '明教')) a where sex = '女'; es查询语句:
{ query: { bool: { must: [ { term: { sect.keyword: { value: 明教, boost: 1.0 } } } ], filter: [ { term: { sex: { value: 女, boost: 1.0 } } } ], adjust_pure_negative: true, boost: 1.0 } }} java:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 构建查询语句searchsourcebuilder.query(querybuilders.boolquery() .must(querybuilders.termquery(sect.keyword, 明教)) .filter(querybuilders.termquery(sex, 女))); 方式三,将must、must_not置于filter下,这种方式是最常用的:
{ query: { bool: { filter: [ { bool: { must: [ { term: { sect.keyword: { value: 明教, boost: 1.0 } } }, { range: { age: { from: 20, to: 35, include_lower: true, include_upper: true, boost: 1.0 } } } ], must_not: [ { term: { sex.keyword: { value: 女, boost: 1.0 } } } ], adjust_pure_negative: true, boost: 1.0 } } ], adjust_pure_negative: true, boost: 1.0 } }} java:
searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 构建查询语句searchsourcebuilder.query(querybuilders.boolquery() .filter(querybuilders.boolquery() .must(querybuilders.termquery(sect.keyword, 明教)) .must(querybuilders.rangequery(age).gte(20).lte(35)) .mustnot(querybuilders.termquery(sex.keyword, 女)))); 3 聚合查询 接下来,我们将用一些案例演示es聚合查询。
3.1 最值、平均值、求和 案例:查询最大年龄、最小年龄、平均年龄。
sql:
select max(age) from persons; es:
get /person/_search{ aggregations: { max_age: { max: { field: age } } }} java:
@autowiredprivate resthighlevelclient client;@testpublic void maxquerytest() throws ioexception { // 聚合查询条件 aggregationbuilder aggbuilder = aggregationbuilders.max(max_age).field(age); searchrequest searchrequest = new searchrequest(person); searchsourcebuilder searchsourcebuilder = new searchsourcebuilder(); // 将聚合查询条件构建到searchsourcebuilder中 searchsourcebuilder.aggregation(aggbuilder); system.out.println(searchsourcebuilder-----> + searchsourcebuilder); searchrequest.source(searchsourcebuilder); // 执行查询,获取searchresponse searchresponse response = client.search(searchrequest, requestoptions.default); system.out.println(jsonobject.tojson(response));} 使用聚合查询,结果中默认只会返回10条文档数据(当然我们关心的是聚合的结果,而非文档)。返回多少条数据可以自主控制:
get /person/_search{ size: 20, aggregations: { max_age: { max: { field: age } } }} 而java中只需增加下面一条语句即可:
searchsourcebuilder.size(20); 与max类似,其他统计查询也很简单:
aggregationbuilder minbuilder = aggregationbuilders.min(min_age).field(age);aggregationbuilder avgbuilder = aggregationbuilders.avg(min_age).field(age);aggregationbuilder sumbuilder = aggregationbuilders.sum(min_age).field(age);aggregationbuilder countbuilder = aggregationbuilders.count(min_age).field(age); 3.2 去重查询 案例:查询一共有多少个门派。
sql:
select count(distinct sect) from persons; es:
{ aggregations: { sect_count: { cardinality: { field: sect.keyword } } }} java:
@testpublic void cardinalityquerytest() throws ioexception { // 创建某个索引的request searchrequest searchrequest = new searchrequest(person); // 查询条件 searchsourcebuilder searchsourcebuilder = new searchsourcebuilder(); // 聚合查询 aggregationbuilder aggbuilder = aggregationbuilders.cardinality(sect_count).field(sect.keyword); searchsourcebuilder.size(0); // 将聚合查询构建到查询条件中 searchsourcebuilder.aggregation(aggbuilder); system.out.println(searchsourcebuilder-----> + searchsourcebuilder); searchrequest.source(searchsourcebuilder); // 执行查询,获取结果 searchresponse response = client.search(searchrequest, requestoptions.default); system.out.println(jsonobject.tojson(response));} 3.3 分组聚合 3.3.1 单条件分组 案例:查询每个门派的人数
sql:
select sect,count(id) from mytest.persons group by sect; es:
{ size: 0, aggregations: { sect_count: { terms: { field: sect.keyword, size: 10, min_doc_count: 1, shard_min_doc_count: 0, show_term_doc_count_error: false, order: [ { _count: desc }, { _key: asc } ] } } }} java:
searchrequest searchrequest = new searchrequest(person);searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();searchsourcebuilder.size(0);// 按sect分组aggregationbuilder aggbuilder = aggregationbuilders.terms(sect_count).field(sect.keyword);searchsourcebuilder.aggregation(aggbuilder); 3.3.2 多条件分组 案例:查询每个门派各有多少个男性和女性
sql:
select sect,sex,count(id) from mytest.persons group by sect,sex; es:
{ aggregations: { sect_count: { terms: { field: sect.keyword, size: 10 }, aggregations: { sex_count: { terms: { field: sex.keyword, size: 10 } } } } }} 3.4 过滤聚合 前面所有聚合的例子请求都省略了 query ,整个请求只不过是一个聚合。这意味着我们对全部数据进行了聚合,但现实应用中,我们常常对特定范围的数据进行聚合,例如下例。
案例:查询明教中的最大年龄。这涉及到聚合与条件查询一起使用。
sql:
select max(age) from mytest.persons where sect = '明教'; es:
get /person/_search{ query: { term: { sect.keyword: { value: 明教, boost: 1.0 } } }, aggregations: { max_age: { max: { field: age } } }} java:
searchrequest searchrequest = new searchrequest(person);searchsourcebuilder searchsourcebuilder = new searchsourcebuilder();// 聚合查询条件aggregationbuilder maxbuilder = aggregationbuilders.max(max_age).field(age);// 等值查询searchsourcebuilder.query(querybuilders.termquery(sect.keyword, 明教));searchsourcebuilder.aggregation(maxbuilder); 另外还有一些更复杂的查询例子。
案例:查询0-20,21-40,41-60,61以上的各有多少人。
sql:
select sum(case when age20 and age 40 and age 60 and age <=200 then 1 else 0 end) agegroup4from mytest.persons; es:
{ size: 0, aggregations: { age_avg: { range: { field: age, ranges: [ { from: 0.0, to: 20.0 }, { from: 21.0, to: 40.0 }, { from: 41.0, to: 60.0 }, { from: 61.0, to: 200.0 } ], keyed: false } } }} 查询结果:
aggregations : { age_avg : { buckets : [ { key : 0.0-20.0, from : 0.0, to : 20.0, doc_count : 3 }, { key : 21.0-40.0, from : 21.0, to : 40.0, doc_count : 13 }, { key : 41.0-60.0, from : 41.0, to : 60.0, doc_count : 4 }, { key : 61.0-200.0, from : 61.0, to : 200.0, doc_count : 1 } ] }} 以上是elasticsearch查询的全部内容,丰富详实,堪比操作手册,强烈建议收藏!
风向传感器原理有哪些,常见的风向传感器有哪些
深证区块链50指数详细介绍
MediaTek的无线接入点和客户端会成为WiFi 6E的测试平台
柯洁对弈国产AI“星阵” 机器真的算无遗策?
人工智能自动化领域将是IBM未来的发展重点之一
ElasticSearch进阶:一文全览各种ES查询在Java中的实现
嘉楠科技推出一款全新人工智能芯片 将广泛应用于各领域
自动驾驶安全需做好前期工作 事后诸葛言论并没有什么用处
红米RedmiK20Pro尊享版正式发布 售价2699元起
怎样通过网络发送传真?
怎样更换Win10彩色磁贴
苹果宣布针对iOS的新隐私更新
解决自动语音识别部署难题
宽带网速全球排名,韩国居一,美国第十,中国落在74
大众推出基于MEB平台的ID.R纯电动跑车 或将于2025年进行量产
4602-010-200加速度传感器的性能指标讲解
idt无线充电芯片及方案介绍
机器人应用各行业发展情况如何?未来应该如何发展预测报告
智慧公安情报分析系统开发指挥调度系统搭建
荣耀xSport Pro运动蓝牙耳机开售采用了渐变设计可以连续听歌18个小时