現在網上的用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的相關方法里。