35 Spring整合Elasticsearch

文章目錄

  • Spring整合Elasticsearch
    • 引入依賴
    • 配置Elasticsearch
    • 解決沖突
  • 使用Elasticsearch
    • Spring Data Elasticsearch
    • 建立映射關系
    • 常用方法
      • 添加數據
      • 修改數據
      • 刪除數據
      • 搜索數據(es核心)
        • 步驟
          • 構造搜索條件 并 應用
          • 進行查詢
          • 使用查詢結果

Spring整合Elasticsearch

引入依賴

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

配置Elasticsearch

  • cluster-name集群名
  • cluster-nodes集群節點
# ElasticsearchProperties
# 配置集群名,與es配置文件中的一致
spring.data.elasticsearch.cluster-name=nowcoder
# 集群節點,格式  節點ip地址:端口
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300

解決沖突

如果項目中使用了redis,則需要解決沖突

es和redis都基于netty,這兩者在啟動netty時,會產生沖突:系統會認為redis已經啟動了netty,es無法再啟動

要盡可能在服務啟動早期的時候,修改es.set.netty.runtime.available.processors為 false

修改入口類,因為入口類是最先被加載的

@PostConstruct: 管理bean的生命周期,主要用于初始化的方法,該注解修飾的方法在構造器調用完以后被執行

在這個初始化方法中修改系統屬性就足夠早

@SpringBootApplication
public class CommunityApplication {@PostConstructpublic void init() {// 解決netty啟動沖突問題// es.set.netty.runtime.available.processors 從 Netty4Utils.setAvailableProcessors() 中找到// 設置系統屬性System.setProperty("es.set.netty.runtime.available.processors", "false");}public static void main(String[] args) {SpringApplication.run(CommunityApplication.class, args);}}

使用Elasticsearch

Spring Data Elasticsearch

用于訪問es服務器的API

  • ElasticsearchTemplate :有特殊情況,DiscussPostRepository處理不了時使用

  • ElasticsearchRepository: 接口,需要定義一個子接口繼承他,聲明訪問哪些數據,Spring會自動實現這個接口

    所有的代碼都是Spring自動生成的,Spring會自動將實體數據和es服務器的索引進行映射,因此需要用注解

    代碼實例:

    // es可以看成特殊的數據庫,因此加上注解@Repository
    // @Mapper是MyBa'ti'd專有注解
    // @Repository是spring提供的,針對數據訪問層的注解
    @Repository
    // es的接口一般取名XXXRepository,該接口訪問的是帖子,故叫DiscussPostRepository
    public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> {// 繼承時要用泛型聲明:當前接口要處理的實體類,以及實體類中的id類型// 父接口ElasticsearchRepository中 已經定義好了對es服務器訪問的增刪改查方法// 聲明完泛型,加上注解之后,spring會自動實現自定義的子接口DiscussPostRepository
    }
    

建立映射關系

要對spring說明哪個實體類和es的索引怎樣進行對應,建立映射關系,映射完成后,spring底層就可以幫我們生成實現類

  1. 用@Document指明 表 和 es 中索引的對應關系

    @Document ( indexName = “…”, type = “…”, shards = , replicas = )

    indexName: 實體數據映射到哪個索引上。通常為全小寫的類名
    type: 實體數據映射到哪個類型上。類型已經在逐步被弱化甚至取消了,因此寫成固定的 _doc
    shards: 創建幾個分片。根據服務器處理能力配
    replicas: 創建幾個副本。
    沒有指定索引會創建這個索引,并且是根據指定分片和副本進行創建的

  2. 指明 實體中屬性 和 es中字段 的對應關系

    給類中每個 屬性 上加注解用于和 索引中的字段 相關聯

? 表的id屬性要 加 @id 注解

    @Id // 與索引中id字段對應private int id;

? 其他普通屬性 加 @Field注解并指明字段類型

    // 用于普通字段,需指明字段類型@Field(type = FieldType.Integer)private int userId;

? 當某些屬性 對應的 es字段要用于關鍵詞匹配時,需在注解中指明使用的analyzer和searchAnalyzer

? analyzer為存儲時候的解析器/分詞器。

當我們存一句話時,會提取出關鍵詞,并用關鍵詞關聯這句話,搜索時就可以通過關鍵詞搜到這句話
因此存的時候,因該盡可能將一句話拆出盡可能多的關鍵詞,以擴大搜索范圍。
故需要一個范圍非常大的分詞器,而我們安裝的中文分詞器中存在這樣的分詞器——ik_max_word

? searchAnalyzer為搜索時候解析器/分詞器

搜索時,輸入的句子不需要拆出過多關鍵詞,不用拆的過細
如”互聯網校招“,可以拆出:互聯網、聯網、網校、校招等關鍵詞,但實際上我們沒有這些意思
此時要使用拆分出盡可能少但滿足用戶需求的詞語——ik_smart

    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String content;
// 將數據庫中帖子存到es服務器里,就可以去es服務器中搜索這些帖子了
@Document(indexName = "discusspost", type = "_doc", shards = 6, replicas = 3)
public class DiscussPost {@Id // 與索引中id字段對應private int id;// userId為普通字段@Field(type = FieldType.Integer)private int userId;// 搜帖子主要在title和content中查找@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String title;@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String content;// 不用在這些字段進行搜索,就不用analyzer和searchAnalyzer屬性@Field(type = FieldType.Integer)private int type;@Field(type = FieldType.Integer)private int status;@Field(type = FieldType.Date)private Date createTime;@Field(type = FieldType.Integer)private int commentCount;@Field(type = FieldType.Double)private double score;}

常用方法

添加數據

一次添加一條數據:save(一條數據)

    @Testpublic void testInsert() {// 給es服務器添加數據:save(一條數據)// 在mysql中找到一條數據discussMapper.selectDiscussPostById(241),添加到es服務器// 不用特地創建索引,索引不存在時,es會幫我們自動創建discussRepository.save(discussMapper.selectDiscussPostById(241));discussRepository.save(discussMapper.selectDiscussPostById(242));discussRepository.save(discussMapper.selectDiscussPostById(243));}

一次添加多條數據:saveAll(多條數據)

    @Testpublic void testInsertList() {// 一次添加多條數據:saveAll(多條數據)// discussMapper.selectDiscussPosts(101, 0, 100) mysql分頁查找discussRepository.saveAll(discussMapper.selectDiscussPosts(101, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(102, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(103, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(111, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(112, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(131, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(132, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(133, 0, 100));discussRepository.saveAll(discussMapper.selectDiscussPosts(134, 0, 100));}

修改數據

調用save方法將之前的數據再覆蓋一遍

    @Testpublic void testUpdate() {// 查出第231條數據,修改屬性DiscussPost post = discussMapper.selectDiscussPostById(231);post.setContent("我是新人,使勁灌水.");// 用save覆蓋原來的discussRepository.save(post);}

刪除數據

一次刪除一條數據:deleteById( id )

    @Testpublic void testDelete() {discussRepository.deleteById(231);}

一次刪除所有數據:deleteAll

風險高,不常用

    @Testpublic void testDelete() {discussRepository.deleteAll();}

搜索數據(es核心)

步驟
  1. 構造搜索條件 并 應用

    搜索條件:要不要排序、分頁、結果要不要高亮顯示等

    高亮顯示:給關鍵詞加em標簽,在文本顯示到網頁上時,前端可以給em加樣式

    搜索條件構造方式:SearchQuery對象,實現類是NativeSearchQuery,而NativeSearchQueryBuilder是一個可以構造NativeSearchQuery的工具類

            SearchQuery searchQuery = new NativeSearchQueryBuilder()// 1)指定查詢條件:withQuery// 查詢條件由QueryBuilders對象構造,multiMatchQuery用于指定查詢關鍵詞和查詢字段范圍.withQuery(QueryBuilders.multiMatchQuery("互聯網寒冬", "title", "content"))// 2)指定排序條件// 優先按照置頂排序,再按分數(精品貼會被折算成分數),都相同就按創建時間排序.withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC)).withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC)).withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))// 3)指定分頁條件.withPageable(PageRequest.of(0, 10))// 4)指定給哪些字段里匹配詞進行高亮顯示.withHighlightFields(new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>"))// 5)執行,即應用搜索條件.build();
    
  2. 進行查詢

    分頁查詢結果用spring提供的Page對象接收
    Page中封裝多個實體,即當前這一頁的實體

    方法一:用Repository進行搜索

            Page<DiscussPost> page = discussRepository.search(searchQuery);
    

    存在問題:

    es返回結果包含:原始結果(即匹配到的結果) 和 高亮顯示部分(即匹配到的關鍵詞前后一部分內容,不是整個內容,不會浪費空間)
    需要將高亮顯示部分整合到原始結果中,進行一個替換,太過麻煩,不夠完善

    問題原因:

    查詢方法discussRepository.search(searchQuery)的源碼底層調用如下方法進行查詢:

    elasticTemplate.queryForPage(searchQuery, class, SearchResultMapper)
    

    得到的兩份數據,需要用SearchResultMapper進行組裝,但默認實現類底層沒有組裝,即底層獲取得到了高亮顯示部分, 但是沒有返回(結果里看不到).

    image-20240226203700628

    方法二:直接用ElasticsearchTemplate進行搜索

    elasticTemplate. queryForPage(搜索條件, 實體類型, 處理兩部分結果合并問題的接口)
    

    通過匿名內部類方式,實現接口:

    new SearchResultMapper() {@Overridepublic <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {... ...}
    }
    

    實現接口方法:

    1. 獲取搜索命令查詢結果

      通過response獲取搜索命令的數據,可能會得到多條數據,放在SearchHits中

      SearchHits hits = response.getHits();
      
    2. 判斷結果是否為空

      搜索命令返回結果的數據量,即返回結果有幾條數據

      if (hits.getTotalHits() <= 0) {return null;
      }
      
    3. 遍歷每一條數據,轉成目標實體存儲

      將這些實體存儲在集合中List<DiscussPost> list

      for (SearchHit hit : hits)
      

      hits中每一條數據hit的形式如下

      image-20240303110245118

      1)處理非高亮顯示內容:

      獲取非高亮內容:

      es的返回數據是json格式,SearchHit對象里將json格式對象數據封裝成了Map格式

      hit.getSourceAsMap():可以獲取map形式數據,通過指定map的key可以調用每一個字段的值

      處理步驟:
      (1)對所有的字段,不管實際有沒有高亮顯示,都先獲取非高亮顯示版本

      不能直接獲取高亮顯示內容并存入實體,可能導致某些實體屬性為空,因為不確定具體在哪個字段中匹配到關鍵字,某些字段可能沒有匹配到關鍵字

      后續處理高亮顯示數據時,會用有高亮顯示的字段,覆蓋 實體屬性 原來的非高亮內容

      hit.getSourceAsMap().get("id")
      

      (2)再把 獲取到的任何類型的數據 都轉成 字符串

      String id = hit.getSourceAsMap().get("id").toString();
      

      (3)存到java實體中時,轉為對應類型

      post.setId(Integer.valueOf(id));
      

      2)處理高亮顯示內容

      獲取高亮顯示數據

      hit.getHighlightFields()
      

      (1)獲取指定字段高亮顯示數據

       HighlightField contentField = hit.getHighlightFields().get("content");
      

      高亮數據格式:

      image-20240226204528255

      (2)判斷該字段是否有高亮顯示數據:有些字段中沒有關鍵字,就沒有高亮內容

      if (contentField != null)
      

      (3)有高亮顯示數據時,獲取高亮內容第一段

      getFragments():返回值是個數組,將內容做了分段,每一段都是 匹配的詞語 前后的一部分內容,如上圖

      由于字段中匹配的詞語可能是多個,因此我們只需要第一段設置高亮了就可以

      contentField.getFragments()[0].toString()
      

      (4)存入實體對應屬性(此時就替換了屬性中非高亮數據)

      post.setContent(contentField.getFragments()[0].toString());
      
    4. 返回一個包含 實體集合 的數據

      方法返回值是AggregatedPage類型
      因此為需要構造AggregatedPage接口的實現類AggregatedPageImpl
      實現類中會傳多個參數,參數順序需要看底層源碼

      // list: 結果集合
      // pageable:方法的參數
      // hits.getTotalHits():數據總條數
      return new AggregatedPageImpl(list, pageable, hits.getTotalHits(), response.getAggregations(), response.getScrollId(), hits.getMaxScore());
      

    代碼匯總

            Page<DiscussPost> page = elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() {@Overridepublic <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {SearchHits hits = response.getHits();if (hits.getTotalHits() <= 0) {return null;}List<DiscussPost> list = new ArrayList<>();for (SearchHit hit : hits) {DiscussPost post = new DiscussPost();// 處理非高亮顯示結果String id = hit.getSourceAsMap().get("id").toString();post.setId(Integer.valueOf(id));String userId = hit.getSourceAsMap().get("userId").toString();post.setUserId(Integer.valueOf(userId));String title = hit.getSourceAsMap().get("title").toString();post.setTitle(title);String content = hit.getSourceAsMap().get("content").toString();post.setContent(content);String status = hit.getSourceAsMap().get("status").toString();post.setStatus(Integer.valueOf(status));String createTime = hit.getSourceAsMap().get("createTime").toString();// String轉Date:String-->Long-->Datepost.setCreateTime(new Date(Long.valueOf(createTime)));String commentCount = hit.getSourceAsMap().get("commentCount").toString();post.setCommentCount(Integer.valueOf(commentCount));// 處理高亮顯示的結果HighlightField titleField = hit.getHighlightFields().get("title");if (titleField != null) {post.setTitle(titleField.getFragments()[0].toString());}HighlightField contentField = hit.getHighlightFields().get("content");if (contentField != null) {post.setContent(contentField.getFragments()[0].toString());}list.add(post);}return new AggregatedPageImpl(list, pageable,hits.getTotalHits(), response.getAggregations(), response.getScrollId(), hits.getMaxScore());}});
    
  3. 使用查詢結果
            // 一共查到多少數據匹配System.out.println(page.getTotalElements());// 一共有多少頁System.out.println(page.getTotalPages());// 當前處在第幾頁System.out.println(page.getNumber());// 每一頁顯示多少條數據System.out.println(page.getSize());// 遍歷Page中數據,逐一查看// Page繼承了Iterable接口,可以被遍歷for (DiscussPost post : page) {System.out.println(post);}
    

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/716276.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/716276.shtml
英文地址,請注明出處:http://en.pswp.cn/news/716276.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring注解之事務 @Transactional

目錄 Spring 對事務的支持 事務 Transactional Spring 對事務的支持 提醒一次&#xff1a;你的程序是否支持事務首先取決于數據庫 &#xff0c;比如使用 MySQL 的話&#xff0c;如果你選擇的是 innodb 引擎&#xff0c;那么恭喜你&#xff0c;是可以支持事務的。但是&#x…

鴻蒙Harmony應用開發—ArkTS聲明式開發(通用屬性:Popup控制)

給組件綁定popup彈窗&#xff0c;并設置彈窗內容&#xff0c;交互邏輯和顯示狀態。 說明&#xff1a; 從API Version 7開始支持。后續版本如有新增內容&#xff0c;則采用上角標單獨標記該內容的起始版本。 popup彈窗的顯示狀態在onStateChange事件回調中反饋&#xff0c;其顯…

opencv內存溢出del釋放變量 (python)

報錯&#xff1a; cv2.error: OpenCV(3.4.17) D:\a\opencv-python\opencv-python\opencv\modules\core\src\alloc.cpp:73: error: (-4:Insufficient memory) Failed to allocate 12211548 bytes in function ‘cv::OutOfMemoryError’ 檢查內存代碼 import psutil# 獲取當前進…

內存空間擔保機制

什么是內存空間擔保機制&#xff1f; 內存空間擔保機制&#xff08;Memory Space Guarantee&#xff09;是垃圾回收&#xff08;Garbage Collection&#xff09;算法中的一種策略。它用于在進行垃圾回收過程&#xff08;如Minor GC或Full GC&#xff09;時&#xff0c;確保老年…

Java項目layui分頁中文亂碼

【問題描述】這部分沒改之前中文亂碼。 【解決辦法】在layui.js或者layui.all.js文件中替換共、頁、條轉換成Unicode碼格式。 字符Unicode共&#x5171頁&#x9875條&#x6761【完美解決】改完之后重新運行項目&#xff0c;瀏覽器F12緩存清除就好了&#xff0c;右鍵

MySQL的單表和多表查詢

我們在前面曾構建過三個用于實驗的表格&#xff0c;下面將基于這三個表進行實踐。 # 建立一個用于實驗的三個表格 mysql> create table emp (-> empno varchar(10),-> ename varchar(50),-> job varchar(50),-> mgr int,-> hiredate timestamp,-&…

課程表系列(BFS)

廣度優先搜索 文章目錄 廣度優先搜索207. 課程表210. 課程表 II思路 630. 課程表 III1462. 課程表 IV547. 省份數量 207. 課程表 207. 課程表 你這個學期必須選修 numCourses 門課程&#xff0c;記為 0 到 numCourses - 1 。 在選修某些課程之前需要一些先修課程。 先修課程…

c++11 標準模板(STL)(std::tuple)(三)

定義于頭文件 <tuple> template< class... Types > class tuple; (C11 起) 類模板 std::tuple 是固定大小的異類值匯集。它是 std::pair 的推廣。 若 (std::is_trivially_destructible_v<Types> && ...) 為 true &#xff0c;則 tuple 的析構函數是…

【AI繪畫】免費GPU Tesla A100 32G算力部署Stable Diffusion

免責聲明 在閱讀和實踐本文提供的內容之前&#xff0c;請注意以下免責聲明&#xff1a; 侵權問題: 本文提供的信息僅供學習參考&#xff0c;不用做任何商業用途&#xff0c;如造成侵權&#xff0c;請私信我&#xff0c;我會立即刪除&#xff0c;作者不對讀者因使用本文所述方法…

Matlab 機器人工具箱 RobotArm類

文章目錄 1 RobotArm1.1 方法1.2 注意2 RobotArm.RobotArm3 RobotArm.cmove4 其他官網:Robotics Toolbox - Peter Corke 1 RobotArm 串聯機械臂類 1.1 方法 方法描述plot顯示機器人的圖形表示teach驅動物理和圖形機器人mirror使用機器人作為從機來驅動圖形</

深入了解Kafka的文件存儲原理

Kafka簡介 Kafka最初由Linkedin公司開發的分布式、分區的、多副本的、多訂閱者的消息系統。它提供了類似于JMS的特性&#xff0c;但是在設計實現上完全不同&#xff0c;此外它并不是JMS規范的實現。kafka對消息保存是根據Topic進行歸類&#xff0c;發送消息者稱為Producer&…

IntelliJ IDEA 常用的插件

IntelliJ IDEA有很多常用的插件&#xff0c;這些插件可以擴展IDE的功能&#xff0c;提高開發效率。以下是一些常用的插件&#xff1a; Maven Helper&#xff1a;這是一款分析Maven依賴沖突的插件。在沒有此插件時&#xff0c;查看Maven的依賴樹和檢查依賴包沖突可能需要輸入命…

梯度下降算法(帶你 原理 實踐)

目錄 一、引言 二、梯度下降算法的原理 三、梯度下降算法的實現 四、梯度下降算法的優缺點 優點&#xff1a; 缺點&#xff1a; 五、梯度下降算法的改進策略 1 隨機梯度下降&#xff08;Stochastic Gradient Descent, SGD&#xff09; 2 批量梯度下降&#xff08;Batch…

LLM分布式訓練第一課(通訊原語)

這個系列作為TFLOPS和顯存消耗的續篇,今天開始正式連載 上一部地址: LLM 參數,顯存,Tflops? 訓練篇(5) (qq.com) 前一篇文章舉了65B模型的訓練所消耗的顯存的案例,如果把條件降低一點,我們看一下7B的模型需要多少顯存? 2byte的模型靜態參數權重(以16bit存儲) = 1…

(一)Python數據分析體系--九五小龐

課程地址&#xff1a;https://space.bilibili.com/387143299/channel/collectiondetail?sid554734 主要內容 知識體系 分析什么樣的數據 為什么使用Python做數據分析 Python近幾年的發展勢頭是有目共睹的&#xff0c;尤其是在科學計算&#xff0c;數據處理&#xff0c;A方面…

駕辰龍跨Llama持Wasm,玩轉Yi模型迎新春

今年新年很特別&#xff0c;AI工具添光彩。今天就來感受下最新的AI神器天選組合“WasmEdgeYi-34B”&#xff0c;只要短短三步&#xff0c;為這個甲辰龍年帶來一份九紫離火運的科技感。 環境準備 這次用的算力是OpenBayes提供的英偉達RTX_4090*1、24GB顯存、20核CPU、80GB內存…

產品營銷展示型wordpress外貿網站模板

工藝品wordpress外貿主題 簡約大氣的wordpress外貿主題&#xff0c;適合做工藝品進出品外貿的公司官網使用。 https://www.jianzhanpress.com/?p5377 餐飲設備wordpress外貿主題 簡潔的wordpress外貿主題&#xff0c;適合食品機械、餐飲設備公司使用。 https://www.jianzh…

Linux 開發工具vim、gcc/g++、makefile

目錄 Linux編輯器-vim 1. 基本概念 2. 基本操作 3. 正常模式命令集 4. 末行模式命令集 5. 其他操作 6. 簡單vim配置 Linux編譯器-gcc/g 1、基本概念 2、程序翻譯的過程 3. gcc如何完成程序翻譯 4、動靜態庫 Linux項目自動化構建工具-make/Makefile 1、背景 2、…

【Qt學習筆記】(四)Qt窗口

Qt窗口 1 菜單欄1.1 創建菜單欄1.2 在菜單欄中添加菜單1.3 創建菜單項1.4 在菜單項之間添加分割線1.5 給菜單項添加槽函數1.6 給菜單項添加快捷鍵 2 工具欄2.1 創建工具欄2.2 設置停靠位置2.3 設置浮動屬性2.4 設置移動屬性2.5 添加 Action 3 狀態欄3.1 狀態欄的創建3.2 在狀態…

2024最新算法:冠豪豬優化算法(CPO)求解23個基準函數

一、冠豪豬優化算法 冠豪豬優化算法(Crested Porcupine Optimizer&#xff0c;CPO)由Mohamed Abdel-Basset等人于2024年提出&#xff0c;該算法模擬冠豪豬的四種不同保護機制&#xff1a;視覺、聽覺、氣味和物理攻擊。第一和第二防御技術&#xff08;視覺和聽覺&#xff09;反…