最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2020年10月24日 14:54:55   作者:空夜  
這篇文章主要介紹了SpringBoot 如何整合 ES 實(shí)現(xiàn) CRUD 操作,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下

本文介紹 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

如此,表示 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)如下情況:

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

http.cors.enabled: true
http.cors.allow-origin: "*"

刷新頁(yè)面:

這里的 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: 8060

spring:
 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)) {
  // 開(kāi)啟一個(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)化一下。開(kāi)始使用的是深度分頁(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ù)開(kāi)啟一個(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)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot+vue實(shí)現(xiàn)登錄圖片驗(yàn)證碼功能

    SpringBoot+vue實(shí)現(xiàn)登錄圖片驗(yàn)證碼功能

    這篇文章主要給大家介紹一下如何SpringBoot+vue實(shí)現(xiàn)登錄圖片驗(yàn)證碼功能,文中有詳細(xì)的代碼示例,具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-07-07
  • 淺談java Collection中的排序問(wèn)題

    淺談java Collection中的排序問(wèn)題

    下面小編就為大家?guī)?lái)一篇淺談java Collection中的排序問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-12-12
  • 一文徹底了解Java的組合模式

    一文徹底了解Java的組合模式

    組合模式(Composite?Pattern)指將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu),?使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。本文就來(lái)帶大家深入了解一下Java的組合模式吧
    2023-02-02
  • java封裝空值建議使用Optional替代null的方法示例解析

    java封裝空值建議使用Optional替代null的方法示例解析

    這篇文章主要為大家介紹了java封裝空值建議使用Optional替代null的方法原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Lombok注解之@SuperBuilder--解決無(wú)法builder父類屬性問(wèn)題

    Lombok注解之@SuperBuilder--解決無(wú)法builder父類屬性問(wèn)題

    這篇文章主要介紹了Lombok注解之@SuperBuilder--解決無(wú)法builder父類屬性問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Sprint Boot 集成MongoDB的操作方法

    Sprint Boot 集成MongoDB的操作方法

    最近接手一個(gè)Springboot項(xiàng)目,需要在原項(xiàng)目上增加一些需求,用到了mongodb。下面通過(guò)本文給大家分享Sprint Boot 集成MongoDB的操作方法,需要的朋友參考下吧
    2017-12-12
  • idea熱部署插件jrebel正式版及破解版安裝詳細(xì)圖文教程

    idea熱部署插件jrebel正式版及破解版安裝詳細(xì)圖文教程

    這篇文章主要介紹了idea熱部署插件jrebel正式版及破解版安裝詳細(xì)教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Java Thread之Sleep()使用方法總結(jié)

    Java Thread之Sleep()使用方法總結(jié)

    這篇文章主要介紹了Java Thread之Sleep()使用方法總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • Spring中使用atomikos+druid實(shí)現(xiàn)經(jīng)典分布式事務(wù)的方法

    Spring中使用atomikos+druid實(shí)現(xiàn)經(jīng)典分布式事務(wù)的方法

    這篇文章主要介紹了Spring中使用atomikos+druid實(shí)現(xiàn)經(jīng)典分布式事務(wù)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-06-06
  • Java排序算法總結(jié)之插入排序

    Java排序算法總結(jié)之插入排序

    這篇文章主要介紹了Java排序算法總結(jié)之插入排序,較為詳細(xì)的分析了插入排序的原理與java實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2015-05-05

最新評(píng)論

乐业县| 水城县| 章丘市| 阜康市| 渝北区| 清新县| 扎兰屯市| 贵州省| 饶平县| 康乐县| 和顺县| 大丰市| 华宁县| 徐州市| 东平县| 安顺市| 同心县| 阳城县| 德惠市| 深州市| 成武县| 五大连池市| 手机| 秀山| 沙田区| 西安市| 泽库县| 儋州市| 莲花县| 婺源县| 信阳市| 肇东市| 睢宁县| 定西市| 九龙坡区| 万宁市| 长丰县| 新竹县| 三亚市| 阿拉善左旗| 政和县|