文章目錄
- 一、什麼是elasticsearch
-
-
- 1 定義
- 2 簡介
- 3 有關概念
-
- 二、elasticsearch安裝
-
-
- 1 Windows安裝
- 2 Linux安裝
-
- 三、kibana安裝
-
-
- 1 什麼是kibana
- 2 安裝
- 3 配置
- 4 運作
- 5 控制台
-
- 四、ik分詞器安裝
-
-
- 1 介紹
- 2 安裝
-
- 五、Rest風格API的學習
-
-
- 1 操作索引
-
-
- 1.1 基本概念
- 1.2 建立索引
- 1.3 檢視索引
- 1.4 删除索引
-
- 2 操作映射
-
-
- 2.1 基本概念
- 2.2 建立映射字段
- 2.3 檢視映射
- 2.4 字段屬性詳解
- 2.5 新增資料
- 2.6 修改資料
- 2.7 删除資料
-
- 3 查詢
-
- 3.1 基本查詢
-
- 3.1.1 查詢所有
- 3.1.2 比對查詢
- 3.1.3 多字段查詢
- 3.1.4 詞條比對
- 3.1.5 多詞條精确比對
- 3.2 結果過濾
-
- 3.2.1 直接指定字段
- 3.2.2 指定includes和excludes
- 3.3 進階查詢
-
- 3.3.1 布爾組合
- 3.3.2 範圍查詢
- 3.3.3 模糊查詢
- 3.4 過濾
-
- 3.4.1 **條件查詢中進行過濾**
- 3.4.2 **無查詢條件,直接過濾**
- 3.5 排序
-
- 3.5.1 單字段排序
- 3.5.2 多字段排序
- 4 聚合aggregations
-
- 4.1 基本概念
-
- 4.1.1 **桶(bucket)**
- 4.1.2 **度量(metrics)**
- 4.2 聚合為桶
- 4.3 桶内度量
- 4.4 桶内嵌套桶
- 4.5.劃分桶的其它方式
-
- 4.5.1.階梯分桶Histogram
- 4.5.2.範圍分桶range
-
一、什麼是elasticsearch
1 定義
- Elasticsearch是一個基于Lucene的搜尋伺服器。
- 它提供了一個分布式多使用者能力的全文搜尋引擎,基于RESTful web接口。
- Elasticsearch是用Java語言開發的,并作為Apache許可條款下的開放源碼釋出,是一種流行的企業級搜尋引擎。
- Elasticsearch用于雲計算中,能夠達到實時搜尋,穩定,可靠,快速,安裝使用友善。
- 官方用戶端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和許多其他語言中都是可用的。
- 根據DB-Engines的排名顯示,Elasticsearch是最受歡迎的企業搜尋引擎,其次是Apache Solr,也是基于Lucene。
2 簡介
- Elasticsearch 是一個分布式、高擴充、高實時的搜尋與資料分析引擎。它能很友善的使大量資料具有搜尋、分析和探索的能力。充分利用Elasticsearch的水準伸縮性,能使資料在生産環境變得更有價值。
- Elasticsearch 的實作原理主要分為以下幾個步驟,首先使用者将資料送出到Elasticsearch 資料庫中,再通過分詞控制器去将對應的語句分詞,将其權重和分詞結果一并存入資料,當使用者搜尋資料時候,再根據權重将結果排名,打分,再将傳回結果呈現給使用者。
- Elasticsearch是與名為Logstash的資料收集和日志解析引擎以及名為Kibana的分析和可視化平台一起開發的。這三個産品被設計成一個內建解決方案,稱為“Elastic Stack”(以前稱為“ELK stack”)。
- Elasticsearch可以用于搜尋各種文檔。它提供可擴充的搜尋,具有接近實時的搜尋,并支援多租戶。
- Elasticsearch是分布式的,這意味着索引可以被分成分片,每個分片可以有0個或多個副本。每個節點托管一個或多個分片,并充當協調器将操作委托給正确的分片。
- 再平衡和路由是自動完成的。相關資料通常存儲在同一個索引中,該索引由一個或多個主分片和零個或多個複制分片組成。一旦建立了索引,就不能更改主分片的數量。
- Elasticsearch使用Lucene,并試圖通過JSON和Java API提供其所有特性。它支援facetting和percolating,如果新文檔與注冊查詢比對,這對于通知非常有用。
- 另一個特性稱為“網關”,處理索引的長期持久性;例如,在伺服器崩潰的情況下,可以從網關恢複索引。Elasticsearch支援實時GET請求,适合作為NoSQL資料存儲,但缺少分布式事務。
-
小結
– 分布式,無需人工搭建叢集(solr就需要人為配置,使用Zookeeper作為注冊中心)
– Restful風格,一切API都遵循Rest原則,容易上手
– 近實時搜尋,資料更新在Elasticsearch中幾乎是完全同步的。
3 有關概念
- cluster:代表一個叢集,叢集中有多個節點,其中有一個為主節點,這個主節點是可以通過選舉産生的,主從節點是對于叢集内部來說的。es的一個概念就是去中心化,字面上了解就是無中心節點,這是對于叢集外部來說的,因為從外部來看es叢集,在邏輯上是個整體,你與任何一個節點的通信和與整個es叢集通信是等價的。
- shards:代表索引分片,es可以把一個完整的索引分成多個分片,這樣的好處是可以把一個大的索引拆分成多個,分布到不同的節點上。構成分布式搜尋。分片的數量隻能在索引建立前指定,并且索引建立後不能更改。
- replicas:代表索引副本,es可以設定多個索引的副本,副本的作用一是提高系統的容錯性,當某個節點某個分片損壞或丢失時可以從副本中恢複。二是提高es的查詢效率,es會自動對搜尋請求進行負載均衡。
- recovery:代表資料恢複或叫資料重新分布,es在有節點加入或退出時會根據機器的負載對索引分片進行重新配置設定,挂掉的節點重新啟動時也會進行資料恢複。
- river:代表es的一個資料源,也是其它存儲方式(如:資料庫)同步資料到es的一個方法。它是以插件方式存在的一個es服務,通過讀取river中的資料并把它索引到es中,官方的river有couchDB的,RabbitMQ的,Twitter的,Wikipedia的。
- gateway:代表es索引快照的存儲方式,es預設是先把索引存放到記憶體中,當記憶體滿了時再持久化到本地硬碟。gateway對索引快照進行存儲,當這個es叢集關閉再重新啟動時就會從gateway中讀取索引備份資料。es支援多種類型的gateway,有本地檔案系統(預設),分布式檔案系統,Hadoop的HDFS和amazon的s3雲存儲服務。
- discovery.zen:代表es的自動發現節點機制,es是一個基于p2p的系統,它先通過廣播尋找存在的節點,再通過多點傳播協定來進行節點之間的通信,同時也支援點對點的互動。
- Transport:代表es内部節點或叢集與用戶端的互動方式,預設内部是使用tcp協定進行互動,同時它支援http協定(json格式)、thrift、servlet、memcached、zeroMQ等的傳輸協定(通過插件方式內建)。
二、elasticsearch安裝
1 Windows安裝
- 下載下傳elasticsearch-6.4.1.zip
- 直接解壓至某目錄,設定該目錄為ES_HOME環境變量
- 安裝JDK,并設定JAVA_HOME環境變量
-
在windows下,運作 %ES_HOME%\bin\elasticsearch.bat即可運作
– 以head插件為例:
– 聯網時,直接運作%ES_HOME%\bin\plugin --install mobz/elasticsearch-head。
– 不聯網時,下載下傳elasticsearch-head的zipball的master包,然後運作%ES_HOME%\bin\plugin --url file:///[path-to-downloadfile] --install head,其中[path-to-downloadfile]是下載下傳後master包的絕對路徑。
– 安裝完成,重新開機服務,在浏覽器打開http://localhost:9200/_plugin/head/ 即可。
2 Linux安裝
-
建立一個使用者leyou
– 出于安全考慮,elasticsearch預設不允許以root賬号運作。
– 建立使用者:
– 設定密碼:useradd 使用者名
– 切換使用者:passwd 使用者名
su - 使用者名
-
上傳安裝包,并解壓
– 我們将安裝包上傳到:/home/使用者名目錄
– 解壓縮:
– 目錄重命名:tar -zxwf elasticsearch-6.2.4.tar.gz
– 檢視目錄結構mv elasticsearch-6.3.0/ elasticsearch
-
修改配置
– 我們進入config目錄:
cd config
需要修改的配置檔案有兩個
– 1. jvm.options
– Elasticsearch基于Lucene的,而Lucene底層是java實作,是以我們需要配置jvm參數。
– 編輯jvm.options:
– 預設配置如下:vim jvm.options
– 記憶體占用太多了,我們調小一些:-Xms1g -Xmx1g
-Xms512m -Xmx512m
– 2. elasticsearch.yml
–
– 修改資料和日志目錄:vim elasticsearch.yml
path.data: /home/使用者名/elasticsearch/data #資料目錄位置
path.logs: /home/使用者名/elasticsearch/logs # 日志目錄位置
– 把data和logs目錄修改指向了elasticsearch的安裝目錄。但是這兩個目錄并不存在,是以我們需要建立出來。
– 進入elasticsearch的根目錄,然後建立:
– 修改綁定的ip:mkdir data mkdir logs
network.host: 0.0.0.0 # 綁定到0.0.0.0,允許任何ip來通路
預設隻允許本機通路,修改為0.0.0.0後則可以遠端通路
– 目前我們是做的單機安裝,如果要做叢集,隻需要在這個配置檔案中添加其它節點資訊即可。
– elasticsearch.yml的其它可配置資訊:
屬性名 | 說明 |
---|---|
cluster.name | 配置elasticsearch的叢集名稱,預設是elasticsearch。建議修改成一個有意義的名稱。 |
node.name | 節點名,es會預設随機指定一個名字,建議指定一個有意義的名稱,友善管理 |
path.conf | 設定配置檔案的存儲路徑,tar或zip包安裝預設在es根目錄下的config檔案夾,rpm安裝預設在/etc/ elasticsearch |
path.data | 設定索引資料的存儲路徑,預設是es根目錄下的data檔案夾,可以設定多個存儲路徑,用逗号隔開 |
path.logs | 設定日志檔案的存儲路徑,預設是es根目錄下的logs檔案夾 |
path.plugins | 設定插件的存放路徑,預設是es根目錄下的plugins檔案夾 |
bootstrap.memory_lock | 設定為true可以鎖住ES使用的記憶體,避免記憶體進行swap |
network.host | 設定bind_host和publish_host,設定為0.0.0.0允許外網通路 |
http.port | 設定對外服務的http端口,預設為9200。 |
transport.tcp.port | 叢集結點之間通信端口 |
discovery.zen.ping.timeout | 設定ES自動發現節點連接配接逾時的時間,預設為3秒,如果網絡延遲高可設定大些 |
discovery.zen.minimum_master_nodes | 主結點數量的最少值 ,此值的公式為:(master_eligible_nodes / 2) + 1 ,比如:有3個符合要求的主結點,那麼這裡要設定為2 |
-
運作
– 進入elasticsearch/bin目錄,然後輸入指令:
./elasticsearch
發現報錯了,啟動失敗。
– 錯誤1:核心過低
– 我們使用的是centos6,其linux核心版本為2.6。而Elasticsearch的插件要求至少3.5以上版本。不過沒關系,我們禁用這個插件即可。修改elasticsearch.yml檔案,在最下面添加如下配置:
bootstrap.system_call_filter: false
然後重新開機
– 錯誤2:檔案權限不足
–
– 我們用的是leyou使用者,而不是root,是以檔案權限不足。首先用root使用者登入。 然後修改配置檔案:[1]: max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536]
添加下面的内容:vim /etc/security/limits.conf
* soft nofile 65536
* hard nofile 131072
* soft nproc 4096
* hard nproc 4096
– 錯誤3:線程數不夠
–
– 這是線程數不夠。繼續修改配置:[1]: max number of threads [1024] for user [leyou] is too low, increase to at least [4096]
修改下面的内容:vim /etc/security/limits.d/90-nproc.conf
改為:* soft nproc 1024
* soft nproc 4096
– 錯誤4:程序虛拟記憶體
–
– vm.max_map_count:限制一個程序可以擁有的VMA(虛拟記憶體區域)的數量,繼續修改配置檔案,[3]: max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144]
添加下面内容:vim /etc/sysctl.conf
然後執行指令:vm.max_map_count=655360
所有錯誤修改完畢,一定要重新開機你的 Xshell終端,否則配置無效。sysctl -p
-
成功标志
– 可以看到綁定了兩個端口:
- 9300:叢集節點間通訊接口
- 9200:用戶端通路接口
三、kibana安裝
1 什麼是kibana
- Kibana是一個基于Node.js的Elasticsearch索引庫資料統計工具,可以利用Elasticsearch的聚合功能,生成各種圖表,如柱形圖,線狀圖,餅圖等。
- 而且還提供了操作Elasticsearch索引資料的控制台,并且提供了一定的API提示,非常有利于我們學習Elasticsearch的文法。
2 安裝
- 因為Kibana依賴于node,我們的虛拟機沒有安裝node,而window中安裝過。是以我們選擇在window下使用kibana。
- 最新版本與elasticsearch保持一緻,也是6.3.0,解壓到特定目錄即可
3 配置
- 進入安裝目錄下的config目錄,修改kibana.yml檔案:
- 修改elasticsearch伺服器的位址:
elasticsearch.url: "http://192.168.56.101:9200"
4 運作
- 進入安裝目錄下的bin目錄:輕按兩下bat運作
- 發現kibana的監聽端口是5601,我們通路:http://127.0.0.1:5601
5 控制台
- 選擇左側的DevTools菜單,即可進入控制台頁面
- 在頁面右側,我們就可以輸入請求,通路Elasticsearch了。
四、ik分詞器安裝
1 介紹
- Lucene的IK分詞器早在2012年已經沒有維護了,現在我們要使用的是在其基礎上維護更新的版本,并且開發為ElasticSearch的內建插件了,與Elasticsearch一起維護更新,版本也保持一緻,最新版本:6.3.0
2 安裝
- zip包,解壓到Elasticsearch目錄的plugins目錄中
- 使用unzip指令解壓:
unzip elasticsearch-analysis-ik-6.3.0.zip -d ik-analyzer
- 然後重新開機elasticsearch
五、Rest風格API的學習
1 操作索引
1.1 基本概念
- Elasticsearch也是基于Lucene的全文檢索庫,本質也是存儲資料,很多概念與MySQL類似的。
- 在SolrCloud中,有一些叢集相關的概念,在Elasticsearch也有類似的:
索引集(Indices,index的複數):邏輯上的完整索引 collection1
分片(shard):資料拆分後的各個部分
副本(replica):每個分片的複制
- 要注意的是:Elasticsearch本身就是分布式的,是以即便你隻有一個節點,Elasticsearch預設也會對你的資料進行分片和副本操作,當你向叢集添加新資料時,資料也會在新加入的節點中進行平衡。
- 對比關系:
索引(indices)--------------------------------Databases 資料庫
類型(type)-----------------------------Table 資料表
文檔(Document)----------------Row 行
字段(Field)-------------------Columns 列
- 詳細說明:
概念 | 說明 |
---|---|
索引庫(indices) | indices是index的複數,代表許多的索引, |
類型(type) | 類型是模拟mysql中的table概念,一個索引庫下可以有不同類型的索引,比如商品索引,訂單索引,其資料格式不同。不過這會導緻索引庫混亂,是以未來版本中會移除這個概念 |
文檔(document) | 存入索引庫原始的資料。比如每一條商品資訊,就是一個文檔 |
字段(field) | 文檔中的屬性 |
映射配置(mappings) | 字段的資料類型、屬性、是否索引、是否存儲等特性 |
1.2 建立索引
-
文法
– Elasticsearch采用Rest風格API,是以其API就是一次http請求,你可以用任何工具發起http請求
– 建立索引的請求格式:
- 請求方式:PUT
- 請求路徑:/索引庫名
- 請求參數:json格式:
{ "settings": { "number_of_shards": 3, "number_of_replicas": 2 } }
-
settings:索引庫的設定
– number_of_shards:分片數量
– number_of_replicas:副本數量
-
使用kibana建立
– kibana的控制台,可以對http請求進行簡化 ,相當于是省去了elasticsearch的伺服器位址而且還有文法提示
1.3 檢視索引
- Get請求可以幫我們檢視索引資訊,格式:
GET /索引庫名
- 或者,我們可以使用*來查詢所有索引庫配置
1.4 删除索引
- 删除索引使用DELETE請求
- 文法:
DELETE /索引庫名
2 操作映射
2.1 基本概念
- 索引有了,接下來肯定是添加資料。但是,在添加資料之前必須定義映射。
- 映射是定義文檔的過程,文檔包含哪些字段,這些字段是否儲存,是否索引,是否分詞等
2.2 建立映射字段
- 請求方式依然是PUT
PUT /索引庫名/_mapping/類型名稱 { "properties": { "字段名": { "type": "類型", "index": true, "store": true, "analyzer": "分詞器" } } }
-
類型名稱:就是前面将的type的概念,類似于資料庫中的不同表
字段名:任意填寫 ,可以指定許多屬性,例如:
- type:類型,可以是text、long、short、date、integer、object等
- index:是否索引,預設為true
- store:是否存儲,預設為false
- analyzer:分詞器,這裡的
即使用ik分詞器ik_max_word
2.3 檢視映射
- 文法:
GET /索引庫名/_mapping
2.4 字段屬性詳解
- type
- String類型,又分兩種:
- text:可分詞,不可參與聚合
- keyword:不可分詞,資料會作為完整字段進行比對,可以參與聚合
- Numerical:數值類型,分兩類
- 基本資料類型:long、interger、short、byte、double、float、half_float
-
浮點數的高精度類型:scaled_float
– 需要指定一個精度因子,比如10或100。elasticsearch會把真實值乘以這個因子後存儲,取出時再還原。
-
Date:日期類型
– elasticsearch可以對日期格式化為字元串存儲,但是建議我們存儲為毫秒值,存儲為long,節省空間。
- String類型,又分兩種:
- index
-
index影響字段的索引情況。
– true:字段會被索引,則可以用來進行搜尋。預設值就是true
– false:字段不會被索引,不能用來搜尋
- index的預設值就是true,也就是說你不進行任何配置,所有字段都會被索引。
- 但是有些字段是我們不希望被索引的,比如商品的圖檔資訊,就需要手動設定index為false。
-
- store
- 是否将資料進行額外存儲。
- 在Elasticsearch中,即便store設定為false,也可以搜尋到結果。
- 原因是Elasticsearch在建立文檔索引時,會将文檔中的原始資料備份,儲存到一個叫做
的屬性中。而且我們可以通過過濾_source
來選擇哪些要顯示,哪些不顯示。_source
- 如果設定store為true,就會在
以外額外存儲一份資料,多餘,是以一般我們都會将store設定為false,事實上,store的預設值就是false。_source
2.5 新增資料
- 随機生成id
- 通過POST請求,可以向一個已經存在的索引庫中添加資料。
POST /索引庫名/類型名 { "key":"value" }
-
:源文檔資訊,所有的資料都在裡面。_source
-
:這條文檔的唯一标示,與文檔自己的id字段沒有關聯_id
- 通過POST請求,可以向一個已經存在的索引庫中添加資料。
- 自定義id
- 如果我們想要自己新增的時候指定id,可以這麼做:
POST /索引庫名/類型/id值 { ... }
- 如果我們想要自己新增的時候指定id,可以這麼做:
- 智能判斷
- Elasticsearch非常智能,你不需要給索引庫設定任何mapping映射,它也可以根據你輸入的資料來判斷類型,動态添加資料映射。
2.6 修改資料
- 把剛才新增的請求方式改為PUT,就是修改了。不過修改必須指定id,
- id對應文檔存在,則修改
- id對應文檔不存在,則新增
2.7 删除資料
- 删除使用DELETE請求,同樣,需要根據id進行删除
-
DELETE /索引庫名/類型名/id值
3 查詢
3.1 基本查詢
- 基本文法
GET /索引庫名/_search { "query":{ "查詢類型":{ "查詢條件":"查詢條件值" } } }
- 這裡的query代表一個查詢對象,裡面可以有不同的查詢屬性
- 查詢類型:
,match_all
,match
,term
等等range
- 查詢條件:查詢條件會根據類型的不同,寫法也有差異,後面詳細講解
3.1.1 查詢所有
GET /heima/_search
{
"query":{
"match_all": {}
}
}
-
:代表查詢對象query
-
:代表查詢所有match_all
- took:查詢花費時間,機關是毫秒
- time_out:是否逾時
- _shards:分片資訊
- hits:搜尋結果總覽對象
- total:搜尋到的總條數
- max_score:所有結果中文檔得分的最高分
- hits:搜尋結果的文檔對象數組,每個元素是一條搜尋到的文檔資訊
- _index:索引庫
- _type:文檔類型
- _id:文檔id
- _score:文檔得分
- _source:文檔的源資料
3.1.2 比對查詢
- or關系
-
類型查詢,會把查詢條件進行分詞,然後進行查詢,多個詞條之間是or的關系match
GET /heima/_search { "query":{ "match":{ "title":"查詢關鍵字" } } }
- 在上面的案例中,不僅會查詢到“查詢”,而且與“關鍵字”相關的都會查詢到,多個詞之間是
的關系。or
-
- and關系
- 某些情況下,我們需要更精确查找,我們希望這個關系變成
,可以這樣做and
GET /heima/_search { "query":{ "match": { "title": { "query": "查詢字段", "operator": "and" } } } }
- 本例中,隻有同時包含
和查詢
的詞條才會被搜尋到。字段
- 某些情況下,我們需要更精确查找,我們希望這個關系變成
- or和and之間
- 在
與or
間二選一有點過于非黑即白。 如果使用者給定的條件分詞後有 5 個查詢詞項,想查找隻包含其中 4 個詞的文檔,該如何處理?将 operator 操作符參數設定成and
隻會将此文檔排除。and
- 有時候這正是我們期望的,但在全文搜尋的大多數應用場景下,我們既想包含那些可能相關的文檔,同時又排除那些不太相關的。換句話說,我們想要處于中間某種結果。
-
查詢支援match
最小比對參數, 這讓我們可以指定必須比對的詞項數用來表示一個文檔是否相關。我們可以将其設定為某個具體數字,更常用的做法是将其設定為一個minimum_should_match
,因為我們無法控制使用者搜尋時輸入的單詞數量:百分數
GET /heima/_search { "query":{ "match":{ "title":{ "query":"最小查詢字段", "minimum_should_match": "75%" } } } }
- 本例中,搜尋語句可以分為3個詞,如果使用and關系,需要同時滿足3個詞才會被搜尋到。這裡我們采用最獨幕喜劇牌數:75%,那麼也就是說隻要比對到總詞條數量的75%即可,這裡3*75% 約等于2。是以隻要包含2個詞條就算滿足條件了。
- 在
3.1.3 多字段查詢
-
與multi_match
類似,不同的是它可以在多個字段中查詢match
GET /heima/_search { "query":{ "multi_match": { "query": "查詢", "fields": [ "title", "subTitle" ] } } }
- 本例中,我們會在title字段和subtitle字段中查詢
這個詞查詢
3.1.4 詞條比對
-
查詢被用于精确值 比對,這些精确值可能是數字、時間、布爾或者那些未分詞的字元串term
GET /heima/_search { "query":{ "term":{ "price":2699.00 } } }
3.1.5 多詞條精确比對
-
查詢和 term 查詢一樣,但它允許你指定多值進行比對。如果這個字段包含了指定值中的任何一個值,那麼這個文檔滿足條件:terms
GET /heima/_search { "query":{ "terms":{ "price":[2699.00,2899.00,3899.00] } } }
3.2 結果過濾
- 預設情況下,elasticsearch在搜尋的結果中,會把文檔中儲存在
的所有字段都傳回。_source
- 如果我們隻想擷取其中的部分字段,我們可以添加
的過濾_source
3.2.1 直接指定字段
GET /heima/_search
{
"_source": ["title","price"],
"query": {
"term": {
"price": 2699
}
}
}
3.2.2 指定includes和excludes
- 我們也可以通過:
- includes:來指定想要顯示的字段
- excludes:來指定不想要顯示的字段
- 二者都是可選的。
GET /heima/_search { "_source": { "includes":["title","price"] }, "query": { "term": { "price": 2699 } } }
- 與下面的結果将是一樣的:
GET /heima/_search { "_source": { "excludes": ["images"] }, "query": { "term": { "price": 2699 } } }
3.3 進階查詢
3.3.1 布爾組合
-
把各種其它查詢通過bool
(與)、must
(非)、must_not
(或)的方式進行組合should
GET /heima/_search { "query":{ "bool":{ "must": { "match": { "title": "最小" }}, "must_not": { "match": { "title": "查詢" }}, "should": { "match": { "title": "字段" }} } } }
3.3.2 範圍查詢
-
查詢找出那些落在指定區間内的數字或者時間range
GET /heima/_search { "query":{ "range": { "price": { "gte": 1000.0, "lt": 2800.00 } } } }
-
查詢允許以下字元:range
作符 說明 gt 大于 gte 大于等于 lt 小于 lte 小于等于
3.3.3 模糊查詢
-
查詢是fuzzy
查詢的模糊等價。它允許使用者搜尋詞條與實際詞條的拼寫出現偏差,但是偏差的編輯距離不得超過2term
GET /heima/_search { "query": { "fuzzy": { "title": "查詢" } } }
- 上面的查詢,也能查詢到查詢字段
- 我們可以通過
來指定允許的編輯距離:fuzziness
GET /heima/_search { "query": { "fuzzy": { "title": { "value":"查詢", "fuzziness":1 } } } }
3.4 過濾
3.4.1 條件查詢中進行過濾
- 所有的查詢都會影響到文檔的評分及排名。如果我們需要在查詢結果中進行過濾,并且不希望過濾條件影響評分,那麼就不要把過濾條件作為查詢條件來用。而是使用
方式:filter
GET /heima/_search { "query":{ "bool":{ "must":{ "match": { "title": "查詢字段" }}, "filter":{ "range":{"price":{"gt":2000.00,"lt":3800.00}} } } } }
- 注意:
中還可以再次進行filter
組合條件過濾。bool
3.4.2 無查詢條件,直接過濾
- 如果一次查詢隻有過濾,沒有查詢條件,不希望進行評分,我們可以使用
取代隻有 filter 語句的 bool 查詢。在性能上是完全相同的,但對于提高查詢簡潔性和清晰度有很大幫助。constant_score
GET /heima/_search { "query":{ "constant_score": { "filter": { "range":{"price":{"gt":2000.00,"lt":3000.00}} } } }
3.5 排序
3.5.1 單字段排序
-
可以讓我們按照不同的字段進行排序,并且通過sort
指定排序的方式order
GET /heima/_search { "query": { "match": { "title": "查詢字段" } }, "sort": [ { "price": { "order": "desc" } } ] }
3.5.2 多字段排序
- 假定我們想要結合使用 price和 _score(得分) 進行查詢,并且比對的結果首先按照價格排序,然後按照相關性得分排序:
GET /goods/_search { "query":{ "bool":{ "must":{ "match": { "title": "查詢字段" }}, "filter":{ "range":{"price":{"gt":200000,"lt":300000}} } } }, "sort": [ { "price": { "order": "desc" }}, { "_score": { "order": "desc" }} ] }
4 聚合aggregations
- 聚合可以讓我們極其友善的實作對資料的統計、分析。例如:
- 什麼品牌的手機最受歡迎?
- 這些手機的平均價格、最高價格、最低價格?
- 這些手機每月的銷售情況如何?
- 實作這些統計功能的比資料庫的sql要友善的多,而且查詢速度非常快,可以實作實時搜尋效果。
4.1 基本概念
- Elasticsearch中的聚合,包含多種類型,最常用的兩種,一個叫
,一個叫桶
:度量
4.1.1 桶(bucket)
- 桶的作用,是按照某種方式對資料進行分組,每一組資料在ES中稱為一個
,例如我們根據國籍對人劃分,可以得到桶
、中國桶
,英國桶
……或者我們按照年齡段對人進行劃分:010,1020,2030,3040等。日本桶
- Elasticsearch中提供的劃分桶的方式有很多:
- Date Histogram Aggregation:根據日期階梯分組,例如給定階梯為周,會自動每周分為一組
- Histogram Aggregation:根據數值階梯分組,與日期類似
- Terms Aggregation:根據詞條内容分組,詞條内容完全比對的為一組
- Range Aggregation:數值和日期的範圍分組,指定開始和結束,然後按段分組
- ……
- bucket aggregations 隻負責對資料進行分組,并不進行計算,是以往往bucket中往往會嵌套另一種聚合:metrics aggregations即度量
4.1.2 度量(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:求總數
- ……
-
為了測試聚合,我們先批量導入一些資料
建立索引:
PUT /cars { "settings": { "number_of_shards": 1, "number_of_replicas": 0 }, "mappings": { "transactions": { "properties": { "color": { "type": "keyword" }, "make": { "type": "keyword" } } } } }
- 注意:在ES中,需要進行聚合、排序、過濾的字段其處理方式比較特殊,是以不能被分詞。這裡我們将color和make這兩個文字類型的字段設定為keyword類型,這個類型不會被分詞,将來就可以參與聚合
- 導入資料
POST /cars/transactions/_bulk { "index": {}} { "price" : 10000, "color" : "red", "make" : "honda", "sold" : "2014-10-28" } { "index": {}} { "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" } { "index": {}} { "price" : 30000, "color" : "green", "make" : "ford", "sold" : "2014-05-18" } { "index": {}} { "price" : 15000, "color" : "blue", "make" : "toyota", "sold" : "2014-07-02" } { "index": {}} { "price" : 12000, "color" : "green", "make" : "toyota", "sold" : "2014-08-19" } { "index": {}} { "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" } { "index": {}} { "price" : 80000, "color" : "red", "make" : "bmw", "sold" : "2014-01-01" } { "index": {}} { "price" : 25000, "color" : "blue", "make" : "ford", "sold" : "2014-02-12" }
4.2 聚合為桶
- 首先,我們按照 汽車的顔色
來劃分color
桶
GET /cars/_search { "size" : 0, "aggs" : { "popular_colors" : { "terms" : { "field" : "color" } } } }
- size: 查詢條數,這裡設定為0,因為我們不關心搜尋到的資料,隻關心聚合結果,提高效率
- aggs:聲明這是一個聚合查詢,是aggregations的縮寫
- popular_colors:給這次聚合起一個名字,任意。
- terms:劃分桶的方式,這裡是根據詞條劃分
- field:劃分桶的字段
- terms:劃分桶的方式,這裡是根據詞條劃分
- popular_colors:給這次聚合起一個名字,任意。
- 結果:
{ "took": 1, "timed_out": false, "_shards": { "total": 1, "successful": 1, "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": "red", "doc_count": 4 }, { "key": "blue", "doc_count": 2 }, { "key": "green", "doc_count": 2 } ] } } }
- hits:查詢結果為空,因為我們設定了size為0
- aggregations:聚合的結果
- popular_colors:我們定義的聚合名稱
- buckets:查找到的桶,每個不同的color字段值都會形成一個桶
- key:這個桶對應的color字段的值
- doc_count:這個桶中的文檔數量
- 通過聚合的結果我們發現,目前紅色的小車比較暢銷!
4.3 桶内度量
- 前面的例子告訴我們每個桶裡面的文檔數量,這很有用。 但通常,我們的應用需要提供更複雜的文檔度量。 例如,每種顔色汽車的平均價格是多少?
- 是以,我們需要告訴Elasticsearch
,使用哪個字段
進行運算,這些資訊要嵌套在使用何種度量方式
内,桶
的運算會基于度量
内的文檔進行桶
- 現在,我們為剛剛的聚合結果添加 求價格平均值的度量:
GET /cars/_search { "size" : 0, "aggs" : { "popular_colors" : { "terms" : { "field" : "color" }, "aggs":{ "avg_price": { "avg": { "field": "price" } } } } } }
- aggs:我們在上一個aggs(popular_colors)中添加新的aggs。可見
也是一個聚合度量
- avg_price:聚合的名稱
- avg:度量的類型,這裡是求平均值
- field:度量運算的字段
- 結果:
"aggregations": { "popular_colors": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "red", "doc_count": 4, "avg_price": { "value": 32500 } }, { "key": "blue", "doc_count": 2, "avg_price": { "value": 20000 } }, { "key": "green", "doc_count": 2, "avg_price": { "value": 21000 } } ] } }
- 可以看到每個桶中都有自己的
字段,這是度量聚合的結果avg_price
4.4 桶内嵌套桶
- 剛剛的案例中,我們在桶内嵌套度量運算。事實上桶不僅可以嵌套運算, 還可以再嵌套其它桶。也就是說在每個分組中,再分更多組。
- 比如:我們想統計每種顔色的汽車中,分别屬于哪個制造商,按照
字段再進行分桶make
GET /cars/_search { "size" : 0, "aggs" : { "popular_colors" : { "terms" : { "field" : "color" }, "aggs":{ "avg_price": { "avg": { "field": "price" } }, "maker":{ "terms":{ "field":"make" } } } } } }
- 原來的color桶和avg計算我們不變
- maker:在嵌套的aggs下新添一個桶,叫做maker
- terms:桶的劃分類型依然是詞條
- filed:這裡根據make字段進行劃分
- 部分結果:
... {"aggregations": { "popular_colors": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "red", "doc_count": 4, "maker": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "honda", "doc_count": 3 }, { "key": "bmw", "doc_count": 1 } ] }, "avg_price": { "value": 32500 } }, { "key": "blue", "doc_count": 2, "maker": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "ford", "doc_count": 1 }, { "key": "toyota", "doc_count": 1 } ] }, "avg_price": { "value": 20000 } }, { "key": "green", "doc_count": 2, "maker": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "ford", "doc_count": 1 }, { "key": "toyota", "doc_count": 1 } ] }, "avg_price": { "value": 21000 } } ] } } } ...
- 我們可以看到,新的聚合
被嵌套在原來每一個maker
的桶中。color
- 每個顔色下面都根據
字段進行了分組make
- 我們能讀取到的資訊:
- 紅色車共有4輛
- 紅色車的平均售價是 $32,500 美元。
- 其中3輛是 Honda 本田制造,1輛是 BMW 寶馬制造。
4.5.劃分桶的其它方式
- 前面講了,劃分桶的方式有很多,例如:
- Date Histogram Aggregation:根據日期階梯分組,例如給定階梯為周,會自動每周分為一組
- Histogram Aggregation:根據數值階梯分組,與日期類似
- Terms Aggregation:根據詞條内容分組,詞條内容完全比對的為一組
- Range Aggregation:數值和日期的範圍分組,指定開始和結束,然後按段分組
- 剛剛的案例中,我們采用的是Terms Aggregation,即根據詞條劃分桶。
- 接下來,我們再學習幾個比較實用的:
4.5.1.階梯分桶Histogram
- 原理:
- histogram是把數值類型的字段,按照一定的階梯大小進行分組。你需要指定一個階梯值(interval)來劃分階梯大小。
- 舉例:
- 比如你有價格字段,如果你設定interval的值為200,那麼階梯就會是這樣的:0,200,400,600,…
- 上面列出的是每個階梯的key,也是區間的啟點。
- 如果一件商品的價格是450,會落入哪個階梯區間呢?計算公式如下:
bucket_key = Math.floor((value - offset) / interval) * interval + offset
- value:就是目前資料的值,本例中是450
- offset:起始偏移量,預設為0
- interval:階梯間隔,比如200
- 是以你得到的key = Math.floor((450 - 0) / 200) * 200 + 0 = 400
- 操作一下:
- 比如,我們對汽車的價格進行分組,指定間隔interval為5000:
GET /cars/_search { "size":0, "aggs":{ "price":{ "histogram": { "field": "price", "interval": 5000 } } } }
- 結果:
{ "took": 21, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 8, "max_score": 0, "hits": [] }, "aggregations": { "price": { "buckets": [ { "key": 10000, "doc_count": 2 }, { "key": 15000, "doc_count": 1 }, { "key": 20000, "doc_count": 2 }, { "key": 25000, "doc_count": 1 }, { "key": 30000, "doc_count": 1 }, { "key": 35000, "doc_count": 0 }, { "key": 40000, "doc_count": 0 }, { "key": 45000, "doc_count": 0 }, { "key": 50000, "doc_count": 0 }, { "key": 55000, "doc_count": 0 }, { "key": 60000, "doc_count": 0 }, { "key": 65000, "doc_count": 0 }, { "key": 70000, "doc_count": 0 }, { "key": 75000, "doc_count": 0 }, { "key": 80000, "doc_count": 1 } ] } } }
- 你會發現,中間有大量的文檔數量為0 的桶,看起來很醜。
-
我們可以增加一個參數min_doc_count為1,來限制最少文檔數量為1,這樣文檔數量為0的桶會被過濾
示例:
結果:GET /cars/_search { "size":0, "aggs":{ "price":{ "histogram": { "field": "price", "interval": 5000, "min_doc_count": 1 } } } }
{ "took": 15, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 8, "max_score": 0, "hits": [] }, "aggregations": { "price": { "buckets": [ { "key": 10000, "doc_count": 2 }, { "key": 15000, "doc_count": 1 }, { "key": 20000, "doc_count": 2 }, { "key": 25000, "doc_count": 1 }, { "key": 30000, "doc_count": 1 }, { "key": 80000, "doc_count": 1 } ] } } }
- 比如,我們對汽車的價格進行分組,指定間隔interval為5000:
4.5.2.範圍分桶range
- 範圍分桶與階梯分桶類似,也是把數字按照階段進行分組,隻不過range方式需要你自己指定每一組的起始和結束大小。