增删改
添加
- 修改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 | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
- 根据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());
    }
}
- 关于ES分页:
高级查询
- 虽然基本查询和自定义方法已经很强大了
- 但是如果是复杂查询(模糊、通配符、词条查询等)就显得力不从心了
- 此时,我们只能使用原生查询
基本查询
- 先来看看基本的玩法
- 修改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();
    }
}









