现在网上的用springboot对ElasticSearch做增删改查的文章,几乎都是用的原生API或者继承了ElasticsearchRepository去做。但是现在我用的springboot2.5.6已经移除了ElasticsearchRepository里的search()方法,只剩了一些特别基础的增删改查,基本上是不能用的。官方明显是想让开发者用ElasticsearchRestTemplate去做,但是搜索网上没有发现很好的增删改查教程,于是我把自己写的代码贴一下:
package com.markerhub.search.model.mq;
import com.alibaba.fastjson.JSON;
import com.markerhub.config.RabbitConfig;
import com.markerhub.entity.Blog;
import com.markerhub.search.model.BlogPostDocument;
import com.markerhub.service.BlogService;
import com.markerhub.util.MyUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RabbitListener(queues = RabbitConfig.ES_QUEUE)
public class MQMessageHandler {
BlogService blogService;
@Autowired
public void setBlogService(BlogService blogService) {
this.blogService = blogService;
}
ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
public void setElasticsearchRestTemplate(ElasticsearchRestTemplate elasticsearchRestTemplate) {
this.elasticsearchRestTemplate = elasticsearchRestTemplate;
}
@SneakyThrows
@RabbitHandler
public void handler(PostMQIndexMessage message) {
switch (message.getType()) {
case PostMQIndexMessage.UPDATE:
Long updateId = message.getPostId();
Blog blogExisted = blogService.getById(updateId);
BlogPostDocument postDocument = MyUtils.blogToDocument(blogExisted);
String obj = JSON.toJSONString(postDocument);
Document document = Document.parse(obj);
UpdateQuery query = UpdateQuery
.builder(String.valueOf(updateId))
.withDocument(document)
.build();
IndexCoordinates indexCoordinates = elasticsearchRestTemplate.getIndexCoordinatesFor(BlogPostDocument.class);
UpdateResponse update = elasticsearchRestTemplate.update(query, indexCoordinates);
String result = String.valueOf(update.getResult());
log.info("更新结果: {}", result);
break;
case PostMQIndexMessage.REMOVE:
Long deleteId = message.getPostId();
String delete = elasticsearchRestTemplate.delete(deleteId.toString(), BlogPostDocument.class);
log.info("删除结果: {}", delete);
break;
case PostMQIndexMessage.CREATE:
Long createId = message.getPostId();
Blog newBlog = blogService.getById(createId);
BlogPostDocument newDocument = MyUtils.blogToDocument(newBlog);
BlogPostDocument save = elasticsearchRestTemplate.save(newDocument);
log.info("保存结果: {}", save);
break;
default:
log.error("没找到对应的消息类型: {}", message);
}
}
}
es里的实体类是BlogPostDocument。上面是对rabbitmq整合时当数据库更新,往队列发一条消息,发消息的代码没有贴出来,不过重点不在这里。这里通知消费者去执行相关的方法。首先ElasticsearchRestTemplate把增删改查分开了,增是增,删是删,改是改,查是查,不能再把增改一起用了。实现比较简单,代码也比较直白,查询代码是这样的:
/**
* 搜索功能,从es搜索
*/
@GetMapping("/search/{currentPage}")
public Result search(@PathVariable Integer currentPage, @RequestParam String keyword) {
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, "title", "description", "content");
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
.withQuery(multiMatchQueryBuilder)
.withSort(SortBuilders.fieldSort("created").order(SortOrder.DESC))
.build();
SearchHits<BlogPostDocument> search = elasticsearchRestTemplate.search(nativeSearchQuery, BlogPostDocument.class);
if (search.getTotalHits() == 0) {
throw new ResourceNotFoundException("没有相关记录");
}
// List<SearchHit<BlogPostDocument>> list = search.getSearchHits();
ArrayList<BlogPostDocument> list = new ArrayList<>();
for (SearchHit<BlogPostDocument> hit : search.getSearchHits()) {
list.add(hit.getContent());
}
Page<BlogPostDocument> page = MyUtils.listToPage(list, currentPage, 5);
return Result.succ(page);
}
@GetMapping("/searchByYear/{currentPage}/{year}")
public Result searchByYear(@PathVariable Integer currentPage, @RequestParam String keyword, @PathVariable Integer year) {
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, "title", "description", "content");
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.rangeQuery("created").gte(year + "-01-01T00:00:00").lte(year + "-12-31T23:59:59")
.includeUpper(true)
.includeLower(true))
.must(multiMatchQueryBuilder))
.withSort(SortBuilders.fieldSort("created").order(SortOrder.DESC))
.build();
SearchHits<BlogPostDocument> search = elasticsearchRestTemplate.search(nativeSearchQuery, BlogPostDocument.class);
if (search.getTotalHits() == 0) {
throw new ResourceNotFoundException("没有相关记录");
}
ArrayList<BlogPostDocument> list = new ArrayList<>();
for (SearchHit<BlogPostDocument> hit : search.getSearchHits()) {
list.add(hit.getContent());
}
Page<BlogPostDocument> page = MyUtils.listToPage(list, currentPage, 5);
return Result.succ(page);
}
这里是多匹配查询,multiMatchQuery会匹配传入的所有field,由于继承了ik分词器,搜索比较智能吧。
总体来看,总而言之,ElasticsearchRestTemplate的应用方法是,先构建相应的query对象(查询MultiMatchQueryBuilder或者更新UpdateQuery之类的),在这个对象里传入你的查询条件或者修改的对象或者新增的对象,再传入ElasticsearchRestTemplate的相关方法里。