【Spring Boot】Spring Boot之使用 Spring Data Elasticsearch 整合elasticsearch
一、Spring Data Elasticsearch简单介绍
Spring Data Elasticsearch项目将核心Spring概念应用于使用Elasticsearch搜索引擎开发解决方案。我们提供了一个“模板”作为存储、查询、排序和划分文档的高级抽象。您将注意到Spring Framewor与Spring data solr和mongodb支持的相似之处
GitHub地址:https://github.com/zhangboqing/spring-boot-demo-elasticsearch
使用的spring boot版本:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
二、整合步骤
1)maven依赖
<!-- Elasticsearch --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>
2)application.yml
spring: elasticsearch: rest: uris: - http://localhost:9200 username: elastic password: 123456
3)定义实体,一个实体对于一个index
/** * @author zhangboqing * @date 2019-12-06 */ public interface EsConsts { /** * 所以index都使用该类型名称 */ String DEFAULT_TYPE_NAME = "_doc"; /** * 索引名称 */ String INDEX_NAME_MERCURY = "goods"; }
/** * @author zhangboqing * @date 2019-12-06 */ @Document(indexName = EsConsts.INDEX_NAME_MERCURY, type = EsConsts.DEFAULT_TYPE_NAME, shards = 1, replicas = 0) @Data @AllArgsConstructor @NoArgsConstructor public class GoodsESEntity { // 筛选条件包括:商品名称,品牌,规格,适用车型,商品编号,原厂编号 /** * 主键,商品ID */ @Id private Long goodsId; /** * 商品名称 */ @Field(type = FieldType.Keyword) private String goodsName; /** * 品牌 */ @Field(type = FieldType.Keyword) private String goodBrand; /** * 规格 */ @Field(type = FieldType.Keyword) private String goodsSpec; /** * 商品编号 */ @Field(type = FieldType.Keyword) private String goodsAccessoriesCode; /** * 原厂编号 */ @Field(type = FieldType.Keyword) private String goodsOriginalFactoryCode; /** * 复合字段,会被分词后存储 */ @Field(type = FieldType.Text, analyzer = "ik_smart") private String groupData; }
4)定义Repository
/** * @author zhangboqing * @date 2019-12-06 */ public interface GoodsESRepository extends ElasticsearchRepository<GoodsESEntity, Long> { /** * 根据goodsId区间查询 */ List<GoodsESEntity> findByGoodsIdBetween(Integer min, Integer max); }
三、测试
/** * @Author zhangboqing * @Date 2020-01-03 */ @SpringBootTest @Slf4j public class GoodsESRepositoryTest { @Autowired private GoodsESRepository goodsESRepository; /** * 测试新增 */ @Test public void save() { GoodsESEntity goodsESEntity1 = new GoodsESEntity(2L, "尾灯 L 大众速腾", "国际品牌", "16D 945 095", "31231231", "16D 945 095", "尾灯 L 大众速腾 国际品牌 16D 945 095 31231231 16D 945 095"); GoodsESEntity goodsESEntity2 = new GoodsESEntity(1L, "后", "国际品牌", "16D 945 095", "31231231", "16D 945 095", "后 国际品牌 16D 945 095 31231231 16D 945 095"); Iterable<GoodsESEntity> goodsESEntities = goodsESRepository.saveAll(Arrays.asList(goodsESEntity1, goodsESEntity2)); log.info("【save】= {}", goodsESEntities); } /** * 测试更新 */ @Test public void update() { goodsESRepository.findById(1L).ifPresent(goodsESEntity -> { goodsESEntity.setGoodsName(goodsESEntity.getGoodsName() + "\n更新更新更新更新更新"); GoodsESEntity save = goodsESRepository.save(goodsESEntity); log.info("【save】= {}", save); }); } /** * 测试删除 */ @Test public void delete() { // 主键删除 goodsESRepository.deleteById(1L); // 对象删除 goodsESRepository.findById(2L).ifPresent(goodsESEntity -> goodsESRepository.delete(goodsESEntity)); // 批量删除 goodsESRepository.deleteAll(goodsESRepository.findAll()); } /** * 测试普通查询,按goodsId倒序 */ @Test public void select() { goodsESRepository.findAll(Sort.by(Sort.Direction.DESC, "goodsId")) .forEach(goodsESEntity -> log.info("【goods】: {}", JSON.toJSONString(goodsESEntity))); } /** * 自定义查询,根据goodsId范围查询 */ @Test public void customSelectRangeOfAge() { goodsESRepository.findByGoodsIdBetween(1, 2).forEach(goodsESEntity -> log.info("【goods】: {}", JSON.toJSONString(goodsESEntity))); } /** * 高级查询 */ @Test public void advanceSelect() { // QueryBuilders 提供了很多静态方法,可以实现大部分查询条件的封装 MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("goodsName", "保时捷跑车V20"); log.info("【queryBuilder】= {}", queryBuilder.toString()); goodsESRepository.search(queryBuilder).forEach(goodsESEntity -> log.info("【goods】: {}", JSON.toJSONString(goodsESEntity))); } /** * 自定义高级查询 */ @Test public void customAdvanceSelect() { // 构造查询条件 NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); // 添加基本的分词条件 queryBuilder.withQuery(QueryBuilders.matchQuery("goodsName", "保时捷跑车V20")); // 排序条件 queryBuilder.withSort(SortBuilders.fieldSort("goodsId").order(SortOrder.DESC)); // 分页条件 queryBuilder.withPageable(PageRequest.of(0, 2)); Page<GoodsESEntity> goodsESEntities = goodsESRepository.search(queryBuilder.build()); log.info("【people】总条数 = {}", goodsESEntities.getTotalElements()); log.info("【people】总页数 = {}", goodsESEntities.getTotalPages()); goodsESEntities.forEach(goodsESEntity -> log.info("【goods】= {}",JSON.toJSONString(goodsESEntity))); } /** * 测试聚合,测试平均goodsId */ @Test public void avg() { // 构造查询条件 NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); // 不查询任何结果 queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null)); // 平均goodsId queryBuilder.addAggregation(AggregationBuilders.avg("goodsIdAvg").field("goodsId")); log.info("【queryBuilder】= {}", JSON.toJSONString(queryBuilder.build())); AggregatedPage<GoodsESEntity> goodsESEntities = (AggregatedPage<GoodsESEntity>) goodsESRepository.search(queryBuilder.build()); double avgGoodsId = ((InternalAvg) goodsESEntities.getAggregation("goodsIdAvg")).getValue(); log.info("【avgGoodsId】= {}", avgGoodsId); } /** * 测试高级聚合查询 */ @Test public void advanceAgg() { // 构造查询条件 NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); // 不查询任何结果 queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null)); // 1. 添加一个新的聚合,聚合类型为terms,聚合名称为goodsName,聚合字段为goodsId queryBuilder.addAggregation(AggregationBuilders.terms("goodsName").field("goodsName") // 2. 在goodsName聚合桶内进行嵌套聚合,求平均goodsId .subAggregation(AggregationBuilders.avg("goodsIdAvg").field("goodsId"))); log.info("【queryBuilder】= {}", JSON.toJSONString(queryBuilder.build())); // 3. 查询 AggregatedPage<GoodsESEntity> people = (AggregatedPage<GoodsESEntity>) goodsESRepository.search(queryBuilder.build()); // 4. 解析 // 4.1. 从结果中取出名为 goodsName 的那个聚合,因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型 StringTerms goodsName = (StringTerms) people.getAggregation("goodsName"); // 4.2. 获取桶 List<StringTerms.Bucket> buckets = goodsName.getBuckets(); for (StringTerms.Bucket bucket : buckets) { // 4.3. 获取桶中的key,即goodsName名称 4.4. 获取桶中的文档数量 log.info("{} 总共有 {} 个", bucket.getKeyAsString(), bucket.getDocCount()); // 4.5. 获取子聚合结果: InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("goodsIdAvg"); log.info("平均goodsId:{}", avg); } } }
你投入得越多,就能得到越多得价值