增删改
添加
- 修改
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);
}
}
批量新增

/**
* @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
/**
* @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
/**
* @author BNTang
**/
public interface ArticleDao extends ElasticsearchRepository<Article, String> {
List<Article> findByContentLike(String content);
}
/**
* @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
/**
* @author BNTang
**/
public interface ArticleDao extends ElasticsearchRepository<Article, String> {
Page<Article> findByContentLikeOrTitleLike(String content, String title, Pageable pageable);
}
/**
* @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();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具