公司項目的的日誌埋點是發到 Elasticsearch
上的,有時開發會去在 kibana
上查找相關的日誌信息,用於診斷用戶的問題。可是 kibana
太太重量級,查找起來比較慢。因而學習了下 Elasticsearch
的用法,此文章我的也只是點到爲止。工做時夠用就好了,全部有些地方可能並無詳細說明。只能充當入門讀物吧。javascript
當前的筆記只介紹 Elasticsearch
的搜索部分。java
文章中的搜索都是在 kibana
的 Dev tools
進行查詢的。python
須要安裝 Elasticsearch
、kibana
、elasticsearch-analysis-ik
git
具體的安裝方式,這裏就再也不闡述了。(安裝完,記得重啓 Elasticsearch
)github
重啓完成後,打開 kibana
的 Dev tools
,輸入下面的DSL代碼,並運行:正則表達式
PUT books
{
"settings": {
"number_of_replicas": 1,
"number_of_shards": 3
},
"mappings": {
"IT": {
"properties": {
"id": {
"type": "long"
},
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"language": {
"type": "keyword"
},
"author": {
"type": "keyword"
},
"price": {
"type": "double"
},
"year": {
"type": "date",
"format": "yyyy-MM-dd"
},
"description": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
複製代碼
運行好後,下載 books.json 文件,並進行導入。若是你安裝的 Elasticsearch
版本小於6.0
,使用下面的命令進行導入 books.json
:數據庫
curl -XPOST "http://localhost:9200/_bulk?pretty" --data-binary @books.json
複製代碼
若是你的 Elasticsearch
版本大於6.0
,則使用下面的命令進行導入:編程
curl -H "Content-Type: application/json" -XPOST "http://localhost:9200/_bulk?pretty" --data-binary @books.json
複製代碼
GET books/_search
{
"query": {
"match_all": {}
}
}
複製代碼
能夠簡寫爲:json
GET books/_search
複製代碼
使用term
來進行查詢,term
查詢不會被解析,只有查詢的詞和文檔中的詞精確匹配纔會被搜索到,應用場景爲:查詢人名、地名等須要精準匹配的需求。數組
查詢title字段中含有思想
的書籍
GET books/_search
{
"query": {
"term": {
"title": "思想"
}
}
}
複製代碼
返回以下:
有時查詢時,會返回成千上萬的數據,這種狀況下,分頁的做用就出來了。
分頁有兩個屬性,分別是from
、size
能夠理解爲:我從from
位置把剩下的文檔所有返回,而後size
限制了返回的數量。
用js代碼來詮釋就是:
const from = 100 - 1; // 數組從0開始,須要減一
const size = 10;
const data = [1, 2, 3, ..., 999, 1000];
const fromDate = data.splice(from);
const result = fromData.splice(0, size);
console.log(result) //=> [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
複製代碼
通常咱們查詢時,都是爲了觀察某一個字段,而不是想看所有的字段。而若是是默認狀況下,Elasticsearch
會返回的文檔的所有字段信息。會對工做形成必定的影響。因而,Elasticsearch
提供了一個接口,用於限制返回的字段。假設我只須要 title
和 author
字段:
GET books/_search
{
"_source": ["title", "author"],
"query": {
"term": {
"title": "java"
}
}
}
複製代碼
結果如圖:
由於 Elasticsearch
在作普通的搜索時,是採用相關性進行搜索的,而相關性是由評分
取決的。因此當咱們進行模糊搜索時,Elasticsearch
可能會返回一些相關性不那麼高的文檔。因此咱們能夠經過 Elasticsearch
提供的接口,來設置一個評分最低標準,低於這個標準的文檔,將不會出如今結果頁中。
好比,我想搜索 title
裏包含 java
的文檔,而且評分不低於0.7
:
GET books/_search
{
"min_score": 0.7,
"query": {
"term": {
"title": "java"
}
}
}
複製代碼
結果如圖:
有時,咱們會把 Elasticsearch
結果直接導入到網頁中,這個時候須要高亮關鍵字,讓用戶更加清楚本身想要的東西,Elasticsearch
已經提供了一個接口,好比我想讓搜索出來的結果中的關鍵字高亮:
GET books/_search
{
"_source": ["title"],
"min_score": 0.7,
"query": {
"term": {
"title": "java"
}
},
"highlight": {
"fields": {
"title": {}
}
}
}
複製代碼
結果如圖:
默認的標籤是<em></em>
,若是你想自定義,可使用: pre_tags
和 post_tags
。最終查詢代碼爲:
GET books/_search
{
"_source": ["title"],
"min_score": 0.7,
"query": {
"term": {
"title": "java"
}
},
"highlight" : {
"pre_tags" : ["<h1>"],
"post_tags" : ["</h1>"],
"fields" : {
"title" : {}
}
}
}
複製代碼
結果如圖:
上節基本都是以 term
進行搜索,但其實 Elasticsearch
提供了不少搜索方法,本章就是介紹 Elasticsearch
有哪些搜索方法、分別起的做用。
本章對 common_terms query
、query_string query
、simple_query_string query
沒有解釋說明,由於使用起來較少,並且解釋起來較爲麻煩。若是想了解,能夠參考網上的文章。這裏就不在闡述了。
咱們先使用 term
進行一次查詢:
GET books/_search
{
"_source": ["title", "author"],
"query": {
"term": {
"title": "java編程"
}
}
}
複製代碼
你會發現,其結果爲空(可是數據庫裏是有這個數據的),如圖:
這是由於 term
是匹配分詞後的詞項來進行查詢的。好比剛剛咱們查的 java編程
,在 Elasticsearch
進行分詞時,會把 java編程
分爲:java
和 編程
。致使匹配不起來。
用代碼詮釋的話就是:
const keyword = 'java編程';
const data = ['java', '編程'];
const result = data.includes(keyword);
console.log(result) //=> false
複製代碼
如今咱們把 term
換成 match
來嘗試下:
GET books/_search
{
"_source": ["title", "author"],
"query": {
"match": {
"title": "java編程"
}
}
}
複製代碼
結果如圖:
能夠發現,已經有結果了,可是爲何會有兩個呢?
緣由是由於 match
會對你的關鍵字進行分詞,而後去匹配文檔分詞後的結果,只要文檔裏的詞項能匹配關鍵字分詞後的任何一個,都會返回到結果裏。
代碼詮釋:
const data = ['java', '編程', '思想']; // 分詞後的文檔裏的數據
const keywords = ['java', '編程', '思想']; // 分詞後的關鍵字
const result = (() => {
for (let x = 0; x < data.length; x++) {
const dataItem = data[x];
for (let y = 0; y < keywords.length; y++) {
const keywordItem = keywords[y];
if (dataItem === keywordItem) {
return true;
}
}
}
return false;
})()
複製代碼
若是我只想讓它返回一個呢,而且只能用 match
來作,能夠麼?
是能夠的,match
提供了一個屬性:operator
。能夠用這個來幫助完成這個需求:
GET books/_search
{
"_source": ["title", "author"],
"query": {
"match": {
"title": {
"query": "java編程",
"operator": "and"
}
}
}
}
複製代碼
最終的結果如圖:
原理是由於 operator
屬性的值爲 and
,這樣的話,就告訴 Elasticsearch
我要讓個人關鍵字都能和文檔裏的詞項匹配上。有一個沒匹配上,我都不要。
若是 operator
屬性的值爲 or
,那結果就和以前是同樣的了。
你能夠把這個方法理解爲自帶了 operator
屬性的值爲 and
的 match
。
這個方法有兩個限制條件,只有都知足,纔會在結果中顯示出:
operator: "and"
順序一致指的是什麼呢?
假設你使用 match
來匹配: 編程java
,那麼結果仍是和上面同樣。因此若是你須要要求順序一致性,那麼你就可使用 match_phrase
來作。
若是使用 編程java
來搜索:
若是使用 java編程
:
這個方法和 match_phrase
方法相似,不過這個方法能夠能夠把最後一個詞項做爲前綴進行匹配,想象一下:用戶在搜索欄中搜索 辣雞UZ
,而後下面列表中出現了 辣雞UZI
。
首先 match_phrase_prefix
會先分詞爲: 辣雞
,而後找了一個文檔,再而後匹配 辣雞
後面的字符串是否以 UZ
開頭的。這個時候文檔知足條件,就返回出結果。能夠假想後面一直有一個(.*)
的通配符,如:辣雞UZ(.*)
。
知道原理了,咱們如今寫一個查詢語句:
GET books/_search
{
"_source": ["title", "author"],
"query": {
"match_phrase_prefix": {
"title": "java編"
}
}
}
複製代碼
結果如圖:
multi_match
是 match
的升級方法,能夠用來搜索多個字段。
好比我不想只在 title
裏搜索 java編程
,我還想在 description
裏進行搜索。那應該怎麼作呢?
Elasticsearch
已經提供了 multi_match
專門用來處理這件事情:
GET books/_search
{
"_source": ["title", "description"],
"query": {
"multi_match": {
"query": "java編程",
"fields": ["title", "description"]
}
}
}
複製代碼
最終結果如圖:
而且 multi_match
還支持通配符。上面的查詢語句,能夠寫成:
GET books/_search
{
"_source": ["title", "description"],
"query": {
"multi_match": {
"query": "java編程",
"fields": ["title", "*tion"]
}
}
}
複製代碼
上一章是全文查詢,這一章是詞項查詢。他們倆的區別在於:
第一章節已經介紹過了,這裏就再也不闡述了。
terms
是 term
查詢的升級版本,能夠用來查詢文檔中某一字段,是否包含了其關鍵字。好比,我想查詢 title
字段中包含了 優化
或者 基礎
的文檔:
GET books/_search
{
"_source": ["title"],
"query": {
"terms": {
"title": ["優化", "基礎"]
}
}
}
複製代碼
其結果如圖:
從名字就能猜想出 range
是範圍匹配。能夠匹配 number
、date
、string
(字符串範圍查詢比較特殊,比較少用,就再也不闡述了)
range
支持如下查詢參數:
如今我想查詢價格低於70,並大於等於50的書籍。僞代碼既:(price >= 50 && price < 70)
:
GET books/_search
{
"_source": ["title", "price"],
"query": {
"range": {
"price": {
"gte": 50,
"lt": 70
}
}
}
}
複製代碼
其結果如圖:
若是我想查詢,出版日期在 2016-1-1
到 2016-12-31
之間的書籍,那麼DSL查詢語句就如同如下這樣:
GET books/_search
{
"_source": ["title", "publish_time"],
"query": {
"range": {
"publish_time": {
"gte": "2016-1-1",
"lte": "2016-12-31",
"format": "yyyy-MM-dd"
}
}
}
}
複製代碼
其結果如圖:
匹配有這個屬性的文檔。好比我想找到存在 title
字段的文檔:
GET books/_search
{
"_source": "title",
"query": {
"exists": {
"field": "title"
}
}
}
複製代碼
結果會返回全部的文檔。那麼如何定義 有這個屬性
呢?
定義的規則以下:
{"title": "js"}
: 存在{"title": ""}
: 存在{"title": ["js"]}
: 存在{"title": ["js", null]}
: 存在(有一個值不爲空就行){"title": null}
: 不存在{"title": []}
不存在{"title": [null]}
不存在{"foo": "bar"}
: 不存在用來匹配文檔分詞後的詞項中的前綴。咱們先寫個DSL進行匹配下:
GET books/_search
{
"_source": "description",
"query": {
"prefix": {
"description": "wi"
}
}
}
複製代碼
其結果如圖:
爲什麼 wi
能夠匹配到這個呢?由於 Elasticsearch
會對 description
進行分詞,其中會把 winPython
分爲 win
Python
。那麼這兩個就是文檔分詞後的詞項,而 prefix
匹配每一個詞項的開頭是否匹配,至關於js的 startsWith
方法。用代碼詮釋的話就是:
const dataItem = ['win', 'python'];
const prefixKeyword = 'wi';
const result = dataItem.some(item => item.startsWith(prefixKeyword));
console.log(result); //=> true
複製代碼
wildcard
爲通配符查詢。不過目前只支持 *
和 ?
。所表明的含義爲:
*
: 零個或多個?
: 一個或多個注意:wildcard
不是匹配全文,仍是會對文檔的字段進行分詞,而後應用於每一個詞項
好比,我如今想查詢 wi*
的文檔:
GET books/_search
{
"_source": "description",
"query": {
"wildcard": {
"description": "wi*"
}
}
}
複製代碼
其結果如圖:
首先 Elasticsearch
會先對 description
進行分詞爲:win
和 python
。而後 wi*
會應用到每一個詞項裏,其中 win
符合規則,則顯示在結果中。
若是我用 win?
,則不會有任何的結果,由於 ?
表明的是一個或多個。那麼匹配到 win
的時候,後面沒有字符串了,則結果爲空。
其爲正則表達式查詢,原理同 wildcard
,這裏就不在闡述了。
能夠把 fuzzy
理解爲模糊查詢。好比用戶輸入關鍵字時,一不當心輸入錯了,變成了 javascrpit
,那麼 fuzzy
的做用就出來了。它仍能夠搜索到 javascript
:
GET books/_search
{
"_source": "description",
"query": {
"fuzzy": {
"description": "javascrpit"
}
}
}
複製代碼
其結果如圖:
複合查詢就是把簡單的查詢組合在一塊兒,從而實現更加複雜的查詢。而且複合查詢還能夠控制另外一個查詢的行爲。
不太經常使用,用於對返回結果的文檔進行打分。
這裏就不在闡述了,若是感興趣,可見:[Elasticsearch] 控制相關度 (四) - 忽略TF/IDF
這個查詢方法,仍是很是重要的。這個方法提供瞭如下操做方法:
must
下面的查詢條件,至關於AND
或者 &&
should
下的查詢條件,匹配不出來也沒事。至關於 OR
或者 ||
must
相反,必須不知足 must_not
下面的查詢條件,至關於 !==
must
同樣,可是不會打分,也就說不會影響文檔的 _score
字段如今,咱們想要查詢:書籍做者(author
)是 葛一鳴
,書籍名稱(title
)裏包含 java
的書籍,價格(price
)不能高於 70
低於 40
,而且書籍描述(description
)能夠包含或者不包含 虛擬機
的書籍。
GET books/_search
{
"query": {
"bool": {
"filter": {
"term": {
"author": "葛一鳴"
}
},
"must": [
{
"match": {
"title": "java"
}
}
],
"should": [
{
"match": {
"description": "虛擬機"
}
}
],
"must_not": [
{
"range": {
"price": {
"gt": 70,
"lt": 40
}
}
}
]
}
}
}
複製代碼
其結果如圖:
這三個就不在闡述了,其主要做用是關係到 _score
,也就是關係到查詢的結果的評分。感興趣的,能夠在網上搜下。
此文章是《從Lucene到Elasticsearch:全文檢索實戰》一書的讀書筆記,若是形成侵權,可與我聯繫,我將刪除此文。
這本書是十分棒的,若是你們有興趣想深刻了解的話,能夠去進行購買此書。
Black-Hole: 158blackhole@gmail.com
Blog: www.bugs.cc
Github: github.com/BlackHole1
我司(愛樂奇)招人,感興趣的小夥伴能夠來投簡歷呀。
彈性工做制、每日水果、同事都特別nice、96五、團建、五險一金...
地點上海浦軟大廈