elasticsearch查询
ElasticSearch查询
- 基本查询
- 分词查询
- 词条查询
- 范围查询
- 布尔查询
- filter功能
- source筛选
- 排序
- 分页
- 高亮
- 聚合
查询所有-matchAll
GET /user/_search { "query": { "match_all": {} } }
java Api
思路分析:
- 创建SearchSourceBuilder对象
- 添加查询条件QueryBuilders
- 如:添加排序、分页等其它条件
- 创建SearchRequest对象,并制定索引库名称
- 添加SearchSourceBuilder对象到SearchRequest对象source中
- 发起请求,得到结果
- 解析结果SearchResponse
- 获取总条数
- 获取SearchHits数组,并遍历
- 获取其中的
_source
,是JSON数据 - 把
_source
反序列化为User对象
- 获取其中的
public void basicMatch() throws IOException { //1.创建SearchSourceBuilder对象 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); //添加查询条件QueryBuilders,这里选择match_all sourceBuilder.query(QueryBuilders.matchAllQuery()); //创建SearchRequest对象,并指定索引库名称 SearchRequest request = new SearchRequest("user"); //添加SearchSourceBuilder对象到SearchRequest中 request.source(sourceBuilder); //发送请求,得到结果 SearchResponse response = client.search(request, RequestOptions.DEFAULT); //解析结果 SearchHits hits = response.getHits(); //获取总条数 long total = hits.getTotalHits().value; System.out.println("总的数目:" +total); //获取SearchHits数组,拿到查询结果 SearchHit[] hitsHits = hits.getHits(); for (SearchHit hit : hitsHits) { //获取得分 float score = hit.getScore(); System.out.println("分数:"+score ); //获得其中的_source, 是json数据 String json = hit.getSourceAsString(); //把得到的json数据转化为User对象 User user = JSON.parseObject(json, User.class); System.out.println(user); } }
词条查询-termQuery
将查询中的字符串当做一个词条。
term查询
和字段类型有关系
Elasticsearch中有两个数据类型
- text:会分词,不支持聚合
- keyword:不会分词,支持聚合
# keyword类型 GET /user/_search { "query": { "term": { "name": { "value": "小红" } } } }
#text类型 GET /user/_search { "query": { "term": { "note": { "value": "主角" } } } }
JavaApi
思路分析:
- 创建SearchSourceBuilder对象
- 添加查询条件QueryBuilders.termQuery()
- 创建SearchRequest对象,并制定索引库名称
- 添加SearchSourceBuilder对象到SearchRequest对象source中
- 发起请求,得到结果
- 解析结果SearchResponse
- 获取总条数
- 获取SearchHits数组,并遍历
- 获取其中的
_source
,是JSON数据 - 把
_source
反序列化为User对象
- 获取其中的
public void termMatch() throws IOException { //1.创建SearchSourceBuilder对象 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); //添加查询条件QueryBuilders,这里选择term_query sourceBuilder.query(QueryBuilders.termQuery("note","主角")); //创建SearchRequest对象,并指定索引库名称 SearchRequest request = new SearchRequest("user"); //添加SearchSourceBuilder对象到SearchRequest中 request.source(sourceBuilder); //发送请求,得到结果 SearchResponse response = client.search(request, RequestOptions.DEFAULT); //解析结果 SearchHits hits = response.getHits(); //获取总条数 long total = hits.getTotalHits().value; System.out.println("总的数目:" +total); //获取SearchHits数组,拿到查询结果 SearchHit[] hitsHits = hits.getHits(); for (SearchHit hit : hitsHits) { //获取得分 float score = hit.getScore(); System.out.println("分数:"+score ); //获得其中的_source, 是json数据 String json = hit.getSourceAsString(); //把得到的json数据转化为User对象 User user = JSON.parseObject(json, User.class); System.out.println(user); } }
term查询特点对搜索的关键词不分词查询
分词匹配查询-matchQuery
match查询:
- 会对查询条件进行分词
- 然后将分词后的查询条件和词条进行等值匹配
- 默认取并集
GET /user/_search { "query": { "match": { "note": "小红是主角" } } }
JavaApi
public void matchQuery() throws IOException { MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("note", "小红是主角"); //指定分词器 // matchQueryBuilder.analyzer("ik_max_word"); //分词后各个词之间的关系默认是OR matchQueryBuilder.operator(Operator.OR); parseResponse(matchQueryBuilder); }
private void parseResponse(QueryBuilder queryBuilder) throws IOException { SearchSourceBuilder query = new SearchSourceBuilder(); query.query(queryBuilder); //创建SearchRequest对象,并指定索引库名称 SearchRequest request = new SearchRequest("user"); //添加SearchSourceBuilder对象到SearchRequest中 request.source(query); //发送请求,得到结果 SearchResponse response = client.search(request, RequestOptions.DEFAULT); //解析结果 SearchHits hits = response.getHits(); //获取总条数 long total = hits.getTotalHits().value; System.out.println("总的数目:" + total); //获取SearchHits数组,拿到查询结果 SearchHit[] hitsHits = hits.getHits(); for (SearchHit hit : hitsHits) { //获取得分 float score = hit.getScore(); System.out.println("分数:" + score); //获得其中的_source, 是json数据 String json = hit.getSourceAsString(); //把得到的json数据转化为User对象 User user = JSON.parseObject(json, User.class); System.out.println(user); } }
- term query 会将查询条件当做一个term去倒排索引中寻找,适合
keyword
、numeric
、date
查询
模糊查询-fuzzy
fuzzy查询默认会按照一定的规则来修正当前查询的条件。
GET /user/_search { "query": { "fuzzy": { "note": { "value": "我是主角", "fuzziness": 2 } } } }
JavaApi
public void fuzzyQuery() throws IOException { FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("note", "我是主角"); fuzzyQueryBuilder.fuzziness(Fuzziness.TWO); parseResponse(fuzzyQueryBuilder); //见上面解析应答函数 }
范围&排序查询-range&sort
//查询年龄在20到28之间的数据,(左闭右开) //再先按年龄倒序,在按id倒序 GET /user/_search { "query": { "range": { "age": { "gte": 20, "lte": 28 } } }, "sort": [ { "age": { "order": "desc" } }, { "id":{ "order": "desc" } } ] }
JavaApi
- 构建rangeQuery对象
- 在sourceBuilder添加排序条件,(排序是对结果的重组,对条件不产生影响)
public void rangeQuery() throws IOException { RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age"); rangeQueryBuilder.gte(20); rangeQueryBuilder.lte(28); parseResponse(rangeQueryBuilder); } //在parseResponse中添加排序规则 //在构建query之后构建 query.sort("age", SortOrder.DESC); query.sort("id",SortOrder.DESC);
多条件查询-queryString
- queryString会对查询条件进行分词。
- 然后将分词后的查询条件和词条进行等值匹配
- 默认取并集(OR)default_operator
- 可以指定多个查询字段
query_string:识别query中的连接符(or 、and)
GET /user/_search { "query": { "query_string": { "fields":["name","note"], "query": "我是主角" //能查到4条记录 "我是 AND 主角" //一条记录都查询不到 } } }
query_string:有default_operator连接符的脚本
GET /user/_search { "query": { "query_string": { "fields":["name","note"], "query": "我是主角", "default_operator": "AND" //一条记录都查不到 } } }
默认搜索域:如果我们查询时不指定搜索的域,那么就按照默认给定的搜索域查询
GET /user/_search { "query": { "query_string": { //"fields":["name","note"], "query": "我是主角", "default_operator": "AND", "default_field": "note" //设置默认搜索域 } } }
simple_query_string
:不识别query中的连接符(or 、and),查询时会将条件分词后分别进行查询
GET /user/_search { "query": { "simple_query_string": { "query": "我是 AND 主角", //查到4条记录 "fields": ["name","note"] } } }
JavaApi
public void queryStringQuery() throws IOException { QueryStringQueryBuilder stringQueryBuilder = QueryBuilders .queryStringQuery("我是主角").field("name").field("note"); parseResponse(stringQueryBuilder); }
bool查询&结果过滤-boolQuery
boolQuery:对多个查询条件连接。
连接方式:
- must(and):条件必须成立
- must_not(not):条件必须不成立
- should(or):条件可以成立
- filter:条件必须成立,性能比must高。不会计算得分
得分:即条件匹配度,匹配度越高,得分越高
默认情况下,所有的查询条件、过滤条件都会影响打分和排名。而对搜索结果打分是比较影响性能的,因此我们一般只对用户输入的搜索条件对应的字段打分,其它过滤项不打分。此时就不能简单实用布尔查询的must来组合条件了,而是实用filter
方式。
GET /user/_search { "query": { "bool": { "should": [ { "match": { "note": "主角" } } ], "filter": [ { "term":{ "name":"Rose" } }, { "range":{ "age":{ "gte":10, "lte":20 } } } ] } } }
JavaApi
public void boolQuery() throws IOException { //构建bool条件对象 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); //构建matchQuery对象,查询note中包含主角的信息 MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("note", "主角"); boolQueryBuilder.should(matchQuery); //过滤姓名包含rose的 TermQueryBuilder termQuery = QueryBuilders.termQuery("name", "Rose"); boolQueryBuilder.filter(termQuery); //年龄在10-20之间的 RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age").gte(10).lte(20); boolQueryBuilder.filter(rangeQuery); parseResponse(boolQueryBuilder); }
分页查询-from
默认情况下ES会设置size=10,查询10条记录。 通过from
和size
来指定分页的开始位置及每页大小。
GET /user/_search { "query": { "match_all": {} }, "from": 0, "size": 2 }
JavaApi
public void basicMatch() throws IOException { //1.创建SearchSourceBuilder对象 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); //添加查询条件QueryBuilders,这里选择match_all //创建SearchRequest对象,并指定索引库名称 parseResponse(QueryBuilders.matchAllQuery()); } //parseRespone中添加 query.from(0); query.size(2);
其本质是逻辑分页,因此为了避免深度分页的问题,ES限制最多查到第10000条。
如果需要查询到10000以后的数据,你可以采用两种方式:
- scroll滚动查询
- search after
可以使用from
和对结果进行分页,size
但是当达到深度分页时,成本变得过高。index.max_result_window
默认值是10,000 ,这是一种保护措施,搜索请求占用的堆内存和时间成正比from + size
。建议使用滚动api进行有效的深度滚动,但是滚动上下文的成本很高,因此不建议将其用于实时用户请求。该search_after
参数通过提供活动光标来解决此问题。想法是使用上一页的结果来帮助下一页的检索
高亮查询-highlight
高亮是在搜索结果中把搜索关键字标记出来,因此必须使用match这样的条件搜索。
高亮要素:
- pre_tags:前置标签,可以省略,默认是em
- post_tags:后置标签,可以省略,默认是em
- fields:需要高亮的字段
- title:这里声明title字段需要高亮,后面可以为这个字段设置特有配置,也可以空
GET /user/_search { "query": { "match": { "note": "主角" } }, "highlight": { "fields": { "note":{ "pre_tags": "<font color='red'>", "post_tags": "</font>" } } } }
JavaApi
public void matchQuery() throws IOException { MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("note", "主角"); //分词后各个词之间的关系默认是OR matchQueryBuilder.operator(Operator.OR); parseResponse(matchQueryBuilder); } //parseResponsez中添加 //构建高亮元素 HighlightBuilder highlight = SearchSourceBuilder.highlight(); highlight.field("note"); highlight.preTags("<font color='red'>"); highlight.postTags("</font>"); query.highlighter(highlight); //解析高亮标签 HighlightField highlightField = hit.getHighlightFields().get("note"); Text[] fragments = highlightField.getFragments(); String note = Arrays.toString(fragments); if(StringUtils.isNotBlank(note)){ user.setNote(note); }
聚合查询-aggregation
聚合(aggregations)可以让我们极其方便的实现对数据的统计、分析。例如:
- 什么品牌的手机最受欢迎?
- 这些手机的平均价格、最高价格、最低价格?
- 这些手机每月的销售情况如何?
参与聚合的字段,必须是keyword类型
Elasticsearch中的聚合,包含多种类型,最常用的两种,一个叫桶
,一个叫度量
:
-
桶
桶的作用,是按照某种方式对数据进行分组,每一组数据在ES中称为一个
桶
,例如我们根据国籍对人划分,可以得到中国桶
、英国桶
,日本桶
……或者我们按照年龄段对人进行划分:010,1020,2030,3040等。Elasticsearch中提供的划分桶的方式有很多:
- Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
- Histogram Aggregation:根据数值阶梯分组,与日期类似,需要知道分组的间隔(interval)
- Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组,类似数据库group by
- Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组
- ……
我们发现bucket aggregations 只负责对数据进行分组,并不进行计算,因此往往bucket中往往会嵌套另一种聚合:metrics aggregations即度量
-
度量
分组完成以后,我们一般会对组中的数据进行聚合运算,例如求平均值、最大、最小、求和等,这些在ES中称为
度量
比较常用的一些度量聚合方式:
- Avg Aggregation:求平均值
- Max Aggregation:求最大值
- Min Aggregation:求最小值
- Percentiles Aggregation:求百分比
- Stats Aggregation:同时返回avg、max、min、sum、count等
- Sum Aggregation:求和
- Top hits Aggregation:求前几
- Value Count Aggregation:求总数
GET /car/_search { "aggs": { "popular_colors": { "terms": { "field": "color.keyword" } } }, "size": 0 }
JavaApi
- 创建SearchRequest对象,并制定索引库名称
- 创建SearchSourceBuilder对象,设置分组相关参数
- 添加SearchSourceBuilder对象到SearchRequest对象source中
- 执行查询
- 得到查询结果
- 解析分组查询数据
public void aggTest() throws IOException { SearchRequest request = new SearchRequest("car"); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); sourceBuilder.aggregation(AggregationBuilders.terms("popular_color").field("color.keyword")); sourceBuilder.size(0); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); SearchHits hits = response.getHits(); long total = hits.getTotalHits().value; System.out.println("总数:"+total); Terms terms = (Terms)response.getAggregations().asMap().get("popular_color"); List<? extends Terms.Bucket> buckets = terms.getBuckets(); for (Terms.Bucket bucket : buckets) { System.out.println(bucket.getKey() + ": " + bucket.getDocCount()); } } //总数:8 //红: 4 //绿: 2 //蓝: 2
//求出每种颜色的平均价格 GET /car/_search { "aggs": { "popular_colors": { "terms": { "field": "color.keyword" }, "aggs": { "avg_price": { "avg": { "field": "price" } } } } }, "size": 0 }
TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("popular_color").field("color.keyword"); AvgAggregationBuilder avgAggregationBuilder = AggregationBuilders.avg("avg_price").field("price"); aggregationBuilder.subAggregation(avgAggregationBuilder); sourceBuilder.aggregation(aggregationBuilder); ParsedAvg parsedAvg = (ParsedAvg)bucket.getAggregations().getAsMap().get("avg_price"); System.out.println(bucket.getKey()+ "平均价格"+ parsedAvg.getValue()); //总数:8 //红: 4 //红平均价格32500.0 //绿: 2 //绿平均价格21000.0 //蓝: 2 //蓝平均价格20000.0
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!