SpringData-ESCRUD

增删改

添加

  • 修改 ArticleService 添加一个方法做添加

public void save(Article article){
    article.setId(UUID.randomUUID().toString());
    articleDao.save(article);
}
  • 测试类代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void save() {
        Article article = new Article();
        article.setAuthor("BNTang");
        article.setTitle("Java从入门到精通");
        article.setContent("PHP是世界上最好的语言,奥利给");
        article.setRead(500);
        article.setTypes("Java");
        articleService.save(article);
    }
}

修改

  • 当数据存在时是更新,当数据不存在时,是修改
  • 修改 ArticleService 代码如下:

/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    @Autowired
    private ArticleDao articleDao;

    public void update(Article article) {
        if (StringUtils.isEmpty(article.getId())) {
            throw new RuntimeException("ID不能为空");
        }
        articleDao.save(article);
    }
}
  • 测试类代码如下:

/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void update() {
        Article article = new Article();
        article.setId("7f4eaee6-9cf4-4af0-a02e-ed666077ecd2");
        article.setAuthor("BNTang");
        article.setTitle("PHP是世界上最好的语言");
        article.setContent("学习不可三天打鱼两天晒网,打铁还需自身硬");
        article.setRead(600);
        article.setTypes("PHP");
        articleService.update(article);
    }
}

批量新增

  • 修改 ArticleService

/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    @Autowired
    private ArticleDao articleDao;

    public void saveBatch(List<Article> articles) {
        articleDao.saveAll(articles);
    }
}
  • 测试类代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void saveBatch() {
        Article article = new Article();
        article.setAuthor("BNTang");
        article.setTitle("文档1");
        article.setContent("我是文档1");
        article.setRead(600);
        article.setTypes("PHP");

        Article article2 = new Article();
        article2.setAuthor("BNTang");
        article2.setTitle("文档2");
        article2.setContent("我是文档2");
        article2.setRead(600);
        article2.setTypes("PHP");

        List<Article> list = new ArrayList<>();
        list.add(article);
        list.add(article2);

        articleService.saveBatch(list);
    }
}

删除

  • 根据 id 删除
  • 修改 ArticleService

/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    @Autowired
    private ArticleDao articleDao;

    public void deleteById(String id) {
        articleDao.deleteById(id);
    }
}
  • 测试类代码如下:

/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    @Autowired
    private ArticleDao articleDao;

    public void deleteById(String id) {
        articleDao.deleteById(id);
    }
}

查询

  • 根据 id 查询
  • 修改 ArticleService
/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    @Autowired
    private ArticleDao articleDao;

    public Article getById(String id) {
        return articleDao.findById(id).get();
    }
}
  • 测试类代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void getById() {
        Article article = articleService.getById("7dwWSXUBaFv6dXD8PRwo");
        System.out.println(article);
    }
}

JPA语法查询

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" : "?"}}}}
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}}}}
  • 根据 content 查询
  • 这里如果是需要分词的字段
  • 首先会将传入的参数进行分词
  • 分完词之后,使用这些分词进行查询
  • 然后取交集
  • 其实就是相当于
  • 分词之后多个 term 取交集
  • 修改 ArticleDao
  • Dao层
/**
 * @author BNTang
 **/
public interface ArticleDao extends ElasticsearchRepository<Article, String> {
    List<Article> findByContent(String content);
}
  • Service层
  • 修改 ArticleService
/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    @Autowired
    private ArticleDao articleDao;
    public List<Article> getByContent(String content) {
        return articleDao.findByContent(content);
    }
}
  • 测试类代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void content() {
        List<Article> list = articleService.getByContent("干就完了,奥利给");
        System.out.println(list);
    }
}
  • 根据 content 查询
  • 这里会将 content 进行分词
  • 根据分词的结果进行查询
  • 取并集
  • 其实就是相当于 match
  • 修改 ArticleDao
  • Dao层
/**
 * @author BNTang
 **/
public interface ArticleDao extends ElasticsearchRepository<Article, String> {
    List<Article> findByContentLike(String content);
}
  • Service层
/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    @Autowired
    private ArticleDao articleDao;
    public List<Article> getByContentLike(String content) {
        return articleDao.findByContentLike(content);
    }
}
  • 测试类代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void contentLike() {
        List<Article> articleList = articleService.getByContentLike("老铁,奥利给");
        System.out.println(articleList);
    }
}

分页查询

  • 分页查询主要涉及到两个类
  • 一个是 Page
  • 一个是 Pageable
  • 根据 content 和 title 分页查询
  • 修改 ArticleDao
  • Dao层
/**
 * @author BNTang
 **/
public interface ArticleDao extends ElasticsearchRepository<Article, String> {
    Page<Article> findByContentLikeOrTitleLike(String content, String title, Pageable pageable);
}
  • Service层
/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    @Autowired
    private ArticleDao articleDao;

    public Page<Article> getByContentOrTitlePage(String content, String title, int page, int size) {
        Pageable pageable = PageRequest.of(page - 1, size);
        Page<Article> articlePage = articleDao.findByContentLikeOrTitleLike(content, title, pageable);
        return articlePage;
    }
}
  • 测试类代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void testPage1() {
        Page<Article> page = articleService.getByContentOrTitlePage("奥利给", "PHP", 1, 2);
        System.out.println("总条数:" + page.getTotalElements());
        System.out.println("总页数:" + page.getTotalPages());
        System.out.println("本页数据:" + page.getContent());
    }
}

高级查询

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

基本查询

  • 先来看看基本的玩法
  • 修改 ArticleService
  • Service层

/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    public List<Article> getByContentNative(String content) {
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("content", content);

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(matchQueryBuilder);

        SearchHits<Article> searchHits = elasticsearchRestTemplate.search(queryBuilder.build(), Article.class);
        return searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());
    }
}
  • 测试代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void getByContentNative() {
        List<Article> articleList = articleService.getByContentNative("干就完了,奥利给");
        System.out.println(articleList);
    }
}
  • QueryBuilders 提供了大量的静态方法
  • 用于生成各种不同类型的查询对象,例如:词条、模糊、通配符等 QueryBuilder 对象
  • NativeSearchQueryBuilder:是 Spring 提供的一个查询条件构建器,帮助构建 JSON 格式的请求体

分页查询

  • 利用 NativeSearchQueryBuilder 可以很方便的实现分页:
  • top.it6666.dao 中创建一个 Page 类,代码如下:
/**
 * @author BNTang
 **/
@Data
@NoArgsConstructor
public class Page<T> {

    private Long totalCount;

    private Integer totalPage;

    private List<T> list;
    public Page(Long totalCount, List<T> list) {
        this.totalCount = totalCount;
        this.list = list;
    }

    public Page(Long totalCount, Integer size, List<T> list) {
        this.totalCount = totalCount;
        this.list = list;
        // 计算总页数
        this.totalPage = (int) Math.ceil(totalCount * 1.0 / size);
    }
}
  • 修改 ArticleService
  • Service层
/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    public Page<Article> getPageByContentNative(String content, String title, int page, int size) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        // 构造queryBuilder
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.should(QueryBuilders.matchQuery("content", content));
        boolQueryBuilder.should(QueryBuilders.matchQuery("title", title));

        queryBuilder.withQuery(boolQueryBuilder);
        queryBuilder.withPageable(PageRequest.of(page - 1, size));
        SearchHits<Article> searchHits = elasticsearchRestTemplate.search(queryBuilder.build(), Article.class);

        // 获取查询的数据
        List<Article> articleList = searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());

        // 获取总条数
        long totalHits = searchHits.getTotalHits();
        return new Page<Article>(totalHits, size, articleList);
    }
}
  • 测试类代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void getPageByContentNative() {
        Page<Article> page = articleService.getPageByContentNative("奥利给,Java,PHP,文档", "奥利给,Java,PHP,文档", 1, 2);
        System.out.println("总条数:" + page.getTotalCount());
        System.out.println("总页数:" + page.getTotalPage());
        System.out.println("本页数据:");
        page.getList().forEach(System.out::println);
    }
}
  • 可以发现,Elasticsearch 中的分页是从第 0 页开始

排序查询

  • 排序也能通过 NativeSearchQueryBuilder 来完成:
  • 修改 ArticleService
  • Service层
/**
 * @author BNTang
 **/
@Service
public class ArticleService {
    
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    public Page<Article> getPageByContentNativeSort(String content, String title, int page, int size) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        // 构造queryBuilder
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.should(QueryBuilders.matchQuery("content", content));
        boolQueryBuilder.should(QueryBuilders.matchQuery("title", title));

        queryBuilder.withQuery(boolQueryBuilder);
        queryBuilder.withPageable(PageRequest.of(page - 1, size));

        // 构造排序对象
        queryBuilder.withSort(SortBuilders.fieldSort("read").order(SortOrder.DESC));

        SearchHits<Article> searchHits = elasticsearchRestTemplate.search(queryBuilder.build(), Article.class);

        // 获取查询的数据
        List<Article> articleList = searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());

        // 获取总条数
        long totalHits = searchHits.getTotalHits();
        return new Page<Article>(totalHits, size, articleList);
    }
}
  • 测试类代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void getPageByContentNativeSort() {
        Page<Article> page = articleService.getPageByContentNativeSort("奥利给,Java,PHP,文档", "奥利给,Java,PHP,文档", 1, 2);
        System.out.println("总条数:" + page.getTotalCount());
        System.out.println("总页数:" + page.getTotalPage());
        System.out.println("本页数据:");
        page.getList().forEach(System.out::println);
    }
}

聚合查询

  • 聚合为桶
  • 桶就是分组,比如这里我们按照帖子分类进行分组:
  • Service层,修改 ArticleService
/**
 * @author BNTang
 **/
@Service
public class ArticleService {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    public void getCountByTypes() {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        // 设置要查询的字段
        nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));

        // 设置一下查询的条数
        nativeSearchQueryBuilder.withPageable(PageRequest.of(0, 1));

        // 设置聚合字段,指定聚合后的字段名,以及根据哪个字段聚合
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("typeCount").field("types"));

        // 搜索
        SearchHits<Article> searchHits = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), Article.class);

        // 取出来聚合的数据
        Aggregations aggregations = searchHits.getAggregations();

        // 取出我们聚合的字段
        Terms terms = aggregations.get("typeCount");
        List<? extends Terms.Bucket> buckets = terms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            System.out.println(bucket.getKey());
            System.out.println(bucket.getDocCount());
        }

    }
}
  • 测试代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void getCountByType() {
        articleService.getCountByTypes();
    }
}
  • 🐤嵌套聚合
  • Service层,修改 ArticleService
/**
 * @author BNTang
 **/
@Service
public class ArticleService {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    public void getCountAndSum() {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        // 设置要查询的字段
        nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));

        // 设置一下查询的条数
        nativeSearchQueryBuilder.withPageable(PageRequest.of(0, 1));

        // 设置聚合字段,指定聚合后的字段名,以及根据哪个字段聚合
        nativeSearchQueryBuilder.addAggregation(
                // 按照types聚合,求总数
                AggregationBuilders.terms("typeCount").field("types")
                        // types聚合完毕后,再根据read聚合
                        .subAggregation(AggregationBuilders.sum("readNum").field("read"))
        );

        // 搜索
        SearchHits<Article> searchHits = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), Article.class);
        Aggregations aggregations = searchHits.getAggregations();
        Terms terms = aggregations.get("typeCount");
        List<? extends Terms.Bucket> buckets = terms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            System.out.println(bucket.getKey());
            System.out.println(bucket.getDocCount());
            Aggregations subAggs = bucket.getAggregations();
            Sum sum = subAggs.get("readNum");
            System.out.println(sum.getValue());
        }
    }
}
  • 测试代码如下:
/**
 * @author BNTang
 **/
@SpringBootTest
public class EsApplicationTests {

    @Autowired
    private ArticleService articleService;

    @Test
    public void getCountAndSum() {
        articleService.getCountAndSum();
    }
}
posted @ 2020-10-21 13:41  BNTang  阅读(142)  评论(0编辑  收藏  举报