預備知識-Restful
起源
在沒有前后端分離概念之前,一個網站的完成總是“all in one”,在這個階段,頁面、數據、渲染全部在服務端完成,這樣做的最大的弊端是后期維護,擴展極其痛苦,開發人員必須同時具備前后端知識。於是后來慢慢的興起了前后端分離的思想:即后端負責數據編造,而前端則負責數據渲染,前端靜態頁面調用指定 api 獲取到有固定格式的數據,再將數據展示出來,這樣呈現給用戶的就是一個”動態“的過程。
而關於 api 這部分的設計則成了一個問題。如何設計出一個便於理解,容易使用的 api 則成了一個問題,而所謂的 RESTful 就是用來規范我們的 API 的一種約束。
REST
作為 REST,其實是 Representational State Transfer(表象層狀態轉變)三個單詞的縮寫,它由 Roy Fielding(Fielding 是 HTTP 協議(1.0 版和 1.1 版)的主要設計者、Apache 服務器軟件的作者之一、Apache 基金會的第一任主席)於2000 年論文中提出,他在論文中提到:"我這篇文章的寫作目的,就是想在符合架構原理的前提下,理解和評估以網絡為基礎的應用軟件的架構設計,得到一個功能強、性能好、適宜通信的架構。REST 指的是一組架構約束條件和原則。"如果一個架構符合 REST 的約束條件和原則,我們就稱它為 RESTful 架構。
要理解 RESTful 架構,最好的方法就是去理解 Representational StateTransfer 這個詞組到底是什么意思,它的每一個詞代表了什么涵義。如果你把這個名稱搞懂了,也就不難體會 REST 是一種什么樣的設計。
資源(Resources)
REST 的名稱"表現層狀態轉化"中,省略了主語。"表現層"其實指的是"資源"(Resources)的"表現層"。
所謂"資源",就是網絡上的一個實體,或者說是網絡上的一個具體信息。它可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的實在。
要讓一個資源可以被識別,需要有個唯一標識,在 Web 中這個唯一標識就是URI(Uniform Resource Identifier)。
URI 既可以看成是資源的地址,也可以看成是資源的名稱。如果某些信息沒有使用 URI 來表示,那它就不能算是一個資源,只能算是資源的一些信息而已。
你可以用一個 URI(統一資源定位符)指向它,每種資源對應一個特定的 URI。
要獲取這個資源,訪問它的 URI 就可以,因此 URI 就成了每一個資源的地址或獨一無二的識別符。
所謂"上網",就是與互聯網上一系列的"資源"互動,調用它的 URI。
URI 設計技巧
1、使用 _ 或 - 來讓 URI 可讀性更好
曾經 Web 上的 URI 都是冰冷的數字或者無意義的字符串,但現在越來越多的網站使用_或-來分隔一些單詞,讓 URI 看上去更為人性化。例如國內比較出名的開源中國社區,它上面的新聞地址就采用這種風格,如
http://www.oschina.net/news/38119/oschina-translate-reward-plan。
2、使用 / 來表示資源的層級關系
例如上述/git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08就表示了一個多級的資源,指的是 git 用戶的 git 項目的某次提交記錄,又例如/orders/2012/10 可以用來表示 2012 年 10 月的訂單記錄。
3、使用 ? 用來過濾資源
很多人只是把?簡單的當做是參數的傳遞,很容易造成 URI 過於復雜、難以理解。可以把?用於對資源的過濾,例如/git/git/pulls 用來表示 git 項目的所有推入請求,而/pulls?state=closed 用來表示 git 項目中已經關閉的推入請求,這種 URL 通常對應的是一些特定條件的查詢結果或算法運算結果。
, 或; 可以用來表示同級資源的關系
有時候我們需要表示同級資源的關系時,可以使用,或;來進行分割。例如哪天github 可以比較某個文件在隨意兩次提交記錄之間的差異,或許可以使用/git/git
/block-sha1/sha1.h/compare/e3af72cdafab5993d18fae056f87e1d675913d08;bd63e61bdf38e872d5215c07b264dcc16e4febca 作為 URI。不過,現在 github 是使用…來做這個事情的,例如/git/git/compare/master…next。
4、URI 不應該包含動詞
因為"資源"表示一種實體,所以應該是名詞,URI 不應該有動詞,動詞應該放在 HTTP 協議中。
舉例來說,某個 URI 是/posts/show/1,其中 show 是動詞,這個 URI 就設計錯了,正確的寫法應該是/posts/1,然后用 GET 方法表示 show。
如果某些動作是 HTTP 動詞表示不了的,你就應該把動作做成一種資源。比如網上匯款,從賬戶 1向賬戶 2匯款 500 元,錯誤的 URI 是:
POST /accounts/1/transfer/500/to/2
正確的寫法是把動詞 transfer 改成名詞 transaction,資源不能是動詞,但是可以是一種服務。
5、URI 中不宜加入版本號
例如:
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo
因為不同的版本,可以理解成同一種資源的不同表現形式,所以應該采用同一個 URI。版本號可以在 HTTP 請求頭信息的 Accept 字段中進行區分。
表現層(Representation)
"資源"是一種信息實體,它可以有多種外在表現形式。我們把"資源"具體呈現出來的形式,叫做它的"表現層"(Representation)。
比如,文本可以用 txt 格式表現,也可以用 HTML 格式、XML 格式、JSON 格式表現,甚至可以采用二進制格式;圖片可以用 JPG 格式表現,也可以用 PNG格式表現。
URI 只代表資源的實體,不代表它的形式。嚴格地說,有些網址最后的".html"后綴名是不必要的,因為這個后綴名表示格式,屬於"表現層"范疇,而URI 應該只代表"資源"的位置。它的具體表現形式,應該在 HTTP 請求的頭信息中用 Accept 和 Content-Type 字段指定,這兩個字段才是對"表現層"的描述。
狀態轉化(State Transfer)
訪問一個網站,就代表了客戶端和服務器的一個互動過程。在這個過程中,勢必涉及到數據和狀態的變化。
互聯網通信協議 HTTP 協議,是一個無狀態協議。這意味着,所有的狀態都保存在服務器端。因此,如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生"狀態轉化"(State Transfer)。而這種轉化是建立在表現層之上的,所以就是"表現層狀態轉化"。
客戶端用到的手段,目前來說只能是 HTTP 協議。具體來說,就是 HTTP 協議里面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET 用來獲取資源,POST 用來新建資源(也可以用於更新資源),PUT用來更新資源,DELETE 用來刪除資源。GET、PUT 和 DELETE 請求都是冪等的,無論對資源操作多少次,結果總是一樣的, POST 不是冪等的。
什么是 RESTful 架構
綜合上面的解釋,我們總結一下什么是 RESTful 架構:
1.架構里,每一個 URI 代表一種資源;
2.客戶端和服務器之間,傳遞這種資源的某種表現層;
3.客戶端通過四個 HTTP 動詞(get、post、put、delete),對服務器端資源進行操作,實現”表現層狀態轉化”。
注意:REST 架構風格並不是綁定在 HTTP 上,只不過目前 HTTP 是唯一與 REST相關的實例。所以我們這里描述的 REST 也是通過 HTTP 實現的 REST。
辨析 URI、URL、URN
RFC 3986 中是這樣說的:
A Uniform Resource Identifier (URI) 是一個緊湊的字符串用來標示抽象或物理資源。一個 URI 可以進一步被分為定位符、名字或兩者都是. 術語“Uniform Resource Locator”(URL) 是 URI 的子集, 除了確定一個資源,還提供一種定位該資源的主要訪問機制。
所以,URI = Universal Resource Identifier 統一資源標志符,包含 URL和URN,支持的協議有 http、https、ftp、mailto、magnet、telnet、data、file、nfs、gopher、ldap 等,java 還大量使用了一些非標准的定制模式,如 rmi,jar、jndi 和 doc,來實現各種不同用途。
URL = Universal Resource Locator 統一資源定位符,URL 唯一地標識一個資源在 Internet 上的位置。不管用什么方法表示,只要能定位一個資源,就叫URL。
URN = Universal Resource Name 統一資源名稱,URN 它命名資源但不指定如何定位資源,比如:只告訴你一個人的姓名,不告訴你這個人在哪。
對於一個資源來說,URN 就好比他的名字,而 URL 就好比是資源的街道住址。
換句話說,URN 標識了一個資源項目,而 URL 則提供了一種找到他的方法。
比如同時指定基本的獲取機制和網絡位置。舉個例子,
http://example.org/wiki/Main_Page,指向了一個被識別為/wike/Main_Page的資源,這個資源的表現形式是 HTML 和相關的代碼。而獲取這個資源的方法是在網絡中從一個名為 example.org 的主機上,通過 HTTP( Hypertext TransferProtocol)獲得。
而 URN 則是一種在特定的名稱空間中通過通過名字來標識資源的 URI。當討論一種資源而不需要知道它的位置或者如何去獲得它的時候,就可以使用 URN。
例如,在 International Standard Book Number (ISBN)系統中,* ISBN
0-486-27557-4 用來指定莎士比亞的作品《羅密歐與朱麗葉》的一個特定版本。
指示這一版本的 URN 是 urn:isbn:0-486-27557-4*,但是如果想要獲得這個版本的書,就需要知道它的位置,這樣就必須知道它的 URL。
什么是 ELK
為什么需要 ELK
官網的說法:Elasticsearch 是一個開源的分布式 RESTful 搜索和分析引擎,能夠解決越來越多不同的應用場景。
看一個應用場景,常見的 WEB 應用日志分析。一般我們會怎么做?
登錄到每台服務器上,直接在日志文件中 grep、awk 就可以獲得自己想要的信息。但在規模較大的場景中,此方法效率低下,面臨問題包括日志量太大如何歸檔、文本搜索太慢怎么辦、如何多維度查詢。
這個時候我們希望集中化的日志管理,所有服務器上的日志收集匯總。常見解決思路是建立集中式日志收集系統,將所有節點上的日志統一收集,管理,訪問。
這樣對於大型系統來說,都是一個分布式部署的架構,不同的服務模塊部署在不同的服務器上,問題出現時,大部分情況需要根據問題暴露的關鍵信息,定位到具體的服務器和服務模塊,構建一套集中式日志系統,可以提高定位問題的效率。
一個完整的集中式日志系統,需要包含以下幾個主要特點:
收集-能夠采集多種來源的日志數據
傳輸-能夠穩定的把日志數據傳輸到中央系統
存儲-如何存儲日志數據分析-可以支持
UI 分析警告-能夠提供錯誤報告,監控機制
ELK就是這樣一整套解決方案,並且都是開源軟件,之間互相配合使用,完美銜接,高效的滿足了很多場合的應用,而不僅僅是日志分析。
什么是 ELK
ELK是三個開源軟件的縮寫,分別表示:Elasticsearch , Logstash, Kibana , 它們都是開源軟件。
Elasticsearch是個開源分布式搜索引擎,提供搜集、分析、存儲數據三大功能。它的特點有:分布式,零配置,自動發現,索引自動分片,索引副本機制,restful風格接口,多數據源,自動搜索負載等。
Logstash 主要是用來日志的搜集、分析、過濾日志的工具,支持大量的數據獲取方式。一般工作方式為 c/s 架構,client端安裝在需要收集日志的主機上,server端負責將收到的各節點日志進行過濾、修改等操作在一並發往 elasticsearch上去。
Kibana 也是一個開源和免費的工具,Kibana 可以為 Logstash 和
ElasticSearch 提供的日志分析友好的 Web 界面,可以幫助匯總、分析和搜索重要數據日志。
新增了一個 Beats 系列組件,它是一個輕量級的日志收集處理工具(Agent),Beats 占用資源少,適合於在各個服務器上搜集日志或信息后傳輸給 Logstash。
加入 Beats 系列組件后,官方名稱就變為了 Elastic Stack,產品如下:
安裝,啟動和 HelloWorld
環境和安裝
運行環境為 Linux,演示服務器操作系統版本情況如下:
因為 Elastic Stack中主要組件都是用 Java 寫的,所以操作系統上還應該安裝好 Java,本次以 Elasticsearch 7 版本為主,所以,需要安裝 JDK1.8 以上。
而 Elastic Stack系列產品我們可以到 Elastic的官網上去下載:
https://www.elastic.co/cn/downloads
選擇 7.7.0版本為本次版本,從具體的下載頁面可以看到 Elastic Stack支持各種形式的安裝。
選擇以免安裝的壓縮包的形式。下載后上傳到服務器解壓縮即可運行。
運行
Elasticsearch 默認不允許用 root用戶運行,會報錯,而且從服務器安全的角度來說,也不應該以 root用戶來做日常工作,因此我們新建一個用戶 elk並以elk用戶登錄。
Elasticsearch
用 tar -xvf命令解壓壓縮包elasticsearch-7.7.0-linux-x86_64.tar.gzip后進入elasticsearch-7.7.0文件夾中的 bin文件夾,並執行命令./elasticsearch。
待啟動完成.....
輸入 curl http://localhost:9200
顯示:
表示Elasticsearch運行成功了,我們試着在本地瀏覽器進行訪問
卻顯示“拒絕了我們的連接請求”,因此我們還需要配置一下 Elasticsearch以允許我們進行外網訪問,進入 elasticsearch-7.7.0下的 config目錄,編輯elasticsearch.yml文件,刪除 network.host前的#字符,並寫入服務器的地址並保存。
再次運行,但是這次卻出了錯
從錯誤提示我們可以知道,還需對 Elasticsearch配置做適當修改,重新編輯elasticsearch.yml文件,並做如下修改:
再次啟動,並在瀏覽器中訪問
顯示 Elasticsearch運行並訪問成功!
我們知道,Elasticsearch提供的是 restful風格接口,我們用瀏覽器訪問不是很方便,除非我們自行編程訪問 restful接口。這個時候,就可以用上 Kibana 了,它已經為我們提供了友好的 Web 界面,方便我們后面對 Elasticsearch的學習。
接下來我們安裝運行 Kibana。
Kibana
同樣用 tar -xvf命令解壓壓縮包,進入解壓后的目錄。為了方便我們的訪問和連接 Elasticsearch,也需要進入 Kibana的 config 目錄對 Kibana 進行配置。
然后進入 Kibana的 bin 目錄運行./kibana,kibana因為是用 node.js 編寫的,所以啟動和運行較慢,需要等待一段時間:
從提示我們可以看出,kibana的訪問端口是 5601,我們依然在本地瀏覽器中訪問:
等候幾分鍾以后,就會進入 kibana的主界面
TIPS:如何檢測系統中是否啟動了 kibana?
我們一般會用ps -ef來查詢某個應用是否在 Linux系統中啟動,比如Elasticsearch,
我們用 ps -ef|grep java 或者 ps -ef|grep elasticsearch均可
但是當我們嘗試 ps -ef|grep kibana,卻是不行的
因為 kibana 是 node 寫的,所以 kibana 運行的時候是運行在 node 里面,我們要查詢的話,只能 ps -ef|grep node
或者使用 netstat -tunlp|grep 5601
因為我們的kibana開放的端口是5601,所以看到5601端口被 Listen (監聽),說明 kibana啟動成功。
附:netstat參數說明:
-t (tcp)僅顯示 tcp相關選項
-u (udp)僅顯示 udp相關選項
-n 拒絕顯示別名,能顯示數字的全部轉化成數字。
-l 僅列出有在 Listen (監聽) 的服務狀態
-p 顯示建立相關鏈接的程序名
更多 netstat的相關說明,自行查閱 Linux手冊
HelloWorld
點擊面板中的“Dev Tools”按鈕,進入 Dev 工具,開始我們的 Elasticsearch初次訪問之旅。
創建索引
在左邊命令窗口中輸入 put enjoy_test,並點擊相關按鈕 ,於是看到右
邊的結果窗口中 es給我們返回了處理結果,表示我們在 es中創建索引成功。
查看索引
要查看我們剛剛創建的索引,執行“get enjoy_test”
添加文檔
往這個索引中添加文檔,執行:
PUT /enjoy_test/_doc/1
{
"msg":"Hello World!"
}
查看文檔
查詢我們剛剛加入的文檔,執行“get /enjoy_test/_doc/1”
Elasticsearch 基本原理和概念
從上面的例子中,我們看到了很多熟悉又陌生的概念,比如索引、文檔等等。這些概念和我們平時看到的數據庫里的比如索引有什么區別呢?
舉個例子,現在我們要保存唐宋詩詞,數據庫中我們們會怎么設計?詩詞表我們可能的設計如下:
朝代 |
作者 |
詩詞年代 |
標題 |
詩詞全文 |
唐 |
李白 |
|
靜夜思 |
床前明月光,疑是地上霜。舉頭望明月,低頭思故鄉。 |
宋 |
李清照 |
|
如夢令 |
常記溪亭日暮,沉醉不知歸路,興盡晚回舟,誤入藕花深處。爭渡,爭渡,驚起一灘鷗鷺。 |
…. |
…. |
… |
…. |
……. |
要根據朝代或者作者尋找詩,都很簡單,比如“select 詩詞全文 from 詩詞表where作者=‘李白’”,如果數據很多,查詢速度很慢,怎么辦?我們可以在對應的查詢字段上建立索引加速查詢。
但是如果我們現在有個需求:要求找到包含“望”字的詩詞怎么辦?用
“select 詩詞全文 from 詩詞表 where 詩詞全文 like‘%望%’”,這個意味着
要掃描庫中的詩詞全文字段,逐條比對,找出所有包含關鍵詞“望”字的記錄,。
基本上,數據庫中一般的 SQL 優化手段都是用不上的。數量少,大概性能還能接受,如果數據量稍微大點,就完全無法接受了,更何況在互聯網這種海量數據的情況下呢?
怎么解決這個問題呢,用倒排索引。
倒排索引 Inverted index
比如現在有:
蜀道難(唐)李白 蜀道之難難於上青天,側身西望長咨嗟。
靜夜思(唐)李白 舉頭望明月,低頭思故鄉。
春台望(唐)李隆基 暇景屬三春,高台聊四望。
鶴沖天(宋)柳永 黃金榜上,偶失龍頭望。明代暫遺賢,如何向?未遂風雲便,爭不恣狂盪。何須論得喪?才子詞人,自是白衣卿相。煙花巷陌,依約丹青屏障。
幸有意中人,堪尋訪。且恁偎紅翠,風流事,平生暢。青春都一餉。忍把浮名,換了淺斟低唱!
都有望字,於是我們可以這么保存
序號 |
關鍵字 |
蜀道難 |
靜夜思 |
春台望 |
鶴沖天 |
1 |
望 |
有 |
有 |
有 |
有 |
|
|
|
|
|
如果查哪個詩詞中包含上,怎么辦,上述的表格可以繼續填入新的記錄
序號 |
關鍵字 |
蜀道難 |
靜夜思 |
春台望 |
鶴沖天 |
1 |
望 |
有 |
有 |
有 |
有 |
2 |
上 |
有 |
|
|
有 |
其實,上述詩詞的中每個字都可以作為關鍵字,然后建立關鍵字和文檔之間的對應關系,也就是標識關鍵字被哪些文檔包含。
所以,倒排索引就是,將文檔中包含的關鍵字全部提取處理,然后再將關鍵字和文檔之間的對應關系保存起來,最后再對關鍵字本身做索引排序。用戶在檢索某一個關鍵字是,先對關鍵字的索引進行查找,再通過關鍵字與文檔的對應關系找到所在文檔。
在存儲在關系型數據庫中的數據,需要我們事先分析將數據拆分為不同的字段,而在 es 這類的存儲中,需要應用程序根據規則自動提取關鍵字,並形成對應關系。
這些預先提取的關鍵字,在全文檢索領域一般被稱為 term(詞項),文檔的詞項提取在 es 中被稱為文檔分析,這是全文檢索很核心的過程,必須要區分哪些是詞項,哪些不是,比如很多場景下,apple和 apples 是同一個東西,望和看其實是同一個動作。
Elasticsearch 基本概念
Elasticsearch 中比較關鍵的基本概念有索引、文檔、映射、映射類型、文檔字段概念,為了方便理解,可以和關系數據庫中的相關概念進行個比對:
Elasticsearch 索引
Elasticsearch 索引是映射類型的容器。一個 Elasticsearch 索引非常像關系型世界的數據庫,是獨立的大量文檔集合。
當然在底層,肯定用到了倒排索引,最基本的結構就是“keyword”和“PostingList”,Posting list就是一個 int的數組,存儲了所有符合某個 term的文檔 id。
另外,這個倒排索引相比特定詞項出現過的文檔列表,會包含更多其它信息。
它會保存每一個詞項出現過的文檔總數,在對應的文檔中一個具體詞項出現的總次數,詞項在文檔中的順序,每個文檔的長度,所有文檔的平均長度等等相關信息。
文檔 (Document)
文檔是 es 中所有可搜索數據的最小單位,比如日志文件中的日志項、一部電影的具體信息等等。
文檔會被序列化 JSON格式保存到 ElasticSearch 中,JSON 對象由字段組成,每個字段都有對象的字段類型(字符串,數值,布爾,日期,二進制,范圍類型)。
同時每個文檔都有一個 Unique ID,可以自己指定 ID,或者通過 ElasticSearch 自動生成。
所以嚴格來說,es 中存儲的文檔是一種半結構化的數據。
映射
映射(mapping)定義了每個字段的類型、字段所使用的分詞器等。
get /enjoy_test/_mapping
可以顯式映射,由我們在索引映射中進行預先定義;也可以動態映射,在添加文檔的時候,由 es 自動添加到索引,這個過程不需要事先在索引進行字段數據類型匹配等等,es 會自己推斷數據類型。
既然說到了字段類型,當然就離不開字段的數據類型了。
文檔字段
文檔中的一個字段 field就相當於關系型數據庫中的一列 column,那么它肯定有數據類型,es 提供的數據類型包括至少有:
數據類型
核心數據類型
# 字符串類型:string,字符串類還可被分為 text和 keyword 類型,如果我們讓 es自動映射數據,那么 es 會把字符串定義為 text,並且還加了一個 keyword類型字段。
text文本數據類型,用於索引全文值的字段。使用文本數據類型的字段,它們會被分詞,在索引之前將字符串轉換為單個術語的列表(倒排索引),分詞過程允許 ES 搜索每個全文字段中的單個單詞。什么情況適合使用 text,只要不具備唯一性的字符串一般都可以使用 text。
keyword,關鍵字數據類型,用於索引結構化內容的字段。使用 keyword 類型的字段,其不會被分析,給什么值就原封不動地按照這個值索引,所以關鍵字字段只能按其確切值進行搜索。什么情況下使用 keyword,具有唯一性的字符串,例如:電子郵件地址、MAC 地址、身份證號、狀態代碼...等等。
# 數字型數據類型:long、integer、short、byte、double、float
# 日期類型:date
# 布爾類型:boolean
復雜數據類型
# 數組:無需專門的數據類型
# 對象數據類型:單獨的 JSON對象
# 嵌套數據類型:nested,關於 JSON對象的數組
地理數據類型:
# 地理點數據類型
# 地理形狀數據類型
專門數據類型:
# IPv4 數據類型
# 單詞計數數據類型 token_count
我們結合前面的映射來看看:
創建一個新的索引:put /open-soft
顯式映射:
put /open-soft/_mapping
{
"properties" : {
"corp" : {
"type" : "text"
},
"lang" : {
"type" : "text"
},
"name" : {
"type" : "text"
}
索引或者說入庫一個文檔,注意這個文檔的字段,比我們顯示映射的字段要多個 star字段:
put /open-soft/_doc/1
{
"name": "Apache Hadoop",
"lang": "Java",
"corp": "Apache",
"stars":200
}
通過 get /open-soft/_mapping,我們可以看到 es 自動幫我們新增了 stars這個字段。
修改映射,增加一個新的字段:
put /open-soft/_mapping
{
"properties" : {
"year" : {
"type" : "integer"
}
}
}
數組
不需要特殊配置,一個字段如果被配置為基本數據類型,就是天生支持數組類型的。任何字段都可以有 0個或多個值,但是在一個數組中數據類型必須一樣。
比如:
put /open-soft/_doc/2
{
"name": ["Apache Activemq","Activemq Artemis"],
"lang": "Java",
"corp": "Apache",
"stars":[500,200]
}
是沒問題的,但是如果:
put /open-soft/_doc/3
{
"name": ["Apache Kafka"],
"lang": "Java",
"corp": "Apache",
"stars":[500,"kafka"]
}
則會出錯。
對象
JSON文檔是有層次結構的,一個文檔可能包含其他文檔,如果一個文檔包含其他文檔,那么該文檔值是對象類型,其數據類型是對象。當然 ElasticSearch中是沒有所謂對象類型的,比如:
put /open-soft/_doc/object
{
"name": ["Apache ShardingSphere"],
"lang": "Java",
"corp": "JingDong",
"stars":400,
"address":{
"city":"BeiJing",
"country":"亦庄"
}
}
對象類型可以在定義索引的映射關系時進行指定。
多數據類型
如果說數組允許你使用同一個設置索引多項數據,那么多數據類型允許使用不同的設置,對同一項數據索引多次。帶來的好處就是可以同一文本有多種不同的索引方式,比如一個字符串類型的字段,可以使用 text類型做全文檢索,使用keyword 類型做聚合和排序。我們可以看到 es 的動態映射生成的字段類型里,往往字符串類型都使用了多數據類型。當然,我們一樣也可以自己定義:
put /open-soft/_mapping
{
"properties" : {
"name" : {
"type" : "text",
"fields":{
"raw":{
"type" : "keyword"
},
"length":{
"type" : "token_count",
"analyzer":"standard"
}
}
}
}
}
在上面的代碼里,我們使用"fields"就把 name字段擴充為多字段類型,為name新增了兩個子字段 raw和 length,raw設置類型為 keyword,length 設置類型為 token_count,告訴 es 這個字段在保存還需要做詞頻統計。
通過 fields字段設置的子字段 raw 和 length,在我們添加文檔時,並不需要單獨設置值,他們 name共享相同的值,只是 es 會以不同的方式處理字段值。
同樣在檢索文檔的時候,它們也不會顯示在結果中,所以它們一般都是在檢索中以查詢條件的形式出現,以減少檢索時的性能開銷。
字段參數
在上面的代碼里出現了analyzer這個詞,這是什么?這個叫字段參數,和 type一樣,可以用來對字段進行配置。常用的字段參數和作用如下:
analyzer
指定分詞器。elasticsearch是一款支持全文檢索的分布式存儲系統,對於 text類型的字段,首先會使用分詞器進行分詞,然后將分詞后的詞根一個一個存儲在倒排索引中,后續查詢主要是針對詞根的搜索。
analyzer該參數可以在每個查詢、每個字段、每個索引中使用,其優先級如下(越靠前越優先):
1、字段上定義的分詞器
2、索引配置中定義的分詞器
3、默認分詞器(standard)
normalizer
規范化,主要針對 keyword 類型,在索引該字段或查詢字段之前,可以先對原始數據進行一些簡單的處理,然后再將處理后的結果當成一個詞根存入倒排索引中,默認為 null,比如:
PUT index
{
"settings": {
"analysis": {
"normalizer": {
"my_normalizer": { // 1
"type": "custom",
"char_filter": [],
"filter": ["lowercase", "asciifolding"] // 2
}
}
}
},
"mappings": {
"_doc": {
"properties": {
"foo": {
"type": "keyword",
"normalizer": "my_normalizer" // 3
}
}
}
}
}
代碼 1:首先在 settings中的 analysis 屬性中定義 normalizer。
代碼 2:設置標准化過濾器,示例中的處理器為小寫、asciifolding。
代碼 3:在定義映射時,如果字段類型為 keyword,可以使用 normalizer
引用定義好的 normalizer
boost
權重值,可以提升在查詢時的權重,對查詢相關性有直接的影響,其默認值為1.0。其影響范圍為詞根查詢(team query),對前綴、范圍查詢。5.0 版本后已廢止。
coerce
數據不總是我們想要的,由於在轉換 JSON body 為真正 JSON 的時候,整型數字5有可能會被寫成字符串"5"或者浮點數 5.0,這個參數可以將數值不合法的部分去除。默認為 true。
例如:將字符串會被強制轉換為整數、浮點數被強制轉換為整數。
例如存在如下字段類型:
"number_one": {
"type": "integer"
}
聲明 number_one字段的類型為數字類型,那是否允許接收“6”字符串形式的數據呢?因為在 JSON中,“6”用來賦給 int類型的字段,也是能接受的,默認 coerce 為 true,表示允許這種賦值,但如果 coerce 設置為 false,此時 es只能接受不帶雙引號的數字,如果在 coerce=false 時,將“6”賦值給 number_one時會拋出類型不匹配異常。
copy_to
copy_to參數允許您創建自定義的_all字段。換句話說,多個字段的值可以復制到一個字段中。
例如,first_name和 last_name 字段可以復制到 full_name 字段如下:
PUT my_index
{
"mappings": {
"_doc": {
"properties": {
"first_name": {
"type": "text",
"copy_to": "full_name"
},
"last_name": {
"type": "text",
"copy_to": "full_name"
表示字段 full_name的值來自 first_name + last_name。
關於 copy_to重點說明:
1、字段的復制是原始值。
2、同一個字段可以復制到多個字段,寫法如下:“copy_to”: [ “field_1”,
“field_2”]
doc_values
Doc values 的存在是因為倒排索引只對某些操作是高效的。倒排索引的優勢在於查找包含某個項的文檔,而對於從另外一個方向的相反操作並不高效,即:確定哪些項是否存在單個文檔里,聚合需要這種次級的訪問模式。
對於以下倒排索引:
如果我們想要獲得所有包含 檔的詞的完整列表,倒排索引是根據項來排序的,所以我們首先在詞 ,然后掃描所有列,找到包含 brown 的文檔。我們可以快速 和 包含 brown 這個 token。
然后,對於聚合部分,我們需要找到 Doc_1 和 Doc_2 里所有唯一的詞項。用倒排索引做這件事情代價很高:我們會迭代索引里的每個詞項並收集 Doc_1 和 Doc_2 列里面 token。這很慢而且難以擴展:隨着詞項和文檔的數量增加,執行時間也會增加。
Doc values 通過轉置兩者間的關系來解決這個問題。倒排索引將詞項映射到包含它們的文檔,doc values 將文檔映射到它們包含的詞項:
當數據被轉置之后,想要收集到 Doc_1 和 Doc_2 的唯一 token 會非常容易。獲得每個文檔行,獲取所有的詞項,然后求兩個集合的並集。
doc_values缺省是 true,即是開啟的,並且只適用於非 text類型的字段。
dynamic
是否允許動態的隱式增加字段。在執行 index api 或更新文檔 API 時,對於_source字段中包含一些原先未定義的字段采取的措施,根據 dynamic 的取值,會進行不同的操作:
true,默認值,表示新的字段會加入到類型映射中。
false,新的字段會被忽略,即不會存入_souce字段中,即不會存儲新字段,也無法通過新字段進行查詢。
strict,會顯示拋出異常,需要新使用 put mapping api 先顯示增加字段映射。
enabled
是否建立索引,默認情況下為 true,es 會嘗試為你索引所有的字段,但有時候某些類型的字段,無需建立索引,只是用來存儲數據即可。也就是說,
ELasticseaech默認會索引所有的字段,enabled設為 false的字段,elasicsearch 會跳過字段內容,該字段只能從_source 中獲取,但是不可搜。只有映射類型(type)和object 類型的字段可以設置 enabled屬性。
eager_global_ordinals
表示是否提前加載全局順序號。Global ordinals 是一個建立在 doc values 和fielddata基礎上的數據結構, 它為每一個精確詞按照字母順序維護遞增的編號。每一個精確詞都有一個獨一無二的編號 並且 精確詞 A 小於精確詞 B 的編號.Global ordinals 只支持 keyword 和 text 型字段,在 keyword 字段中, 默認是啟用的 而在 text 型字段中 只有 fielddata 和相關屬性開啟的狀態下才是可用的。
fielddata
為了解決排序與聚合,elasticsearch 提供了 doc_values 屬性來支持列式存儲,但doc_values 不支持 text 字段類型。因為 text 字段是需要先分析(分詞),會影響 doc_values 列式存儲的性能。
es 為了支持 text字段高效排序與聚合,引入了一種新的數據結構(fielddata),使用內存進行存儲。默認構建時機為第一次聚合查詢、排序操作時構建,主要存儲倒排索引中的詞根與文檔的映射關系,聚合,排序操作在內存中執行。因此fielddata需要消耗大量的 JVM 堆內存。一旦 fielddata加載到內存后,它將永久存在。
通常情況下,加載 fielddata 是一個昂貴的操作,故默認情況下,text 字段的字段默認是不開啟 fielddata機制。在使用 fielddata 之前請慎重考慮為什么要開啟fielddata。
format
在 JSON文檔中,日期表示為字符串。Elasticsearch 使用一組預先配置的格式來識別和解析這些字符串,並將其解析為 long類型的數值(毫秒),支持自定義格式,也有內置格式。
比如:
PUT my_index
{
"mappings": {
"_doc": {
"properties": {
"date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}}}}}
elasticsearch為我們內置了大量的格式,如下:
epoch_millis
時間戳,單位,毫秒,范圍受限於 Java Long.MIN_VALUE和 Long.MAX_VALUE。
epoch_second
時間戳,單位,秒,范圍受限於 Java的限制 Long.MIN_VALUE 並 Long.
MAX_VALUE 除以 1000(一秒中的毫秒數)。
date_optional_time或者 strict_date_optional_time
日期必填,時間可選,其支持的格式如下:
date-opt-time = date-element ['T' [time-element] [offset]]
date-element = std-date-element | ord-date-element | week-date-element
std-date-element = yyyy ['-' MM ['-' dd]]
ord-date-element = yyyy ['-' DDD]
week-date-element = xxxx '-W' ww ['-' e]
time-element = HH [minute-element] | [fraction]
minute-element = ':' mm [second-element] | [fraction]
second-element = ':' ss [fraction]
比如"yyyy-MM-dd"、"yyyyMMdd"、"yyyyMMddHHmmss"、
"yyyy-MM-ddTHH:mm:ss"、"yyyy-MM-ddTHH:mm:ss.SSS"、
"yyyy-MM-ddTHH:mm:ss.SSSZ"格式,不支持常用的"yyyy-MM-dd HH:mm:ss"等格式。
注意,"T"和"Z"是固定的字符。
tips:如果看到“strict_”前綴的日期格式要求,表示 date_optional_time 的嚴格級別,這個嚴格指的是年份、月份、天必須分別以 4位、2 位、2 位表示,不足兩位的話第一位需用 0補齊。
basic_date
其格式表達式為 :yyyyMMdd
basic_date_time
其格式表達式為:yyyyMMdd’T’HHmmss.SSSZ
basic_date_time_no_millis
其格式表達式為:yyyyMMdd’T’HHmmssZ
basic_ordinal_date
4位數的年 + 3 位(day of year),其格式字符串為 yyyyDDD
basic_ordinal_date_time
其格式字符串為 yyyyDDD’T’HHmmss.SSSZ
basic_ordinal_date_time_no_millis
其格式字符串為 yyyyDDD’T’HHmmssZ
basic_time
其格式字符串為 HHmmss.SSSZ
basic_time_no_millis
其格式字符串為 HHmmssZ
basic_t_time
其格式字符串為’T’HHmmss.SSSZ
basic_t_time_no_millis
其格式字符串為’T’HHmmssZ
basic_week_date
其格式字符串為 xxxx’W’wwe,4 為年 ,然后用’W’, 2 位 week of year(所在年里周序號)1位 day of week。
basic_week_date_time
其格式字符串為 xxxx’W’wwe’T’HH:mm:ss.SSSZ.
basic_week_date_time_no_millis
其格式字符串為 xxxx’W’wwe’T’HH:mm:ssZ.
date
其格式字符串為 yyyy-MM-dd
date_hour
其格式字符串為 yyyy-MM-dd’T’HH
date_hour_minute
其格式字符串為 yyyy-MM-dd’T’HH:mm
date_hour_minute_second
其格式字符串為 yyyy-MM-dd’T’HH:mm:ss
date_hour_minute_second_fraction
其格式字符串為 yyyy-MM-dd’T’HH:mm:ss.SSS
date_hour_minute_second_millis
其格式字符串為 yyyy-MM-dd’T’HH:mm:ss.SSS
date_time
其格式字符串為 yyyy-MM-dd’T’HH:mm:ss.SSS
date_time_no_millis
其格式字符串為 yyyy-MM-dd’T’HH:mm:ss
hour
其格式字符串為 HH
hour_minute
其格式字符串為 HH:mm
hour_minute_second
其格式字符串為 HH:mm:ss
hour_minute_second_fraction
其格式字符串為 HH:mm:ss.SSS
hour_minute_second_millis
其格式字符串為 HH:mm:ss.SSS
ordinal_date
其格式字符串為 yyyy-DDD,其中 DDD為 day of year。
ordinal_date_time
其格式字符串為 yyyy-DDD‘T’HH:mm:ss.SSSZZ,其中 DDD為 day of year。
ordinal_date_time_no_millis
其格式字符串為 yyyy-DDD‘T’HH:mm:ssZZ
time
其格式字符串為 HH:mm:ss.SSSZZ
time_no_millis
其格式字符串為 HH:mm:ssZZ
t_time
其格式字符串為’T’HH:mm:ss.SSSZZ
t_time_no_millis
其格式字符串為’T’HH:mm:ssZZ
week_date
其格式字符串為 xxxx-'W’ww-e,4 位年份,ww 表示 week of year,e 表示 dayof week。
week_date_time
其格式字符串為 xxxx-'W’ww-e’T’HH:mm:ss.SSSZZ
week_date_time_no_millis
其格式字符串為 xxxx-'W’ww-e’T’HH:mm:ssZZ
weekyear
其格式字符串為 xxxx
weekyear_week
其格式字符串為 xxxx-'W’ww,其中 ww 為 week of year。
weekyear_week_day
其格式字符串為 xxxx-'W’ww-e,其中 ww 為 week of year,e為 day of week。
year
其格式字符串為 yyyy
year_month
其格式字符串為 yyyy-MM
year_month_day
其格式字符串為 yyyy-MM-dd
ignore_above
ignore_above用於指定字段索引和存儲的長度最大值,超過最大值的會被忽略。
ignore_malformed
ignore_malformed可以忽略不規則數據,對於 login 字段,有人可能填寫的是date類型,也有人填寫的是郵件格式。給一個字段索引不合適的數據類型發生異常,導致整個文檔索引失敗。如果 ignore_malformed參數設為 true,異常會被忽
略,出異常的字段不會被索引,其它字段正常索引。
index
index 屬性指定字段是否索引,不索引也就不可搜索,取值可以為 true 或者false,缺省為 true。
index_options
index_options 控制索引時存儲哪些信息到倒排索引中,,用於搜索和突出顯示目的。
docs 只存儲文檔編號
freqs 存儲文檔編號和詞項頻率。
positions 文檔編號、詞項頻率、詞項的位置被存儲
offsets 文檔編號、詞項頻率、詞項的位置、詞項開始和結束的字符位置都被存儲。
fields
fields可以讓同一文本有多種不同的索引方式,比如一個 String 類型的字段,可以使用 text類型做全文檢索,使用 keyword 類型做聚合和排序。
norms
norms參數用於標准化文檔,以便查詢時計算文檔的相關性。norms 雖然對評分有用,但是會消耗較多的磁盤空間,如果不需要對某個字段進行評分,最好不要開啟 norms。
null_value
一般來說值為 null的字段不索引也不可以搜索,null_value參數可以讓值為null的字段顯式的可索引、可搜索。
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"status_code": {
"type": "keyword",
"null_value": "NULL"
}
}
}
}
}
GET my_index/_search
{
"query": {
"term": {
"status_code": "NULL"
}
}
}
文檔 1可以被搜索到,因為 status_code的值為 null,文檔 2 不可以被搜索到,因為 status_code為空數組,但是不是 null。
position_increment_gap
文本數組元素之間位置信息添加的額外值。
舉例,一個字段的值為數組類型:"names": [ "John Abraham", "Lincoln Smith"]為了區別第一個字段和第二個字段,Abraham和 Lincoln在索引中有一個間
距,默認是 100。例子如下,這是查詢”Abraham Lincoln”是查不到的:
PUT my_index/groups/1
{
"names": [ "John Abraham", "Lincoln Smith"]
}
GET my_index/groups/_search
{
"query": {
"match_phrase": {
"names": {
"query": "Abraham Lincoln"
}}}}
指定間距大於 10 0 可以查詢到:
GET my_index/groups/_search
{
"query": {
"match_phrase": {
"names": {
"query": "Abraham Lincoln",
"slop": 101
}}}}
想要調整這個值,在 mapping中通過 position_increment_gap 參數指定間距即可。
properties
Object或者 nested類型,下面還有嵌套類型,可以通過 properties 參數指定。
比如:
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"manager": {
"properties": {
"age": { "type": "integer" },
"name": { "type": "text" }
}
},
"employees": {
"type": "nested",
"properties": {
"age": { "type": "integer" },
"name": { "type": "text" }
}}}}}}
對應的文檔結構:
PUT my_index/my_type/1
{
"region": "US",
"manager": {
"name": "Alice White",
"age": 30
},
"employees": [
{
"name": "John Smith",
"age": 34
},
{
"name": "Peter Brown",
"age": 26
}
]
}
search_analyzer
通常,在索引時和搜索時應用相同的分析器,以確保查詢中的術語與反向索引中的術語具有相同的格式,如果想要在搜索時使用與存儲時不同的分詞器,則使用 search_analyzer屬性指定,通常用於 ES 實現即時搜索(edge_ngram)。
similarity
指定相似度算法,其可選值:
BM25
當前版本的默認值,使用 BM25算法。
classic
使用 TF/IDF算法,曾經是 es,lucene 的默認相似度算法。
boolean
一個簡單的布爾相似度,當不需要全文排序時使用,並且分數應該只基於查詢條件是否匹配。布爾相似度為術語提供了一個與它們的查詢boost相等的分數。
store
默認情況下,字段值被索引以使其可搜索,但它們不存儲。這意味着可以查詢字段,但無法檢索原始字段值。通常這並不重要。字段值已經是_source字段的一部分,該字段默認存儲。如果您只想檢索單個字段或幾個字段的值,而不是整個_source,那么這可以通過字段過濾上下文 source filting context來實現。
在某些情況下,存儲字段是有意義的。例如,如果您有一個包含標題、日期和非常大的內容字段的文檔,您可能只想檢索標題和日期,而不需要從大型_source字段中提取這些字段,可以將標題和日期字段的 store定義為 ture。
term_vector
Term vectors 包含分析過程產生的索引詞信息,包括:
索引詞列表
每個索引詞的位置(或順序)
索引詞在原始字符串中的原始位置中的開始和結束位置的偏移量。
term vectors 會被存儲,索引它可以作為一個特使的文檔返回。
term_vector可取值:
不存儲 term_vector信息,默認值。
yes
只存儲字段中的值。
with_positions
存儲字段中的值與位置信息。
with_offsets
存儲字段中的值、偏移量
with_positions_offsets
存儲字段中的值、位置、偏移量信息。
元字段 meta-fields
一個文檔根據我們定義的業務字段保存有數據之外,它還包含了元數據字段
(meta-fields)。元字段不需要用戶定義,在任一文檔中都存在,有點類似於數據庫的表結構數據。在名稱上有個顯著的特征,都是以下划線“_”開頭。
我們可以看看:get /open-soft/_doc/1
大體分為五種類型:身份(標識)元數據、索引元數據、文檔元數據、路由元數據以及其他類型的元數據,當然不是每個文檔這些元字段都有的。
身份(標識)元數據
_index:文檔所屬索引 , 自動被索引,可被查詢,聚合,排序使用,或者腳本里訪問
_type:文檔所屬類型,自動被索引,可被查詢,聚合,排序使用,或者腳本里訪問
_id:文檔的唯一標識,建索引時候傳入 ,不被索引,可通過_uid被查詢,腳本里使用,不能參與聚合或排序
_uid:由_type和_id字段組成,自動被索引 ,可被查詢,聚合,排序使用,或者腳本里訪問,6.0.0版本后已廢止。
索引元數據
_all:自動組合所有的字段值,以空格分割,可以指定分器詞索引,但是整個值不被存儲,所以此字段僅僅能被搜索,不能獲取到具體的值。6.0.0版本后已廢止。
_field_names:索引了每個字段的名字,可以包含 null值,可以通過 exists查詢或 missing查詢方法來校驗特定的字段
文檔元數據
_source : 一個 doc的原生的 json 數據,不會被索引,用於獲取提取字段值,啟動此字段,索引體積會變大,如果既想使用此字段又想兼顧索引體積,可以開啟索引壓縮。
_source是可以被禁用的,不過禁用之后部分功能不再支持,這些功能包括:部分 update api、運行時高亮搜索結果
索引重建、修改 mapping以及分詞、索引升級
debug查詢或者聚合語句
索引自動修復
_size:整個_source 字段的字節數大小,需要單獨安裝一個 mapper-size插件才能展示。
路由元數據
_routing:一個 doc可以被路由到指定的 shard上。
_meta:一般用來存儲應用相關的元信息。
其他
例如:
put /open-soft/_mapping
{
"_meta": {
"class": "cn.enjoyedu.User",
"version": {"min": "1.0", "max": "1.3"}
}
}
管理 Elasticsearch 索引和文檔
在 es 中,索引和文檔是 REST 接口操作的最基本資源,所以對索引和文檔的管理也是我們必須要知道的。索引一般是以索引名稱出現在 REST請求操作的資源路徑上,而文檔是以文檔 ID 為標識出現在資源路徑上。映射類型_doc 也可以認為是一種資源,但在 es7 中廢除了映射類型,所以可以_doc 也視為一種接口。
索引的管理
在前面的學習中我們已經知道,GET 用來獲取資源,PUT 用來更新資源,DELETE用來刪除資源。所以對索引,GET用來查看索引,PUT用來創建索引,DELETE用來刪除索引,還有一個 HEAD 請求,用來檢驗索引是否存在。除此之外,對索引的管理還有
列出所有索引
GET /_cat/indices?v
關閉索引和打開
POST /open-soft/_close、、
除了刪除索引,還可以選擇關閉它們。如果關閉了一個索引,就無法通過Elasticsearch來讀取和寫人其中的數據,直到再次打開它。
在現實世界中,最好永久地保存應用日志,以防要查看很久之前的信息。另一方面,在 Elasticsearch 中存放大量數據需要增加資源。對於這種使用案例,關閉舊的索引非常有意義。你可能並不需要那些數據,但是也不想刪除它們。
一旦索引被關閉,它在 Elasticsearch 內存中唯-的痕跡是其元數據,如名字
以及分片的位置。如果有足夠的磁盤空間,而且也不確定是否需要在那個數據中再次搜索,關閉索引要比刪除索引更好。關閉它們會讓你非常安心,永遠可以重新打開被關閉的索引,然后在其中再次搜索。
重新打開 POST /open-soft/_open
配置索引
通過 settings參數配置索引,索引的所有配置項都以“index”開頭。索引的管理分為靜態設置和動態設置兩種。
靜態設置
只能在索引創建時或在狀態為 closed index(閉合索引)上設置,主要配置索引主分片、壓縮編碼、路由等相關信息
index.number_of_shards主分片數,默認為 5.只能在創建索引時設置,不能修改
index.shard.check_on_startup 是否應在索引打開前檢查分片是否損壞,當檢查到分片損壞將禁止分片被打開。false:默認值;checksum:檢查物理損壞;true:檢查物理和邏輯損壞,這將消耗大量內存和 CPU;fix:檢查物理和邏輯損壞。
有損壞的分片將被集群自動刪除,這可能導致數據丟失
index.routing_partition_size 自定義路由值可以轉發的目的分片數。默認為 1,只能在索引創建時設置。此值必須小於 index.number_of_shards
index.codec 默認使用 LZ4壓縮方式存儲數據,也可以設置為
best_compression,它使用 DEFLATE 方式以犧牲字段存儲性能為代價來獲得更高的壓縮比例。
如:
put test1{
"settings":{
"index.number_of_shards":3,
"index.codec":"best_compression"
}
}
動態設置
通過接口“_settings”進行,同時查詢配置也通過這個接口進行,比如:get _settings
get /open-soft/_settings
get /open-soft,test1/_settings
配置索引則通過:
put test1/_settings
{
"refresh_interval":"2s"
}
常用的配置參數如下:
index.number_of_replicas 每個主分片的副本數。默認為 1
index.auto_expand_replicas 基於可用節點的數量自動分配副本數量,默認為false(即禁用此功能)
index.refresh_interval 執行刷新操作的頻率。默認為 1s。可以設置為 -1 以禁用刷新。
index.max_result_window 用於索引搜索的 from+size 的最大值。默認為10000
index.blocks.read_only 設置為 true 使索引和索引元數據為只讀,false 為允許寫入和元數據更改。
index.blocks.read 設置為 true 可禁用對索引的讀取操作
index.blocks.write 設置為 true 可禁用對索引的寫入操作
index.blocks.metadata 設置為 true 可禁用索引元數據的讀取和寫入
index.max_refresh_listeners 索引的每個分片上可用的最大刷新偵聽器數index.max_docvalue_fields_search 一次查詢最多包含開啟 doc_values 字段
的個數,默認為 100
index.max_script_fields 查詢中允許的最大 script_fields數量。默認為 32。
index.max_terms_count 可以在 terms 查詢中使用的術語的最大數量。默認為65536。
index.routing.allocation.enable 控制索引分片分配。All(所有分片)、primaries(主分片)、new_primaries(新創建分片)、none(不分片)
index.routing.rebalance.enable 索引的分片重新平衡機制。all、primaries、replicas、none
index.gc_deletes 文檔刪除后(刪除后版本號)還可以存活的周期,默認
為 60s
index.max_regex_length用於正在表達式查詢(regex query)正在表達式長度,默認為 1000
配置映射
通過_mapping 接口進行,在我們前面的章節中,已經展示過了。
get /open-soft/_mapping
或者只看某個字段的屬性:
get /open-soft/_mapping/field/lang
修改映射,當然就是通過 put或者 post方法了。但是要注意,已經存在的映射只能添加字段或者字段的多類型。但是字段創建后就不能刪除,大多數參數也不能修改,可以改的是 ignore_above。所以設計索引時要做好規划,至少初始時的必要字段要規划好。
增加文檔
增加文檔,我們在前面的章節已經知道了,比如:
put /open-soft/_doc/1
{
"name": "Apache Hadoop",
"lang": "Java",
"corp": "Apache",
"stars":200
}
如果增加文檔時,在 Elasticsearch中如果有相同 ID的文檔存在,則更新此文檔,比如執行
put /open-soft/_doc/1
{
"name": "Apache Hadoop2",
"lang": "Java8",
"corp": "Apache",
"stars":300
}
則會發現已有文檔的內容被更新了
文檔的 id
當創建文檔的時候,如果不指定 ID,系統會自動創建 ID。自動生成的 ID 是一個不會重復的隨機數。使用 GUID 算法,可以保證在分布式環境下,不同節點同一時間創建的_id一定是不沖突的。比如:
post /open-soft/_doc
{
"message":"Hello"
}
查詢文檔
get /open-soft/_doc/
更新文檔
前面我們用 put方法更新了已經存在的文檔,但是可以看見他是整體更新文檔,如果我們要更新文檔中的某個字段怎么辦?需要使用_update接口。
post /open-soft/_update/1/
{
"doc":{
"year": 2016
}
}
如果文檔中存在 year字段,更新 year 字段的值,如果不存在 year 字段,則會新增 year字段,並將值設為 2016。
update 接口在文檔不存在時提示錯誤,如果希望在文檔不存在時創建文檔,則可以在請求中添加 upsert參數或 doc_as_upsert 參數,例如:
POST /open-soft/_update/5
{
"doc": {
"year": "2020"
},
"upsert":{
"name" : "Enjoyedu Framework",
"corp" : "enjoyedu "
}
}
或
POST /open-soft/_update/6
{
"doc": {
"year": "2020"
},
"doc_as_upsert" : true
}
upsert參數定義了創建新文檔使用的文檔內容,而 doc_as_upsert 參數的含義是直接使用 doc參數中的內容作為創建文檔時使用的文檔內容。
刪除文檔
delete /open-soft/_doc/1
數據檢索和分析
為了方便我們學習,我們導入 kibana 為我們提供的范例數據。
目前為止,我們已經探索了如何將數據放入 Elasticsearch,現在來討論下如何將數據從 Elasticsearch中拿出來,那就是通過搜索。畢竟,如果不能搜索數據,那么將其放入搜索引擎的意義又何在呢?幸運的是,Elasticsearch 提供了豐富的接口來搜索數據,涵蓋了 Lucene 所有的搜索功能。因為 Elasticsearch允許構建搜索請求的格式很靈活,請求的構建有無限的可能性。要了解哪些查詢和過濾器的組合適用於你的數據,最佳的方式就是進行實驗,因此不要害怕在項目的數據上嘗試這些組合,這樣才能弄清哪些更適合你的需求。
_search 接口
所有的 REST搜索請求使用_search 接口,既可以是 GET請求,也可以是 POST請求,也可以通過在搜索 URL 中指定索引來限制范圍。
_search 接口有兩種請求方法,一種是基於 URI 的請求方式,另一種是基於請求體的方式,無論哪種,他們執行的語法都是基於 DSL(ES 為我們定義的查詢語言,基於 JSON的查詢語言),只是形式上不同。我們會基於請求體的方式來學習。比如說:
get kibana_sample_data_flights/_search
{
"query":{
"match_all":{}
}
}
或
get kibana_sample_data_flights/_search
{
"query":{
"match_none":{}
}
}
當然上面的查詢沒什么太多的用處,因為他們分別代表匹配所有和全不匹配。
所以我們經常要使用各種語法來進行查詢,一旦選擇了要搜索的索引,就需要配置搜索請求中最為重要的模塊。這些模塊涉及文檔返回的數量,選擇最佳的文檔返回,以及配置不希望哪些文檔出現在結果中等等。
■ query-這是搜索請求中最重要的組成部分,它配置了基於評分返回的最佳文檔,也包括了你不希望返回哪些文檔。
■ size-代表了返回文檔的數量。
■ from-和 size 一起使用,from 用於分頁操作。需要注意的是,為了確定第2頁的 10 項結果,Elasticsearch 必須要計算前 20 個結果。如果結果集合不斷增加,獲取某些靠后的翻頁將會成為代價高昂的操作。
■ _source指定_ source 字段如何返回。默認是返回完整的_ source字段。
通過配置_ source,將過濾返回的字段。如果索引的文檔很大,而且無須結果中的全部內容,就使用這個功能。請注意,如果想使用它,就不能在索引映射中關閉_source 字段。
■ sort默認的排序是基於文檔的得分。如果並不關心得分,或者期望許多文檔的得分相同,添加額外的 sort將幫助你控制哪些文檔被返回。
結果起始和頁面大小
命名適宜的 from和 size字段,用於指定結果的開始點,以及每“頁"結果的數量。舉個例子,如果發送的 from 值是 7,size值是 5,那么 Elasticsearch 將返回第8、9、10、11 和 12項結果(由於 from 參數是從 0開始,指定 7 就是從第 8項結果開始)。如果沒有發送這兩個參數,Elasticsearch 默認從第一項結果開始(第 0 項結果),在回復中返回 10項結果。
例如
get kibana_sample_data_flights/_search
{
"from":100,
"size":20,
"query":{
"term":{
"DestCountry":"CN"
}
}
}
但是注意,from與 size 的和不能超過 index. max_result_window 這個索引配置項設置的值。默認情況下這個配置項的值為 10000,所以如果要查詢 10000 條以后的文檔,就必須要增加這個配置值。例如,要檢索第 10000 條開始的 200條數據,這個參數的值必須要大於 10200,否則將會拋出類似“Result window is toolarge'的異常。
由此可見,Elasticsearch 在使用 from 和 size處理分頁問題時會將所有數據全部取出來,然后再截取用戶指定范圍的數據返回。所以在查詢非常靠后的數據時,即使使用了 from和 size定義的分頁機制依然有內存溢出的可能,而 max_result_ window設置的 10000條則是對 Elastiesearch 的一.種保護機制。
那么 Elasticsearch 為什么要這么設計呢?首先,在互聯網時代的數據檢索應該通過相似度算法,提高檢索結果與用戶期望的附和度,而不應該讓用戶在檢索結果中自己挑選滿意的數據。以互聯網搜索為例,用戶在瀏覽搜索結果時很少會看到第 3頁以后的內容。假如用戶在翻到第 10000 條數據時還沒有找到需要的結果,那么他對這個搜索引擎一定會非常失望。
_source 參數
元字段_source 中存儲了文檔的原始數據。如果請求中沒有指定_source,Elasticsearch 默認返回整個_ source,或者如果_ source沒有存儲,那么就只返回匹配文檔的元數據:_ id、_type、_index 和_score。
例如:
get kibana_sample_data_flights/_search
{
"query":{
"match_all":{}
},
"_source":["OriginCountry","DestCountry"]
}
你不僅可以返回字段列表,還可以指定通配符。例如,如果想同時返回"
DestCountry "和" DestWeather "字段,可以這樣配置_ source: "Dest*"。也可以使用通配字符串的數組來指定多個通配符,例如_ source:[" Origin*", "* Weather "]。
get kibana_sample_data_flights/_search
{
"query":{
"match_all":{}
},
"_source":["Origin*","*Weather"]
}
不僅可以指定哪些字段需要返回,還可以指定哪些字段無須返回。比如:get kibana_sample_data_flights/_search
{
"_source":{
"includes":["*.lon","*.lat"],
"excludes":"DestLocation.*"
}
}
排序
大多搜索最后涉及的元素都是結果的排序( sort )。如果沒有指定 sort 排序選項,Elasticsearch返回匹配的文檔的時候,按照_ score 取值的降序來排列,這樣最為相關的(得分最高的)文檔就會排名在前。為了對字段進行升序或降序排列,
指定映射的數組,而不是字段的數組。通過在 sort 中指定字段列表或者是字段映射,可以在任意數量的字段上進行排序。
例如:
get kibana_sample_data_flights/_search
{
"from":100,
"size":20,
"query":{
"match_all":{}
},
"_source":["Origin*","*Weather"],
"sort":[{"DistanceKilometers":"asc"},{"FlightNum":"desc"}]
}