elasticsearch学习第四天-springboot整合es巩固

1,148 阅读5分钟

前言

springboot整合es咱们已经做完了之前,今天再把springboot整合es巩固下。

准备数据

你可以使用java代码创建索引,整合之后咱们已经知道保存就是创建嘛。也可以像我下面这样,使用kibana进行创建。 创建索引:

PUT /car
{
  "mappings": {
    "orders": {
      "properties": {
        "color": {
          "type": "keyword"
        },
        "make": {
          "type": "keyword"
        }
      }
    }
  }
}

注意:在ES中,需要进行聚合、排序、过滤的字段其处理方式比较特殊,因此不能被分词,必须使用keyword数值类型。这里我们将color和make这两个文字类型的字段设置为keyword类型,这个类型不会被分词,将来就可以参与聚合

导入数据,复制下直接在kibana运行就可以了。

POST /car/orders/_bulk
{ "index": {}}
{ "price" : 10000, "color" : "红", "make" : "本田", "sold" : "2014-10-28" }
{ "index": {}}
{ "price" : 20000, "color" : "红", "make" : "本田", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 30000, "color" : "绿", "make" : "福特", "sold" : "2014-05-18" }
{ "index": {}}
{ "price" : 15000, "color" : "蓝", "make" : "丰田", "sold" : "2014-07-02" }
{ "index": {}}
{ "price" : 12000, "color" : "绿", "make" : "丰田", "sold" : "2014-08-19" }
{ "index": {}}
{ "price" : 20000, "color" : "红", "make" : "本田", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 80000, "color" : "红", "make" : "宝马", "sold" : "2014-01-01" }
{ "index": {}}
{ "price" : 25000, "color" : "蓝", "make" : "福特", "sold" : "2014-02-12" }

开始练习

创建car这个index对应的实体类:

package com.cmdc.bean;

import lombok.*;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.util.Date;

/**
 * @author : wuwensheng
 * @date : 16:47 2021/12/6
 */
@Document(indexName = "car", type = "orders", shards = 1, replicas = 0)
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Car {
    @Id
    private Long _id;

    @Field(type = FieldType.Long)
    private long price;

    @Field(type = FieldType.Keyword)
    private String color;

    @Field(type = FieldType.Keyword)
    private String make;

    @Field(type = FieldType.Date)
    private Date sold;
}

springboot整合测试:

import com.cmdc.EsApplication;
import com.cmdc.bean.Car;
import com.cmdc.dao.CarRepository;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.avg.Avg;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

/**
 * @author : wuwensheng
 * @date : 16:52 2021/12/6
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = EsApplication.class)
public class CarTest {
@Autowired
private CarRepository carRepository;

}

开始做:

查询所有

@Test
public void getAll() {
    for (Car car : carRepository.findAll()) {
        System.out.println("当前小车是:" + car);
    }
}

image.png

查询颜色是红色的小车,使用match匹配的手法

/**
 * 查询颜色是红色的小车
 */
@Test
public void matchQueryColor() {
    MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("color", "红");
    Iterable<Car> search = carRepository.search(matchQueryBuilder);
    for (Car car : search) {
        System.out.println("当前小车:" + car);
    }
}

image.png

查询颜色是红色的小车,使用term手法

/**
 * 当然 使用term查询来查询颜色是红色的小车一样也是可以的
 */
@Test
public void termQueryColor() {
    TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("color", "红");
    Iterable<Car> search = carRepository.search(termsQueryBuilder);
    search.forEach(System.out::println);
}

image.png

bool查询练习

/**
 * ## 选取不仅颜色是红色的,并且是本田品牌的汽车,并且价格不是20000,那么这里需要bool查询
 */
@Test
public void testBool() {
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("color", "红");
    MatchQueryBuilder term2QueryBuilder = new MatchQueryBuilder("make", "本田");
    boolQueryBuilder.must(matchQueryBuilder);
    boolQueryBuilder.must(term2QueryBuilder);
    boolQueryBuilder.mustNot(QueryBuilders.rangeQuery("price").gte(20000L).lte(20000L));
    carRepository.search(boolQueryBuilder).forEach(System.out::println);
}

image.png

上面的bool在NativeSearchQueryBuilder也可以加以体现,并且是不会影响排名的过滤

/**
 * 现在希望查询出是本田制造 然后将价格为20000的过滤掉
 */
@Test
public void filterTest() {
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
    nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("make", "本田").operator(Operator.AND));
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    boolQueryBuilder.mustNot(QueryBuilders.rangeQuery("price").gte(20000L).lte(20000L));
    nativeSearchQueryBuilder.withFilter(boolQueryBuilder);
    Page<Car> search = carRepository.search(nativeSearchQueryBuilder.build());
    // 打印总条数
    System.out.println(search.getTotalElements());
    // 打印总页数
    System.out.println(search.getTotalPages());
    search.forEach(System.out::println);
}

image.png 并且咱们还得到了总条数和总页数。

排序、分页练习

/**
 * 排序 还有 分页的测试
 */
@Test
public void sortPageTest() {
    // 首先随便查询点什么
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
    // 构建查询的条件,就把红色的车查询出来
    nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("color", "红"));
    // 坐下过滤 将价格10000以下的直接过滤掉
    nativeSearchQueryBuilder.withFilter(QueryBuilders.rangeQuery("price").gt(10000L).lte(100000L));
    // 接下来做下排序 按照价格降序排列
    nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC));
    // 接下来再来下分页的查询
    nativeSearchQueryBuilder.withPageable(PageRequest.of(0, 1));
    // 进行查询看下结果
    Page<Car> search = carRepository.search(nativeSearchQueryBuilder.build());
    // 那么现在呢,除了获取结果之外。还能获取总的页数和总条数
    int totalPages = search.getTotalPages();
    long totalElements = search.getTotalElements();
    System.out.println("总页数:" + totalPages);
    System.out.println("总条数:" + totalElements);
    for (Car car : search) {
        System.out.println("car:" + car);
    }
}

image.png

集合查询,按颜色聚合

特别注意下,用来聚合的字段不要忘记加个‘.keyword’。

/**
 * 接下来咱们开始做聚合查询的操作
 * 第一个,按照颜色分桶
 */
@Test
public void colorAggregationTest() {
    NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
    // 不查询任何结果
    queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
    // 1、添加一个新的聚合,聚合类型为terms,聚合名称为color_aggregation,聚合字段为color
    queryBuilder.addAggregation(
            AggregationBuilders.terms("color_aggregation").field("color.keyword"));
    // 2、查询,需要把结果强转为AggregatedPage类型
    AggregatedPage<Car> aggPage = (AggregatedPage<Car>) this.carRepository.search(queryBuilder.build());
    // 3、解析
    // 3.1、从结果中取出名为brands的那个聚合,
    // 因为是利用String类型字段来进行的term聚合,所以结果要强转为Terms类型
    Terms agg = (Terms) aggPage.getAggregation("color_aggregation");
    // 3.2、获取桶
    List<? extends Terms.Bucket> buckets = agg.getBuckets();
    // 3.3、遍历
    for (Terms.Bucket bucket : buckets) {
        // 3.4、获取桶中的key,颜色
        System.out.println(bucket.getKeyAsString());
        // 3.5、获取桶中的文档数量
        System.out.println(bucket.getDocCount());
    }
}

image.png

按制造商聚合,且桶内度量每个制造商的平均价格、最后嵌套一个颜色的桶

/**
 * 这把除了在制造商的前提下,进行桶内的平均售价的度量之外,我们再嵌套一个桶,区分出不同制造商制造的小车都是什么颜色的
 */
@Test
public void makeAggregationTest3() {
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
    // 不查询任何的结果
    nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
    // 添加一个聚合
    TermsAggregationBuilder termsAggregationBuilder =
            AggregationBuilders.terms("make_aggregation").field("make.keyword");
    termsAggregationBuilder.subAggregation(AggregationBuilders.avg("avg_price").field("price"));
    termsAggregationBuilder.subAggregation(AggregationBuilders.terms("color_a").field("color.keyword"));
    nativeSearchQueryBuilder.addAggregation(termsAggregationBuilder);
    // 查询并且强转结果
    AggregatedPage<Car> search = (AggregatedPage<Car>) carRepository.search(nativeSearchQueryBuilder.build());
    // 取出聚合记过
    Terms make_aggregation = (Terms) search.getAggregation("make_aggregation");
    // 获取聚合中的桶
    List<? extends Terms.Bucket> buckets = make_aggregation.getBuckets();
    // 执行遍历
    for (Terms.Bucket bucket : buckets) {
        Avg avg_price = (Avg) bucket.getAggregations().asMap().get("avg_price");
        Terms color_a = (Terms) bucket.getAggregations().asMap().get("color_a");
        List<? extends Terms.Bucket> buckets1 = color_a.getBuckets();
        for (Terms.Bucket bucket1 : buckets1) {
            String keyAsString = bucket1.getKeyAsString();
            long docCount = bucket1.getDocCount();
            System.out.println("当前的制造商下,颜色为:" + keyAsString + "的小车共有:" + docCount + "台");
        }
        double value = avg_price.getValue();
        long docCount = bucket.getDocCount();
        String keyAsString = bucket.getKeyAsString();
        System.out.println("制造商:" + keyAsString + "制造的小车共有:" + docCount + "个。" + "均价是:" + value);
        System.out.println("===================================================================");
    }
}

image.png ok!

我多补充一句,如果在使用match查询的时候查询的结果和预期有差距,考虑下增加下面的操作:

image.png

如果使用term查询的时候查询的结果和预期有差距,考虑下下面的操作;

image.png

ok,接下来我是有极大的可能在实战中使用es的,到时候的心得体会再分享给大家。