文章目錄
- 1. 基于Lucene的全文檢索
- 1.1 全文檢索概念
- 1.2 全文檢索程序
- 1.3 全文檢索相關概念
- 1.4 全文檢索的使用場景
- 2. ElasticSearch
- 2.1 ElasticSearch概念
- 2.2 ElasticSearch應用案例
- 2.3 ElasticSearch和Solr對比
- 2.4 ElasticSearch 術語
- 2.4.1概述
- 2.4.2 Index 索引
- 2.4.3 Type 型別
- 2.4.4 Document 檔案
- 2.4.5 Field 欄位
- 2.4.6 Mapping 映射
- 2.4.7 NRT 接近實時
- 2.4.8 Cluster集群
- 2.4.9 Node 節點
- 2.4.10 Shards&Replicas 分片和復制
- 2.5 ElasticSearch安裝
- 2.6 ElasticSearch的客戶端操作
- 2.7 IK分詞器
- 2.7.1 IK分詞器概念
- 2.7.2 IK分詞器特性
- 2.7.3 IK分詞器安裝
- 2.7.4 IK分詞器測驗
- 2.8 Kibana
- 2.8.1 Kibana概念
- 2.8.2 Kibana安裝
- 2.8.3 Kibana使用
- 2.8.3.1 配置索引
- 2.8.3.2資料搜索
- 2.8.3.3 資料可視化
- 2.8.3.4 控制臺操作資料
- 2.8.4 使用DSL陳述句
- 2.8.4.1 Query DSL結構化查詢
- 2.8.4.2 常用快捷鍵
- 2.8.4.3 索引操作
- 2.8.4.4 資料操作
- 2.8.4.5 查詢模式
- 2.8.5 ElasticSearch原生API編程
- 2.8.5.1 POM檔案
- 2.8.5.2 創建客戶端連接物件
- 2.8.5.3 創建索引Index
- 2.8.5.3 創建映射Mapping
- 2.8.5.4 創建檔案Document
- 2.8.5.5 查詢檔案
- 2.8.5.5.1 公共查詢部分
- 2.8.5.5.2 term 查詢
- 2.8.5.5.3 queryString 查詢
- 2.8.5.5.4 match 查詢
- 2.8.5.5.5 根據檔案id查詢
- 2.8.5.5.6 分頁查詢
- 2.8.5.6 查詢高亮顯示
- 2.8.6 Spring Data ElasticSearch
- 2.8.6.1 Spring Data概念
- 2.8.6.2 Spring Data ElasticSearch概念
- 2.8.6.3 入門案例
- 2.8.6.3.1 POM檔案
- 2.8.6.3.2 創建ES配置類
- 2.8.6.3.3 創建檔案物體類
- 2.8.6.3.4 撰寫Dao層
- 2.8.6.3.5 測驗
- 2.8.6.4 聚合查詢
- 2.8.6.4.1 物體類
- 2.8.6.4.2 撰寫Dao層
- 2.8.6.4.3 測驗
- 2.8.6.4.4 劃分桶
- 2.8.6.4.5 桶內度量
1. 基于Lucene的全文檢索
1.1 全文檢索概念
????將非結構化資料中的一部分資訊提取出來,重新組織,使其變得有一定結構,然后對此有一定結構的資料進行檢索,從而達到搜索相對較快的目的,從非結構化資料中提取出來并重新組織的資訊,我們稱之為索引,
例如:字典中的拼音表、偏旁部首表就相當于字典的索引,
????這種先建立索引,再對索引進行搜索的程序就叫全文檢索(Full-text Search),
注意:索引創建程序十分耗時,但是一旦創建有利于復用,全文檢索主要處理的是查詢,所以雖然創建索引耗時,但卻值得,否則使用順序掃描的方式去查詢非結構化資料是非常慢的,
因此,在大資料量的情況下,如果你需要用到搜索,就可以用到全文檢索引擎,
????1998年9月4日,谷歌公司在美國成立,正如大家所說的,它是一家做搜索引擎起家的公司,無獨有偶,有個叫 Doug Cutting 的美國工程師,也迷上了搜索引擎,他做了一個用于文本搜索的函式庫(可以理解成一個軟體的功能組件),名為Lucene(ElasticSearch和Solr都是基于它封裝的),
注:故事來自鮮棗課堂公眾號,此外,hadoop的作者也是 Doug Cutting,
????Lucene 是用 Java 撰寫的資訊檢索工具包(不包含搜索引擎系統),目的是為各種中小型應用軟體增添全文檢索功能,因為好用且開源得到了程式員的歡迎,2001年,Lucene 成為了Apache軟體基金會jakarta專案的一個子專案,
????Nutch 是一個建立在Lucene核心之上的網頁搜索應用程式,可以直接download使用,它在Lucene的基礎上增加了網路爬蟲和一些網頁相關的功能,目的就是像谷歌一樣從一個簡單的站內檢索推廣到全球網路的搜索上,
注:這里是本人看了B站狂神說相關視頻了解到的一個前菜,個人記住了3個詞:大資料 = 海量存盤 + 計算,除此之外還有一些技術的關聯歷史,推薦大家去了解下,有利于對技術的發展追溯和認知,因為這是一個大資料的背景,包括我們之后學的ES也是處于這種背景之下,
1.2 全文檢索程序

1.3 全文檢索相關概念
-
索引庫
即將索引存盤在磁盤上的一系列檔案,里面保存了建立好的索引資訊以及檔案物件,
一個索引庫可類比于資料庫中的一張表,

-
檔案物件
獲取原始內容的目的是為了索引,在創建索引前需要將原始內容創建成檔案,檔案中包括很多個域,域中存盤內容,且每個檔案都有唯一的編號,即檔案id,
一個檔案可類比于表里的一條記錄,

-
域物件
域(Field)是索引庫中存盤資料的最小單位,其資料型別可以分成數值型別和文本型別兩種,一般查詢的欄位都是文本型別的,域還有如下屬性:
域可以類比成記錄的欄位,
-
是否分詞
是否對域的內容進行分詞處理,前提是我們要對域的內容進行查詢,如果是類似商品編號、商品價格可以不用分詞,
例如,標題——中國人民銀行,可以分詞為:
- 標題:中國;
- 標題:人民;
- 標題:銀行;
- 標題:中國人民,
-
是否索引
將Field分析后的詞或整個Field進行索引,只有索引才能搜索到,
例如:商品名稱、商品簡介分析后進行索引,訂單號、身份證號可以不用分詞但是也要索引,它們將來都可以作為查詢的條件,
-
是否存盤
是否將Field值存盤在檔案中,存盤在檔案中的Field才能從檔案中獲取到,
例如商品名、訂單號,凡是將來要從檔案中獲取的域都要存盤,
-
term物件
從檔案物件中拆分出來的每個詞都是一個term,term中包含兩部分:檔案域名和單詞的內容,
注意:不同域中拆分出來的統一單詞是不同的term,term是創建索引的關鍵詞物件,
-
1.4 全文檢索的使用場景
- 搜索的資料物件是大量的非結構化的文本資料,
- 檔案記錄量達到數十萬或數百萬個甚至更多,
- 支持大量基于互動式文本的查詢,
- 需求非常靈活的全文搜索查詢,
- 對高度相關的搜索結果的有特殊需求,但是沒有可用的關系資料庫可以滿足,
- 對不同記錄型別、非文本資料操作或安全事務處理的需求相對較少的情況,
2. ElasticSearch
2.1 ElasticSearch概念
????ElasticSearch(ES)是一個開源的、高擴展的、分布式的全文檢索引擎,它可以==近乎實時地存盤、檢索資料==,并且由于本身擴展性很好,可以擴展到上百臺服務器,所以能夠處理PB級別的資料,ES 也可以使用 Java 開發,并使用Lucene作為其核心,從而實作所有索引和檢索的功能,ES 的目的是通過簡單的Restful API 來隱藏Lucene的復雜性,使得全文搜索變得十分簡單,
注:2016年,ES已經成為了排名第一的搜索引擎類應用,
2.2 ElasticSearch應用案例
- GitHub:2013年初拋棄了Solr,采用 ElasticSearch 來做PB級別的搜索,“GitHub使用 ElasticSearch 搜索20TB的資料,包括13 億檔案和1300億行代碼”,
- 百度:目前廣泛使用 ElasticSearch 作為文本資料分析,采集百度所有服務器其上的各類指標資料和用戶自定義資料,通過對各種資料進行多維分析戰術,輔助定位分析實體例外或業務層面例外,單集群最大100臺機器,200ES節點,每天匯入30TB+資料,
- 新浪:使用 ElasticSearch 分析處理32億實時日志,
- 阿里巴巴:使用 ElasticSearch 構建自己的日志采集和分析體系,
- ……
2.3 ElasticSearch和Solr對比
-
都是基于 Lucene 而改造、封裝的全文檢索引擎;
-
Solr 利用Zookeeper進行分布式管理;ElasticSearch 自身帶有分布式協調管理功能;
-
Solr 支持更多格式的資料(json、xml、csv…);ElasticSearch 僅支持json資料格式;
-
Solr 官方提供更多的功能;ElasticSearch 本身更注重核心功能,高級功能多有第三方插件提供;
例如:IK分詞器、圖形化界面需要Kibana友好支持,
-
Solr 在傳統的搜索應用中表現優于 ElasticSearch,在處理實時搜索應用時效率低于ElasticSearch,
-
單純地對已有的資料進行搜索的時候,Solr更快;當實時建立索引的時候,Solr會產生IO阻塞,查詢性能相對較差,ES有明顯的優勢,
-
隨著資料量的增加,Solr的搜索效率會變得更低,而ES沒有明顯變化,
-
Solr查詢快,但更新索引時慢,適合電商等查詢多的應用;ES建立索引快(查詢慢)實時性查詢快,適合facebook、新浪等搜索,
2.4 ElasticSearch 術語
2.4.1概述
????ElasticSearch 是面向檔案(document oriented)的,這意味著它可以存盤整個物件或檔案(document),然而它不僅僅是存盤,還會索引(index)每個檔案的內容使之可以被搜索,在 ElasticSearch中,你可以對檔案(而非成行成列的資料)進行索引、搜索、排序、過濾,
????借用了一位仁兄的圖:

注意:和1.3小節有細微差別,一切都是json,在這里提一嘴,不要一上來就記各種名詞,容易繞懵(我一開始上來就懵逼了),通過類比進行記憶并理解各自對應的內容、作用即可,
2.4.2 Index 索引
? 一個索引就是一個擁有幾分相似特征的檔案的集合,比如說,你可以有一個客戶資料的索引,另一個產品目錄的索引,還有一個訂單資料的索引,一個索引由一個名字來標識(必須全部是小寫字母的),并且當我們要對對應于這個索引中的檔案進行索引、搜索、更新和洗掉的時候,都要使用到這個名字,在一個集群中,可以定義任意多的索引,
2.4.3 Type 型別
????在一個索引中,你可以定義一種或多種型別,一個型別是你的索引的一個邏輯上的分類/磁區,其語意完全由你來定,通常,會為具有一組共同欄位的檔案定義一個型別,比如說,我們假設你運營一個博客平臺并且將你所有的資料存盤到一個索引中,在這個索引中,你可以為用戶資料定義一個型別,為博客資料定義另一個型別,當然,也可以為評論資料定義另一個型別,
注意:Elasticsearch 6.X中,一個 index 下已經只能包含一個Type,Elasticsearch 7.X中, Type的概念已經被洗掉了,因為這里的版本用的是6.7.1,所以提一嘴,之后學完高版本之后再回過頭來更新,
為什么要洗掉映射型別?
2.4.4 Document 檔案
????一個檔案是一個可被索引的基礎資訊單元,比如,你可以擁有某一個客戶的檔案,某一個產品的一個檔案,當然,也可以擁有某個訂單的一個檔案,檔案以JSON(JavaScript Object Notation)格式來表示,而JSON是一個到處存在的互聯網資料互動格式,在一個Index/Type里面,你可以存盤任意多的檔案,注意,盡管一個檔案物理上存在于一個索引之中,但是檔案必須被索引/賦予一個索引的Type(看版本),
2.4.5 Field 欄位
????相當于是資料表的欄位,對檔案資料根據不同屬性進行的分類標識,
2.4.6 Mapping 映射
????Mapping是處理資料的方式和規則方面做一些限制,如某個欄位的資料型別、默認值、分析器、是否被索引等等,這些都是映射里面可以設定的,其它就是處理ES里面資料的一些使用規則設定也叫做映射,按著最優規則處理資料對性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能對性能更好,
注意:映射可類比于資料庫表格中對欄位的定義,
2.4.7 NRT 接近實時
????ElasticSearch 是一個接近實時的搜索平臺,這意味著,從索引一個檔案直到這個檔案能夠被搜索到有一個輕微的延遲(通常是1秒以內),
2.4.8 Cluster集群
????一個集群就是由一個或多個節點組織在一起,它們共同持有整個的資料,并一起提供索引和搜索功能,一個集群由一個唯一的名字標識(一個人就是一個集群!戲精一點的說法就是一人一城既視感hhhh,只不過默認這個集群里只有一個節點而已),這個名字是重要的,因為一個節點只能通過指定某個集群的名字,來加入這個集群,
注意:集群名一般默認就是“elasticsearch”,但是在2.5小節中發現默認是”docker-cluster“,這與ES的版本和構建型別有關,后面先統一稱之為“elasticsearch”,
2.4.9 Node 節點
????一個節點是集群中的一個服務器,作為集群的一部分,它存盤資料,參與集群的索引和搜索功能,和集群類似,一個節點也是由一個名字來標識的,默認情況下,這個名字是一個隨機的漫威漫畫角色的名字,這個名字會在啟動的時候賦予節點,這個名字對于管理作業來說挺重要的,因為在這個管理程序中,你會去確定網路中的哪些服務器對應于Elasticsearch集群中的哪些節點,
????一個節點可以通過配置集群名稱的方式來加入一個指定的集群,默認情況下,每個節點都會被安排加入到一個叫做“elasticsearch”的集群中,這意味著,如果你在你的網路中啟動了若干個節點,并假定它們能夠相互發現彼此,它們將會自動地形成并加入到一個叫做“elasticsearch”的集群中,
????在一個集群里,只要你想,可以擁有任意多個節點,而且,如果當前你的網路中沒有運行任何 ElasticSearch 節點,這時啟動一個節點,會默認創建并加入一個叫做“elasticsearch”的集群,
2.4.10 Shards&Replicas 分片和復制
????一個索引可以存盤超出單個結點硬體限制的大量資料,比如,一個具有10億檔案的索引占據1TB的磁盤空間,而任一節點都沒有這樣大的磁盤空間;或者單個節點處理搜索請求,回應太慢,為了解決這個問題,ElasticSearch提供了將索引劃分成多份的能力,這就叫做分片,
????當你創建一個索引的時候,你可以指定你想要的分片的數量,每個分片本身也是一個功能完善并且獨立的“索引”,這個“索引”可以被放置到集群中的任何節點上,分片很重要,主要有兩方面的原因:
- 允許你水平分割/擴展你的內容容量,
- 允許你在分片(潛在地,位于多個節點上)之上進行分布式的、并行的操作,進而提高性能/吞吐量,
????至于一個分片怎樣分布,它的檔案怎樣聚合回搜索請求,是完全由ElasticSearch管理的,對于作為用戶的你來說,這些都是透明的,
注意:實際上,一個分片是一個Lucene索引,一個包含倒排索引的檔案目錄,倒排索引的結構使得ES在不掃描全部檔案的情況下,就能告訴你哪些檔案包含特定的關鍵字,那么像我這種菜雞第一次看到肯定在想啥又是倒排索引??持續懵逼…
倒排索引(有人說正確的翻譯應該叫反轉索引):
????ES使用的是一種倒排索引的結構(Lucene底層就用了倒排),這種結構使用于快速的全文搜索,一個索引由檔案中所有不重復的串列構成,對于每個詞都有一個包含它的檔案串列,
?????為了創建倒排索引,我們首先將每個檔案拆分成獨立的詞,然后創建一個包含所有不重復詞條的索引串列,然后列出每個詞條在哪個檔案有出現,
????這樣的話,我們在搜索某些詞的時候,會根據這個串列去查找滿足條件的檔案,例如,詞abc在檔案1和檔案2都出現了,詞def只在檔案1出現了,那么我們在搜索abc def 的時候就會搜索到這兩個表格,而且檔案1的權重(score)更高,因為它更符合條件,
? 因此,查找倒排索引之后的資料會比直接查原始資料快得多,所以抓住重點,什么是倒排索引:檔案key—>詞value,查檔案的時候就反過來根據詞再對檔案排一次(權重高低),
總之,一個ES索引(庫概念)是由多個Lucene索引組成的(因為多個分片),一般情況下,說到的索引指的是ES索引而不是Lucene索引,
????在一個網路/云的環境里,失敗隨時都可能發生,在某個分片/節點不知怎么的就處于離線狀態,或者由于任何原因消失了,這種情況下,有一個故障轉移機制是非常有用并且是強烈推薦的,為此目的,ElasticSearch允許你創建分片的一份或多份拷貝,這些拷貝叫做復制分片,或者直接叫復制,
????復制之所以重要,有兩個主要原因:
-
在分片/節點失敗的情況下,提供了高可用性,
因為這個原因,注意到復制分片從不與原/主要(original/primary)分片置于同一節點上是非常重要的,
-
擴展你的搜索量/吞吐量,
因為搜索可以在所有的復制上并行運行,總之,每個索引可以被分成多個分片,一個索引也可以被復制0次(意思是沒有復制)或多次,一旦復制了,每個索引就有了主分片(作為復制源的原來的分片)和復制分片(主分片的拷貝)之別,分片和復制的數量可以在索引創建的時候指定,在索引創建之后,你可以在任何時候動態地改變復制的數量,但是你事后不能改變分片的數量,
????默認情況下,Elasticsearch中的每個索引被分片5個主分片和1個復制,這意味著,如果你的集群中至少有兩個節點,你的索引將會有5個主分片和另外5個復制分片(1個完全拷貝),這樣的話每個索引總共就有10個分片,
注意:需要考慮版本的不同,7.0以上不會默認幫你創建分片了,需要自己創建索引的時候設定,
2.5 ElasticSearch安裝
這里采用Docker進行安裝ES 6.7.1,
-
Docker拉取ES鏡像;
docker pull elasticsearch:6.7.1 -
創建ES資料的資料卷目錄,并設定權限;
這里掛載資料卷一定要給權限,
mkdir -p /data/elasticsearch/data sudo chmod 777 /data/elasticsearch/ sudo chmod 777 /data/elasticsearch/data/
-
后臺運行ES容器;
docker run -d --name elasticsearch \ -p 9200:9200 \ -p 9300:9300 \ -v /data/elasticsearch/data:/usr/share/elasticsearch/data \ elasticsearch:6.7.1 -
遇到ES閃退,查看日志發現最大虛擬記憶體區域的數量太低了;

docker logs elasticsearch #查看日志 vim /etc/sysctl.conf #追加以下內容 (限制一個行程可以擁有的VMA(虛擬記憶體區域)的數量 ) vm.max_map_count=655360 sysctl -p #修改內核引數馬上生效 -
訪問9200埠;
9200埠是ES的Web管理平臺埠,9300是ES的服務默認埠,
注意:這里使用的構建型別是docker,默認集群名是”docker-cluster“,

-
修改elasticsearch.yml檔案;
docker exec -it elasticsearch /bin/bash #進入容器 dir # 查看檔案目錄 cd config #進入config目錄 ls #查看檔案 vi elasticsearch.yml #修改elasticsearch.yml檔案node.name: es0 #節點名稱 network.host: 192.168.31.35 #本節點ip地址 http.host: 0.0.0.0 transport.host: 0.0.0.0 cluster.name: my-elasticsearch #修改集群名 node.master: true #是否作為集群的主節點 node.data: true #是否存盤資料 discovery.zen.ping.unicast.hosts: ["192.168.31.35:9300"] #配置集群中節點的位置,節點之間可以相互通信 #跨域設定 http.cors.enabled: true #默認false http.cors.allow-origin: "*"exit docker restart elasticsearch注:ES中的config/elasticsearch.ymal配置項說明

-
設定容器開啟自動重啟,
docker update --restart=always elasticsearch -
修改JVM記憶體,
vi jvm.options-Xms512m #設定JVM初始可用記憶體為512M,默認1G -Xmx512m #設定JVM最大可用記憶體為512M,默認1Gexec docker restart elasticsearch注意:也可以在創建容器的時候就指定,即加上:-e ES_JAVA_POTS="-Xms512m -Xmx512m"
2.6 ElasticSearch的客戶端操作
????實際開發中,主要有三種方式可以作為 ElasticSearch 服務的客戶端:
-
第一種,elasticsearch-head插件;
注意:ES需要設定跨域才行,
-
第二種,使用 ElasticSearch 提供的Restful介面直接訪問;
使用postman進行客戶端操作,需要注意的是:
- 6.x版本的是否分詞設定成 “index”:true或false,
- 測驗一下標椎分詞器的效果需要使用post請求,
-
第三種,使用 ElasticSearch 提供的API進行訪問,
2.7 IK分詞器
2.7.1 IK分詞器概念
????IKAnalyzer是一個開源的、基于 Java 語言開發的輕量級的中文分詞工具包,從2006年12月推出1.0版開始,IKAnalyzer已經推出 了3個大版本,最初,它是以開源專案 Lucene 為應用主體的,結合詞典分詞和文法分析演算法的中文分詞組件,新版本的IKAnalyzer3.0則發展為面向 Java 的公用分詞組件,獨立于 Lucene 專案,同時提供了對 Lucene 的默認優化實作,
2.7.2 IK分詞器特性
- 采用了特有的“正向迭代最細粒度切分演算法“,具有60萬字/秒的高速處理能力,
- 采用了多子處理器分析模式,支持:英文字母(IP地址、Email、URL)、數字(日期,常用中文數量詞,羅馬數字,科學計數法),中文詞匯(姓名、地名處理)等分詞處理,
- 對中英聯合支持不是很好,在這方面的處理比較麻煩,需再做一次查詢,同時是支持個人詞條的優化的詞典存盤,更小的記憶體占用,
- 支持用戶詞典擴展定義,
- 針對Lucene全文檢索優化的查詢分析器IKQueryParser;采用歧義分析演算法優化查詢關鍵字的搜索排列組合,能極大的提高Lucene檢索的命中率,
2.7.3 IK分詞器安裝
-
下載 IK 分詞器壓縮包;
注意:IK分詞器下載地址、ES和IK的版本對應,
-
將IK分詞器解壓后上傳至服務器,并改名字為ik;

-
將ik目錄拷貝到Docker容器的plugins目錄下,
docker cp ./ik elasticsearch:/usr/share/elasticsearch/plugins
2.7.4 IK分詞器測驗
POST http://192.168.31.35:9200/_analyze
注意:這里使用的是6.7.1版本的,不同版本之間的方式不一樣,
-
ik_smart:會做最粗粒度的拆分;
攜帶引數如下:
{ "analyzer": "ik_smart", "text": "我是程式員" }
-
ik_max_word:會將文本做最細粒度的拆分,
攜帶引數如下:
{ "analyzer": "ik_max_word", "text": "我是程式員" }
2.8 Kibana
2.8.1 Kibana概念
????Kibana 是一款開源的資料分析和可視化平臺,它是 Elastic Stack 成員之一,設計用于和 ElasticSearch 協作,您可以使用 Kibana 對 ElasticSearch 索引中的資料進行搜索、查看、互動操作,您可以很方便的利用圖表、表格及地圖對資料進行多元化的分析和呈現,
????Kibana 可以使大資料通俗易懂,它很簡單,基于瀏覽器的界面便于您快速創建和分享動態資料儀表板來追蹤 ElasticSearch 的實時資料變化,搭建 Kibana 非常簡單,您可以分分鐘完成 Kibana 的安裝并開始探索 Elasticsearch 的索引資料?—?沒有代碼、不需要額外的基礎設施,
2.8.2 Kibana安裝
這里以Docker創建Kibana容器為例,
-
拉取Kibana鏡像;
docker pull docker.io/kibana:6.7.1注意:Kibana的版本,
-
創建并運行Kibana容器,
docker run -d -e ELASTICSEARCH_URL=http://192.168.31.35:9200 --name kibana --restart=always -p 5601:5601 kibana:6.7.1 -
如果啟動失敗,檢查kibana.yml檔案,
docker exec -it kibana /bin/bash cd config vi kivana.ymlelasticsearch.hosts: [ "http://192.168.31.35:9200" ]#修改elasticsearch.hosts的值為自己的es的ip

2.8.3 Kibana使用
2.8.3.1 配置索引
? ? ? ? 要使用Kibana,您必須至少配置一個索引,索參考于標識 Elasticearch 索引以運行搜索和分析,它們還用于配置欄位,


2.8.3.2資料搜索
? ? ? ? Discover為資料搜索部分,可以對日志資訊進行搜索操作,可以使用Discover實作資料搜索過濾和搜索條件顯示以及關鍵詞搜索,如下圖:

2.8.3.3 資料可視化

2.8.3.4 控制臺操作資料

2.8.4 使用DSL陳述句
2.8.4.1 Query DSL結構化查詢
????Query DSL是一個 Java 開源框架用于構建型別安全的 SQL 查詢陳述句,采用 API 代替傳統的拼接字串來構造查詢陳述句,目前,Querydsl支持的平臺包括JPA、JDO、SQL、Java Collections、RDF、Lucene、Hibernate Search,ElasticSearch 提供了一整套基于JSON 的 DSL 語言來定義查詢,
2.8.4.2 常用快捷鍵
ctrl+i #自動縮進
ctrl+enter #提交請求
down #打開自動補全選單
enter或tab #選中項自動補全
esc #關閉補全選單
2.8.4.3 索引操作
-
查詢所有索引;
GET /_cat/indices?v
-
新增索引;
PUT /user
注意:從7.0.0版本開始,默認只有一個分片,而不是5個了,所以如果想要多個分片需要在創建索引的時候指定,
-
洗掉某個索引;
DELETE /user
-
創建映射;
PUT /user/userinfo/_mapping { "properties": { "name": { "type": "text", "analyzer": "ik_smart", "search_analyzer": "ik_smart" }, "city": { "type": "text", "analyzer": "ik_smart", "search_analyzer": "ik_smart" }, "age": { "type": "long" }, "description": { "type": "text", "analyzer": "ik_smart", "search_analyzer": "ik_smart" } } }
為什么要洗掉映射型別?各版本之間陳述句的使用有什么區別?
2.8.4.4 資料操作
-
新增檔案資料;
PUT /user/userinfo/1 { "name": "李四", "age": 22, "city": "深圳", "description": "李四來自湖北武漢!" } PUT /user/userinfo/2 { "name": "王五", "age": 35, "city": "深圳", "description": "王五家住在深圳!" } PUT /user/userinfo/3 { "name": "張三", "age": 19, "city": "深圳", "description": "在深圳打工,來自湖北武漢" } PUT /user/userinfo/4 { "name": "張三豐", "age": 66, "city": "武漢", "description": "在武漢讀書,家在武漢!" } PUT /user/userinfo/5 { "name": "趙子龍", "age": 77, "city": "廣州", "description": "趙子龍來自深圳寶安,但是在廣州作業!", "address": "廣東省茂名市" } PUT /user/userinfo/6 { "name": "趙毅", "age": 55, "city": "廣州", "description": "趙毅來自廣州白云區,從事電子商務8年!" } PUT /user/userinfo/7 { "name": "趙哈哈", "age": 57, "city": "武漢", "description": "武漢趙哈哈,在深圳打工已有半年了,月薪7500!" }
注意:更新資料也可以使用新增操作,這種操作會將整個資料替換掉,
-
修改檔案資料;
使用POST更新某個列的資料,
POST /user/userinfo/4/_update { "doc": { "name": "張三豐", "description": "在武漢讀書,家在武漢!在深圳作業!" } }
-
根據id查詢,獲取檔案資料;
GET /user/userinfo/4
-
查詢索引下的全部檔案資料;
GET /user/_search查詢全部索引的全部檔案資料:GET _search

-
排序查詢檔案資料;
GET /user/_search { "query": { "match_all": {} }, "sort": { "age": { "order": "desc" } } }
-
分頁查詢檔案資料;
GET /user/_search { "query": { "match_all": {} }, "sort": { "age": { "order": "desc" } }, "from": 0, "size": 2 }- from:從下N的記錄開始查詢;
- size:每頁顯示條數,

-
洗掉檔案資料;
DELETE user/userinfo/7
2.8.4.5 查詢模式
-
term查詢;
term主要用于精確匹配(不分詞),如字串、數值、日期等,
不適合情況:1.列中除英文字符外有其它值 ;2.字串值中有冒號或中文 ;3.系統自帶屬性如_version,
GET _search { "query": { "term": { "city": "武漢" } } }
-
terms查詢;
terms 跟 term 有點類似,但 terms 允許指定多個匹配條件, 如果某個欄位指定了多個值,那么檔案需要一起去做匹配,
GET _search { "query": { "terms": { "city": [ "武漢", "廣州" ] } } }
-
match查詢;
與term查詢不同的是,match查詢會使用映射中設定的分詞器,對搜索值進行分詞后再匹配查詢,
GET _search { "query": { "match": { "city": "廣州武漢" } } }
-
query_string查詢;
默認使用空格拆分成多個子項,并且每個子項都會去分詞查詢,可以通過 default_operator 指定子項之間的關系,默認是或 ,match 查詢只是分詞以后查詢,相當于 query_string 用空格隔開的一個個子項查詢,query_string查詢相當于match查詢的增強版,
GET _search { "query": { "query_string": { "default_field": "city", "query": "廣州武漢" } } }query_string 和 match有什么區別?
-
range 查詢;
range過濾允許我們按照指定范圍查找一批資料,例如查詢年齡范圍,
GET _search { "query": { "range": { "age": { "gte": 30, "lte": 57 } } } }
-
exists查詢;
exists 過濾可以用于查找擁有某個域的資料,
GET _search { "query": { "exists": { "field": "address" } } }
-
bool 查詢;
bool 查詢可以用來合并多個條件查詢結果的布爾邏輯,它包含一下運算子:
- must : 多個查詢條件的完全匹配,相當于 and,
- must_not : 多個查詢條件的相反匹配,相當于 not,
- should : 至少有一個查詢條件匹配, 相當于 or,
GET _search { "query": { "bool": { "must": [ { "term": { "city": { "value": "深圳" } } }, { "range": { "age": { "gte": 20, "lte": 99 } } } ] } } }
-
match_all 查詢;
可以查詢到所有檔案,是沒有查詢條件下的默認陳述句,
前面已經用了很多次了,這里就不介紹了,
-
prefix 查詢;
以什么字符開頭的,可以更簡單地用 prefix ,例如查詢所有以張開始的用戶描述,
GET _search { "query": { "prefix": { "name": { "value": "張" } } } }
-
multi_match 查詢,
multi_match查詢允許你做match查詢的基礎上同時搜索多個欄位,在多個欄位中同時查一個,
GET _search { "query": { "multi_match": { "query": "深圳", "fields": [ "city", "description" ] } } }
-
filter查詢,
在查詢前添加過濾條件后再進行查詢,盡量使用過濾代替查詢,因為多次過濾可以使用快取,而且過濾不需要計算分數,從性能上來說過濾更好,
注意:一般使用查詢(query)來進行==全文搜索或其它任何需要影響相關性得分==的搜索,除此以外的情況都使用過濾(filters) ,
GET _search { "query": { "bool": { "must": [ { "match_all":{} } ], "filter": { "range": { "age": { "gte": 30, "lte": 57 } } } } } }
2.8.5 ElasticSearch原生API編程
2.8.5.1 POM檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lijinghua</groupId>
<artifactId>es-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>6.7.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>6.7.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.24</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
2.8.5.2 創建客戶端連接物件
private TransportClient client;
@Before
public void init() throws UnknownHostException {
//(1)配置
Settings settings = Settings.builder().put("cluster.name", "my-elasticsearch").build();
//(2)創建客戶端連接物件
client = new PreBuiltTransportClient(settings).addTransportAddress(
new TransportAddress(InetAddress.getByName("192.168.31.35"), 9300));
}
2.8.5.3 創建索引Index
@Test
public void createIndex() throws UnknownHostException {
//(3)使用api創建索引
client.admin().indices().prepareCreate("index_test").get();
//(4)資源釋放
client.close();
}

2.8.5.3 創建映射Mapping
@Test
public void createMapping() throws Exception {
//(3)創建映射
/**
* {
* "mappings":{
* "article":{
* "properties": {
* "id":{
* "type":"long",
* "store":true,
* },
* "title":{
* "type":"text",
* "store":true,
* "index":"analyzed",
* "analyzer":"ik_smart"
* },
* "content":{
* "type":"text",
* "store":true,
* "index":"analyzed",
* "analyzer":"ik_smart"
* }
* }
* }
* }
* }
*/
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.startObject("article")
.startObject("properties")
.startObject("id")
.field("type", "long").field("store", true)
.endObject()
.startObject("title")
.field("type", "text").field("store", true).field("index",true).field("analyzer",
"ik_smart")
.endObject()
.startObject("content")
.field("type", "text").field("store", true).field("index",true).field("analyzer",
"ik_smart")
.endObject()
.endObject()
.endObject()
.endObject();
//http://192.168.31.35:9200/index_test/article/_mapping
client.admin().indices().preparePutMapping("index_test").setType("article").setSource(builder).get();
//(4)資源釋放
client.close();
}

2.8.5.4 創建檔案Document
-
方法一:使用 XContentBuilder 構建Document物件;
/** * 使用 XContentBuilder 構建Document物件 * * @Author: lijinghua * @Date: 2021/9/21 */ @Test public void createDocument() throws Exception { //(3)創建檔案 XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .field("id", 2l) .field("title", "北方入秋速度明顯加快 多地降溫幅度最多可達10度") .field("content", "阿聯酋一架客機在紐約機場被隔離 10名乘客病倒") .endObject(); //(4)把檔案物件添加到索引庫 //http://192.168.31.35:9200/index_test/article/2 client.prepareIndex().setIndex("index_test").setType("article").setId("2").setSource(builder).get(); //(5)關閉客戶端 client.close(); }
-
方法二:使用物體轉Json,
-
創建Article物體類;
public class Article { private Long id; private String title; private String content; //getter/setter... } -
添加 Jackson 依賴;
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.8.1</version> </dependency> -
創建檔案物件,
/** * 使用 物體類轉json 構建Document物件 * * @Author: lijinghua * @Date: 2021/9/21 */ @Test public void createDocument2() throws Exception { //(3)創建檔案物體類 Article article = new Article(); article.setId(3l); article.setTitle("MH370墜毀在柬埔寨密林?中國一公司調十顆衛星去拍攝"); article.setContent("警惕荒唐的死亡游戲!俄15歲少年輸掉游戲后用電鋸自殺"); //(4)把物體類物件轉換成json格式的字串 ObjectMapper objectMapper = new ObjectMapper(); String jsonDocument = objectMapper.writeValueAsString(article); System.out.println(jsonDocument); //(5)使用client物件把檔案寫入索引庫 client.prepareIndex("index_test", "article", "3") .setSource(jsonDocument, XContentType.JSON).get(); //(6)資源釋放 client.close(); }
-
2.8.5.5 查詢檔案
2.8.5.5.1 公共查詢部分
private void search(QueryBuilder queryBuilder) {
//(4)執行查詢
SearchResponse searchResponse = client.prepareSearch("index_test")
.setTypes("article").setQuery(queryBuilder).get();
//(5)獲取查詢結果
SearchHits searchHits = searchResponse.getHits();
System.out.println("查詢結果總記錄數:" + searchHits.getTotalHits());//取查詢結果的總記錄數
//查詢結果串列
Iterator<SearchHit> iterator = searchHits.iterator();
while (iterator.hasNext()) {
SearchHit searchHit = iterator.next();
//列印檔案物件,以json格式輸出
System.out.println(searchHit.getSourceAsString());
System.out.println("-----------檔案的屬性");
Map<String, Object> document = searchHit.getSourceAsMap();
System.out.println(document.get("id"));
System.out.println(document.get("title"));
System.out.println(document.get("content"));
}
//(6)資源釋放
client.close();
}
注意:可以發現,查詢默認查詢前十條,
2.8.5.5.2 term 查詢
@Test
public void termQuery() {
//(3)創建一個QueryBuilder物件:要搜索的欄位、要搜索的關鍵詞
QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "女護士");
search(queryBuilder);
}
2.8.5.5.3 queryString 查詢
@Test
public void queryStringQuery() {
//(3)創建一個QueryBuilder物件:要搜索的欄位、要搜索的關鍵詞
QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("美麗的女護士").defaultField("title");
search(queryBuilder);
}
2.8.5.5.4 match 查詢
@Test
public void matchQuery() {
//(3)創建一個QueryBuilder物件:要搜索的欄位、要搜索的關鍵詞
QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "美麗的女護士");
search(queryBuilder);
}

2.8.5.5.5 根據檔案id查詢
@Test
public void idsQuery() throws Exception {
//(3)創建一個QueryBuilder物件:要搜索的id范圍
QueryBuilder queryBuilder = QueryBuilders.idsQuery().addIds("1", "2");
search(queryBuilder);
}

2.8.5.5.6 分頁查詢
private void searchByPage(QueryBuilder queryBuilder, int pageNum, int pageSize) {
//(4)執行查詢
SearchResponse searchResponse = client.prepareSearch("index_test")
.setTypes("article").setQuery(queryBuilder).setFrom((pageNum-1)*pageSize).setSize(pageSize).get();
//(5)獲取查詢結果
SearchHits searchHits = searchResponse.getHits();
System.out.println("查詢結果總記錄數:" + searchHits.getTotalHits());//取查詢結果的總記錄數
//查詢結果串列
Iterator<SearchHit> iterator = searchHits.iterator();
while (iterator.hasNext()) {
SearchHit searchHit = iterator.next();
//列印檔案物件,以json格式輸出
System.out.println(searchHit.getSourceAsString());
System.out.println("-----------檔案的屬性");
Map<String, Object> document = searchHit.getSourceAsMap();
System.out.println(document.get("id"));
System.out.println(document.get("title"));
System.out.println(document.get("content"));
}
//(6)資源釋放
client.close();
}
2.8.5.6 查詢高亮顯示
????在進行關鍵字搜索時,搜索出的內容中的關鍵字會顯示不同的顏色,稱之為高亮,
????ElasticSearch可以對查詢出的內容中關鍵字部分進行標簽和樣式的設定,但是你需要告訴ElasticSearch使用什么標簽對高亮關鍵字進行包裹,


注意:通過開發者模式查看發現,關鍵詞使用了em標簽,
@Test
public void multiMatchQuery() {
//(3)創建一個QueryBuilder物件:要搜索的關鍵詞、要搜索的欄位(可變引數)
QueryBuilder queryBuilder = QueryBuilders.multiMatchQuery( "美麗的女護士","title","content");
//search(queryBuilder);
searchByHighLight(queryBuilder, 1, 5);
}
private void searchByHighLight(QueryBuilder queryBuilder, int pageNum, int pageSize) {
//(4)高亮顯示設定
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title").field("content");//設定多個域高亮
highlightBuilder.preTags("<em>");//設定標簽樣式
highlightBuilder.postTags("</em>");
//(5)執行查詢
SearchResponse searchResponse = client.prepareSearch("index_test")
.setTypes("article").setQuery(queryBuilder)
.setFrom((pageNum-1)*pageSize).setSize(pageSize)
.highlighter(highlightBuilder).get();
//(6)獲取查詢結果
SearchHits searchHits = searchResponse.getHits();
System.out.println("查詢結果總記錄數:" + searchHits.getTotalHits());//取查詢結果的總記錄數
//查詢結果串列
Iterator<SearchHit> iterator = searchHits.iterator();
while (iterator.hasNext()) {
SearchHit searchHit = iterator.next();
//列印檔案物件,以json格式輸出
System.out.println("-----------檔案-----------");
System.out.println(searchHit.getSourceAsString());
System.out.println("-----------高亮-----------");
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
for (Map.Entry<String, HighlightField> entry : highlightFields.entrySet()) {
System.out.println(entry.getKey() + ":\t" +
Arrays.toString(entry.getValue().getFragments()));
}
}
//(7)資源釋放
client.close();
}

在實際應用時,我們只需要判斷這個欄位存不存在高亮片段,例如title的高亮片段,如果存在就回傳高亮的title,而不回傳原來的title即可,
2.8.6 Spring Data ElasticSearch
參考網址
2.8.6.1 Spring Data概念
? Spring Data是一個用于簡化資料庫訪問,并支持云服務的開源框架,其主要目標是使得對資料的訪問變得方便快捷,并支持map-reduce框架和云計算資料服務, Spring Data可以極大的簡化 JPA 的寫法,可以在幾乎不用寫實作的情況下,實作對資料的訪問和操作,除了CRUD外,還包括如分頁、排序等一些常用的功能,
Spring Data的官網


2.8.6.2 Spring Data ElasticSearch概念
????Spring Data ElasticSearch 基于 Spring Data API 簡化 ElasticSearch 操作,將原始操作 ElasticSearch 的客戶端API 進行封裝,Spring Data為 ElasticSearch 專案提供集成搜索引擎,Spring Data ElasticSearch POJO 的關鍵功能區域為中心的模型與 ElastichSearch互動檔案和輕松地撰寫一個存盤庫資料訪問層,
在作用上,可以類比于Myabtis-Plus對Mybatis的簡化封裝,
2.8.6.3 入門案例
2.8.6.3.1 POM檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lijinghua</groupId>
<artifactId>es-demo2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>es-demo2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.8.6.3.2 創建ES配置類
# ip+埠
elasticSearch:
host.port: 192.168.31.35:9200
# Socket連接超時時間
socketTimeout: 60
@Configuration
public class ElasticSearchClientConfig extends AbstractElasticsearchConfiguration {
@Value("${elasticSearch.host.port}")
private String hostAndPort;
@Value("${elasticSearch.socketTimeout}")
private long socketTimeout;
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(hostAndPort)
.withSocketTimeout(Duration.ofSeconds(socketTimeout))
.build();
return RestClients.create(clientConfiguration).rest();
}
@Bean
public ElasticsearchRestTemplate restTemplate() {
return new ElasticsearchRestTemplate(elasticsearchClient());
}
}
2.8.6.3.3 創建檔案物體類
@Data
@Document(indexName = "index_springdata", type = "article")
public class Article {
@Id
@Field(type = FieldType.Long, store = true)
private Long id;
@Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
private String title;
@Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
private String content;
}
2.8.6.3.4 撰寫Dao層
方法命名規則查詢的基本語法findBy + 屬性 + 關鍵詞 + 連接符
| 關鍵字 | 命名規則 | 解釋 | 示例 |
|---|---|---|---|
| and | findByField1AndField2 | 根據Field1和Field2 獲得資料 | findByTitleAndContent |
| or | findByField1OrField2 | 根據Field1或Field2 獲得資料 | findByTitleOrContent |
| is | findByField | 根據Field獲得資料 | findByTitle |
| not | findByFieldNot | 根據Field獲得補集資料 | findByTitleNot |
| between | findByFieldBetween | 獲得指定范圍的資料 | findByPriceBetween |
| lessThanEqual | findByFieldLessThan | 獲得小于等于指定值的資料 | findByPriceLessThan |
public interface ArticleRepository extends ElasticsearchRepository<Article, Long> {
List<Article> findByTitle(String title);
List<Article> findByTitleOrContent(String title, String content);
List<Article> findByTitleOrContent(String title, String content, Pageable pageable);
}
????除此之外,ElasticSearchRepository內置了其他CRUD方法,此外我們也可以利用ElasticSearchRestTemplate去使用,例如:
@Autowired
private ElasticsearchRestTemplate restTemplate;
@Test
public void createIndex(){
// 創建索引
restTemplate.createIndex(Article.class);//我們在物體類中已經宣告了映射
}
注意: ElasticsearchTemplate使用的是 transport ,而ElasticsearchRestTemplate使用的是 HighLevelRestClient ,ElasticsearchTemplate當然可以使用了,但是,ES 官方從 v6.* 開始就deprecated了這個介面,且宣稱從 v8.* 開始,將不再支持基于 transport 的介面,所以我們暫時用用還行,新專案還是不要再用了,改為使用基于 rest 介面的 ElasticsearchRestTemplate 比較好,參考文章

2.8.6.3.5 測驗
@RunWith(SpringRunner.class)
@SpringBootTest
class EsDemo2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate restTemplate;
@Autowired
private ArticleRepository articleRepository;
@Autowired
@Test
public void createIndex() {
// 創建索引
restTemplate.createIndex(Article.class);
}
@Test
public void addDocument() {
for (int i = 10; i <= 20; i++) {
//創建一個Article物件
Article article = new Article();
article.setId(i + 0l);
article.setTitle("女護士路遇昏迷男子跪地搶救:救人是職責更是本能" + i);
article.setContent("這是一個美麗的女護士妹妹" + i);
//把檔案寫入索引庫
articleRepository.save(article);
}
}
@Test
public void deleteDocumentById() {
articleRepository.deleteById(11l);
//全部洗掉
//articleRepository.deleteAll();
}
@Test
public void findAll() {
Iterable<Article> articles = articleRepository.findAll();
articles.forEach(a -> System.out.println(a));
}
@Test
public void testFindById() {
Optional<Article> optional = articleRepository.findById(10l);
Article article = optional.get();
System.out.println(article);
}
@Test
public void testFindByTitle() {
//List<Article> list = articleRepository.findByTitle("美麗的女護士");
List<Article> list = articleRepository.findByTitleLike("美麗的女護士");
list.stream().forEach(a -> System.out.println(a));
}
@Test
public void testFindByTitleOrContent() {
Pageable pageable = PageRequest.of(1, 5);
articleRepository.findByTitleOrContent("美麗的女護士", "女護士", pageable)
.forEach(a -> System.out.println(a));
}
@Test
public void testNativeSearchQuery() {
//創建一個查詢物件
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.queryStringQuery("女護士").defaultField(" title"))
.withPageable(PageRequest.of(0, 15))
.build();
//執行查詢
List<Article> articleList = restTemplate.queryForList(query, Article.class);
articleList.forEach(a -> System.out.println(a));
}
}
2.8.6.4 聚合查詢
2.8.6.4.1 物體類
@Data
@Document(indexName = "car_index", type = "car")
public class Car {
@Id
@Field(type = FieldType.Long, store = true)
private Long id;
@Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
private String name;
@Field(type = FieldType.Text, store = true, analyzer = "ik_smart", fielddata = true)
private String color;
@Field(type = FieldType.Text, store = true, analyzer = "ik_smart", fielddata = true)
private String brand;
@Field(type = FieldType.Double, store = true)
private Double price;
}
2.8.6.4.2 撰寫Dao層
public interface CarRepository extends ElasticsearchRepository<Car, Long> {
}
2.8.6.4.3 測驗
@Autowired
private CarRepository carRepository;
@Test
public void initIndex() {
// 創建索引
carRepository.save(new Car(1l, "比亞迪A1", "紅色", "比亞迪", 50000d));
carRepository.save(new Car(2l, "比亞迪A2", "白色", "比亞迪", 70000d));
carRepository.save(new Car(3l, "比亞迪A3", "白色", "比亞迪", 80000d));
carRepository.save(new Car(4l, "比亞迪A4", "紅色", "比亞迪", 60000d));
carRepository.save(new Car(5l, "比亞迪A5", "紅色", "比亞迪", 90000d));
carRepository.save(new Car(6l, "寶馬A1", "紅色", "寶馬", 10000d));
carRepository.save(new Car(7l, "寶馬A2", "黑色", "寶馬", 20000d));
carRepository.save(new Car(8l, "寶馬A3", "黑色", "寶馬", 30000d));
carRepository.save(new Car(9l, "寶馬A4", "紅色", "寶馬", 40000d));
carRepository.save(new Car(10l, "寶馬A5", "紅色", "寶馬", 50000d));
carRepository.save(new Car(11l, "奔馳A1", "紅色", "奔馳", 10000d));
carRepository.save(new Car(12l, "奔馳A2", "黑色", "奔馳", 20000d));
carRepository.save(new Car(13l, "奔馳A3", "黑色", "奔馳", 30000d));
carRepository.save(new Car(14l, "奔馳A4", "紅色", "奔馳", 40000d));
carRepository.save(new Car(15l, "奔馳A5", "紅色", "奔馳", 50000d));
}

2.8.6.4.4 劃分桶
GET /car_index/car/_search
{
"query": {
"bool": {
"should": [
{
"match_all": {}
}
]
}
},
"aggs": {
"group_by_bland": {
"terms": {
"field": "color"
}
}
}
}

????Spring Data ElasticSearch代碼實作如下:
@Test
public void testQuerySelfAggs() {
//查詢條件的構建器
NativeSearchQueryBuilder queryBuilder = new
NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery());
//資源過濾:1.查詢要包含的欄位、2.要排除的欄位
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{}, new String[]{"brand"}));
//添加聚合條件,聚合名為group_by_color,按照分詞:color進行聚合
queryBuilder.addAggregation(AggregationBuilders.terms("group_by_color").field("color"));
//執行查詢,把查詢結果直接轉為聚合page
AggregatedPage<Car> aggPage = (AggregatedPage<Car>) carRepository.search(queryBuilder.build());
//從所有的聚合中獲取對應名稱的聚合
Terms agg = (Terms) aggPage.getAggregation("group_by_color");
//從聚合的結果中獲取所有的桶資訊
for (Terms.Bucket bucket : agg.getBuckets()) {
System.out.println(bucket.getKeyAsString()+" "+bucket.getDocCount());
}
}


2.8.6.4.5 桶內度量
GET /car_index/car/_search
{
"query": {
"bool": {
"should": [
{
"match_all": {}
}
]
}
},
"aggs": {
"group_by_color": {
"terms": {
"field": "color"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
其實就是嵌套的聚合

????Spring Data ElasticSearch代碼實作如下:
@Test
public void testQuerySubAggs() {
//查詢條件的構建器
NativeSearchQueryBuilder queryBuilder = new
NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery());
//資源過濾:1.查詢要包含的欄位、2.要排除的欄位
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{}, new String[]{"brand"}));
//添加聚合條件,聚合名為group_by_color,按照分詞:color進行聚合
queryBuilder.addAggregation(
AggregationBuilders.terms("group_by_color").field("color")
.subAggregation(AggregationBuilders.avg("avg_price").field("price"))//嵌套子聚合
);
//執行查詢,把查詢結果直接轉為聚合page
AggregatedPage<Car> aggPage = (AggregatedPage<Car>) carRepository.search(queryBuilder.build());
//從所有的聚合中獲取對應名稱的聚合
Terms agg = (Terms) aggPage.getAggregation("group_by_color");
//從聚合的結果中獲取所有的桶資訊
for (Terms.Bucket bucket : agg.getBuckets()) {
System.out.println(bucket.getKeyAsString()+" "+bucket.getDocCount());
Avg avg_price = (Avg) bucket.getAggregations().asMap().get("avg_price");
System.out.println(avg_price.getValue());
System.out.println("---------------");
}
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/302994.html
標籤:其他
