日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

LuceneElasticSeach

發布時間:2024/3/13 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LuceneElasticSeach 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 前言
  • 1 什么是全文檢索
    • 1.1 數據分類
    • 1.2 結構化數據搜索
    • 1.3 非結構化數據查詢方法
    • 1.4 如何實現全文檢索
    • 1.5 全文檢索的應用場景
  • 2 Lucene 實現全文檢索的流程說明
    • 2.1 索引和搜索流程圖
    • 2.2 創建索引
    • 2.3 倒排索引
  • 3 Lucene實戰
    • 3.1 需求說明
    • 3.2 準備開發環境
    • 3.3創建索引
    • 3.4查詢索引
    • 3.5中文分詞器的使用
  • 4.Elastic search介紹和安裝
    • 4.1.簡介
      • 4.1.1.Elastic
      • 4.1.2.Elasticsearch
      • 4.1.3.版本
    • 4.2.安裝和配置
    • 4.3.訪問
    • 4.4.安裝kibana
      • 4.4.1.什么是Kibana
      • 4.4.2.安裝
      • 4.4.3.配置運行
      • 4.4.4.控制臺
    • 4.5.安裝ik分詞器
      • 4.5.1.安裝
    • 4.6 安裝Head插件
      • 4.6.1 elasticsearch-head 簡介
      • 4.6.2 elasticsearch-head 安裝
  • 5.使用kibana對索引庫操作
    • 5.1.基本概念
      • 5.1.1.節點、集群、分片及副本
      • 5.1.2 文檔、類型、索引及映射
    • 5.2.創建索引庫
      • 5.2.1.語法
      • 5.2.3.使用kibana創建
    • 5.3.查看索引庫
    • 5.4.刪除索引庫
  • 6.使用kibana對類型及映射操作
  • # 6.1.創建字段映射
    • 6.2.查看映射關系
    • 6.3.映射屬性詳解
    • 6.4.一次創建索引庫和類型
  • 7.使用kibana對文檔操作
    • 7.1.新增文檔
      • 7.1.1.新增并隨機生成id
    • 7.2.查看文檔
    • 7.3.新增文檔并自定義id
    • 7.4.修改數據
    • 7.5.刪除數據
    • 7.6.智能判斷
    • 7.7.動態映射模板
  • 8.查詢
    • 8.1.基本查詢:
      • 8.1.1 查詢所有(match_all)
      • 8.1.2 匹配查詢(match)
      • 8.1.3 詞條匹配(term)
      • 8.1.4 布爾組合(bool)
      • 8.1.5 范圍查詢(range)
      • 8.1.6 模糊查詢(fuzzy)
    • 8.2.結果過濾
      • 8.2.1.直接指定字段
      • 8.2.2.指定includes和excludes
    • 8.3 過濾(filter)
    • 8.4 排序
      • 8.4.1 單字段排序
      • 8.4.2 多字段排序
    • 8.5.分頁
    • 8.6.高亮
  • 9. 聚合aggregations
    • 9.1 基本概念
    • 9.2 聚合為桶
    • 9.3 桶內度量
  • 10.Elasticsearch集群
    • 10.1.單點的問題
    • 10.2.集群的結構
      • 10.2.1.數據分片
      • 10.2.2.數據備份
    • 10.3.搭建集群
    • 10.4.測試集群中創建索引庫
    • 10.5.集群工作原理
      • 10.5.1.shad與replica機制
      • 10.5.2.集群寫入數據
      • 10.5.3.ES查詢數據
  • 11.Elasticsearch客戶端
    • 11.1.客戶端介紹
    • 11.2.創建Demo工程
      • 11.2.1.初始化項目
      • 11.2.2.pom文件
      • 11.2.3.配置文件
    • 11.3.索引庫及映射
    • 11.4.索引數據操作
      • 11.4.1.初始化客戶端
      • 11.4.2.新增文檔
      • 11.4.3.查看文檔
      • 11.4.4.修改文檔
      • 11.4.5.刪除文檔
    • 11.5 搜索數據
      • 11.5.1.查詢所有match_all
      • 11.5.2.關鍵字搜索match
      • 11.5.3.范圍查詢range
      • 11.5.4.source過濾
    • 11.6排序
    • 11.7.分頁
  • 12.Spring Data Elasticsearch
    • 12.1.什么是SpringDataElasticsearch
    • 12.2配置SpringDataElasticsearch
    • 12.3.索引庫操作
      • 12.3.1.創建索引庫
      • 12.3.2.創建映射
    • 12.4索引數據CRUD
      • 12.4.1 創建索引數據
      • 12.4.2.查詢索引數據
      • 12.4.3.自定義方法查詢
    • 12.5.原生查詢


前言

文章內容輸出來源:拉勾教育JAVA就業訓練營


1 什么是全文檢索

1.1 數據分類

我們生活中的數據總體分為兩種:結構化數據和非結構化數據。

  • 結構化數據:指具有固定格式或有限長度的數據,如數據庫,元數據等。
  • 非結構化數據:指不定長或無固定格式的數據,如郵件,word 文檔等磁盤上的文件

1.2 結構化數據搜索

常見的結構化數據也就是數據庫中的數據。
在數據庫中搜索很容易實現,通常都是使用 sql語句進行查詢,而且能很快的得到查詢結果。

1.3 非結構化數據查詢方法

( 1 ) 順序掃描法(Serial Scanning)
用戶搜索----->文件
所謂順序掃描,比如要找內容包含某一個字符串的文件,就是一個文檔一個文檔的看,對于每一個文檔,從頭看到尾,如果此文檔包含此字符串,則此文檔為我們要找的文件,接著看下一個文件,直到掃描完所有的文件。如利用 windows 的搜索也可以搜索文件內容 ,只是相當的慢。
( 2 ) 全文檢索(Full-text Search)

  • 用戶通過查詢索引庫---->生成索引----->文檔
  • 全文檢索是指計算機索引程序通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞在文章中出現的次數和位置,當用戶查詢時,檢索程序就根據事先建立的索引進行查找,并將查找的結果反饋給用戶的檢索方法。這個過程類似于通過字典的目錄查字的過程。
  • 將非結構化數據中的一部分信息提取出來,重新組織,使其變得有一定結構,然后對此有一定結構的數據進行搜索,從而達到搜索相對較快的目的。這部分從非結構化數據中提取出的然后重新組織的信息,我們稱之索引。
  • 例如:字典。字典的拼音表和部首檢字表就相當于字典的索引,對每一個字的解釋是非結構化的,如果字典沒有音節表和部首檢字表,在茫茫辭海中找一個字只能順序掃描。然而字的某些信息可以提取出來進行結構化處理,比如讀音,就比較結構化,分聲母和韻母,分別只有幾種可以一一列舉,于是將
    讀音拿出來按一定的順序排列,每一項讀音都指向此字的詳細解釋的頁數。我們搜索時按結構化的拼音搜到讀音,然后按其指向的頁數,便可找到我們的非結構化數據——也即對字的解釋。
  • 這種先建立索引,再對索引進行搜索的過程就叫全文檢索(Full-Text Search) 。雖然創建索引的過程也是非常耗時的,但是索引一旦創建就可以多次使用,全文檢索主要處理的是查詢,所以耗時間創建索引是值得的。
  • 建立索引
  • 檢索索引

1.4 如何實現全文檢索

  • 可以使用 Lucene 實現全文檢索。Lucene 是 apache 下的一個開放源代碼的全文檢索引擎工具包。提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。Lucene 的目的是為軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能。

Lucene適用場景:

  • 在應用中為數據庫中的數據提供全文檢索實現。
  • 開發獨立的搜索引擎服務、系統
    Lucene的特性:
  • 1). 穩定、索引性能高
    • 每小時能夠索引150GB以上的數據
    • 對內存的要求小,只需要1MB的堆內存
    • 增量索引和批量索引一樣快
    • 索引的大小約為索引文本大小的20%~30%
  • 2).高效、準確、高性能的搜索算法
    -良好的搜索排序
    • 強大的查詢方式支持:短語查詢、通配符查詢、臨近查詢、范圍查詢等
    • 支持字段搜索(如標題、作者、內容)
    • 可根據任意字段排序
    • 支持多個索引查詢結果合并
    • 支持更新操作和查詢操作同時進行
    • 支持高亮、join、分組結果功能
    • 速度快
    • 可擴展排序模塊,內置包含向量空間模型、BM25模型可選
    • 可配置存儲引擎
  • 3).跨平臺
    • 純java編寫
    • 作為Apache開源許可下的開源項目,你可以在商業或開源項目中使用
    • Lucene有多種語言實現版(如C,C++、Python等),不僅僅是JAVA

Lucene架構:


1.5 全文檢索的應用場景

對于數據量大、數據結構不固定的數據可采用全文檢索方式搜索,

  • 單機軟件的搜索:word、markdown
  • 站內搜索:京東、淘寶、拉勾,索引源是數據庫
  • 搜索引擎:百度、Google,索引源是爬蟲程序抓取的數據

2 Lucene 實現全文檢索的流程說明

2.1 索引和搜索流程圖


1、綠色表示索引過程,對要搜索的原始內容進行索引構建一個索引庫,索引過程包括:

  • 確定原始內容即要搜索的內容–>采集文檔–>創建文檔–>分析文檔–>索引文檔

2、紅色表示搜索過程,從索引庫中搜索內容,搜索過程包括:

  • 用戶通過搜索界面–>創建查詢–>執行搜索,從索引庫搜索–>渲染搜索結果

2.2 創建索引

核心概念:
Document:

  • 用戶提供的源是一條條記錄,它們可以是文本文件、字符串或者數據庫表的一條記錄等等。一條記錄經過索引之后,就是以一個Document的形式存儲在索引文件中的。用戶進行搜索,也是以Document列表的形式返回。

Field

  • 一個Document可以包含多個信息域,例如一篇文章可以包含“標題”、“正文”、“最后修改時間”等信息域,這些信息域就是通過Field在Document中存儲的。
  • Field有兩個屬性可選:存儲和索引。通過存儲屬性你可以控制是否對這個Field進行存儲;通過索引屬性你可以控制是否對該Field進行索引。
  • 如果對標題和正文進行全文搜索,所以我們要把索引屬性設置為真,同時我們希望能直接從搜索結果中提取文章標題,所以我們把標題域的存儲屬性設置為真,但是由于正文域太大了,我們為了縮小索引文件大小,將正文域的存儲屬性設置為假,當需要時再直接讀取文件;我們只是希望能從搜索解果中提取最后修改時間,不需要對它進行搜索,所以我們把最后修改時間域的存儲屬性設置為真,索引屬性設置為假。上面的三個域涵蓋了兩個屬性的三種組合,還有一種全為假的沒有用到,事實上Field不允許你那么設置,因為既不存儲又不索引的域是沒有意義的。

Term:

  • Term是搜索的最小單位,它表示文檔的一個詞語,Term由兩部分組成:它表示的詞語和這個詞語所出現的Field的名稱。

我們以拉勾招聘網站的搜索為例,在網站上輸入關鍵字搜索顯示的內容不是直接從數據庫中來的,而是從索引庫中獲取的,網站的索引數據需要提前創建的。以下是創建的過程:

  • 第一步:獲得原始文檔:就是從mysql數據庫中通過sql語句查詢需要創建索引的數據
  • 第二步:創建文檔對象(Document),把查詢的內容構建成lucene能識別的Document對象,獲取原始內容的目的是為了索引,在索引前需要將原始內容創建成文檔,文檔中包括一個一個的域(Field),這個域對應就是表中的列。
  • 注意:每個 Document 可以有多個 Field,不同的 Document 可以有不同的 Field,同一個Document可以有相同的 Field(域名和域值都相同)。每個文檔都有一個唯一的編號,就是文檔 id。
  • 第三步:分析文檔
    將原始內容創建為包含域(Field)的文檔(document),需要再對域中的內容進行分析,分析的過程是經過對原始文檔提取單詞、將字母轉為小寫、去除標點符號、去除停用詞等過程生成最終的語匯單元,可以將語匯單元理解為一個一個的單詞。分好的詞會組成索引庫中最小的單元:term,一個term由域名和詞組成
  • 第四步:創建索引,
    對所有文檔分析得出的語匯單元進行索引,索引的目的是為了搜索,最終要實現只搜索被索引的語匯單元從而找到 Document(文檔)。
    注意:創建索引是對語匯單元索引,通過詞語找文檔,這種索引的結構叫 倒排索引結構。
    倒排索引結構是根據內容(詞語)找文檔,如下圖:
    倒排索引結構也叫反向索引結構,包括索引和文檔兩部分,索引即詞匯表,它的規模較小,而文檔集合較大。

2.3 倒排索引

倒排索引記錄每個詞條出現在哪些文檔,及在文檔中的位置,可以根據詞條快速定位到包含這個詞條的文檔及出現的位置。

  • 文檔:索引庫中的每一條原始數據,例如一個商品信息、一個職位信息
  • 詞條:原始數據按照分詞算法進行分詞,得到的每一個詞
    創建倒排索引,分為以下幾步:
    1)創建文檔列表:
    lucene首先對原始文檔數據進行編號(DocID),形成列表,就是一個文檔列表

    2)創建倒排索引列表
    對文檔中數據進行分詞,得到詞條(分詞后的一個又一個詞)。對詞條進行編號,以詞條創建索引。然后記錄下包含該詞條的所有文檔編號(及其它信息)。

搜索的過程:
當用戶輸入任意的詞條時,首先對用戶輸入的數據進行分詞,得到用戶要搜索的所有詞條,然后拿著這些詞條去倒排索引列表中進行匹配。找到這些詞條就能找到包含這些詞條的所有文檔的編號。然后根據這些編號去文檔列表中找到文檔

2.4 查詢索引
查詢索引也是搜索的過程。搜索就是用戶輸入關鍵字,從索引(index)中進行搜索的過程。根據關鍵字搜索索引,根據索引找到對應的文檔

  • 第一步:創建用戶接口:用戶輸入關鍵字的地方
  • 第二步:創建查詢 指定查詢的域名和關鍵字
  • 第三步:執行查詢
  • 第四步:渲染結果 (結果內容顯示到頁面上 關鍵字需要高亮)

3 Lucene實戰

3.1 需求說明

生成職位信息索引庫,從索引庫檢索數據

3.2 準備開發環境

第一步:創建一個maven工程 ,已經學過Spring Boot,我們就創建一個SpringBoot項目
第二步:導入依賴
第三步:創建引導類
第四步:配置properties文件
第五步:創建實體類、mapper、service

3.3創建索引

@RunWith(SpringRunner.class) //springboot做junit測試固定的寫法 @SpringBootTest public class LuceneManager {@Autowiredprivate JobInfoServiceImpl jobInfoService;@Testpublic void testCreateIndex() throws Exception{ // 1、指定索引文件存儲的位置 D:\class\indexDirectory directory = FSDirectory.open(new File("D:\\class\\index")); // 2、配置版本和分詞器Analyzer analyzer = new StandardAnalyzer();//標準分詞器IndexWriterConfig config = new IndexWriterConfig(Version.LATEST,analyzer); // 3、創建一個用來創建索引的對象 IndexWriterIndexWriter indexWriter = new IndexWriter(directory,config);indexWriter.deleteAll(); //先刪除索引 // 4、獲取原始數據List<JobInfo> jobInfoList = jobInfoService.findAll(); // 有多少的數據就應該構建多少lucene的文檔對象documentfor (JobInfo jobInfo : jobInfoList) {Document document = new Document(); // 域名 值 源數據是否存儲document.add(new LongField("id",jobInfo.getId(), Field.Store.YES));document.add(new TextField("companyName",jobInfo.getCompanyName(), Field.Store.YES));document.add(new TextField("companyAddr",jobInfo.getCompanyAddr(), Field.Store.YES));document.add(new TextField("jobName",jobInfo.getJobName(), Field.Store.YES)); document.add(new TextField("jobAddr",jobInfo.getJobAddr(), Field.Store.YES));document.add(new IntField("salary",jobInfo.getSalary(), Field.Store.YES));document.add(new StringField("url",jobInfo.getUrl(), Field.Store.YES)); // StringField 不需要分詞時使用 舉例:url 、電話號碼、身份證號indexWriter.addDocument(document);} // 關閉資源indexWriter.close();} }

生成的索引目錄:D:\class\index

  • 索引(Index):
    • 在Lucene中一個索引是放在一個文件夾中的。
    • 如下圖,同一文件夾中的所有的文件構成一個Lucene索引。
  • 段(Segment):
    • 按層次保存了從索引,一直到詞的包含關系:索引(Index) –> 段(segment) –> 文檔
      (- Document) –> 域(Field) –> 詞(Term)
    • 也即此索引包含了那些段,每個段包含了那些文檔,每個文檔包含了那些域,每個域包含了那些詞。
    • 一個索引可以包含多個段,段與段之間是獨立的,添加新文檔可以生成新的段,不同的段可以合并。
    • 如上圖,具有相同前綴文件的屬同一個段,圖中共一個段 “_0” 。
    • segments.gen和segments_1是段的元數據文件,也即它們保存了段的屬性信息。

      Field的特性
      -Document(文檔)是Field(域)的承載體, 一個Document由多個Field組成. Field由名稱和值兩部分組成,Field的值是要索引的內容, 也是要搜索的內容.
  • 是否分詞(tokenized)
    是: 將Field的值進行分詞處理, 分詞的目的是為了索引. 如: 商品名稱, 商品描述. 這些內容用戶會通過輸入關鍵詞進行查詢, 由于內容多樣, 需要進行分詞處理建立索引.
    否: 不做分詞處理. 如: 訂單編號, 身份證號, 是一個整體, 分詞以后就失去了意義, 故不需要分詞.
  • 是否索引(indexed)
    是: 將Field內容進行分詞處理后得到的詞(或整體Field內容)建立索引, 存儲到索引域. 索引的目的是為了搜索. 如: 商品名稱, 商品描述需要分詞建立索引. 訂單編號, 身份證號作為整體建立索引. 只要可能作為用戶查詢條件的詞, 都需要索引.
    否: 不索引. 如: 商品圖片路徑, 不會作為查詢條件, 不需要建立索引.
  • 是否存儲(stored)
    是: 將Field值保存到Document中. 如: 商品名稱, 商品價格. 凡是將來在搜索結果頁面展現給用戶的內容, 都需要存儲.
    否: 不存儲. 如: 商品描述. 內容多格式大, 不需要直接在搜索結果頁面展現, 不做存儲. 需要的時候可以從關系數據庫取.

常用的Field類型:

3.4查詢索引

public class LuceneQuery {@Testpublic void testQueryIndex() throws Exception{ // 1、指定索引文件存儲的位置 D:\class\indexDirectory directory = FSDirectory.open(new File("D:\\class\\index")); // 2、 創建一個用來讀取索引的對象 indexReaderIndexReader indexReader = DirectoryReader.open(directory); // 3、 創建一個用來查詢索引的對象 IndexSearcherIndexSearcher indexSearcher = new IndexSearcher(indexReader); // 使用term查詢:指定查詢的域名和關鍵字Query query = new TermQuery(new Term("companyName","北京"));TopDocs topDocs = indexSearcher.search(query, 100);//第二個參數:最多顯示多 少條數據int totalHits = topDocs.totalHits; //查詢的總數量System.out.println("符合條件的總數:"+totalHits);ScoreDoc[] scoreDocs = topDocs.scoreDocs; //獲取命中的文檔 存儲的是文檔的idfor (ScoreDoc scoreDoc : scoreDocs) {int docID = scoreDoc.doc; // 根據id查詢文檔Document document = indexSearcher.doc(docID);System.out.println( "id:"+ document.get("id"));System.out.println( "companyName:"+ document.get("companyName"));System.out.println( "companyAddr:"+ document.get("companyAddr"));System.out.println( "salary:"+ document.get("salary"));System.out.println( "url:"+ document.get("url"));System.out.println("*********************************************************** ***");}} }

3.5中文分詞器的使用

使用方式:

  • 第一步:導依賴
<!--IK中文分詞器--> <dependency><groupId>com.janeluo</groupId><artifactId>ikanalyzer</artifactId><version>2012_u6</version> </dependency>
  • 第二步:可以添加配置文件

放入到resources文件夾中。

  • 第三步 創建索引時使用IKanalyzer

    考慮一個問題:一個大型網站中的索引數據會很龐大的,所以使用lucene這種原生的寫代碼的方式就不合適了,所以需要借助一個成熟的項目或軟件來實現,目前比較有名是solr和elasticSearch

4.Elastic search介紹和安裝

Elasticsearch是一個需要安裝配置的軟件。
ELK技術棧說明

  • Elastic有一條完整的產品線:Elasticsearch、Logstash、Kibana等,前面說的三個就是大家常說的ELK技術棧(開源實時日志分析平臺)。

  • Logstash 的作用就是一個數據收集器,將各種格式各種渠道的數據通過它收集解析之后格式化輸出到Elasticsearch ,最后再由Kibana 提供的比較友好的 Web 界面進行匯總、分析、搜索。
  • ELK 內部實際就是個管道結構,數據從 Logstash 到 Elasticsearch 再到 Kibana 做可視化展示。這三個組件各自也可以單獨使用,比如 Logstash 不僅可以將數據輸出到Elasticsearch ,也可以到數據庫、緩存等

4.1.簡介

4.1.1.Elastic

Elastic官網:https://www.elastic.co/cn/


Elastic有一條完整的產品線:Elasticsearch、Logstash、Kibana等,前面說的三個就是大家常說的ELK技術棧。

4.1.2.Elasticsearch

Elasticsearch官網:https://www.elastic.co/cn/products/elasticsearch

功能:

  • 分布式的搜索引擎:百度、Google、站內搜索
  • 全文檢索:提供模糊搜索等自動度很高的查詢方式,并進行相關性排名,高亮等功能
  • 數據分析引擎(分組聚合):電商網站—一周內手機銷量Top10
  • 對海量數據進行近乎實時處理:水平擴展,每秒鐘可處理海量事件,同時能夠自動管理索引和查詢在集群中的分布方式,以實現極其流暢的操作。

如上所述,Elasticsearch具備以下特點:
高速、擴展性、最相關的搜索結果

  • 分布式:節點對外表現對等,每個節點都可以作為入門,加入節點自動負載均衡
  • JSON:輸入輸出格式是JSON
  • Restful風格,一切API都遵循Rest原則,容易上手
  • 近實時搜索,數據更新在Elasticsearch中幾乎是完全同步的,數據檢索近乎實時
  • 安裝方便:沒有其它依賴,下載后安裝很方便,簡單修改幾個參數就可以搭建集群
  • 支持超大數據:可以擴展到PB級別的結構化和非結構化數據

4.1.3.版本

目前Elasticsearch最新的版本是7.x,企業內目前用的比較多是6.x,我們以6.2.4進行講解,需要JDK1.8及以上。

4.2.安裝和配置

為了快速看到效果我們直接在本地window下安裝Elasticsearch。環境要求:JDK8及以上版本

  • 第一步:把今天資料文件夾中準備好的軟件放到一個沒有中文沒有空格的位置,解壓即可
  • 第二步:修改配置文件
    1、修改索引數據和日志數據存儲的路徑


第33行和37行,修改完記得把注釋打開

path.data: d:\class\es\data # # Path to log files: # path.logs: d:\class\es\log
  • 第三步:進入bin目錄中直接雙擊 圖下的命令文件。

    如果啟動失敗(估計好多同學都會啟動失敗的),需要修改虛擬機內存的大小找到jvm.options文件 如圖修改
  • Xms 是指設定程序啟動時占用內存大小。一般來講,大點,程序會啟動的快一點,但是也可能會導致機器暫時間變慢。
  • Xmx 是指設定程序運行期間最大可占用的內存大小。如果程序運行需要占用更多的內存,超出了這個設置值,就會拋出OutOfMemory異常

4.3.訪問


可以看到綁定了兩個端口:
9300:集群節點間通訊接口,接收tcp協議
9200:客戶端訪問接口,接收Http協議
我們在瀏覽器中訪問:http://127.0.0.1:9200

4.4.安裝kibana

4.4.1.什么是Kibana

Kibana是一個基于Node.js的Elasticsearch索引庫數據統計工具,可以利用Elasticsearch的聚合功能,生成各種圖表,如柱形圖,線狀圖,餅圖等。而且還提供了操作Elasticsearch索引數據的控制臺,并且提供了一定的API提示,非常有利于我們學習Elasticsearch的語法。

4.4.2.安裝

因為Kibana依賴于node,需要在windows下先安裝Node.js,雙擊運行課前資料提供的node.js的安裝包:
一路下一步即可安裝成功,然后在任意DOS窗口輸入名:

4.4.3.配置運行

進入安裝目錄下的config目錄,修改kibana.yml文件的第21行(注釋放開即可)

修改elasticsearch服務器的地址:

elasticsearch.url: "http://127.0.0.1:9200"

進入安裝目錄下的bin目錄:

雙擊運行
發現kibana的監聽端口是5601
我們訪問:http://127.0.0.1:5601

4.4.4.控制臺


在頁面右側,我們就可以輸入請求,訪問Elasticsearch了。

4.5.安裝ik分詞器

Lucene的IK分詞器早在2012年已經沒有維護了,現在我們要使用的是在其基礎上維護升級的版本,并且開發為Elasticsearch的集成插件了,與Elasticsearch一起維護升級,版本也保持一致
https://github.com/medcl/elasticsearch-analysis-ik

4.5.1.安裝

  • 解壓elasticsearch-analysis-ik-6.2.4.zip后,將解壓后的文件夾拷貝到elasticsearch-6.2.4\plugins下,并重命名文件夾為ik
  • 重新啟動ElasticSearch,即可加載IK分詞器
  • 4.6 安裝Head插件

    4.6.1 elasticsearch-head 簡介

    elasticsearch-head 簡介

    • elasticsearch-head是一個界面化的集群操作和管理工具,可以對集群進行傻瓜式操作。你可以通過插件把它集成到es(首選方式),也可以安裝成一個獨立webapp。

    es-head主要有三個方面的操作:

  • 顯示集群的拓撲,并且能夠執行索引和節點級別操作
  • 搜索接口能夠查詢集群中原始json或表格格式的檢索數據
  • 能夠快速訪問并顯示集群的狀態
    官方的文檔: https://github.com/mobz/elasticsearch-head
  • 4.6.2 elasticsearch-head 安裝

    elasticsearch-head 安裝(基于谷歌瀏覽器)
    (1)直接下載壓縮包,地址:https://files.cnblogs.com/files/sanduzxcvbnm/elasticsearch-head.7z
    (2)解壓
    (3)在谷歌瀏覽器中點擊“加載已解壓的壓縮程序”,找到elasticsearch-head文件夾,點擊打開即可進行安裝。


    5.使用kibana對索引庫操作

    5.1.基本概念

    5.1.1.節點、集群、分片及副本

    1、節點 (node)

    • 一個節點是一個Elasticsearch的實例。
    • 在服務器上啟動Elasticsearch之后,就擁有了一個節點。如果在另一臺服務器上啟動Elasticsearch,這就是另一個節點。甚至可以通過啟動多個Elasticsearch進程,在同一臺服務器上擁有多個節點。

    2、集群(cluster)

    • 多個協同工作的Elasticsearch節點的集合被稱為集群。
    • 在多節點的集群上,同樣的數據可以在多臺服務器上傳播。這有助于性能。這同樣有助于穩定性,如果每個分片至少有一個副本分片,那么任何一個節點宕機后,Elasticsearch依然可以進行服務,返回所有數據。
    • 但是它也有缺點:必須確定節點之間能夠足夠快速地通信,并且不會產生腦裂效應(集群的2個部分不能彼此交流,都認為對方宕機了)。

    3、分片 (shard)

    • 索引可能會存儲大量數據,這些數據可能超過單個節點的硬件限制。例如,十億個文檔的單個索引占用了1TB的磁盤空間,可能不適合單個節點的磁盤,或者可能太慢而無法單獨滿足來自單個節點的搜索請求。

    • 為了解決此問題,Elasticsearch提供了將索引細分為多個碎片的功能。創建索引時,只需定義所需的分片數量即可。每個分片本身就是一個功能齊全且獨立的“索引”,可以托管在群集中的任何節點上。

    • 分片很重要,主要有兩個原因:

      • 它允許您水平分割/縮放內容量
      • 它允許您跨碎片(可能在多個節點上)分布和并行化操作,從而提高性能/吞吐量
    • 分片如何分布以及其文檔如何聚合回到搜索請求中的機制完全由Elasticsearch管理,并且對您作為用戶是透明的。
      在隨時可能發生故障的網絡/云環境中,非常有用,強烈建議您使用故障轉移機制,以防碎片/節點因某種原因脫機或消失。為此,Elasticsearch允許您將索引分片的一個或多個副本制作為所謂的副本分片(簡稱副本)。

    4、副本(replica)

    • 分片處理允許用戶推送超過單機容量的數據至Elasticsearch集群。副本則解決了訪問壓力過大時單機無法處理所有請求的問題。
    • 分片可以是主分片,也可以是副本分片,其中副本分片是主分片的完整副本。副本分片用于搜索,或者是在原有的主分片丟失后成為新的主分片。
    • 注意:可以在任何時候改變每個分片的副本分片的數量,因為副本分片總是可以被創建和移除的。這并不適用于索引劃分為主分片的數量,在創建索引之前,必須決定主分片的數量。過少的分片將限制可擴展性,但是過多的分片會影響性能。默認設置的5份是一個不錯的開始。

    5.1.2 文檔、類型、索引及映射

    1、文檔 (document)
    Elasticsearch是面向文檔的,這意味著索引和搜索數據的最小單位是文檔。
    在Elasticsearch中文檔有幾個重要的屬性。

    • 它是自我包含的。一篇文檔同時包含字段和它們的取值。
    • 它可以是層次的。文檔中還包含新的文檔,字段還可以包含其他字段和取值。例如,“location”字段可以同時包含“city”和“street“兩個字段。
    • 它擁有靈活的結構。文檔不依賴于預先定義的模式。并非所有的文檔都需要擁有相同的字段,它們不受限于同一個模式。

    2、類型 (type)

    • 類型是文檔的邏輯容器,類似于表格是行的容器。在不同的類型中,最好放入不同結構的文檔。例如,可以用一個類型定義聚會時的分組,而另一個類型定義人們參加的活動。

    3、索引 (index)

    • 索引是映射類型的容器。一個Elasticsearch索引是獨立的大量的文檔集合。 每個索引存儲在磁盤上的同組文件中,索引存儲了所有映射類型的字段,還有一些設置。

    4、映射(mapping)

    • 所有文檔在寫入索引前都將被分析,用戶可以設置一些參數,決定如何將輸入文本分割為詞條,哪些詞條應該被過濾掉,或哪些附加處理有必要被調用(比如移除HTML標簽)。這就是映射扮演的角色:存儲分析鏈所需的所有信息。
    • Elasticsearch也是基于Lucene的全文檢索庫,本質也是存儲數據,很多概念與MySQL類似的。
      對比關系:

      詳細說明:
    索引庫(indices)indices是index的復數,代表許多的索引,
    概念說明
    類型(type)類型是模擬mysql中的table概念,一個索引庫下可以有不同類型的索引(目前6.X以后的版本只能有一個類型),類似數據庫中的表概念。數據庫表中有表結構,也就是表中每個字段的約束信息;索引庫的類型中對應表結構的叫做 映射(mapping) ,用來定義每個字段的約束。
    文檔(document)存入索引庫原始的數據。比如每一條商品信息,就是一個文檔
    字段(field)文檔中的屬性
    映射配置(mappings)字段的數據類型、屬性、是否索引、是否存儲等特性

    5.2.創建索引庫

    5.2.1.語法

    Elasticsearch采用Rest風格API,因此其API就是一次http請求,你可以用任何工具發起http請求
    創建索引的請求格式:

    • 請求方式:PUT
    • 請求路徑:/索引庫名
    • 請求參數:json格式
    {"settings": {"屬性名": "屬性值"} }

    settings:就是索引庫設置,其中可以定義索引庫的各種屬性,目前我們可以不設置,都走默認

    5.2.3.使用kibana創建

    kibana的控制臺,可以對http請求進行簡化,示例:


    相當于是省去了elasticsearch的服務器地址
    而且還有語法提示,非常舒服。

    5.3.查看索引庫

    Get請求可以幫我們查看索引信息,格式:

    GET /索引庫名

    5.4.刪除索引庫

    刪除索引使用DELETE請求
    語法

    DELETE /索引庫名

    示例

    6.使用kibana對類型及映射操作

    • 有了 索引庫 ,等于有了數據庫中的 database 。接下來就需要索引庫中的 類型 了,也就是數據庫中的表 。創建數據庫表需要設置字段約束,索引庫也一樣,在創建索引庫的類型時,需要知道這個類型下有哪些字段,每個字段有哪些約束信息,這就叫做 字段映射(mapping)

    • 注意:Elasticsearch7.x取消了索引type類型的設置,不允許指定類型,默認為_doc,但字段仍然是有的,我們需要設置字段的約束信息,叫做字段映(mapping)

    • 字段的約束我們在學習Lucene中我們都見到過,包括到不限于:

      • 字段的數據類型
      • 是否要存儲
      • 是否要索引
      • 是否分詞
      • 分詞器是什么

    # 6.1.創建字段映射

    語法
    請求方式依然是PUT

    PUT /索引庫名/_mapping/typeName { "properties": {"字段名": {"type": "類型","index": true,"store": true,"analyzer": "分詞器"} } }

    • 類型名稱:就是前面將的type的概念,類似于數據庫中的表
      字段名:任意填寫,下面指定許多屬性,例如:
      t- ype:類型,可以是text、keyword、long、short、date、integer、object等
      • index:是否索引,默認為true
      • store:是否存儲,默認為false
      • analyzer:分詞器,這里的 ik_max_word 即使用ik分詞器
        示例
        發起請求:
    PUT lagou/_mapping/goods {"properties": {"title": {"type": "text","analyzer": "ik_max_word"},"images": {"type": "keyword","index": "false"},"price": {"type": "float"} } }

    響應結果:

    { "acknowledged": true }

    上述案例中,就給lagou這個索引庫添加了一個名為 goods 的類型,并且在類型中設置了3個字段:

    • title:商品標題
    • images:商品圖片
    • price:商品價格

    6.2.查看映射關系

    語法:

    GET /索引庫名/_mapping

    查看某個索引庫中的所有類型的映射。如果要查看某個類型映射,可以再路徑后面跟上類型名稱。即:

    GET /索引庫名/_mapping/類型名

    示例:

    GET /lagou/_mapping/goods

    響應:

    {"lagou": {"mappings": {"goods": {"properties": {"images": {"type": "keyword","index": false},"price": {"type": "float"},"title": {"type": "text","analyzer": "ik_max_word"}}}} } }

    6.3.映射屬性詳解

    1)type
    Elasticsearch中支持的數據類型非常豐富:

    一級分類二級分類具體類型使用
    核心類型字符串類型text,keyword結構化搜索,全文文本搜索、聚合、排
    -序等整數類型integer,long,short,byte字段的長度越短,索引和搜索的效率越高。
    -浮點類型double,float,half_float,scaled_float-
    -邏輯類型boolean-
    -日期類型date-
    -范圍類型range-
    -二進制類型binary該 binary 類型接受二進制值作為Base64編碼的字符串。該字段默認情況下不存儲(store),并且不可搜索
    復合類型數組類型array-
    -對象類型object 用于單個JSON對象-
    -嵌套類型nested用于JSON對象數組
    地理類型地理坐標類型geo_point 緯度/經度積分
    -地理地圖geo_shape 用于多邊形等復雜形狀
    特殊類型IP類型ip 用于IPv4和IPv6地址
    -范圍類型completion提供自動完成建議
    -令牌計數類型token_count計算字符串中令牌的數量

    我們說幾個關鍵的:
    String類型,又分兩種:

    • text:使用文本數據類型的字段,它們會被分詞,文本字段不用于排序,很少用于聚合,如文章標題、正文。
    • keyword:關鍵字數據類型,用于索引結構化內容的字段,不會被分詞,必須完整匹配的內容,如郵箱,身份證號。支持聚合

    這兩種類型都是比較常用的,但有的時候,對于一個字符串字段,我們可能希望他兩種都支持,此時,可以利用其多字段特性

    "properties": {"title":{"type": "text","analyzer": "ik_max_word","fields": {"sort":{"type": "keyword"}},"index": true}}

    Numerical:數值類型,分兩類

    • 基本數據類型:long、interger、short、byte、double、float、half_float
    • double 雙精度64位
    • float 單精度32位
    • half_float 半精度16位
    • 浮點數的高精度類型:scaled_float
      • 帶有縮放因子的縮放類型浮點數,依靠一個 long 數字類型通過一個固定的( double 類型)縮放因數進行縮放.
      • 需要指定一個精度因子,比如10或100。elasticsearch會把真實值乘以這個因子后存儲,取出時再還原。

    Date:日期類型

    • elasticsearch可以對日期格式化為字符串存儲,但是建議我們存儲為毫秒值,存儲為long,節省空間。

    Array:數組類型

    • 進行匹配時,任意一個元素滿足,都認為滿足排序時,如果升序則用數組中的最小值來排序,如果降序則用數組中的最大值來排序
    字符串數組:["one", "two"] 整數數組:[1,2] 數組的數組:[1, [2, 3]],等價于[1,2,3] 對象數組:[ { "name": "Mary", "age": 12 }, { "name": "John", "age": 10 }]

    Object:對象

    • JSON文檔本質上是分層的:文檔包含內部對象,內部對象本身還包含內部對象。
    { "region": "US", "manager.age": 30, "manager.name ": "John Smith" } 索引方法如下: {"mappings": {"properties": {"region": { "type": "keyword" },"manager": {"properties": {"age": { "type": "integer" },"name": { "type": "text" }}}}} }

    如果存儲到索引庫的是對象類型,例如上面的girl,會把girl編程兩個字段:girl.name和girl.age

    • ip地址
    PUT my_index { "mappings": {"_doc": {"properties": {"ip_addr": {"type": "ip"}}} } } PUT my_index/_doc/1 { "ip_addr": "192.168.1.1" } GET my_index/_search { "query": {"term": {"ip_addr": "192.168.0.0/16"} } }

    2)index
    index影響字段的索引情況。

    • true:字段會被索引,則可以用來進行搜索過濾。默認值就是true,只有當某一個字段的index值設置為true時,檢索ES才可以作為條件去檢索。
    • false:字段不會被索引,不能用來搜索

    index的默認值就是true,也就是說你不進行任何配置,所有字段都會被索引。
    但是有些字段是我們不希望被索引的,比如商品的圖片信息(URL),就需要手動設置index為false。

    3)store

    • 是否將數據進行額外存儲。
    • 在學習lucene時,我們知道如果一個字段的store設置為false,那么在文檔列表中就不會有這個字段的值,用戶的搜索結果中不會顯示出來。
    • 但是在Elasticsearch中,即便store設置為false,也可以搜索到結果。
    • 原因是Elasticsearch在創建文檔索引時,會將文檔中的原始數據備份,保存到一個叫做 _source 的屬性中。而且我們可以通過過濾 _source 來選擇哪些要顯示,哪些不顯示。
    • 而如果設置store為true,就會在 _source 以外額外存儲一份數據,多余,因此一般我們都會將store設置為false,事實上,store的默認值就是false。
    • 在某些情況下,這對 store 某個領域可能是有意義的。例如,如果您的文檔包含一個 title ,一個date 和一個非常大的 content 字段,則可能只想檢索the title 和the date 而不必從一個大 _source字段中提取這些字段:
    PUT my_index { "mappings": {"_doc": {"properties": {"title": {"type": "text","store": true},"date": {"type": "date","store": true},"content": {"type": "text"}}} } }

    4)boost

    • 網站權重:網站權重是指搜索引擎給網站(包括網頁)賦予一定的權威值,對網站(含網頁)權威的評估評價。一個網站權重越高,在搜索引擎所占的份量越大,在搜索引擎排名就越好。提高網站權重,不但利于網站(包括網頁)在搜索引擎的排名更靠前,還能提高整站的流量,提高網站信任度。所以提高網站的權重具有相當重要的意義。 權重即網站在SEO中的重要性,權威性。英文:Page Strength。1、權重不等于排名 2、權重對排名有著非常大的影響 3、整站權重的提高有利于內頁的排名。

    權重,新增數據時,可以指定該數據的權重,權重越高,得分越高,排名越靠前。

    PUT my_index { "mappings": {"_doc": {"properties": {"title": {"type": "text","boost": 2},"content": {"type": "text"}}} } }

    title 字段上的匹配項的權重是字段上的匹配項的權重的兩倍 content ,默認 boost 值為 1.0 。
    提升僅適用于Term查詢(不提升prefix,range和模糊查詢)。

    6.4.一次創建索引庫和類型

    第一步: PUT /lagou 第二步: PUT lagou/_mapping/goods { "properties": {"title": {"type": "text","analyzer": "ik_max_word"},"images": {"type": "keyword","index": "false"},"price": {"type": "float"} } }

    剛才 的案例中我們是把創建索引庫和類型分開來做,其實也可以在創建索引庫的同時,直接制定索引庫中的類型,基本語法

    put /索引庫名 {"settings":{"索引庫屬性名":"索引庫屬性值"},"mappings":{"類型名":{"properties":{"字段名":{"映射屬性名":"映射屬性值"}}}} }

    7.使用kibana對文檔操作

    文檔,即索引庫中某個類型下的數據,會根據規則創建索引,將來用來搜索。可以類比做數據庫中的每一行數據。

    7.1.新增文檔

    7.1.1.新增并隨機生成id

    通過POST請求,可以向一個已經存在的索引庫中添加文檔數據。

    POST /索引庫名/類型名 {"key":"value" }

    示例:

    POST /lagou/goods/ {"title":"小米手機","images":"http://image.lagou.com/12479122.jpg","price":2699.00 }

    響應:

    {"_index": "lagou","_type": "goods","_id": "tURGznQB29tVfg_iWHfl","_version": 1,"result": "created","_shards": {"total": 3,"successful": 1,"failed": 0 },"_seq_no": 0,"_primary_term": 2 }

    • 可以看到結果顯示為: created ,應該是創建成功了。
    • 另外,需要注意的是,在響應結果中有個 _id 字段,這個就是這條文檔數據的 唯一標示 ,以后的增刪改查都依賴這個id作為唯一標示。
    • 可以看到id的值為: tURGznQB29tVfg_iWHfl ,這里我們新增時沒有指定id,所以是ES幫我們隨機生成的id。

    7.2.查看文檔

    根據rest風格,新增是post,查詢應該是get,不過查詢一般都需要條件,這里我們把剛剛生成數據的id帶上。
    通過kibana查看數據:

    GET /lagou/goods/tURGznQB29tVfg_iWHfl

    查看結果:

    {"_index": "lagou","_type": "goods","_id": "tURGznQB29tVfg_iWHfl","_version": 1,"found": true,"_source": {"title": "小米手機","images": "http://image.lagou.com/12479122.jpg","price": 2699 } }

    • _source :源文檔信息,所有的數據都在里面。
    • _id :這條文檔的唯一標示
    • 自動生成的id,長度為20個字符,URL安全,base64編碼,GUID(全局唯一標識符),分布式系統并行生成時不可能會發生沖突
    • 在實際開發中不建議使用ES生成的ID,太長且為字符串類型,檢索時效率低。建議:將數據表中唯一的ID,作為ES的文檔ID

    7.3.新增文檔并自定義id

    如果我們想要自己新增的時候指定id,可以這么做:

    POST /lagou/goods/2 {"title":"大米手機","images":"http://image.lagou.com/12479122.jpg","price":2899.00 }

    7.4.修改數據

    PUT:修改文檔
    POST:新增文檔
    把剛才新增的請求方式改為PUT,就是修改了。不過修改必須指定id,id對應文檔存在,則修改,id對應文檔不存在,則新增
    比如,我們把使用id為3,不存在,則應該是新增:

    PUT /lagou/goods/3 {"title":"超米手機","images":"http://image.lagou.com/12479122.jpg","price":3899.00,"stock": 100,"saleable":true }

    7.5.刪除數據

    刪除使用DELETE請求,同樣,需要根據id進行刪除:

    7.6.智能判斷

    • 剛剛我們在新增數據時,添加的字段都是提前在類型中定義過的,如果我們添加的字段并沒有提前定義過,能夠成功嗎?
    • 事實上Elasticsearch非常智能,你不需要給索引庫設置任何mapping映射,它也可以根據你輸入的數據來判斷類型,動態添加數據映射
    POST /lagou/goods/3 {"title":"超大米手機","images":"http://image.lagou.com/12479122.jpg","price":3299.00,"stock": 200,"saleable":true,"subTitle":"大米" }

    7.7.動態映射模板


    1)模板名稱,隨便起
    2)匹配條件,凡是符合條件的未定義字段,都會按照這個規則來映射
    3)映射規則,匹配成功后的映射規則
    舉例,我們可以把所有未映射的string類型數據自動映射為keyword類型:

    PUT lagou3 {"mappings": {"goods": {"properties": {"title": {"type": "text","analyzer": "ik_max_word"}},"dynamic_templates": [{"strings": {"match_mapping_type": "string","mapping": {"type": "keyword","index":false,"store":true}}}]} } }

    在這個案例中,我們把做了兩個映射配置:

    • title字段:統一映射為text類型,并制定分詞器
    • 其它字段:只要是string類型,統一都處理為keyword類型。
      這樣,未知的string類型數據就不會被映射為text和keyword并存,而是統一以keyword來處理!

    8.查詢

    • 基本查詢
    • 結果過濾
    • 高級查詢
    • 排序

    8.1.基本查詢:

    基本語法

    GET /索引庫名/_search {"query":{"查詢類型":{"查詢條件":"查詢條件值"}} }

    這里的query代表一個查詢對象,里面可以有不同的查詢屬性

    • 查詢類型:
      • 例如: match_all , match , term , range 等等
    • 查詢條件:查詢條件會根據類型的不同,寫法也有差異,后面詳細講解

    8.1.1 查詢所有(match_all)

    GET /lagou/_search {"query":{"match_all": {}} }
    • query :代表查詢對象
    • match_all :代表查詢所有

    • took:查詢花費時間,單位是毫秒
    • time_out:是否超時
    • _shards:分片信息
    • hits:搜索結果總覽對象
      • total:搜索到的總條數
      • max_score:所有結果中文檔得分的最高分
      • hits:搜索結果的文檔對象數組,每個元素是一條搜索到的文檔信息
        • _index:索引庫
        • _type:文檔類型
        • _id:文檔id
        • _score:文檔得分
        • _source:文檔的源數據

    文檔得分:使用ES時,對于查詢出的文檔無疑會有文檔相似度之別。而理想的排序是和查詢條件相關性越高排序越靠前,而這個排序的依據就是_score

    8.1.2 匹配查詢(match)

    GET /lagou/_search {"query":{"match":{"title":"小米電視"}} }

    在上面的案例中,不僅會查詢到電視,而且與小米相關的都會查詢到,多個詞之間是 or 的關系。

    • and關系
      某些情況下,我們需要更精確查找:比如在電商平臺精確搜索商品時,我們希望這個關系(查詢條件切分詞之后的關系)變成 and (既要滿足你,又要滿足我),可以這樣做
    GET /lagou/_search {"query":{"match":{"title":{"query":"小米電視","operator":"and"}}} }

    8.1.3 詞條匹配(term)

    term 查詢被用于精確值 匹配,這些精確值可能是數字、時間、布爾或者那些未分詞的字符串,keyword類型的字符串

    • 效果類似于:select * from tableName where name=‘value’;
    GET /lagou/_search {"query":{"term":{"price":2699.00}} }

    8.1.4 布爾組合(bool)

    bool 把各種其它查詢通過 must (與)、 must_not (非)、 should (或)的方式進行組合

    GET /lagou/_search {"query":{"bool":{"must": { "match": { "title": "大米" }},"must_not": { "match": { "title": "電視" }},"should": { "match": { "title": "手機" }}}} }

    8.1.5 范圍查詢(range)

    range 查詢找出那些落在指定區間內的數字或者時間

    GET /lagou/_search {"query":{"range": {"price": {"gte": 1000.0,"lt": 2800.00}}} }

    range 查詢允許以下字符:

    操作符說明
    gt大于
    gte大于等于
    lt小于
    lte小于等于

    8.1.6 模糊查詢(fuzzy)

    fuzzy 查詢是 term 查詢的模糊等價,很少直接使用它。
    fuzzy 查詢是 term 查詢的模糊等價。它允許用戶搜索詞條與實際詞條的拼寫出現偏差,但是偏差的編輯距離不得超過2:

    GET /lagou/_search {"query": {"fuzzy": {"title": "appla"} } }

    8.2.結果過濾

    默認情況下,elasticsearch在搜索的結果中,會把文檔中保存在 _source 的所有字段都返回。
    如果我們只想獲取其中的部分字段,我們可以添加 _source 的過濾

    8.2.1.直接指定字段

    GET /lagou/_search {"_source": ["title","price"],"query": {"term": {"price": 2699} } }

    8.2.2.指定includes和excludes

    我們也可以通過:
    includes:來指定想要顯示的字段
    excludes:來指定不想要顯示的字段
    二者都是可選的。

    GET /lagou/_search {"_source": {"includes":["title","price"] },"query": {"term": {"price": 2699} } }

    8.3 過濾(filter)

    Elasticsearch 使用的查詢語言(DSL)擁有一套查詢組件,這些組件可以以無限組合的方式進行搭配。
    這套組件可以在以下兩種情況下使用:過濾情況(filtering context)和查詢情況(query context)。

    如何選擇查詢與過濾

    • 通常的規則是,使用查詢(query)語句來進行 全文 搜索或者其它任何需要影響 相關性得分 的搜索。
      除此以外的情況都使用過濾(filters)。
    • 條件查詢中進行過濾
    • 所有的查詢都會影響到文檔的評分及排名。如果我們需要在查詢結果中進行過濾,并且不希望過濾條件影響評分,那么就不要把過濾條件作為查詢條件來用。而是使用 filter 方式:
    GET /lagou/_search {"query":{"bool":{"must":{ "match": { "title": "小米手機" }},"filter":{"range":{"price":{"gt":2000.00,"lt":3800.00}}}}} }
    • 無查詢條件,直接過濾
    • 如果一次查詢只有過濾,沒有查詢條件,不希望進行評分,我們可以使用 constant_score 取代只有filter 語句的 bool 查 詢。在性能上是完全相同的,但對于提高查詢簡潔性和清晰度有很大幫助。
    GET /lagou/_search {"query":{"constant_score": {"filter": {"range":{"price":{"gt":2000.00,"lt":3000.00}}}} } }

    8.4 排序

    8.4.1 單字段排序

    sort 可以讓我們按照不同的字段進行排序,并且通過 order 指定排序的方式

    GET /lagou/_search {"query": {"match": {"title": "小米手機"}},"sort": [{"price": {"order": "desc"}} ] }

    8.4.2 多字段排序

    假定我們想要結合使用 price和 _score(得分) 進行查詢,并且匹配的結果首先按照價格排序,然后按照相關性得分排序

    GET /lagou/_search {"query":{"bool":{"must":{ "match": { "title": "小米手機" }},"filter":{"range":{"price":{"gt":200000,"lt":300000}}}}},"sort": [{ "price": { "order": "desc" }},{ "_score": { "order": "desc" }}] }

    8.5.分頁

    Elasticsearch中數據都存儲在分片中,當執行搜索時每個分片獨立搜索后,數據再經過整合返回。那么,如果要實現分頁查詢該怎么辦呢?

    • elasticsearch的分頁與mysql數據庫非常相似,都是指定兩個值:
      • from:目標數據的偏移值(開始位置),默認from為0
      • size:每頁大小
    GET /lagou/_search {"query": {"match_all": {} },"sort": [{"price": {"order": "asc"}} ],"from": 3,"size": 3 }

    8.6.高亮

    高亮原理:

    • 服務端搜索數據,得到搜索結果
    • 把搜索結果中,搜索關鍵字都加上約定好的標簽
    • 前端頁面提前寫好標簽的CSS樣式,即可高亮

    elasticsearch中實現高亮的語法比較簡單:

    GET /lagou/_search {"query": {"match": {"title": "手機"} },"highlight": {"pre_tags": "<em>","post_tags": "</em>","fields": {"title": {}} } }

    在使用match查詢的同時,加上一個highlight屬性:

    • pre_tags:前置標簽
    • post_tags:后置標簽
    • fields:需要高亮的字段
    • title:這里聲明title字段需要高亮

    9. 聚合aggregations

    聚合可以讓我們極其方便的實現對數據的統計、分析。例如:

    • 什么品牌的手機最受歡迎?
    • 這些手機的平均價格、最高價格、最低價格?
    • 這些手機每月的銷售情況如何?

    實現這些統計功能的比數據庫的sql要方便的多,而且查詢速度非常快,可以實現近實時搜索效果。

    9.1 基本概念

    Elasticsearch中的聚合,包含多種類型,最常用的兩種,一個叫 桶 ,一個叫 度量 :

    • 桶(bucket) 類似于 group by

    桶的作用,是按照某種方式對數據進行分組,每一組數據在ES中稱為一個 桶 ,例如我們根據國籍對人劃分,可以得到 中國桶 、 英國桶 , 日本桶 ……或者我們按照年齡段對人進行劃分:010,1020,2030,3040等。

    Elasticsearch中提供的劃分桶的方式有很多:

    • Date Histogram Aggregation:根據日期階梯分組,例如給定階梯為周,會自動每周分為一組
    • Histogram Aggregation:根據數值階梯分組,與日期類似,需要知道分組的間隔(interval)
    • Terms Aggregation:根據詞條內容分組,詞條內容完全匹配的為一組
    • Range Aggregation:數值和日期的范圍分組,指定開始和結束,然后按段分組
    • ……

    綜上所述,我們發現bucket aggregations 只負責對數據進行分組,并不進行計算,因此往往bucket中往往會嵌套另一種聚合:metrics aggregations即度量

    • 度量(metrics) 相當于聚合的結果

    分組完成以后,我們一般會對組中的數據進行聚合運算,例如求平均值、最大、最小、求和等,這些在ES中稱為 度量
    比較常用的一些度量聚合方式:

    • Avg Aggregation:求平均值
    • Max Aggregation:求最大值
    • Min Aggregation:求最小值
    • Percentiles Aggregation:求百分比
    • Stats Aggregation:同時返回avg、max、min、sum、count等
    • Sum Aggregation:求和
    • Top hits Aggregation:求前幾
    • Value Count Aggregation:求總數
    • ……
      注意:在ES中,需要進行聚合、排序、過濾的字段其處理方式比較特殊,因此不能被分詞,必須使用keyword 或 數值類型 。這里我們將color和make這兩個文字類型的字段設置為keyword類型,這個類型不會被分詞,將來就可以參與聚合

    9.2 聚合為桶

    首先,我們按照 汽車的顏色 color來 劃分 桶 ,按照顏色分桶,最好是使用TermAggregation類型,按照顏色的名稱來分桶

    GET /car/_search {"size" : 0,"aggs" : {"popular_colors" : {"terms" : {"field" : "color"}}} }
    • size: 查詢條數,這里設置為0,因為我們不關心搜索到的數據,只關心聚合結果,提高效率
    • aggs:聲明這是一個聚合查詢,是aggregations的縮寫
      • popular_colors:給這次聚合起一個名字,可任意指定。
        • terms:聚合的類型,這里選擇terms,是根據詞條內容(這里是顏色)劃分
          • field:劃分桶時依賴的字段
            結果:
    {"took": 33,"timed_out": false,"_shards": {"total": 5,"successful": 5,"skipped": 0,"failed": 0},"hits": {"total": 8,"max_score": 0,"hits": [] },"aggregations": {"popular_colors": {"doc_count_error_upper_bound": 0,"sum_other_doc_count": 0,"buckets": [{"key": "紅","doc_count": 4},{"key": "綠","doc_count": 2},{"key": "藍","doc_count": 2}]} } }
    • hits:查詢結果為空,因為我們設置了size為0
    • aggregations:聚合的結果
    • popular_colors:我們定義的聚合名稱
    • buckets:查找到的桶,每個不同的color字段值都會形成一個桶
      • key:這個桶對應的color字段的值
      • doc_count:這個桶中的文檔數量

    9.3 桶內度量

    • 前面的例子告訴我們每個桶里面的文檔數量,這很有用。 但通常,我們的應用需要提供更復雜的文檔度量。 例如,每種顏色汽車的平均價格是多少?
    • 因此,我們需要告訴Elasticsearch 使用哪個字段 , 使用何種度量方式 進行運算,這些信息要嵌套在 桶內, 度量 的運算會基于 桶 內的文檔進行
    • 現在,我們為剛剛的聚合結果添加 求價格平均值的度量:
    GET /car/_search {"size" : 0,"aggs" : {"popular_colors" : {"terms" : {"field" : "color"},"aggs":{"avg_price": {"avg": {"field": "price"}}}}} }
    • aggs:我們在上一個aggs(popular_colors)中添加新的aggs。可見度量也是一個聚合
    • avg_price:聚合的名稱
    • avg:度量的類型,這里是求平均值
    • field:度量運算的字段

    10.Elasticsearch集群

    我們都是使用單點的elasticsearch,接下來我們會學習如何搭建Elasticsearch的集群。

    10.1.單點的問題

    單點的elasticsearch存在哪些可能出現的問題呢?

    • 單臺機器存儲容量有限,無法實現高存儲
    • 單服務器容易出現單點故障,無法實現高可用
      -單服務的并發處理能力有限,無法實現高并發

    所以,為了應對這些問題,我們需要對elasticsearch搭建集群

    10.2.集群的結構

    10.2.1.數據分片

    首先,我們面臨的第一個問題就是數據量太大,單點存儲量有限的問題。
    大家覺得應該如何解決?
    沒錯,我們可以把數據拆分成多份,每一份存儲到不同機器節點(node),從而實現減少每個節點數據量的目的。這就是數據的分布式存儲,也叫做: 數據分片(Shard) 。


    10.2.2.數據備份

    • 數據分片解決了海量數據存儲的問題,但是如果出現單點故障,那么分片數據就不再完整,這又該如何解決呢?
    • 沒錯,就像大家為了備份手機數據,會額外存儲一份到移動硬盤一樣。我們可以給每個分片數據進行備份,存儲到其它節點,防止數據丟失,這就是數據備份,也叫 數據副本(replica) 。
    • 數據備份可以保證高可用,但是每個分片備份一份,所需要的節點數量就會翻一倍,成本實在是太高了!
    • 為了在高可用和成本間尋求平衡,我們可以這樣做:
      • 首先對數據分片,存儲到不同節點
      • 然后對每個分片進行備份,放到對方節點,完成互相備份
    • 這樣可以大大減少所需要的服務節點數量,如圖,我們以3分片,每個分片備份一份為例:


      在這個集群中,如果出現單節點故障,并不會導致數據缺失,所以保證了集群的高可用,同時也減少了節點中數據存儲量。并且因為是多個節點存儲數據,因此用戶請求也會分發到不同服務器,并發能力也得到了一定的提升。

    10.3.搭建集群

    • 集群需要多臺機器,我們這里用一臺機器來模擬,因此我們需要在一臺虛擬機中部署多個elasticsearch節點,每個elasticsearch的端口都必須不一樣。
    • 一臺機器進行模擬:將我們的ES的安裝包復制三份,修改端口號,data和log存放位置的不同。
    • 實際開發中:將每個ES節點放在不同的服務器上。
    • 我們計劃集群名稱為:lagou-elastic,部署3個elasticsearch節點,分別是:
      • node-01:http端口9201,TCP端口9301
      • node-02:http端口9202,TCP端口9302
      • node-03:http端口9203,TCP端口9303
      • http:表示使用http協議進行訪問時使用 端口,elasticsearch-head、kibana、postman,默認端口號是9200。
      • tcp:集群間的各個節點進行通訊的端口,默認9300

    第一步:復制es軟件粘貼3次,分別改名

    第二步:修改每一個節點的配置文件 config下的- elasticsearch.yml,下面已第一份配置文件為例三個節點的配置文件幾乎一致,除了:node.name、path.data、path.log、http.port、transport.tcp.port
    node1

    #允許跨域名訪問 http.cors.enabled: true #當設置允許跨域,默認為*,表示支持所有域名 http.cors.allow-origin: "*" #允許所有節點訪問 network.host: 0.0.0.0 # 集群的名稱,同一個集群下所有節點的集群名稱應該一致 cluster.name: lagou-elastic #當前節點名稱 每個節點不一樣 node.name: node-01 #數據的存放路徑 每個節點不一樣,不同es服務器對應的data和log存儲的路徑不能一樣 path.data: d:\class\es-9201\data #日志的存放路徑 每個節點不一樣 path.logs: d:\class\es-9201\logs # http協議的對外端口 每個節點不一樣,默認:9200 http.port: 9201 # TCP協議對外端口 每個節點不一樣,默認:9300 transport.tcp.port: 9301 #三個節點相互發現,包含自己,使用tcp協議的端口號 discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"] #聲明大于幾個的投票主節點有效,請設置為(nodes / 2) + 1 discovery.zen.minimum_master_nodes: 2 # 是否為主節點 node.master: true

    node2、node3一樣

    第三步:啟動集群

    • 把三個節點分別啟動,啟動時不要著急,要一個一個地啟動
      使用head插件查看:

    10.4.測試集群中創建索引庫

    配置kibana,再重啟

    搭建集群以后就要創建索引庫了,那么問題來了,當我們創建一個索引庫后,數據會保存到哪個服務節點上呢?如果我們對索引庫分片,那么每個片會在哪個節點呢?

    settings:就是索引庫設置,其中可以定義索引庫的各種屬性,目前我們可以不設置,都走默認。這里給搭建看看集群中分片和備份的設置方式,

    PUT /lagou {"settings": {"number_of_shards": 3,"number_of_replicas": 1 } }

    這里有兩個配置:

    • number_of_shards:分片數量,這里設置為3
    • number_of_replicas:副本數量,這里設置為1,每個分片一個備份,一個原始數據,共2份。

    通過chrome瀏覽器的head查看,我們可以查看到分片的存儲結構:


    可以看到,lagou這個索引庫,有三個分片,分別是0、1、2,每個分片有1個副本,共6份。

    • node-01上保存了1號分片和2號分片的副本
    • node-02上保存了0號分片和2號分片的副本
      -node-03上保存了0號分片和1號分片的副本

    10.5.集群工作原理

    10.5.1.shad與replica機制

    (1)一個index包含多個shard,也就是一個index存在多個服務器上
    (2)每個shard都是一個最小工作單元,承載部分數據,比如有三臺服務器,現在有三條數據,這三條數
    據在三臺服務器上各方一條.
    (3)增減節點時,shard會自動在nodes中負載均衡
    (4)primary shard(主分片)和replica shard(副本分片),每個document肯定只存在于某一個primary shard以及其對應的replica shard中,不可能存在于多個primary shard
    (5)replica shard是primary shard的副本,負責容錯,以及承擔讀請求負載
    (6)primary shard的數量在創建索引的時候就固定了,replica shard的數量可以隨時修改
    (7)primary shard的默認數量是5,replica默認是1(每個主分片一個副本分片),默認有10個
    shard,5個primary shard,5個replica shard
    (8)primary shard不能和自己的replica shard放在同一個節點上(否則節點宕機,primary shard和副本都丟失,起不到容錯的作用),但是可以和其他primary shard的replica shard放在同一個節點上

    10.5.2.集群寫入數據

  • 客戶端選擇一個node發送請求過去,這個node就是coordinating node (協調節點)
  • coordinating node,對document進行路由,將請求轉發給對應的node。(根據一定的算法選擇對應的節點進行存儲)
  • 實際上的node上的primary shard處理請求,將數據保存在本地,然后將數據同步到replica node
  • coordinating node,如果發現primary node和所有的replica node都搞定之后,就會返回請求到客戶端
  • 這個路由簡單的說就是取模算法,比如說現在有3太服務器,這個時候傳過來的id是5,那么5%3=2,就放在第2臺服務器

    10.5.3.ES查詢數據

    倒排序算法
    查詢有個算法叫倒排序:簡單的說就是:通過分詞把詞語出現的id進行記錄下來,再查詢的時候先去查到哪些id包含這個數據,然后再根據id把數據查出來
    查詢過程

  • 客戶端發送一個請求給coordinate node
  • 協調節點將搜索的請求轉發給所有的shard對應的primary shard 或replica shard
  • query phase(查詢階段):每一個shard 將自己搜索的結果(其實也就是一些唯一標識),返回給協調節點,有協調節點進行數據的合并,排序,分頁等操作,產出最后的結果
  • fetch phase(獲取階段) ,接著由協調節點,根據唯一標識去各個節點進行拉取數據,最終返回給客戶端
  • 11.Elasticsearch客戶端

    11.1.客戶端介紹

    在elasticsearch官網中提供了各種語言的客戶端:https://www.elastic.co/guide/en/elasticsearch/client/index.html
    注意點擊進入后,選擇版本到 6.2.4 ,因為我們之前按照的都是 6.2.4 版本:

    11.2.創建Demo工程

    11.2.1.初始化項目

    11.2.2.pom文件

    注意,這里我們直接導入了SpringBoot的啟動器,方便后續講解。不過還需要手動引入elasticsearch的High-level-Rest-Client的依賴:

    <properties><java.version>11</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.5</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version></dependency><!--Apache開源組織提供的用于操作JAVA BEAN的工具包--><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.1</version></dependency><!--ES高級Rest Client--><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>6.4.3</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

    11.2.3.配置文件

    我們在resource下創建application.yml

    11.3.索引庫及映射

    創建索引庫的同時,我們也會創建type及其映射關系,但是這些操作不建議使用java客戶端完成,原因如下:

    • 索引庫和映射往往是初始化時完成,不需要頻繁操作,不如提前配置好
    • 官方提供的創建索引庫及映射API非常繁瑣,需要通過字符串拼接json結構:
    package com.lagou.es.pojo; public class Product {private Long id;private String title; //標題private String category;// 分類private String brand; // 品牌private Double price; // 價格private String images; // 圖片地址 }

    分析一下數據結構:

    • id:可以認為是主鍵,將來判斷數據是否重復的標示,不分詞,可以使用keyword類型
    • title:搜索字段,需要分詞,可以用text類型
    • category:商品分類,這個是整體,不分詞,可以使用keyword類型
    • brand:品牌,與分類類似,不分詞,可以使用keyword類型
    • price:價格,這個是double類型
      -images:圖片,用來展示的字段,不搜索,index為false,不分詞,可以使用keyword類型

    我們可以編寫這樣的映射配置

    PUT /lagou {"settings": {"number_of_shards": 3,"number_of_replicas": 1 },"mappings": {"item": {"properties": {"id": {"type": "keyword"},"title": {"type": "text","analyzer": "ik_max_word"},"category": {"type": "keyword"},"brand": {"type": "keyword"},"images": {"type": "keyword","index": false},"price": {"type": "double"}}} } }

    11.4.索引數據操作

    有了索引庫,我們接下來看看如何新增索引數據
    操作MYSQL數據庫:
    1.獲取數據庫連接
    2.完成數據的增刪改查
    3.釋放資源

    11.4.1.初始化客戶端

    我們先編寫一個測試類:

    public class ElasticSearchTest {private RestHighLevelClient client; // Json工具private Gson gson = new Gson();@Beforepublic void init(){// 初始化HighLevel客戶端client = new RestHighLevelClient(RestClient.builder(new HttpHost("127.0.0.1", 9201, "http"),new HttpHost("127.0.0.1", 9202, "http"),new HttpHost("127.0.0.1", 9203, "http")));}@Afterpublic void close() throws IOException {// 關閉客戶端client.close();} }

    11.4.2.新增文檔

    @Testpublic void testInsert() throws IOException {//1.文檔數據Product product = new Product();product.setBrand("華為");product.setCategory("手機");product.setId(1L);product.setImages("http://image.huawei.com/1.jpg");product.setPrice(5999.99);product.setTitle("華為P50就是棒");//2.將文檔數據轉換為json格式String source = gson.toJson(product);//3.創建索引請求對象 訪問哪個索引庫、哪個type、指定文檔ID//public IndexRequest(String index, String type, String id)IndexRequest request = new IndexRequest("lagou","item",product.getId().toString());request.source(source, XContentType.JSON);//4.發出請求IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);System.out.println(response);}

    11.4.3.查看文檔

    @Test public void testFindIndex() throws IOException {// 創建get請求,并指定idGetRequest request = new GetRequest("lagou", "item", "1");// 查詢,得到響應GetResponse response = client.get(request, RequestOptions.DEFAULT);// 解析響應,應該是jsonString source = response.getSourceAsString();// 轉換json數據Product item = gson.fromJson(source, Product.class);System.out.println(item); }

    11.4.4.修改文檔

    新增時,如果傳遞的id是已經存在的,則會完成修改操作,如果不存在,則是新增。

    11.4.5.刪除文檔

    根據id刪除:

    @Test public void testDeleteIndex() throws IOException {// 準備刪除的請求,參數為idDeleteRequest request = new DeleteRequest("lagou", "item", "1");// 發起請求DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);System.out.println("response = " + response); }

    11.5 搜索數據

    11.5.1.查詢所有match_all

    @Test public void testMatchAll() throws IOException {// 創建搜索對象SearchRequest request = new SearchRequest();// 查詢構建工具SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 添加查詢條件,通過QueryBuilders獲取各種查詢sourceBuilder.query(QueryBuilders.matchAllQuery());request.source(sourceBuilder);// 搜索SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 解析SearchHits hits = response.getHits();SearchHit[] searchHits = hits.getHits();for (SearchHit hit : searchHits) {// 取出source數據String json = hit.getSourceAsString();// 反序列化Product item = gson.fromJson(json, Item.class);System.out.println("item = " + item);} }
    • 注意,上面的代碼中,搜索條件是通過 sourceBuilder.query(QueryBuilders.matchAllQuery())來添加的。這個 query() 方法接受的參數是: QueryBuilder 接口類型。
    • 這個接口提供了很多實現類,分別對應我們在之前中學習的不同類型的查詢,例如:term查詢、match查詢、range查詢、boolean查詢等

    • 因此,我們如果要使用各種不同查詢,其實僅僅是傳遞給 sourceBuilder.query() 方法的參數不同而已。而這些實現類不需要我們去 new ,官方提供了 QueryBuilders 工廠幫我們構建各種實現類

    11.5.2.關鍵字搜索match

    其實搜索類型的變化,僅僅是利用QueryBuilders構建的查詢對象不同而已,其他代碼基本一致:

    因此,我們可以把這段代碼封裝,然后把查詢條件作為參數傳遞:

    private void basicQuery(SearchSourceBuilder sourceBuilder) throws IOException {// 創建搜索對象SearchRequest request = new SearchRequest();request.source(sourceBuilder);// 搜索SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 解析SearchHits hits = response.getHits();SearchHit[] searchHits = hits.getHits();for (SearchHit hit : searchHits) {// 取出source數據String json = hit.getSourceAsString();// 反序列化Product item = gson.fromJson(json, Item.class);System.out.println("item = " + item);}}

    11.5.3.范圍查詢range

    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price"); 方法說明
    gt(Object from)大于
    gte(Object from)大于等于
    lt(Object from)小于
    lte(Object from)小于等于

    11.5.4.source過濾

    _source:存儲原始文檔

    • 默認情況下,索引庫中所有數據都會返回,如果我們想只返回部分字段,可以通過source filter來控制。
    @Test public void testSourceFilter() throws IOException {// 創建搜索對象SearchRequest request = new SearchRequest();// 查詢構建工具SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 添加查詢條件,通過QueryBuilders獲取各種查詢sourceBuilder.query(QueryBuilders.matchAllQuery());// 添加source過濾sourceBuilder.fetchSource(new String[]{"id", "title", "price"}, null);basicQuery(sourceBuilder); }

    11.6排序

    依然是通過sourceBuilder來配置

    public void testSortQuery() throws IOException {// 創建搜索對象SearchRequest request = new SearchRequest();// 查詢構建工具SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 添加查詢條件,通過QueryBuilders獲取各種查詢sourceBuilder.query(QueryBuilders.matchAllQuery());// 添加排序sourceBuilder.sort("price", SortOrder.ASC);basicQuery(sourceBuilder); }

    11.7.分頁

    分頁需要視圖層傳遞兩個參數給我們:

    • 當前頁:page
    • 每頁大小:size
      而elasticsearch中需要的不是當前頁,而是起始位置,還好有公式可以計算出:
    • from–>起始位置,0表示第一條
    • 起始位置:start = (page - 1) * size
    • 第一頁:(1-1)*5 = 0
    • 第二頁:(2-1)*5 = 5
    public void testSortAndPageQuery() throws IOException {// 創建搜索對象SearchRequest request = new SearchRequest();// 查詢構建工具SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 添加查詢條件,通過QueryBuilders獲取各種查詢sourceBuilder.query(QueryBuilders.matchAllQuery());// 添加排序sourceBuilder.sort("price", SortOrder.ASC);// 添加分頁int page = 1;int size = 3;int start = (page - 1) * size;// 配置分頁sourceBuilder.from(start);sourceBuilder.size(3);basicQuery(sourceBuilder);}

    12.Spring Data Elasticsearch

    12.1.什么是SpringDataElasticsearch

    • Spring Data Elasticsearch(以后簡稱SDE)是Spring Data項目下的一個子模塊。
    • Spring Data 的使命是給各種數據訪問提供統一的編程接口,不管是關系型數據庫(如MySQL),還是非關系數據庫(如Redis),或者類似Elasticsearch這樣的索引數據庫。從而簡化開發人員的代碼,提高開發效率。
    • Spring Data Elasticsearch的頁面:https://projects.spring.io/spring-data-elasticsearch/
      特征:
      • 支持Spring的基于 @Configuration 的java配置方式,或者XML配置方式
      • 提供了用于操作ES的便捷工具類 ElasticsearchTemplate 。包括實現文檔到POJO之間的自動智能映射。
      • 利用Spring的數據轉換服務實現的功能豐富的對象映射
      • 基于注解的元數據映射方式,而且可擴展以支持更多不同的數據格式,可以定義JavaBean:類名、屬性
      • 根據持久層接口自動生成對應實現方法,無需人工編寫基本操作代碼(類似mybatis,根據接口自動得到實現)。當然,也支持人工定制查詢

    12.2配置SpringDataElasticsearch

    我們在pom文件中,引入SpringDataElasticsearch的啟動器:

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

    然后,只需要在resources下新建application.yml文件,引入elasticsearch的host和port即可:

    spring: data:elasticsearch:cluster-name: lagou-elasticcluster-nodes: 127.0.0.1:9301,127.0.0.1:9302,127.0.0.1:9303
    • 需要注意的是,SpringDataElasticsearch底層使用的不是Elasticsearch提供的RestHighLevelClient,而是TransportClient,并不采用Http協議通信,而是訪問elasticsearch對外開放的tcp端口,我們之前集群配置中,設置的分別是:9301,9302,9303

    添加引導類

    import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class EsApplication {public static void main(String[] args) {SpringApplication.run(EsApplication.class,args);} }

    12.3.索引庫操作

    12.3.1.創建索引庫

    準備一個pojo對象
    然后準備一個新的實體類,作為下面與索引庫對應的文檔:

    我們先創建一個測試類,然后注入ElasticsearchTemplate:

    @RunWith(SpringRunner.class) @SpringBootTest public class SpringElasticsearchTest {@Autowiredprivate ElasticsearchTemplate esTemplate;}

    下面是創建索引庫的API示例:

    @Test public void testCreateIndex(){// 創建索引庫,并制定實體類的字節碼esTemplate.createIndex(Goods.class); }
    • 發現沒有,創建索引庫需要指定的信息,比如:索引庫名、類型名、分片、副本數量、還有映射信息都沒有填寫,這是怎么回事呢?
    • 實際上,與我們自定義工具類類似,SDE也是通過實體類上的注解來配置索引庫信息的,我們需要在Goods上添加下面的一些注解:
    @Document(indexName = "lagou", type = "product", shards = 3, replicas = 1) public class Product {@Idprivate Long id;@Field(type = FieldType.Text, analyzer = "ik_max_word")private String title; //標題@Field(type = FieldType.Keyword)private String category;// 分類@Field(type = FieldType.Keyword)private String brand; // 品牌@Field(type = FieldType.Double)private Double price; // 價格@Field(type = FieldType.Keyword, index = false)private String images; // 圖片地址//

    幾個用到的注解:

    • @Document:聲明索引庫配置
      • indexName:索引庫名稱
      • type:類型名稱,默認是“docs”
      • shards:分片數量,默認5
      • replicas:副本數量,默認1
    • @Id:聲明實體類的id
    • @Field:聲明字段屬性
      • type:字段的數據類型
      • analyzer:指定分詞器類型
        -index:是否創建索引

    12.3.2.創建映射

    剛才的注解已經把映射關系也配置上了,所以創建映射只需要這樣

    12.4索引數據CRUD

    SDE的索引數據CRUD并沒有封裝在ElasticsearchTemplate中,而是有一個叫做 ElasticsearchRepository的接口:


    我們需要自定義接口,繼承ElasticsearchRespository:

    package com.lagou.es.repository; import com.lagou.es.pojo.Goods; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface GoodsRepository extends ElasticsearchRepository<Goods, Long> { }

    12.4.1 創建索引數據

    @Autowired private GoodsRepository goodsRepository; @Test public void addDocument(){Goods goods = new Goods(1L, "小米手機9", " 手機","小米", 3499.00, "http://image.lagou.com/13123.jpg");// 添加索引數據goodsRepository.save(goods); }

    批量創建:

    @Test public void addDocuments(){// 準備文檔數據:List<Goods> list = new ArrayList<>();list.add(new Goods(1L, "小米手機7", "手機", "小米", 3299.00, "/13123.jpg"));list.add(new Goods(2L, "堅果手機R1", "手機", "錘子", 3699.00, "/13123.jpg"));list.add(new Goods(3L, "華為META10", "手機", "華為", 4499.00, "/13123.jpg"));list.add(new Goods(4L, "小米Mix2S", "手機", "小米", 4299.00, "/13123.jpg"));list.add(new Goods(5L, "榮耀V10", "手機", "華為", 2799.00, "/13123.jpg"));// 添加索引數據goodsRepository.saveAll(list); }

    12.4.2.查詢索引數據

    默認提供了根據id查詢,查詢所有兩個功能:

    @Test public void testQueryById(){Optional<Goods> goodsOptional = goodsRepository.findById(3L);System.out.println(goodsOptional.orElse(null)); }@Test public void testQueryAll(){Iterable<Goods> list = goodsRepository.findAll();list.forEach(System.out::println); }

    12.4.3.自定義方法查詢

    ProductRepository提供的查詢方法有限,但是它卻提供了非常強大的自定義查詢功能:
    只要遵循SpringData提供的語法,我們可以任意定義方法聲明:

    public interface GoodsRepository extends ElasticsearchRepository<Goods, Long> {/*** 根據價格區間查詢* @param from 開始價格* @param to 結束價格* @return 符合條件的goods*/List<Product> findByPriceBetween(Double from, Double to); } KeywordSampleElasticsearch Query String
    AndfindByNameAndPrice{“bool” : {“must” : [ {“field”: {“name” : “?”}}, {“field” :{“price” : “?”}} ]}}
    OrfindByNameOrPrice{“bool” : {“should” : [{“field” : {“name” : “?”}},{“field” : {“price” : “?”}}]}}
    IsfindByName{“bool” : {“must” : {“field” :{“name” : “?”}}}}
    NotfindByNameNot{“bool” : {“must_not” :{“field” : {“name” : “?”}}}}
    BetweenfindByPriceBetween{“bool” : {“must” : {“range” :{“price” : {“from” : ?,“to” :?,“include_lower” :true,“include_upper” :true}}}}}
    LessThanEqualfindByPriceLessThan{“bool” : {“must” : {“range” :{“price” : {“from” : null,“to”: ?,“include_lower” :true,“include_upper” :true}}}}}
    GreaterThanEqualfindByPriceGreaterThan{“bool” : {“must” : {“range” :{“price” : {“from” : ?,“to” :null,“include_lower” :true,“include_upper” :true}}}}}
    BeforefindByPriceBefore{“bool” : {“must” : {“range” :{“price” : {“from” : null,“to”: ?,“include_lower” :true,“include_upper” :true}}}}}
    AfterfindByPriceAfter{“bool” : {“must” : {“range” :{“price” : {“from” : ?,“to” :null,“include_lower” :true,“include_upper” :true}}}}}
    LikefindByNameLike{“bool” : {“must” : {“field” :{“name” : {“query” : “?*”,“analyze_wildcard” :true}}}}}
    StartingWithfindByNameStartingWith{“bool” : {“must” : {“field” :{“name” : {“query” : “?*”,“analyze_wildcard” :true}}}}}
    EndingWithfindByNameEndingWith{“bool” : {“must” : {“field” :{“name” : {“query” :"*?",“analyze_wildcard” :true}}}}}
    Contains/ContainingfindByNameContaining{“bool” : {“must” : {“field” :{“name” : {“query” : “?”,“analyze_wildcard” :true}}}}}
    InfindByNameIn(Collectionnames){“bool” : {“must” : {“bool” :{“should” : [ {“field” :{“name” : “?”}}, {“field” :{“name” : “?”}} ]}}}}
    NotInfindByNameNotIn(Collectionnames){“bool” : {“must_not” :{“bool” : {“should” : {“field”: {“name” : “?”}}}}}}
    NearfindByStoreNearNot Supported Yet
    TruefindByAvailableTrue{“bool” : {“must” : {“field” :{“available” : true}}}}
    FalsefindByAvailableFalse{“bool” : {“must” : {“field” :{“available” : false}}}}
    OrderByfindByAvailableTrueOrderByNameDesc{“sort” : [{ “name” : {“order”: “desc”} }],“bool” : {“must”: {“field” : {“available” :true}}}}

    12.5.原生查詢

    • 如果覺得上述接口依然不符合你的需求,SDE也支持原生查詢,這個時候還是使用ElasticsearchTemplate
    • 而查詢條件的構建是通過一個名為 - NativeSearchQueryBuilder 的類來完成的,不過這個類的底層還是使用的原生API中的 QueryBuilders 、AggregationBuilders 、 HighlightBuilders 等工具。

    需求:
    查詢title中包含小米手機的商品,以價格升序排序,分頁查詢:每頁展示2條,查詢第1頁。
    對查詢結果進行聚合分析:獲取品牌及個數

    @Test public void testNativeQuery(){// 原生查詢構建器NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();// 1.1 source過濾queryBuilder.withSourceFilter(new FetchSourceFilter(new String[0], new String[0]));// 1.2搜索條件queryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米手機"));// 1.3分頁及排序條件queryBuilder.withPageable(PageRequest.of(0, 2,Sort.by(Sort.Direction.ASC, "price")));// 1.4高亮顯示// queryBuilder.withHighlightBuilder(new HighlightBuilder().field("title"));// 1.5聚合queryBuilder.addAggregation(AggregationBuilders.terms("brandAgg").field("brand" )); // 構建查詢條件,并且查詢AggregatedPage<Goods> result = esTemplate.queryForPage(queryBuilder.build(), Goods.class);// 2、解析結果:// 2.1分頁結果long total = result.getTotalElements();int totalPages = result.getTotalPages();List<Goods> list = result.getContent();System.out.println("總條數 = " + total);System.out.println("總頁數 = " + totalPages);System.out.println(list);// 2.2.聚合結果Aggregations aggregations = result.getAggregations();Terms terms = aggregations.get("brandAgg");terms.getBuckets().forEach(b -> {System.out.println("品牌 = " + b.getKeyAsString());System.out.println("count = " + b.getDocCount());}); }

    高亮展示:
    1、自定義搜索結果映射

    public class EsResultMapper implements SearchResultMapper {@Overridepublic <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {// 記錄總條數long totalHits = response.getHits().getTotalHits();// 記錄列表(泛型) - 構建Aggregate使用List<T> list = new ArrayList<>();// 獲取搜索結果(真正的的記錄)SearchHits hits = response.getHits();for (SearchHit hit : hits) {if (hits.getHits().length <= 0) {return null;}// 將原本的JSON對象轉換成Map對象Map<String, Object> map = hit.getSourceAsMap();// 獲取高亮的字段MapMap<String, HighlightField> highlightFields = hit.getHighlightFields();for (Map.Entry<String, HighlightField> highlightField : highlightFields.entrySet()) {// 獲取高亮的KeyString key = highlightField.getKey();// 獲取高亮的ValueHighlightField value = highlightField.getValue(); // 實際fragments[0]就是高亮的結果,無需遍歷拼接Text[] fragments = value.getFragments();// 因為高亮的字段必然存在于Map中,就是key值map.put(key, fragments[0].toString());}// 把Map轉換成對象Gson gson = new Gson();T item = gson.fromJson(gson.toJson(map), aClass);list.add(item);}// 返回的是帶分頁的結果return new AggregatedPageImpl<>(list, pageable, totalHits);} }

    2、高亮實現:

    @Testpublic void testNativeQuery() {// 原生查詢構建器NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();// 1.1 source過濾//queryBuilder.withSourceFilter(new FetchSourceFilter(new String[0], new String[0]));// 1.2搜索條件queryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米手機"));// 1.3分頁及排序條件queryBuilder.withPageable(PageRequest.of(0, 6,Sort.by(Sort.Direction.ASC, "price")));// 1.4高亮顯示//queryBuilder.withHighlightBuilder(new HighlightBuilder().field("title"));// 構建高亮查詢HighlightBuilder.Field field = new HighlightBuilder.Field("title").preTags("<font style='color:red'>").postTags(" </font>");queryBuilder.withHighlightFields(field); // 名字高亮NativeSearchQuery build = queryBuilder.build();// 1.5聚合queryBuilder.addAggregation(AggregationBuilders.terms("brandAgg").field("brand" ));// 構建查詢條件,并且查詢AggregatedPage<Product> result = template.queryForPage(queryBuilder.build(), Product.class,new EsResultMapper());// 2、解析結果:long total = result.getTotalElements();int totalPages = result.getTotalPages();List<Product> list = result.getContent();list.stream().forEach(product -> System.out.println(product));}

    總結

    以上是生活随笔為你收集整理的LuceneElasticSeach的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。