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 @ 2022-04-25 16:45  浪上了天  阅读(748)  评论(0编辑  收藏  举报