美文网首页
springboot整合elasticsearch

springboot整合elasticsearch

作者: _麻辣香锅不要辣 | 来源:发表于2019-06-03 20:34 被阅读0次

    导包

        <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            </dependency>
    

    添加配置

    spring:
      data:
        elasticsearch:
          cluster-name: elasticsearch
          cluster-nodes: 192.168.88.130:9300
    

    创建一个实体类

    @Data
    public class TestModel {
        private int id;
        private String name;
        private String brand;
        private String imageurl;
    }
    

    映射

    Spring Data通过注解来声明字段的映射属性,有下面的三个注解:

    • @Document 作用在类,标记实体类为文档对象,一般有两个属性
      • indexName:对应索引库名称
      • type:对应在索引库中的类型
      • shards:分片数量,默认5
      • replicas:副本数量,默认1
    • @Id 作用在成员变量,标记一个字段作为id主键
    • @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
      • type:字段类型,取值是枚举:FieldType
      • index:是否索引,布尔类型,默认是true
      • store:是否存储,布尔类型,默认是false
      • analyzer:分词器名称

    示例:

    @Data
    @Document(indexName = "testindex", type = "equipment", shards = 1, replicas = 1)
    public class TestModel {
        @Id
        private int id;
        @Field(type = FieldType.text, analyzer = "ik_max_word")
        private String name;
        @Field(type = FieldType.keyword)
        private String brand;
        @Field(type = FieldType.keyword, index = false)
        private String imageurl;
    }
    

    1.Template索引操作

    1.1创建索引和映射
    @RestController
    @RequestMapping("/")
    public class TestController {
    
        @Autowired
        private ElasticsearchTemplate elasticsearchTemplate;
    
        @PutMapping("test1")
        public void createIndex(){
            elasticsearchTemplate.createIndex(TestModel.class); //根据TestModel类,生成index
            elasticsearchTemplate.putMapping(TestModel.class); //根据TestModel类,生成映射
        }
    
    }
    
    1.2测试api 测试
    1.3结果: 结果
    删除索引用 deleteIndex(自行测试)

    2.Repository文档操作

    Spring Data 的强大之处,就在于你不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能。

    我们只需要定义接口,然后继承它就OK了。

    2.1创建Repository接口
    public interface TestModelRepository extends ElasticsearchRepository<TestModel,Integer> {
    }
    
    2.2新增文档
        @Autowired
        private TestModelRepository testModelRepository;
        @PostMapping("test2")
        public void saveDocment(){
            TestModel testModel = new TestModel(1, "testname", "testbrand", "1.jpg");
            testModelRepository.save(testModel);
        }
    
    2.2.1测试 测试
    2.2,2结果 结果
    2.3使用saveAll()方法可以实现批量新增
    2.4修改文档

    修改和新增是同一个接口,区分的依据就是id,这一点跟我们在页面发起PUT请求是类似的。

    2.5基本查询
     @GetMapping("test3")
        public Object getAll(){
            return  testModelRepository.findAll();
        }
    
    2.5.1测试&结果
    结果
    2.6 自定义方法查询

    Spring Data 的另一个强大功能,是根据方法名称自动实现功能。

    比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类。

    当然,方法名称要符合一定的约定:

    Keyword Sample Elasticsearch Query String
    And findByNameAndPrice {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
    Or findByNameOrPrice {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
    Is findByName {"bool" : {"must" : {"field" : {"name" : "?"}}}}
    Not findByNameNot {"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
    Between findByPriceBetween {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
    LessThanEqual findByPriceLessThan {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
    GreaterThanEqual findByPriceGreaterThan {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
    Before findByPriceBefore {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
    After findByPriceAfter {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
    Like findByNameLike {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
    StartingWith findByNameStartingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
    EndingWith findByNameEndingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}
    Contains/Containing findByNameContaining {"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}
    In findByNameIn(Collection<String>names) {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
    NotIn findByNameNotIn(Collection<String>names) {"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}
    Near findByStoreNear Not Supported Yet !
    True findByAvailableTrue {"bool" : {"must" : {"field" : {"available" : true}}}}
    False findByAvailableFalse {"bool" : {"must" : {"field" : {"available" : false}}}}
    OrderBy findByAvailableTrueOrderByNameDesc {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

    例如,我们来按照价格区间查询,定义这样的一个方法:

    public interface TestModelRepository extends ElasticsearchRepository<TestModel,Integer> {
        /**
         * 根据id区间查询
         * @param start
         * @param end
         * @return
         */
        List<TestModel> findByIdBetween(Integer start, Integer end);
    }
    

    不需要写实现类,然后我们直接去运行:

      @GetMapping("test4")
        public Object queryById()
        {
            return testModelRepository.findByIdBetween(1,2);
        }
    
    2.6.1结果:
    image.png

    虽然基本查询和自定义方法已经很强大了,但是如果是复杂查询(模糊、通配符、词条查询等)就显得力不从心了。此时,我们只能使用原生查询。

    2.7高级查询

    2.7.1基本玩法
        @GetMapping("test5")
        public Object testQuery()
        {
            // 词条查询
            MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("name", "testname");
            //执行查询
            Iterable<TestModel> testModels = testModelRepository.search(queryBuilder);
            return testModels;
        }
    
    结果
    结果 Repository的search方法需要QueryBuilder参数,elasticSearch为我们提供了一个对象QueryBuilders:QueryBuilders提供了大量的静态方法,用于生成各种不同类型的查询对象,例如:词条、模糊、通配符等QueryBuilder对象。 image.png

    虽然elasticsearch提供很多可用的查询方式,但是不够灵活。如果想玩过滤或者聚合查询等就很难了

    2.8自定义查询

    2.8.1基本match query
       @GetMapping("test6")
        public Object testNativeQuery(){
            // 构建查询条件
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            // 添加基本的分词查询
            queryBuilder.withQuery(QueryBuilders.matchQuery("name", "testname"));
            // 执行搜索,获取结果
            Page<TestModel> testModels = testModelRepository.search(queryBuilder.build());
            // 打印总条数
            System.out.println(testModels.getTotalElements());
            // 打印总页数
            System.out.println(testModels.getTotalPages());
            return testModels;
        }
    

    这个效果和上面的match query一样

    2.8.2 分页查询
    @GetMapping("test7")
        public Object testNativeQuery(){
            // 构建查询条件
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            // 添加基本的分词查询
            queryBuilder.withQuery(QueryBuilders.matchQuery("name", "testname"));
            // 初始化分页参数
            int page = 0;
            int size = 1;
            // 设置分页参数
            queryBuilder.withPageable(PageRequest.of(page, size));
            // 执行搜索,获取结果
            Page<TestModel> testModels = testModelRepository.search(queryBuilder.build());
            // 打印总条数
            System.out.println(testModels.getTotalElements());
            // 打印总页数
            System.out.println(testModels.getTotalPages());
            return testModels;
        }
    

    注意:page是从0开始的,不是从1开始的

    2.8.3 排序
    @GetMapping("test8")
    public Object testNativeQuery(){
        // 构建查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 添加基本的分词查询
        queryBuilder.withQuery(QueryBuilders.matchQuery("name", "testname"));
        //排序
        queryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));
        // 执行搜索,获取结果
        Page<TestModel> testModels = testModelRepository.search(queryBuilder.build());
        // 打印总条数
        System.out.println(testModels.getTotalElements());
        // 打印总页数
        System.out.println(testModels.getTotalPages());
        return testModels;
    }
    

    这里是根据id,降序排序

    结果
    2.9 聚合
    @GetMapping("test9")
        public void testNativeQuery(){
            // 构建查询条件
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            // 添加基本的分词查询
            queryBuilder.withQuery(QueryBuilders.matchQuery("name", "testname"));
            //添加聚合,聚合的名称为brands,聚合的字段为brand
            queryBuilder.addAggregation(AggregationBuilders.terms("brands").field("brand"));
            // 执行搜索,获取结果
           AggregatedPage<TestModel> aggPage = (AggregatedPage<TestModel>)testModelRepository.search(queryBuilder.build());
            //解析
            // 从结果中取出名为brands的那个聚合,
            // 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
           StringTerms brands = (StringTerms)aggPage.getAggregation("brands");
            List<StringTerms.Bucket> buckets = brands.getBuckets();
            System.out.println(buckets);
            for (StringTerms.Bucket bucket : buckets) {
                // 获取桶中的key,即品牌名称
                System.out.println(bucket.getKeyAsString());
                // 获取桶中的文档数量
                System.out.println(bucket.getDocCount());
            }
    
    image.png
    2.9.1嵌套聚合
        @GetMapping("test9")
        public void testNativeQuery(){
            // 构建查询条件
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            // 添加基本的分词查询
            queryBuilder.withQuery(QueryBuilders.matchQuery("name", "testname"));
            //添加聚合,聚合的名称为brands,聚合的字段为brand
            queryBuilder.addAggregation(AggregationBuilders.terms("brands").field("brand")
                    .subAggregation(AggregationBuilders.avg("avgId").field("id")));
            // 执行搜索,获取结果
           AggregatedPage<TestModel> aggPage = (AggregatedPage<TestModel>)testModelRepository.search(queryBuilder.build());
           //解析
            // 从结果中取出名为brands的那个聚合,
            // 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
           StringTerms brands = (StringTerms)aggPage.getAggregation("brands");
            List<StringTerms.Bucket> buckets = brands.getBuckets();
            System.out.println(buckets);
            for (StringTerms.Bucket bucket : buckets) {
                // 获取桶中的key,即品牌名称
                System.out.println(bucket.getKeyAsString());
                // 获取桶中的文档数量
                System.out.println(bucket.getDocCount());
    
                InternalAvg avgId = (InternalAvg)bucket.getAggregations().asMap().get("avgId");
                System.out.println(avgId.getValue());
            }
        }
    

    相关文章

      网友评论

          本文标题:springboot整合elasticsearch

          本文链接:https://www.haomeiwen.com/subject/jzqfxctx.html