现在哪些行业需要建设网站,手机网站欣赏,谷歌网站提交入口,wordpress网站被挂马Spring Data Elasticsearch 文章目录 Spring Data Elasticsearch1. 定义文档映射实体类2. Repository3. ElasticsearchRestTemplate3.1 查询相关特性3.1.1 过滤3.1.2 排序3.1.3 自定义分词器 3.2 高级查询 4. 索引管理4.1 创建索引4.2 检索索引4.3 修改映射4.4 删除索引 5. 异常…Spring Data Elasticsearch 文章目录 Spring Data Elasticsearch1. 定义文档映射实体类2. Repository3. ElasticsearchRestTemplate3.1 查询相关特性3.1.1 过滤3.1.2 排序3.1.3 自定义分词器 3.2 高级查询 4. 索引管理4.1 创建索引4.2 检索索引4.3 修改映射4.4 删除索引 5. 异常处理6. 性能优化7. 应用案例8. Spring Data Elasticsearch优势 Spring Data Elasticsearch为文档的存储查询排序和统计提供了一个高度抽象的模板。使用Spring Data ElasticSearch来操作Elasticsearch可以较大程度的减少我们的代码量提高我们的开发效率。 要使用Elasticsearch我们需要引入如下依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-elasticsearch/artifactIdversion2.1.7.RELEASE/version
/dependency还需要在配置文件中增加如下配置
spring:elasticsearch:rest:# elasticsearch server的地址uris: 192.168.0.102:9200# 连接超时时间connection-timeout: 6s# 访问超时时间read-timeout: 10s1. 定义文档映射实体类
类比于MyBatis-Plus可以定义实体类去映射数据库中的表中的数据使用Spring Data Elasticsearch时我们也可以通过定义一个实体类映射ES索引中的文档。
Data
Document(indexName goods, shards 1, replicas 0)
public class Goods {// 商品Id skuId _idIdprivate Long id;Field(type FieldType.Keyword, index false)private String defaultImg;// elasticsearch 中能分词的字段这个字段数据类型必须是 textkeyword 不分词Field(type FieldType.Text, analyzer ik_max_word)private String title;Field(type FieldType.Double)private Double price;Field(type FieldType.Long)private Long tmId;Field(type FieldType.Keyword)private String tmName;Field(type FieldType.Keyword)private String tmLogoUrl;Field(type FieldType.Long)private Long firstLevelCategoryId;Field(type FieldType.Keyword)private String firstLevelCategoryName;Field(type FieldType.Long)private Long secondLevelCategoryId;Field(type FieldType.Keyword)private String secondLevelCategoryName;Field(type FieldType.Long)private Long thirdLevelCategoryId;Field(type FieldType.Keyword)private String thirdLevelCategoryName;// 商品的热度 我们将商品被用户点查看的次数越多则说明热度就越高Field(type FieldType.Long)private Long hotScore 0L;// 平台属性集合对象// Nested 支持嵌套查询Field(type FieldType.Nested)private ListSearchAttr attrs;}/*该类映射nested平台属性
*/
Data
public class SearchAttr {// 平台属性IdField(type FieldType.Long)private Long attrId;// 平台属性值名称Field(type FieldType.Keyword)private String attrValue;// 平台属性名Field(type FieldType.Keyword)private String attrName;
}在Goods类上通过添加Document注解我们将Goods类映射的文档所属的索引
Document注解的indexName属性用来定义实体类所映射的文档所属的目标索引名称Document的shards属性表示目标索引的住分片数量Document的replicas属性表示每个主分片所拥有的副本分片的数量
在Goods类的成员变量Id上通过添加Id注解指定Id成员变量映射到Goods索引中文档的id字段同时也映射到文档的唯一表示_id字段。
在Goods类的其他成员变量上通过添加Field注解定义成员变量和文档字段的映射关系
默认同名成员变量映射到文档中的同名字段(也可以由Field注解的name属性显示指定)通过Field注解的type属性指定文档中同名字段的数据类型通过Field注解的analyzer属性指定成员变量所映射的文档字段所使用的分词器
2. Repository
类比于Mybatis-Plus中定义BaseMaper子接口即可对单表做增删改查的操作Spring Data Elastisearch中我们可以通过定义ElasticsearchRepository子接口迅速实现对索引中的文档数据的增删改查以及通过自定义方法实现自定义查询。
public interface GoodsRepository extends ElasticsearchRepositoryGoods,Long {}ElasticsearchRepository接口需要接收两个泛型第一个泛型即映射实体类第二个泛型是在实体类中加了Id注解的成员变量的数据类型即映射到文档唯一标识_id字段的成员变量类型。
一旦我们定义好了ElasticsearchRepository的子接口马上就可以实现对goods索引中文档的增删改查功能 // 注入repository对象Autowiredprivate GoodsRepository goodsRepository;// 保存单个文档对象Goods good ....goodsRepository.save(good);// 批量保存多个文档对象ListGoods goods ...goodsRepository.save(goods);// 根据id查询goodsRepository.findById(id);// 根据id删除goodsRepository.deleteById(id);
同时还需要注意一点一旦定义好了ElasticsearchRepository接口而且被SpringBoot启动类扫描到那么在应用启动的时候如果ElasticsearchRepository子接口所访问的索引在ES中不存在Spring Data Elasticsearch会在ES中自动创建索引并根据映射实体类定义索引的映射。
但是大多数时候我们可能需要对索引中的文档数据做自定义查询此时仅仅使用ElasticsearchRepository接口中继承的方法无法满足我们的需求。此时就需要在自己的Repository接口中通过自定义方法来实现各种自定义查询。
利用Query注解自定义查询脚本 /* 1. 通过Query注解定义具体的查询字符串(也可以替换为其他查询)2. 字符串中的?0是固定格式表示第0个参数的占位符在实际查询时会被方法的第一个参数值title的值替换, 如果有多个参数依次类推即可3. ListGoods// 模糊查询Query({\fuzzy\: {\title\: \?0\}}) // 范围查询Query({\range\: {\price\: {\gte\: ?0, \lte\: ?1}}}) // 前缀查询Query({\prefix\: {\title\: \?0\}})*/Query({ \match\: {\n \title\: \?0\\n } })ListGoods matchSearch(String title);这里的Query注解中只需要包含我们查询脚本中query{}里面的内容即可比如上面的Query注解所表示的查询等价于
GET goods/_search
{query: {match: {title: 具体待查询的参数值}}
}AutowiredProductRepository productRepository;Testpublic void testMatchSearch() {// 在调用的时候传递查询的参数值ListGoods list goodsRepository.matchSearch(荣耀手机);System.out.println(list);}利用Query注解结合分页参数实现分页查询 /*1. 针对一个查询结果返回对应的一页数据2. Pageable参数是当想要获取分页数据的时候必须携带的参数表示分页信息比如查询第多少页数据每页多少条数据等该参数不会用来替换我们的Query字符串中的参数3. 返回的结果是一个包含一页文档数据的Page对象*/Query( { \match\: {\n \title\: \?0\\n } })PageGoods testSearchPage(String title, Pageable pageable);// 注入repository对象Autowiredprivate GoodsRepository goodsRepository;/*测试分页查询*/Testpublic void testSearchPage() {// 创建表示分页信息的Pageable对象// 表示查询第几页数据这里一定要注意页数是从0开始算的int page 0;// 每页假设10个文档int pageCount 10;// 调用Sort方法得到Sort对象一个Sort对象表示Sort sort Sort.by(Sort.Direction.ASC, price);// PageRequest 是 Pageable接口子类对象PageRequest pageInfo PageRequest.of(page, pageCount,sort);// 这里的Page对象可以被看做是ListPageGoods pageResult goodsRepository.testSearchPage(小米手机, pageInfo);// 遍历集合从每个SearchHit对象中取出文档对象,// 如果需要返回可以在遍历的时候将其放入一个List中返回pageResult.forEach( goods - {// 访问查询到的一条文档// ...});// 获取满足条件的总的文档数量long totalElements pageResult.getTotalElements();}Query注解 Highlight 分页参数实现高亮分页自定义查询 Query( { \match\: {\n \title\: \?0\\n } })Highlight(fields {HighlightField(name title,parameters HighlightParameters(preTags font colorred, postTags /font))})ListSearchHitGoods testHighlight(String title, Pageable pageable);Testpublic void testHighlight() {// 分页参数PageRequest of PageRequest.of(0, 10);// 调用Repository方法获取搜索结果ListSearchHitGoods result goodsRepository.testHighlight(小米手机, of);// 结果集ListGoods itemDocuments new ArrayList();result.forEach(hit - {// 获取目标文档Goods content hit.getContent();// 获取高亮字段title对应的高亮字符串ListString title hit.getHighlightField(title);// 在文档对象中用高亮字符串替换掉原来的值content.setTitle(title.get(0));// 加入结果集itemDocuments.add(content);});System.out.println(itemDocuments.size());}虽然testHighlight方法既实现了分页查询又实现了高亮查询但是有一个缺陷就是该方法无法获取到满足查询条件的总的文档数量它只会返回满足条件的一页文档数据。不知道满足条件的文档总数前端就无法完成分页。
所以很明显Repository好用但是具有一定的局限性如果面对比较复杂的查询此时就只能使用Spring Data Elasticsearch提供的另外一个工具ElasticsearchRestTemplate了。
3. ElasticsearchRestTemplate
3.1 查询相关特性
3.1.1 过滤
使用BoolQuery进行过滤
BoolQueryBuilder qb QueryBuilders.boolQuery();
qb.filter(QueryBuilders.termQuery(price, 199)); // 过滤条件3.1.2 排序
使用SortBuilders构建排序条件
SortBuilder sort SortBuilders.fieldSort(price).order(SortOrder.ASC);3.1.3 自定义分词器
在字段上使用analyzer属性指定分词器
Field(analyzer ik_max_word)
private String title;3.2 高级查询
构造自定义分页高亮nested以及聚合查询并发起请求 AutowiredElasticsearchRestTemplate restTemplate;AutowiredGoodsConverter goodsConverter;Testpublic void testRestTemplate() {// 该Builder包含所有搜索请求的参数NativeSearchQueryBuilder queryBuilder new NativeSearchQueryBuilder();// 获取bool查询BuilderBoolQueryBuilder boolQueryBuilder QueryBuilders.boolQuery();// 构造bool查询中match查询MatchQueryBuilder matchQuery QueryBuilders.matchQuery(title, 小米手机);// 将该查询加入bool查询must中boolQueryBuilder.must(matchQuery);TermQueryBuilder subQueryForAttrNested QueryBuilders.termQuery(attrs.attrValue, 8G);// 构造nested查询NestedQueryBuilder attrsNestedQuery QueryBuilders.nestedQuery(attrs, subQueryForAttrNested, ScoreMode.None);// 将nested查询作为一个过滤条件boolQueryBuilder.filter(attrsNestedQuery);// 将整个bool查询添加到NativeSearchQueryBuilderqueryBuilder.withQuery(boolQueryBuilder);// 构造分页参数//PageRequest price PageRequest.of(0, 10, Sort.by(Sort.Order.desc(price)));PageRequest price PageRequest.of(0, 10);// 向NativeSearchQueryBuilder添加分页参数queryBuilder.withPageable(price);// 按照指定字段值排序FieldSortBuilder priceSortBuilder SortBuilders.fieldSort(price).order(SortOrder.ASC);queryBuilder.withSort(priceSortBuilder);// 构造高亮参数HighlightBuilder highlightBuilder new HighlightBuilder();highlightBuilder.field(title).preTags(font colorred).postTags(/font);// 向NativeSearchQueryBuilder添加高亮参数queryBuilder.withHighlightBuilder(highlightBuilder);// 设置品牌聚合(平台属性等的聚合也是相同的方式)TermsAggregationBuilder termsAggregationBuilder AggregationBuilders.terms(tmIdAgg).field(tmId).subAggregation(AggregationBuilders.terms(tmNameAgg).field(tmName)).subAggregation(AggregationBuilders.terms(tmLogoUrlAgg).field(tmLogUrl));// 向NativeSearchQueryBuilder添加聚合参数queryBuilder.addAggregation(termsAggregationBuilder);// 结果集过滤只包含原始文档的iddefaultImgtitlepricequeryBuilder.withFields(id, defaultImg, title, price);// 使用ElasticsearchRestTemplate发起搜索请求NativeSearchQuery build queryBuilder.build();SearchHitsGoods search restTemplate.search(build, Goods.class);//封装所有的查询数据SearchResponseDTO searchResponseDTO new SearchResponseDTO();// 获取满足条件的总文档数量long totalHits search.getTotalHits();// 设置查询到的总文档条数searchResponseDTO.setTotal(totalHits);// 获取包含所有命中文档的SearchHit对象ListSearchHitGoods searchHits search.getSearchHits();// 处理搜索到的结果集即SearchHitGoods集合, 并使用高亮字符串替换ListGoodsDTO goodsList searchHits.stream().map(hit - {// 获取命中的文档Goods content hit.getContent();//获取高亮字段ListString title hit.getHighlightField(title);// 用高亮字段替换content.setTitle(title.get(0));// 将Goods对象转化为GoodsDTO对象GoodsDTO goodsDTO goodsConverter.goodsPO2DTO(content);return goodsDTO;}).collect(Collectors.toList());// 设置查询到的结果列表searchResponseDTO.setGoodsList(goodsList);// 从品牌聚合中获取品牌集合// 根据id获取品牌id terms聚合结果Terms terms search.getAggregations().get(tmIdAgg);ListSearchResponseTmDTO trademarkList terms.getBuckets().stream().map(tmIdBucket - {// 封装品牌数据SearchResponseTmDTO searchResponseTmDTO new SearchResponseTmDTO();String tmIdStr tmIdBucket.getKeyAsString();// 获取品牌idLong tmId Long.parseLong(tmIdStr);// 设置品牌idsearchResponseTmDTO.setTmId(tmId);// 获取品牌名称聚合(子聚合)Terms tmNameAgg tmIdBucket.getAggregations().get(tmNameAgg);// 通过聚合桶的名称获取品牌名称String tmName tmNameAgg.getBuckets().get(0).getKeyAsString();// 设置品牌名称searchResponseTmDTO.setTmName(tmName);// 获取品牌logo聚合(子聚合)Terms tmLogoUrlAgg tmIdBucket.getAggregations().get(tmLogoUrlAgg);// 通过聚合桶的名称获取品牌名称String tmLogoUrl tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();// 设置品牌名称searchResponseTmDTO.setTmLogoUrl(tmLogoUrl);return searchResponseTmDTO;}).collect(Collectors.toList());// 设置聚合品牌数据searchResponseDTO.setTrademarkList(trademarkList);// .....}4. 索引管理
4.1 创建索引
可以通过ElasticsearchRestTemplate的createIndex方法创建索引
// 创建索引,使用实体类进行映射
restTemplate.createIndex(Goods.class);// 自定义索引设置
restTemplate.createIndex(Goods.class, c - c.settings(s - s .put(index.number_of_shards, 3) // 指定主分片数.put(index.number_of_replicas, 2) // 指定副本分片数).mapping(m - m.put(dynamic, false) // 禁用动态映射)
);4.2 检索索引
使用getIndex方法获取索引信息
GetIndexResponse response restTemplate.getIndex(Goods.class);
MapString, Object settings response.getSettings();
MapString, Object mappings response.getMappings();4.3 修改映射
使用putMapping方法更新索引字段映射
// 新增一个text字段
restTemplate.putMapping(Goods.class, m - m.textField(newField));4.4 删除索引
使用deleteIndex删除索引
restTemplate.deleteIndex(Goods.class);5. 异常处理
使用ExceptionHandler注解处理Elasticsearch异常
ExceptionHandler(ElasticsearchException.class)
public Response handleError(Exception e) {// 处理异常逻辑return Response.status(500).build();
}6. 性能优化
加载批量文档使用 bulk 或 batch 方式使用 scroll api 避免深分页问题控制请求缓存大小不要缓存过多数据定期优化索引提高查询性能
7. 应用案例
以电商网站的商品搜索为例
定义商品索引、文档映射实体构建查询实现精确匹配、分词匹配、过滤、聚合等复杂查询使用高级功能如Suggest完成自动补全可视化分析商品热门趋势、用户行为数据
8. Spring Data Elasticsearch优势
简化开发不需要了解 ES 客户端细节查询方式灵活支持自定义复杂 DSL 语句提供索引管理、错误处理等功能易于测试和扩展与 Spring Boot 无缝集成开发效率高