elasticsearch查询

ElasticSearch查询

  • 基本查询
  • 分词查询
  • 词条查询
  • 范围查询
  • 布尔查询
    • filter功能
  • source筛选
  • 排序
  • 分页
  • 高亮
  • 聚合

查询所有-matchAll

GET /user/_search
{
"query": {
"match_all": {}
}
}

java Api

思路分析:

  1. 创建SearchSourceBuilder对象
  2. 添加查询条件QueryBuilders
  3. 如:添加排序、分页等其它条件
  4. 创建SearchRequest对象,并制定索引库名称
  5. 添加SearchSourceBuilder对象到SearchRequest对象source中
  6. 发起请求,得到结果
  7. 解析结果SearchResponse
  8. 获取总条数
  9. 获取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

思路分析:

  1. 创建SearchSourceBuilder对象
  2. 添加查询条件QueryBuilders.termQuery()
  3. 创建SearchRequest对象,并制定索引库名称
  4. 添加SearchSourceBuilder对象到SearchRequest对象source中
  5. 发起请求,得到结果
  6. 解析结果SearchResponse
  7. 获取总条数
  8. 获取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去倒排索引中寻找,适合keywordnumericdate查询

模糊查询-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条记录。 通过fromsize来指定分页的开始位置及每页大小。

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

  1. 创建SearchRequest对象,并制定索引库名称
  2. 创建SearchSourceBuilder对象,设置分组相关参数
  3. 添加SearchSourceBuilder对象到SearchRequest对象source中
  4. 执行查询
  5. 得到查询结果
  6. 解析分组查询数据
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
posted @   浪上了天  阅读(794)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示