springboot2.x+整合electricsearch7.0.1

pom

springboot2.x+整合electricsearch7.0.1
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <parent>
 6         <groupId>org.springframework.boot</groupId>
 7         <artifactId>spring-boot-starter-parent</artifactId>
 8         <version>2.2.5.RELEASE</version>
 9         <relativePath/> <!-- lookup parent from repository -->
10     </parent>
11     <groupId>com.example</groupId>
12     <artifactId>demo</artifactId>
13     <version>0.0.1-SNAPSHOT</version>
14     <name>demo</name>
15     <description>Demo project for Spring Boot</description>
16 
17     <properties>
18         <java.version>1.8</java.version>
19     </properties>
20     <repositories>
21         <repository>
22             <id>spring-milestones</id>
23             <name>Spring Milestones</name>
24             <url>https://repo.spring.io/milestone</url>
25         </repository>
26     </repositories>
27     <dependencies>
28         <dependency>
29             <groupId>com.alibaba</groupId>
30             <artifactId>fastjson</artifactId>
31             <version>1.2.8</version>
32         </dependency>
33         <dependency>
34             <groupId>org.projectlombok</groupId>
35             <artifactId>lombok</artifactId>
36             <optional>true</optional>
37         </dependency>
38         <dependency>
39             <groupId>org.springframework.boot</groupId>
40             <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
41         </dependency>
42         <dependency>
43             <groupId>org.springframework.boot</groupId>
44             <artifactId>spring-boot-starter-web</artifactId>
45         </dependency>
46 
47         <dependency>
48             <groupId>org.springframework.boot</groupId>
49             <artifactId>spring-boot-starter-test</artifactId>
50             <scope>test</scope>
51             <exclusions>
52                 <exclusion>
53                     <groupId>org.junit.vintage</groupId>
54                     <artifactId>junit-vintage-engine</artifactId>
55                 </exclusion>
56             </exclusions>
57         </dependency>
58     </dependencies>
59     <build>
60         <plugins>
61             <plugin>
62                 <groupId>org.springframework.boot</groupId>
63                 <artifactId>spring-boot-maven-plugin</artifactId>
64             </plugin>
65         </plugins>
66     </build>
67 
68 </project>
pom
Goods.java
springboot2.x+整合electricsearch7.0.1
 1 package com.example.demo;
 2 
 3 import lombok.AllArgsConstructor;
 4 import lombok.NoArgsConstructor;
 5 import org.springframework.data.annotation.Id;
 6 import org.springframework.data.elasticsearch.annotations.Document;
 7 import org.springframework.data.elasticsearch.annotations.Field;
 8 import org.springframework.data.elasticsearch.annotations.FieldType;
 9 
10 /**
11  * @Author: pengbenlei
12  * @Date: 2020/3/9 11:39
13  * @Description:
14  */
15 @NoArgsConstructor
16 @AllArgsConstructor
17 @Document(indexName = "item",type = "docs", shards = 1, replicas = 0)
18 public class Goods {
19     @Id
20     private Long id;
21     @Field(type = FieldType.Text, analyzer = "ik_max_word")
22     private String title; //标题
23     @Field(type = FieldType.Keyword)
24     private String category;// 分类
25     @Field(type = FieldType.Keyword)
26     private String brand; // 品牌
27     @Field(type = FieldType.Double)
28     private Double price; // 价格
29     @Field(index = false, type = FieldType.Keyword)
30     private String images; // 图片地址
31 
32     public Goods(long id, String title, String category, String brand, double price, String images) {
33         this.id=id;
34         this.title=title;
35         this.category=category;
36         this.brand=brand;
37         this.price=price;
38         this.images=images;
39     }
40 
41     public Long getId() {
42         return id;
43     }
44 
45     public void setId(Long id) {
46         this.id = id;
47     }
48 
49     public String getTitle() {
50         return title;
51     }
52 
53     public void setTitle(String title) {
54         this.title = title;
55     }
56 
57     public String getCategory() {
58         return category;
59     }
60 
61     public void setCategory(String category) {
62         this.category = category;
63     }
64 
65     public String getBrand() {
66         return brand;
67     }
68 
69     public void setBrand(String brand) {
70         this.brand = brand;
71     }
72 
73     public Double getPrice() {
74         return price;
75     }
76 
77     public void setPrice(Double price) {
78         this.price = price;
79     }
80 
81     public String getImages() {
82         return images;
83     }
84 
85     public void setImages(String images) {
86         this.images = images;
87     }
88 }
商品类
商品类中有几个注解,解释一下:
1、@Document (相当于Hibernate实体的@Entity/@Table)(必写),加上了@Document注解之后,默认情况下这个实体中所有的属性都会被建立索引、并且分词。
Document参数对照表
类型 属性名 默认值 说明
String indexName
无 索引库的名称,建议以项目的名称命名
 
String type ""
类型,建议以实体的名称命名
short shards 5
默认分区数
short replica 1
每个分区默认的备份数
String refreshInterval "1s" 刷新间隔
String indexStoreType "fs" 索引文件储存

2、@Id (相当于Hibernate实体的主键@Id注解)(必写)
3、@Field (相当于Hibernate实体的@Column注解),@Field默认是可以不加的,默认所有属性都会添加到ES中。加上@Field之后,@document默认把所有字段加上索引失效,只有加@Field 才会被索引(同时也看设置索引的属性是否为no)

                            Field 参数对照表

类型 属性名 默认值 说明
FieldType type FieldType.Auto 自动检测属性的类型
FieldIndex index FieldIndex.analyzed 默认情况下粉尘
boolean store false 默认情况下不存储原文
String searchAnalyzer "" 指定字段搜索是使用的分词器
String indexAnalyzer "" 指定字段建立索引时指定的分词器
String[] ignoreFields {} 如果某个字段需要被忽略


type:字段类型,是是枚举:FieldType,可以是text、long、short、date、integer、object等
text:存储数据时候,会自动分词,并生成索引
keyword:存储数据时候,不会分词建立索引
Numerical:数值类型,分两类
基本数据类型:long、interger、short、byte、double、float、half_float
浮点数的高精度类型:scaled_float
需要指定一个精度因子,比如10或100。elasticsearch会把真实值乘以这个因子后存储,取出时再还原。
Date:日期类型
elasticsearch可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。
index:是否索引,布尔类型,默认是true
store:是否存储,布尔类型,默认是false
analyzer:分词器名称,这里的ik_max_word即使用ik分词器
GoodsRepository.java
springboot2.x+整合electricsearch7.0.1
 1 package com.example.demo;
 2 
 3 import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
 4 import org.springframework.stereotype.Component;
 5 
 6 import java.util.List;
 7 
 8 /**
 9  * @Author: pengbenlei
10  * @Date: 2020/3/9 11:40
11  * @Description:
12  */
13 @Component
14 public interface GoodsRepository extends ElasticsearchRepository<Goods,Long> {
15 
16     /**
17      * 根据价格区间查询
18      * @param price1
19      * @param price2
20      * @return
21      */
22     List<Goods> findByPriceBetween(double price1, double price2);
23 
24     Goods findByTitle(String title);
25 }
商品仓接口

其中有两个方法,没有实现,确能正常执行,是因为方法名称中的Between和findBy关键字,es会智能根据关键字和字段名称拼接查询,对照表如下:

查询关键字对照表
关键字 方法名称实例  
And findByNameAndPrice }
Or findByNameOrPrice  
Is findByName  
Not findByNameNot  
Between findByPriceBetween  
LessThanEqual findByPriceLessThan  
GreaterThanEqual findByPriceGreaterThan  
Before findByPriceBefore  
After findByPriceAfter  
Like findByNameLike  
StartingWith findByNameStartingWith  
EndingWith findByNameEndingWith  
Contains/Containing findByNameContaining  
In
findByNameIn(Collection<String>names)
 
NotIn
findByNameNotIn(Collection<String>names)
 
Near
findByStoreNear Not Supported Yet !
 
True findByAvailableTrue  
False findByAvailableFalse  
OrderBy findByAvailableTrueOrderByNameDesc  

 

基础测试类:

 

springboot2.x+整合electricsearch7.0.1
  1 package com.example.demo;
  2 
  3 import com.alibaba.fastjson.JSON;
  4 import org.elasticsearch.action.get.MultiGetRequest;
  5 import org.elasticsearch.client.Client;
  6 import org.elasticsearch.client.RestHighLevelClient;
  7 import org.elasticsearch.index.query.MatchQueryBuilder;
  8 import org.elasticsearch.index.query.QueryBuilders;
  9 import org.elasticsearch.search.aggregations.AggregationBuilders;
 10 import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
 11 import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg;
 12 import org.elasticsearch.search.sort.SortBuilders;
 13 import org.elasticsearch.search.sort.SortOrder;
 14 import org.junit.jupiter.api.Test;
 15 import org.springframework.beans.factory.annotation.Autowired;
 16 import org.springframework.boot.test.context.SpringBootTest;
 17 import org.springframework.data.domain.Page;
 18 import org.springframework.data.domain.PageRequest;
 19 import org.springframework.data.domain.Sort;
 20 import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
 21 import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
 22 import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
 23 import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
 24 
 25 import java.util.ArrayList;
 26 import java.util.List;
 27 
 28 @SpringBootTest
 29 class DemoApplicationTests {
 30     @Autowired
 31     GoodsRepository goodsRepository;
 32     @Test
 33     void contextLoads() {
 34     }
 35 //    @Autowired
 36 //    private ElasticsearchTemplate elasticsearchTemplate;
 37 
 38 
 39 
 40 
 41     @Test
 42     public void addDocument() {
 43         Goods goods = new Goods(1L, "大米1S", "手机",
 44                 "大米", 3499.00, "https://img13.360buyimg.com/n1/s450x450_jfs/t1/79993/29/9874/153231/5d7809f4E8f387bff/1dc9e1b6b262f0fb.jpg");
 45         Goods goods0 = goodsRepository.save(goods);
 46         System.out.println(JSON.toJSONString(goods0));
 47     }
 48 
 49     /**
 50      * 批量新增
 51      */
 52     @Test
 53     public void createDocumentList() {
 54         List<Goods> list = new ArrayList<>();
 55         list.add(new Goods(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.leyou.com/123.jpg"));
 56         list.add(new Goods(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.leyou.com/3.jpg"));
 57         // 接收对象集合,实现批量新增
 58         goodsRepository.saveAll(list);
 59     }
 60 
 61     @Test
 62     public void findDocument() {
 63         // 查询全部,并安装价格降序排序
 64         Iterable<Goods> goodsIterable = this.goodsRepository.findAll(Sort.by(Sort.Direction.DESC, "price"));
 65         goodsIterable.forEach(goods -> System.out.println(JSON.toJSONString(goods)));
 66     }
 67 
 68     @Test
 69     public void indexList() {
 70         List<Goods> list = new ArrayList<>();
 71         list.add(new Goods(1L, "小米手机7", "手机", "小米", 3299.00, "http://image.leyou.com/13123.jpg"));
 72         list.add(new Goods(2L, "坚果手机R1", "手机", "锤子", 3699.00, "http://image.leyou.com/13123.jpg"));
 73         list.add(new Goods(3L, "华为META10", "手机", "华为", 4499.00, "http://image.leyou.com/13123.jpg"));
 74         list.add(new Goods(4L, "小米Mix2S", "手机", "小米", 4299.00, "http://image.leyou.com/13123.jpg"));
 75         list.add(new Goods(5L, "荣耀V10", "手机", "华为", 2799.00, "http://image.leyou.com/13123.jpg"));
 76         // 接收对象集合,实现批量新增
 77         goodsRepository.saveAll(list);
 78     }
 79 
 80     @Test
 81     public void queryByPriceBetween(){
 82         List<Goods> list = this.goodsRepository.findByPriceBetween(2000.00, 3500.00);
 83         for (Goods goods: list) {
 84             System.out.println(goods);
 85         }
 86     }
 87     @Test
 88     public void testQuery(){
 89         MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("id", "1");
 90         Iterable<Goods> items =goodsRepository.search(queryBuilder);
 91         for (Goods item : items) {
 92             System.out.println(JSON.toJSONString(item));
 93         }
 94     }
 95 
 96     @Test
 97     void findByTitle()
 98     {
 99         Goods goods= goodsRepository.findByTitle("小米Mix2S");
100         System.out.println(JSON.toJSONString(goods));
101     }
102 
103 
104     @Test
105     public void testNativeQuery(){
106         // 构建查询条件
107         NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
108         // 添加基本的分词查询
109         queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机"));
110 
111         // 初始化分页参数
112         int page = 0;
113         int size = 20;
114         // 设置分页参数
115         queryBuilder.withPageable(PageRequest.of(page, size));
116 
117         // 执行搜索,获取结果
118         Page<Goods> items = goodsRepository.search(queryBuilder.build());
119         // 打印总条数
120         System.out.println(items.getTotalElements());
121         // 打印总页数
122         System.out.println(items.getTotalPages());
123         // 每页大小
124         System.out.println(items.getSize());
125         // 当前页
126         System.out.println(items.getNumber());
127         for (Goods item : items) {
128             System.out.println(JSON.toJSONString(item));
129         }
130     }
131     @Test
132     public void testSort(){
133         // 构建查询条件
134         NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
135         // 添加基本的分词查询
136         queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机"));
137 
138         // 排序
139         queryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.ASC));
140 
141         // 执行搜索,获取结果
142         Page<Goods> items = goodsRepository.search(queryBuilder.build());
143         // 打印总条数
144         System.out.println(items.getTotalElements());
145         for (Goods item : items) {
146             System.out.println(JSON.toJSONString(item));
147         }
148     }
149     @Test
150     /**
151      * 按照品牌brand进行分组 统计各品牌的总数
152      * */
153     public void testAgg(){
154         NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
155         // 不查询任何结果
156         queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
157         // 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
158         queryBuilder.addAggregation(
159                 AggregationBuilders.terms("brands").field("brand"));
160         // 2、查询,需要把结果强转为AggregatedPage类型
161         AggregatedPage<Goods> aggPage = (AggregatedPage<Goods>) goodsRepository.search(queryBuilder.build());
162         // 3、解析
163         // 3.1、从结果中取出名为brands的那个聚合,
164         // 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
165         StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
166         // 3.2、获取桶
167         List<StringTerms.Bucket> buckets = agg.getBuckets();
168         // 3.3、遍历
169         for (StringTerms.Bucket bucket : buckets) {
170             // 3.4、获取桶中的key,即品牌名称
171             System.out.println(bucket.getKeyAsString());
172             // 3.5、获取桶中的文档数量
173             System.out.println(bucket.getDocCount());
174         }
175     }
176     @Test
177     /**
178      * 嵌套聚合,求平均值
179      * */
180     public void testSubAgg(){
181         NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
182         // 不查询任何结果
183         queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
184         // 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
185         queryBuilder.addAggregation(
186                 AggregationBuilders.terms("brands").field("brand")
187                         .subAggregation(AggregationBuilders.avg("priceAvg").field("price")) // 在品牌聚合桶内进行嵌套聚合,求平均值
188         );
189         // 2、查询,需要把结果强转为AggregatedPage类型
190         AggregatedPage<Goods> aggPage = (AggregatedPage<Goods>) goodsRepository.search(queryBuilder.build());
191         // 3、解析
192         // 3.1、从结果中取出名为brands的那个聚合,
193         // 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
194         StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
195         // 3.2、获取桶
196         List<StringTerms.Bucket> buckets = agg.getBuckets();
197         // 3.3、遍历
198         for (StringTerms.Bucket bucket : buckets) {
199             // 3.4、获取桶中的key,即品牌名称  3.5、获取桶中的文档数量
200             System.out.println(bucket.getKeyAsString() + ",共" + bucket.getDocCount() + "台");
201 
202             // 3.6.获取子聚合结果:
203             InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("priceAvg");
204             System.out.println("平均售价:" + avg.getValue());
205         }
206     }
207 
208 }
功能测试类

 

上一篇:freemarker常用的方法


下一篇:SpringBoot2.X拦截器