五.SpringDataElsticsearch#
这是原来画的图,JEST直接放弃了,让我们自己拼接json串,简直只有难受
终于到编码阶段,这部分相比前面的原生api看起来就好受多了,Spring一整合,啥东西都简单的只剩下两件事,1,写个配置文件,2.用它的方法,玩他的注解--, 当然我现在回顾学习的整个过程,最重要的是还是那句话,不要忘记了自己的需求,不然学着学着容易迷失,不知道自己想干啥,对于Elasticsearch吧先觉的它啥都能干,又觉得它啥也干不了,这是个很尴尬的事情! 那,我用它做全文检索,我就得去搞明白 知道下面那几件事
- 怎么搭建起开发环境,使我的java代码和ES交互
- spring整合它嘛,提供了哪些注解,表示我的javaBean是个document对象
- 如何建立索引库
- 基本的CRUD
- 花里胡哨的查询方法
下面挨个做这几件事!
5.1 搭建开发环境#
坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
配置文件(java和它的交互走的是tcp协议)
spring: application: name: search-service data: elasticsearch: cluster-nodes: 192.168.43.150:9300 cluster-name: elasticsearch
启动类
5.2实体类及注解#
实体类在java中就像是接盘侠,啥样的东东,它都有能给接下来,看完了下面的注解,就知道了如何把数据存进es认识的实体类,准备把他们存在索引库
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; @Data @Document(indexName="changwu",type = "item",shards = 1) public class Item { @Id @Field(type=FieldType.Long) Long id; @Field(type = FieldType.Text,analyzer = "ik_smart") String title; //标题 @Field(type=FieldType.Keyword,index = true) String category;// 分类 @Field(type=FieldType.Keyword) String brand; // 品牌 @Field(type=FieldType.Double) Double price; // 价格 @Field(type=FieldType.Keyword,index = false) String images; // 图片地址 // 指定存储时和检索时使用的分词器是同一个 // index=true 表示索引 // 是否索引, 就是看这个字段是否能被搜索, 比如: 如果对整篇文章建立了索引,那么从文章中任意抽出一段来,都可以搜索出这个文章 // 是否分词, 就是表示搜索的时候,是整体匹配还是单词匹配 比如: 如果不分词的话,搜索时,一个词不一样,都搜索不出来结果 // 是否存储, 就是,是否在页面上展示 , 但是在es中默认字段值已经存储在_source 字段里, 也是能检索出原始字段的 @Field(index = true, store = true,type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word") private String title; /** * analyzer 存进去的时候,按这个分词 * searchAnalyzer: 搜索时,按这个分词 */ @Field(index = true, store = true, type = FieldType.Text ,analyzer = "ik_max_word", searchAnalyzer = "ik_max_word") private String content; @Field(index = true,type = FieldType.Keyword) private String state; }
- @Document
- 作用在类,标记实体类为文档对象
- indexName:对应索引库名称---- 数据库名
- type:对应在索引库中的类型---- 表名
- shards:分片数量,默认5
-replicas:副本数量,默认1
- @Id
- 作用在成员变量id上
- 标记一个字段作为id主键(这个id别写错了,不然程序都启动不起来)
- @Field
- 作用在成员变量,标记为文档的字段,并指定字段映射属性:
- type:字段类型,取值是枚举:FieldType
- index:是否索引,布尔类型,默认是true
- store:是否存储,布尔类型,默认是false
- analyzer:分词器名称
另外不要忘记了它的智能推断,如果我们不在字段上添加@Field,他根据值的类型进行推断
比如: 下面三个都会被推断为long类型,
private Long cid3;// private Date createTime;// private List<Long> price;//
5.3 创建索引库#
索引库的创建放到text里面就ok了
- 使用的是ElasticsearchTemplate,Spring一直都是这样,整合完了,给你个模板
创建索引,添加映射,删除索引
template.createIndex(Goods.class); template.putMapping(Goods.class); template.deleteIndex()
5.4 基本的CRUD#
SpringData的强大之后就是,我们不再去写dao层了,她会通过反射给我们写好,有点Mybatis里面的通用mapper的意思,但是它更强大,---你给方法名它自动的生成方法
public interface GoodsRepository extends ElasticsearchRepository<Goods,Long> {}
使用 repository点一下,findXX saveXXX, deleteXXX全出来了,不再细说
另外:
如果可以看懂原生的语法,那么对他的使用就不多说了...就是无脑使用
到了这,就知道了如何把通过java代码,对索引库里面的数据进行简单的增删改查
5.5 着重看一下如何进行繁杂查询#
真正使用es的时候,Repository里面的方法可以满足大部分的功能,但是聚合,过滤的话,只能使用原生的API
假设我们有下面的需求: 前端把用户需要搜索的信息收集起来了--全文检索
- 全文检索
/** * 全文检索 */ @Test public void textQuery(){ //创建查询构建器 NativeSearchQueryBuilder QueryBuilder = new NativeSearchQueryBuilder(); /** * 给查询构造器添加条件 * 1. 它仍然需要的是一个 QueryBuilder ,通过QueryBuilders里面的静态方法创建,间接继承了QueryBuild * 2. 可也看到,基本上所有常用的条件查询都有了, bool , 词条term , match , 模糊查询 fuzzy * 3. 我们也可以抽出来一个方法, 方法里使用bool查询, 他支持先添加查询,紧接着过滤, 还记不记得那个 match{},filter:{} * 3.1 注意区分开结果过滤_source 和 filter * * matchQuery @Param field * matchQuery @Param field的值 * */ QueryBuilder.withQuery(QueryBuilders.matchQuery("title","小米")); /** * 结果过滤 * @Param : SourceFilter ,但是它是和接口,于是我用它唯一的实现类 */ QueryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"title","price"},null)); /** * 排序 */ QueryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC)); /** * 分页 * 原生的api使用下面两个字段控制 * "from": 0, * "size":10 * 但是注意,他是的第一页是0 * @Param : Pageable 他是个接口,我们使用它的实现类 PageRequest(静态方法of) */ QueryBuilder.withPageable(PageRequest.of(1,10)); Page<Goods> result = repository.search(QueryBuilder.build()); //它仍然需要的是一个QueryBuilder , 通过构造器.build()构建出来 //解析结果 long elements = result.getTotalElements(); int totalPages = result.getTotalPages(); List<Goods> content = result.getContent(); } /** * 创建基本的查询条件 * 创建布尔查询, * 一部分当作查询条件(must) * 一部分当作过滤条件(filter) * @param searchRequest * @return QueryBuilder 给QueryBuild.withQuery()使用 */ private QueryBuilder buildBasicQuery(SearchRequest searchRequest) { //创建布尔查询 BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery(); //查询条件 queryBuilder.must(QueryBuilders.matchQuery("字段名",字段值)); if(过滤条件){ /* *把处理好的字段,传给过滤器 过滤 *@param name The name of the field *@param value The value of the term */ queryBuilder.filter(QueryBuilders.termQuery(字段名,字段值)); } return queryBuilder; }
- 聚合查询
/** * 聚合查询 */ @Test public void textAgg(){ //同样少不了 查询构造器 NativeSearchQueryBuilder QueryBuilder = new NativeSearchQueryBuilder(); /** * 添加聚合add, 可以聚合多次 * @Param AbstractAggregationBuilder 它间接继承与 AggregationBuilder 我们下面的工具类 * * AggregationBuilders下面基本上涵盖了我们所有的聚合方式 */ QueryBuilder.addAggregation(AggregationBuilders.terms("popular_brand").field("brand")); /** * 推荐使用和这个,支持聚合查询,并返回带聚合的结果 * @Param :SearchQuery * @Param :Class<T> * @Return: */ AggregatedPage<Goods> result = template.queryForPage(QueryBuilder.build(), Goods.class); /** * 解析聚合 * */ Aggregations aggregations = result.getAggregations(); // 获取指定名称 聚合 //Aggregations agg = aggregations.get("popular_brand"); StringTerms agg = aggregations.get("popular_brand"); /** * 问题来了, 不存在 agg.getBuckets() * 原因看上面的图,是 Aggregations是个顶级接口, */ List<StringTerms.Bucket> buckets = agg.getBuckets(); //遍历buckets for (StringTerms.Bucket bucket : buckets) { System.out.println(bucket.getKeyAsString()); System.out.println(bucket.getDocCount()); } }