前言
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);
}
}
查询颜色是红色的小车,使用match匹配的手法
/**
* 查询颜色是红色的小车
*/
@Test
public void matchQueryColor() {
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("color", "红");
Iterable<Car> search = carRepository.search(matchQueryBuilder);
for (Car car : search) {
System.out.println("当前小车:" + car);
}
}
查询颜色是红色的小车,使用term手法
/**
* 当然 使用term查询来查询颜色是红色的小车一样也是可以的
*/
@Test
public void termQueryColor() {
TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("color", "红");
Iterable<Car> search = carRepository.search(termsQueryBuilder);
search.forEach(System.out::println);
}
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);
}
上面的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);
}
并且咱们还得到了总条数和总页数。
排序、分页练习
/**
* 排序 还有 分页的测试
*/
@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);
}
}
集合查询,按颜色聚合
特别注意下,用来聚合的字段不要忘记加个‘.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());
}
}
按制造商聚合,且桶内度量每个制造商的平均价格、最后嵌套一个颜色的桶
/**
* 这把除了在制造商的前提下,进行桶内的平均售价的度量之外,我们再嵌套一个桶,区分出不同制造商制造的小车都是什么颜色的
*/
@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("===================================================================");
}
}
ok!
我多补充一句,如果在使用match查询的时候查询的结果和预期有差距,考虑下增加下面的操作:
如果使用term查询的时候查询的结果和预期有差距,考虑下下面的操作;
ok,接下来我是有极大的可能在实战中使用es的,到时候的心得体会再分享给大家。