午夜剧场伦理_日本一道高清_国产又黄又硬_91黄色网战_女同久久另类69精品国产_妹妹的朋友在线

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

SpringBoot 如何整合 ES 實(shí)現(xiàn) CRUD 操作

瀏覽:99日期:2023-04-14 15:06:59

本文介紹 Spring Boot 項(xiàng)目中整合 ElasticSearch 并實(shí)現(xiàn) CRUD 操作,包括分頁(yè)、滾動(dòng)等功能。之前在公司使用 ES,一直用的是前輩封裝好的包,最近希望能夠從原生的 Spring Boot/ES 語(yǔ)法角度來(lái)學(xué)習(xí) ES 的相關(guān)技術(shù)。希望對(duì)大家有所幫助。

本文為 spring-boot-examples 系列文章節(jié)選,示例代碼已上傳至 https://github.com/laolunsi/spring-boot-examples

安裝 ES 與可視化工具

前往 ES 官方 https://www.elastic.co/cn/downloads/elasticsearch 進(jìn)行,如 windows 版本只需要下載安裝包,啟動(dòng) elasticsearch.bat 文件,瀏覽器訪問(wèn) http://localhost:9200

SpringBoot 如何整合 ES 實(shí)現(xiàn) CRUD 操作

如此,表示 ES 安裝完畢。

為更好地查看 ES 數(shù)據(jù),再安裝一下 elasticsearch-head 可視化插件。前往下載地址:https://github.com/mobz/elasticsearch-head主要步驟:

git clone git://github.com/mobz/elasticsearch-head.git cd elasticsearch-head npm install npm run start open http://localhost:9100/

可能會(huì)出現(xiàn)如下情況:

SpringBoot 如何整合 ES 實(shí)現(xiàn) CRUD 操作

發(fā)現(xiàn)是跨域的問(wèn)題。解決辦法是在 elasticsearch 的 config 文件夾中的 elasticsearch.yml 中添加如下兩行配置:

http.cors.enabled: truehttp.cors.allow-origin: '*'

刷新頁(yè)面:

SpringBoot 如何整合 ES 實(shí)現(xiàn) CRUD 操作

這里的 article 索引就是我通過(guò) spring boot 項(xiàng)目自動(dòng)創(chuàng)建的索引。下面我們進(jìn)入正題。

Spring Boot 引入 ES

創(chuàng)建一個(gè) spring-boot 項(xiàng)目,引入 es 的依賴:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>

配置 application.yml:

server: port: 8060spring: elasticsearch: rest: uris: http://localhost:9200

創(chuàng)建一個(gè)測(cè)試的對(duì)象,article:

import org.springframework.data.annotation.Id;import org.springframework.data.elasticsearch.annotations.Document;import java.util.Date;@Document(indexName = 'article')public class Article { @Id private String id; private String title; private String content; private Integer userId; private Date createTime; // ... igonre getters and setters}

下面介紹 Spring Boot 中操作 ES 數(shù)據(jù)的三種方式:

實(shí)現(xiàn) ElasticsearchRepository 接口 引入 ElasticsearchRestTemplate 引入 ElasticsearchOperations

實(shí)現(xiàn)對(duì)應(yīng)的 Repository:

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;public interface ArticleRepository extends ElasticsearchRepository<Article, String> {}

下面可以使用這個(gè) ArticleRepository 來(lái)操作 ES 中的 Article 數(shù)據(jù)。我們這里沒(méi)有手動(dòng)創(chuàng)建這個(gè) Article 對(duì)應(yīng)的索引,由 elasticsearch 默認(rèn)生成。

下面的接口,實(shí)現(xiàn)了 spring boot 中對(duì) es 數(shù)據(jù)進(jìn)行插入、更新、分頁(yè)查詢、滾動(dòng)查詢、刪除等操作。可以作為一個(gè)參考。其中,使用了 Repository 來(lái)獲取、保存、刪除 ES 數(shù)據(jù),使用 ElasticsearchRestTemplate 或 ElasticsearchOperations 來(lái)進(jìn)行分頁(yè)/滾動(dòng)查詢。

根據(jù) id 獲取/刪除數(shù)據(jù)

@Autowired private ArticleRepository articleRepository; @GetMapping('{id}') public JsonResult findById(@PathVariable String id) { Optional<Article> article = articleRepository.findById(id); JsonResult jsonResult = new JsonResult(true); jsonResult.put('article', article.orElse(null)); return jsonResult; } @DeleteMapping('{id}') public JsonResult delete(@PathVariable String id) { // 根據(jù) id 刪除 articleRepository.deleteById(id); return new JsonResult(true, '刪除成功'); }

保存數(shù)據(jù)

@PostMapping('') public JsonResult save(Article article) { // 新增或更新 String verifyRes = verifySaveForm(article); if (!StringUtils.isEmpty(verifyRes)) { return new JsonResult(false, verifyRes); } if (StringUtils.isEmpty(article.getId())) { article.setCreateTime(new Date()); } Article a = articleRepository.save(article); boolean res = a.getId() != null; return new JsonResult(res, res ? '保存成功' : ''); } private String verifySaveForm(Article article) { if (article == null || StringUtils.isEmpty(article.getTitle())) { return '標(biāo)題不能為空'; } else if (StringUtils.isEmpty(article.getContent())) { return '內(nèi)容不能為空'; } return null; }

分頁(yè)查詢數(shù)據(jù)

@Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate; @Autowired ElasticsearchOperations elasticsearchOperations; @GetMapping('list') public JsonResult list(Integer currentPage, Integer limit) { if (currentPage == null || currentPage < 0 || limit == null || limit <= 0) { return new JsonResult(false, '請(qǐng)輸入合法的分頁(yè)參數(shù)'); } // 分頁(yè)列表查詢 // 舊版本的 Repository 中的 search 方法被廢棄了。 // 這里采用 ElasticSearchRestTemplate 或 ElasticsearchOperations 來(lái)進(jìn)行分頁(yè)查詢 JsonResult jsonResult = new JsonResult(true); NativeSearchQuery query = new NativeSearchQuery(new BoolQueryBuilder()); query.setPageable(PageRequest.of(currentPage, limit)); // 方法1: SearchHits<Article> searchHits = elasticsearchRestTemplate.search(query, Article.class); // 方法2: // SearchHits<Article> searchHits = elasticsearchOperations.search(query, Article.class); List<Article> articles = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList()); jsonResult.put('count', searchHits.getTotalHits()); jsonResult.put('articles', articles); return jsonResult; }

滾動(dòng)查詢數(shù)據(jù)

@GetMapping('scroll') public JsonResult scroll(String scrollId, Integer size) { // 滾動(dòng)查詢 scroll api if (size == null || size <= 0) { return new JsonResult(false, '請(qǐng)輸入每頁(yè)查詢數(shù)'); } NativeSearchQuery query = new NativeSearchQuery(new BoolQueryBuilder()); query.setPageable(PageRequest.of(0, size)); SearchHits<Article> searchHits = null; if (StringUtils.isEmpty(scrollId)) { // 開啟一個(gè)滾動(dòng)查詢,設(shè)置該 scroll 上下文存在 60s // 同一個(gè) scroll 上下文,只需要設(shè)置一次 query(查詢條件) searchHits = elasticsearchRestTemplate.searchScrollStart(60000, query, Article.class, IndexCoordinates.of('article')); if (searchHits instanceof SearchHitsImpl) { scrollId = ((SearchHitsImpl) searchHits).getScrollId(); } } else { // 繼續(xù)滾動(dòng) searchHits = elasticsearchRestTemplate.searchScrollContinue(scrollId, 60000, Article.class, IndexCoordinates.of('article')); } List<Article> articles = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList()); if (articles.size() == 0) { // 結(jié)束滾動(dòng) elasticsearchRestTemplate.searchScrollClear(Collections.singletonList(scrollId)); scrollId = null; } if (scrollId == null) { return new JsonResult(false, '已到末尾'); } else { JsonResult jsonResult = new JsonResult(true); jsonResult.put('count', searchHits.getTotalHits()); jsonResult.put('size', articles.size()); jsonResult.put('articles', articles); jsonResult.put('scrollId', scrollId); return jsonResult; } }

ES 深度分頁(yè) vs 滾動(dòng)查詢

上次遇到一個(gè)問(wèn)題,同事跟我說(shuō)日志檢索的接口太慢了,問(wèn)我能不能優(yōu)化一下。開始使用的是深度分頁(yè),即 1,2,3..10, 這樣的分頁(yè)查詢,查詢條件較多(十多個(gè)參數(shù))、查詢數(shù)據(jù)量較大(單個(gè)日志索引約 2 億條數(shù)據(jù))。

分頁(yè)查詢速度慢的原因在于:ES 的分頁(yè)查詢,如查詢第 100 頁(yè)數(shù)據(jù),每頁(yè) 10 條,是先從每個(gè)分區(qū) (shard,一個(gè)索引默認(rèn)是 5 個(gè) shard) 中把命中的前 100 * 10 條數(shù)據(jù)查出來(lái),然后由協(xié)調(diào)節(jié)點(diǎn)進(jìn)行合并等操作,最后給出第 100 頁(yè)的數(shù)據(jù)。也就是說(shuō),實(shí)際被加載到內(nèi)存中的數(shù)據(jù)遠(yuǎn)超過(guò)理想情況。

這樣,索引的 shard 越大,查詢頁(yè)數(shù)越多,查詢速度就越慢。ES 默認(rèn)的 max_result_window 是 10000 條,也就是正常情況下,用分頁(yè)查詢到 10000 條數(shù)據(jù)時(shí),就不會(huì)再返回下一頁(yè)數(shù)據(jù)了。

如果不需要進(jìn)行跳頁(yè),比如直接查詢第 100 頁(yè)數(shù)據(jù),或者數(shù)據(jù)量非常大,那么可以考慮用 scroll 查詢。在 scroll 查詢下,第一次需要根據(jù)查詢參數(shù)開啟一個(gè) scroll 上下文,設(shè)置上下文緩存時(shí)間。以后的滾動(dòng)只需要根據(jù)第一次返回的 scrollId 來(lái)進(jìn)行即可。

scroll 只支持往下滾動(dòng),如果想要往回滾動(dòng),還可以根據(jù) scrollId 緩存查詢結(jié)果,這樣就可以實(shí)現(xiàn)上下滾動(dòng)查詢了 —— 就像大家經(jīng)常使用的淘寶商品檢索時(shí)上下滾動(dòng)一樣。

以上就是SpringBoot 如何整合 ES 實(shí)現(xiàn) CRUD 操作的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot實(shí)現(xiàn) CRUD 操作的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 毛片网站在线观看 | 视频一区国产精品 | 欧美自拍视频在线观看 | 欧美黑人猛猛猛 | 国产综合久久 | 欧美日韩一区二区三区不卡 | 污网站在线观看免费 | 四虎在线免费视频 | 一区二区三区福利视频 | 97伊人网 | 亚洲福利网 | 久操视频网站 | 国产手机在线 | 91久久精品视频 | 曰韩一级 | 一级做a爱片性色毛片 | 欧美日韩久久久久久 | 欧美日韩精品一区二区三区四区 | 成人国产精品久久久网站 | 玖玖爱在线精品视频 | 青青操免费在线视频 | 亚洲精品在线视频观看 | av一二三| 亚洲天堂2020 | 欧美亚洲在线视频 | 久久久久9 | 夜夜cao| 成人欧美日韩 | 一级国产精品 | 国产精品成人一区二区网站软件 | 日韩一区二区三区视频在线观看 | 午夜激情影院 | 超碰动漫 | 黄色大片久久 | 久久最新视频 | 伊人天堂av | 久久av一区二区三区亚洲 | 九色综合网| 一级片a级片 | 欧美日韩一区二区三 | 久久影院视频 |