ElasticSearch的學習
簡介
ElasticSearch是一個分布式的開源搜索和分析引擎,MySQL專攻于數據的持久化存儲與管理(即CRUD),在真正要處理海量數據的檢索與分析時,ElasticSearch是更勝一籌的,可以秒級地從海量數據中檢索出需要需要的數據,而MySQL如果單表達到百萬以上的數據再進行檢索是非常慢的。
ElasticSearch功能有很多,包括各種檢索功能(應用程序搜索、網站搜索、企業搜索)、對檢索來的數據做處理分析(特別是日志處理和分析)、應用指標的監控、數據的分析和可視化等。
ElasticSearchm是目前全文搜索引擎的首選,可以快速地存儲、搜索和分析海量數據。數據默認放在內存中
其底層是用以前Apache的開源庫Lucene,對Lucene做了再一次的簡化封裝,直接提供REST API的操作接口,比如直接給ElasticSearch發請求就可以使用其復雜的檢索功能。
官方文檔
一、基本概念
1、Index(索引)
動詞,相當于MySQL中的insert,在MySQL中插入一條數據 = 在ElasticSearch中索引一條數據;
名詞,相當于MySQL中的Database,MySQL中的庫 = ElasticSearch的索引
2、Type(類型)【根據當前ES版本最新文檔,“Before 7.0.0, the mapping definition included a type name. Elasticsearch 7.0.0 and later no longer accept a default mapping. See Removal of mapping types .”,類型已被移除。具體原因及解決見 Mapping 知識塊】
在Index(索引)中,可以定義一個或多個類型;
類似于MySQL中的Table;每一種類型的數據放在一起
3、Document
保存在某個索引(Index)下,某種類型(Type)的一個數據(Document),文檔是JSON格式的,Document就像是MySQL中的某個Table里面的內容(一條條的記錄)
4、ElasticSearch概念 — 倒排索引機制
分詞:將整句分拆為單詞;
將拆分出來的單詞和該單詞出現的記錄存放在ElasticSearch額外維護的倒排索引表中
檢索:拆分檢索的數據為單詞,在倒排索引表中查詢這些單詞分別在哪些記錄中
相關性得分,將相關性得分高的記錄數據查出來
二、docker安裝ElasticSearch
修改 Linux 網絡設置
[root@localhost ~]# cd /etc/sysconfig/network-scripts/
[root@localhost network-scripts]# ls
ifcfg-eth0 ifdown-bnep ifdown-isdn ifdown-sit ifup ifup-ippp ifup-plusb ifup-sit ifup-wireless
ifcfg-eth1 ifdown-eth ifdown-post ifdown-Team ifup-aliases ifup-ipv6 ifup-post ifup-Team init.ipv6-global
ifcfg-lo ifdown-ippp ifdown-ppp ifdown-TeamPort ifup-bnep ifup-isdn ifup-ppp ifup-TeamPort network-functions
ifdown ifdown-ipv6 ifdown-routes ifdown-tunnel ifup-eth ifup-plip ifup-routes ifup-tunnel network-functions-ipv6
[root@localhost network-scripts]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:54:00:4d:77:d3 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0
valid_lft 83062sec preferred_lft 83062sec
inet6 fe80::5054:ff:fe4d:77d3/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:87:53:b1 brd ff:ff:ff:ff:ff:ff
inet 192.168.56.10/24 brd 192.168.56.255 scope global noprefixroute eth1
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:fe87:53b1/64 scope link
valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:da:49:0e:5c brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:daff:fe49:e5c/64 scope link
valid_lft forever preferred_lft forever
6: veth9f2577f@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 4a:e1:fa:70:da:a3 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::48e1:faff:fe70:daa3/64 scope link
valid_lft forever preferred_lft forever
8: veth938fc84@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 2a:e1:8c:3b:bc:66 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::28e1:8cff:fe3b:bc66/64 scope link
valid_lft forever preferred_lft forever
12: vethb2c64c0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 0e:fa:e2:77:1a:62 brd ff:ff:ff:ff:ff:ff link-netnsid 3
inet6 fe80::cfa:e2ff:fe77:1a62/64 scope link
valid_lft forever preferred_lft forever
18: veth9f373db@if17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 1e:42:ad:3e:88:f2 brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::1c42:adff:fe3e:88f2/64 scope link
valid_lft forever preferred_lft forever
[root@localhost network-scripts]# vi ifcfg-eth1
#VAGRANT-BEGIN
# The contents below are automatically generated by Vagrant. Do not modify.
NM_CONTROLLED=yes
BOOTPROTO=none
ONBOOT=yes
IPADDR=192.168.56.10
NETMASK=255.255.255.0
GATEWAY=192.168.56.1# 設置網關
DNS1=114.114.114.114# 公共DNS,用于解析域名
DNS2=8.8.8.8
DEVICE=eth1
PEERDNS=no
#VAGRANT-END
[root@localhost network-scripts]# service network restart
修改 Linux 的 yum 源
備份原 yum 源 使用新 yum 源 生成緩存
[root@localhost network-scripts]# mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
[root@localhost network-scripts]# curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.163.com/.help/CentOS7-Base-163.repo
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1572 100 1572 0 0 3849 0 --:--:-- --:--:-- --:--:-- 3852
[root@localhost network-scripts]# yum makecache
Loaded plugins: fastestmirror
Determining fastest mirrors
base | 3.6 kB 00:00:00
docker-ce-stable | 3.5 kB 00:00:00
extras | 2.9 kB 00:00:00
updates | 2.9 kB 00:00:00
(1/11): base/7/x86_64/other_db | 2.6 MB 00:00:01
(2/11): base/7/x86_64/filelists_db | 7.2 MB 00:00:01
(3/11): docker-ce-stable/7/x86_64/filelists_db | 24 kB 00:00:05
(4/11): extras/7/x86_64/filelists_db | 226 kB 00:00:00
(5/11): extras/7/x86_64/other_db | 134 kB 00:00:00
(6/11): extras/7/x86_64/primary_db | 225 kB 00:00:00
(7/11): updates/7/x86_64/primary_db | 5.6 MB 00:00:00
(8/11): updates/7/x86_64/other_db | 454 kB 00:00:00
(9/11): updates/7/x86_64/filelists_db | 3.4 MB 00:00:00
(10/11): docker-ce-stable/7/x86_64/other_db | 117 kB 00:00:01
(11/11): docker-ce-stable/7/x86_64/primary_db | 56 kB 00:00:09
Metadata Cache Created
1、下載鏡像文件
docker pull elasticsearch:7.10.1#存儲和檢索數據,相當于MySQL服務
docker pull kibana:7.10.1#可視化檢索數據,相當于SQLyog
兩個鏡像版本需統一
2、創建實例
1)、Elasticsearch
mkdir -p /mydata/elasticsearch/config
#將在docker容器中的ElasticSearch所有的配置文件信息都掛載在外面虛擬機中的/mydata/elasticsearch/config文件夾下,通過修改虛擬機/mydata/elasticsearch/config中配置文件的配置信息,就能修改docker容器中ElasticSearch的配置
mkdir -p /mydata/elasticsearch/data
#同樣將ElasticSearch中的一些數據文件掛載在外面虛擬機中的/mydata/elasticsearch/data文件夾下。
echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml
#將http.host: 0.0.0.0寫入elasticsearch.yml配置文件中,以便ElasticSearch可以讓遠程的任何機器進行訪問
#如果在之后一部步啟動elasticsearch容器報錯,并無法通過瀏覽器訪問elasticsearch的9200端口訪問到信息,可以查看日志排查,如是文件權限問題,進行以下修改。也可以事先進行文件權限修改
docker logs elasticsearch#查看elasticsearch啟動的日志
ll /mydata/elasticsearch/#查看/mydata/elasticsearch/目錄下的文件的權限,包括config、data、plugins
#發現三個文件都為drwxr-xr-x權限,文件所有人(這里是root用戶)可讀可寫可執行rwx,文件所有組和其他人只有可讀和可執行的權限r-x。
#解讀drwxr-xr-x:
#第1位 d代表文件類型,-:普通文件,d:目錄文件,l:鏈接文件,b:設備文件,c:字符設備文件,p:管道文件
#第2-4位rwx代表文件所有者(屬主)有可讀可寫可執行的權限
#第5-7位r-x代表文件所有組(屬組,與屬主同一組的用戶)有可讀可執行的權限
#第8-10位r-x代表其他人有可讀可執行的權限
#r:可讀,數字表示為4;w:可寫,數字表示為2;x:可執行,數字表示為1。
chmod -R 777 /mydata/elasticsearch/ #chmod是修改權限的命令;-R是可選項,表示遞歸;777表示將任何用戶任何組的權限改為可讀可寫可執行,對應了三個角色;改的是/mydata/elasticsearch/目錄下的所有文件
#-R是命令可選項,參考如下說明:
#-c : 若該文件權限確實已經更改,才顯示其更改動作
#-f : 若該文件權限無法被更改也不要顯示錯誤訊息
#-v : 顯示權限變更的詳細資料
#-R : 對目前目錄下的所有文件與子目錄進行相同的權限變更(即以遞回的方式逐個變更)
#--help : 顯示輔助說明
#--version : 顯示版本
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" -v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /mydata/elasticsearch/data:/usr/share/elasticsearch/data -v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins -d elasticsearch:7.10.1
#--name elasticsearch表示為ElasticSearch鏡像起個名字為elasticsearch;
#-p 9200:9200 -p 9300:9300暴露了兩個端口,一個為9200,一個為9300。9200是后來由REST API向ElasticSearch的9200端口發送http請求,9300是ElasticSearch在分布式集群狀態下節點之間的通信端口
#-e "discovery.type=single-node"ElasticSearch以單節點模式運行
#-e ES_JAVA_OPTS="-Xms64m -Xmx512m"很重要!如果不指定該條,ElasticSearch一啟動會將內存全部占用,會導致整個虛擬機卡死,因此在此指定Xms64m初始內存為64兆,最大占用內存為512兆(測試期間夠用,但真正服務上線后,公司的檢索服務器內存一般都是32G左右,因此可以給ElasticSearch多分配)
#-v /mydata/elasticsearch/config/elasticsearch.yml:/user/share/elasticsearch/config/elasticsearch.yml -v是進行掛載,相當于將容器中ElasticSearch中的所有配置,跟外部虛擬機創建的配置文件進行一一關聯,以后修改外部的就相當于修改容器內的。包括掛載data數據目錄、plugins插件目錄
#-d elasticsearch:7.10.1最后-d用elasticsearch:7.10.1鏡像啟動ElasticSearch
2)、Kibana
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.56.10:9200 -p 5601:5601 -d kibana:7.10.1
#-e修改參數ELASTICSEARCH_HOSTS,修改ES主機地址修改為自己虛擬機的地址
#-p映射到端口5601,通過訪問5601端口訪問到kibana的可視化界面
#然后從可視化界面,kibana把請求發送給ES的http://192.168.56.10:9200
三、初步檢索
*:http://192.168.56.10:9200
1、_cat
GET */cat/nodes# 查看所有節點
GET */_cat/health# 查看ES健康狀況
GET */_cat/master# 查看主節點
GET */_cat/indices# 查看所有索引show databases;
2、索引一個文檔(保存記錄)
保存一個數據,保存在哪個索引的哪個記錄下,指定用哪個唯一標識
PUT customer/external/1;在customer索引下的external類型下保存1號數據為:
PUT */customer/external/1# postman發送請求:
{"name":"Cyril_P"} # 并帶上數據:
PUT請求和POST請求都可以保存數據,但是PUT請求必須帶上id,POST請求可以不帶id。
如果POST請求不指定id,會自動生成id;如果指定id,并且該id之前沒數據就會新增,繼續指定該id,就會修改這個數據,并新增版本號
PUT可以新增可以修改,但PUT必須指定id;由于PUT需要指定id,我們一般都用來做修改操作,PUT不指定id會報錯
3、查詢文檔
GET */customer/external/1
{// 帶下劃線'_'表示元數據
"_index": "customer",// 在哪個索引
"_type": "external",// 在哪個類型
"_id": "1",// 記錄id
"_version": 1,// 版本號
"_seq_no": 0,// 并發控制字段,每次更新就會 1,用來做樂觀鎖
"_primary_term": 1,// 同上,主分片重新分配,如重啟,就會變化,用來做樂觀鎖
"found": true,// 為true,代表找到數據
"_source": {// 查詢到的真正內容
"name": "Cyril_P"
}
}
樂觀鎖處理多個修改請求,更新文檔uri需攜帶 ?if_seq_no=0&if_primary_trem=1
4、更新文檔
POST */customer/external/1/_update
{"doc": {"name": "PrPrPr"}}
會對比原來數據,與原來一樣就什么都不做,version、seq_no都不變;
或
POST */customer/external/1/
{"doc": {"name": "PrPrPr"}}
直接更新數據,version和seq_no會改變;
或
PUT */customer/external/1/
{"doc": {"name": "PrPrPr"}}
直接更新數據,version和seq_no會改變。
更新同時增加屬性,以上三個都可以:
{“doc”: {“name”: “PrPrPr”,“age”:21}}
5、刪除文檔&索引
DELETE */customer/external/1/
DELETE */customer
6、bulk批量API 【在這之后的請求體無法在postman中測試,均移步至Kibana上測試】
POST /customer/external/_bulk
{"index":{"_id":"1"}} # index表示插入動作
{"name":"Cyril_P"}
{"index":{"_id":"2"}}
{"name":"PrPrPrPr"}
語法格式:
{action:{metadata}}
{requestbody}
{action:{metadata}}
{requestbody}
復雜實例:(metadata前帶有"_",此處markdown沒有顯示,index、type和id前都有)
POST /_bulk
{"delete":{"_index":"website","_type":"blog","_id":"123"}}# delete表示刪除動作
{"create":{"_index":"website","_type":"blog","_id":"123"}}# create表示創建動作
{"title":"My First Blog POST"}# 真正的內容
{"index":{"_index":"website","_type":"blog"}}# index表示插入動作
{"title":"My Second Blog POST"}# 真正的內容
{"update":{"_index":"website","_type":"blog","_id":"123","retry_on_conflict":3}}# update表示更新動作
{"doc":{"title":"My Updated Blog POST"}}# 真正的內容
四、進階檢索
1、SearchAPI
ES支持兩種基本方式檢索:
一個是通過使用 REST request URI 發送搜索參數(uri 檢索參數);
另一個是通過使用 REST request body 來發送它們(uri 請求體)。
1)、檢索信息
GET /bank/_search# 檢索bank下所有信息,包括type和docs
GET /bank/_search?q=*&sort=account_number:asc# 請求參數方式檢索
響應結果解釋:
took —— ElasticSearch執行搜索的時間(毫秒)
time_out —— 表示搜索是否超時
_shards —— 表示多少個分片被搜索了,以及統計了成功/失敗的搜索分片
hits —— 搜索結果
hits.total —— 搜索結果
hits.hits —— 實際的搜索結果數組(默認為前10的文檔)
sort —— 結果的排序key(鍵)(沒有則按score排序)
score 和 max_score —— 相關性得分和最高得分(全文檢索用)
GET/bank/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"account_number": "asc"
}
]
}
HTTP 客戶端工具(postman),GET 請求不能攜帶請求體,我們變為 POST 也是一樣的,我們 POST 一個 JSON 格式的查詢請求體到_search API。
需要了解,一旦搜索的結果被返回,ElasticSearch 就完成了這次請求,并且不會維護任何服務端的資源或者結果的 cursor(游標)。
2、Query DSL
1)、基本語法格式
ElasticSearch 提供了一個可以執行查詢的 JSON 格式的 DSL(domain-specific language 領域特定語言),這個被稱為 Query DSL。
{
QUERY_NAME:{
ARGUMENT:VALUE,
ARGUMENT:VALUE,...
}
}
{
QUERY_NAME:{
FILED_NAME:{
ARGUMENT:VALUE,
ARGUMENT:VALUE,...
}
}
}
案例:
GET /bank/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"balance": {
"order": "desc"
}
}
],
"from": 0,
"size": 5
}
query 定義如何查詢; match_all 查詢類型【代表查詢所有的所有】,ES中可以在query中組合非常多的查詢類型完成復雜查詢; 除了query參數之外,我們也可以傳遞其他的參數以改變查詢結果。如sort、size等; from size 限定數,完成分頁功能; sort 排序,多字段排序,會在前序字段相等時后續字段內部排序,否則以前序字段為準。
2)、返回部分字段
案例:
GET /bank/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"balance": {
"order": "desc"
}
}
],
"from": 0,
"size": 5,
"_source": ["account_number","balance"]
}
真正內容"_source"只查詢"account_number"、"balance"兩個字段
3)、match【匹配查詢】
GET /bank/_search
{
"query": {
"match": {
"account_number": "20"
}
}
}
match返回 account_number = 20 的文檔。
字符串,全文檢索(如是單詞便進行全文檢索;如多個單詞的字符串,會先進行分詞,并進行全文檢索;都會根據相關性得分排序)
GET /bank/_search
{
"query": {
"match": {
"address": "mill lane"
}
}
}
會對檢索條件"mill lane"進行分詞匹配,最終查詢出 address 中包含 "mill "或者 "lane "或者 “mill lane” 的所有文檔,并給出相關性得分,全文檢索按照相關性得分進行排序。
4)、match_phrase【短語匹配】
將需要匹配的值當成一個整體單詞(不分詞)進行檢索。
GET /bank/_search
{
"query": {
"match_phrase": {
"address": "mill lane"
}
}
}
查出 address 中包含 “mill lane” 的所有文檔,并給出相關性得分,全文檢索按照相關性得分進行排序。
5)、multi_match【多字段匹配】
GET /bank/_search
{
"query": {
"multi_match": {
"query": "mill movico",
"fields": ["address","city"]
}
}
}
會對 “mill movico” 進行分詞,查出 address 或者 city 包含 "mill "或者 "movico "或者 “mill movico” 的所有文檔。
6)、bool【復合查詢】
bool 用來做復合查詢:
復合語句可以合并任何其他查詢語句,包括復合語句,意味著 復合語句之間可以互相嵌套,表達非常復雜的邏輯。
must、must_not、should:
GET /bank/_search
{
"query": {
"bool": {
"must": [
{"match": {
"gender": "M"
}},
{"match": {
"address": "mill"
}}
],
"must_not": [
{"match": {
"age": "18"
}}
],
"should": [
{"match": {
"lastname": "Hines"
}}
]
}
}
}
查詢出 gender 必須是 "M"并且 address 必須包含 “mill”,如果 lastname 里有 “Hines” 就最好并且會加額外相關性得分,但是 age 必須不能是 18 的所有文檔。
7)、filter【結果過濾】
并不是所有的查詢都需要產生相關性得分,特別是那些僅用于 “filtering”(過濾)的文檔。為了不計算相關性得分 ElasticSearch 會自動檢查場景并且優化查詢的執行。
GET /bank/_search
{
"query": {
"bool": {
"filter": [
{
"range": {
"age": {
"gte": 18,
"lte": 30
}
}
}
]
}
}
}
GET /bank/_search
{
"query": {
"bool": {
"must": [
{"match": {
"gender": "M"
}},
{"match": {
"address": "mill"
}}
],
"must_not": [
{"match": {
"age": "18"
}}
],
"should": [
{"match": {
"lastname": "Hines"
}}
],
"filter": [
{
"range": {
"age": {
"gte": 18,
"lte": 30
}
}
}
]
}
}
}
filter 不會計算相關性得分,會直接將不滿足 filter 的文檔給過濾掉。
8)、term
和 match 一樣,匹配某個屬性的值。全文檢索字段用 match ,其他非 text 字段匹配用 term 。
GET /bank/_search
{
"query": {
"term": {
"age": "25"
}
}
}
GET /bank/_search
{
"query": {
"match_phrase": {
"address": "927 Bay"
}
}
}
GET /bank/_search
{
"query": {
"match": {
"address.keyword": "927 Bay"
}
}
}
非text(非文本)字段建議使用 term 進行匹配 ,文本字段的全文檢索建議使用 match
對于文本字段的匹配,“match_phrase” 與 “address.keyword” 不同的是整個 "address.keyword "的內容就是 “927 Bay” 的全部值,進行的是精確匹配;而 “match_phrase” 做的是短語匹配,意思是 address 文本里面包含一個完整短語 “927 Bay”
9)、aggregations(執行聚合)
聚合提供了從函數中分組和提取數據的能力,最簡單的聚合方法大致等于SQL GROUP BY 和 SQL 聚合函數。在 ElasticSearch 中,可以執行搜索返回 hits(命中結果),并且同時返回聚合結果,把一個響應中的所有 hits(命中結果)分隔開。我們就能夠執行查詢和多個聚合,并且在一次使用中得到各自的(任何一個的)返回結果,使用一次簡潔的 API 來避免網絡往返。
搜索 address 中包含 mill 的所有人的年齡分布以及平均年齡,但不顯示這些人的詳情。
GET /bank/_search
{
"query": {# 先查詢出來 address 中包含 mill 的所有文檔
"match": {
"address": "mill"
}
},
"aggs": {# 執行聚合
"ageAgg": {# 本次聚合的名字,方便展示在結果集中
"terms": {# 聚合的類型【只看某個字段的信息】
"field": "age",# 對哪個字段進行聚合
"size": 10# 對聚合的結果作取幾條數據處理
}
},
"ageAvg":{# 本次聚合的名字,方便展示在結果集中
"avg": {# 聚合的類型 【取平均值】
"field": "age"# 對哪個字段進行聚合
}
},
"balanceAvg":{# 本次聚合的名字,方便展示在結果集中
"avg": {# 聚合的類型 【取平均值】
"field": "balance"# 對哪個字段進行聚合
}
}
},
"size": 0# 不顯示搜索數據
}
按照年齡聚合,并且請求這些年齡段的這些人的平均薪資
先根據年齡聚合,再在這些年齡的聚合中套用聚合算出薪資平均值
GET /bank/_search
{
"query": {
"match_all": {}
},
"aggs": {
"ageAgg": {
"terms": {
"field": "age",
"size": 100
},
"aggs": {
"balanceAvg": {
"avg": {
"field": "balance"
}
}
}
}
}
}
查出所有年齡分布,并且這些年齡段中 M 的平均薪資和 F 的平均薪資以及這個年齡段的總體平均薪資
GET /bank/_search
{
"query": {
"match_all": {}
},
"aggs": {
"ageAgg": {
"terms": {
"field": "age",
"size": 100
},
"aggs": {
"genderAgg": {
"terms": {
"field": "gender.keyword",
"size": 10
},
"aggs": {
"balanceAvg": {
"avg": {
"field": "balance"
}
}
}
},
"ageBalanceAvg":{
"avg": {
"field": "balance"
}
}
}
}
}
}
3、Mapping【詳細參考 】
1)、字段類型【詳細參考 】
核心類型 字符串(String) text, keyword 數字類型(Numeric) long, integer, short, byte, double, float, half_float, scaled_float 日期類型(Date) date 布爾類型(Boolean) boolean 二進制類型(Binary) binary
復合類型 數組類型(Array) Array 支持 不針對特定的類型 對象類型(Object) object 用于 單JSON對象 嵌套類型(Nested) nested 用于 JSON對象數組
地理類型 地理坐標(Geo-Points) geo_point 用于描述 經緯度坐標 地理圖形(Geo-Shape) geo_shape 用于描述 復雜形狀,如多邊形
特定類型 IP類型 ip 用于描述 ipv4 和 ipv6 地址 補全類型(Completion) completion 提供自動完成提示 令牌計數類型(Token count) token_count 用于 統計字符串中的詞條數量 附件類型(Attachment) 參考 mapper-attachments 插件,支持將附件如 Microsoft Office格式、 Open Document格式、ePub、HTML等等索引為 attachment 數據類型 抽取類型(Percolator) 接受特定領域查詢語言(query-dsl)的查詢
多字段 通常用于為不同目的用不同的方法索引同一個字段。例如,string 字段可以映射為一個 text 字段用于全文檢索,同樣可以映射為一個 keyword 字段用于排序和聚合。另外,你可以使用 standard analyzer,english analyzer,french analyzer 來索引一個text字段。 這就是 muti-fields 的目的。大多數的數據類型通過 fields 參數來支持 muti-fields。
2)、映射
Mapping(映射)
Mapping 是用來定義一個文檔(document),以及它所包含的屬性(field)是如何存儲和索引的。比如,使用 mapping 來定義:
哪些字符串屬性應該被看成全文本屬性(full text firlds); 哪些屬性包含數字、日期或者地理位置; 文檔中的所有屬性是否都能被索引( _all 配置); 日期的格式; 自定義映射規則來執行動態添加屬性
查看 mapping 信息:
GET /bank/_mapping
3)、新版本改變
ElasticSearch7 及以后版本移除了 type 概念
1、創建索引,并指定索引下每一個數據的類型,指定數據的映射規則
PUT /my_index# 發送PUT請求,指定索引,在索引后不需要加type了
{
"mappings": {# "mappings" 創建映射規則
"properties": {# "properties" 指明每一個屬性的映射類型
"age":{"type": "integer"},# "屬性名":{【詳細規則:】"type": "指定什么類型"}
"email":{"type": "keyword"},# "屬性名":{"type": "指定什么類型"}
"name":{"type": "text"}# "屬性名":{"type": "指定什么類型"}
}
}
}
2、添加新的字段映射
PUT /my_index/_mapping# 發送PUT請求,指定修改某個索引"index"下的映射"_mapping",僅限于添加新的字段
{
"properties": {# "properties" 指明每一個屬性的映射類型
"employee-id": {# 定義新增加的屬性,為"employee-id"
"type": "keyword", # 指定類型
"index": false# 映射參數"index"用于控制該屬性能否被索引,能否被檢索到,默認為true可以被索引
}
}
}
3、更新映射
對于已經存在的映射字段,不能通過上面兩個操作實現更新操作。更新必須通過創建新的索引并進行數據遷移。
4、數據遷移
先創建出一個正確映射,同時最好正確指定好每一個數據的類型,然后使用 “_reindex” 進行數據遷移
創建步驟參考第一個創建索引并指定每一個數據類型操作,字段名要統一,可以先查看舊索引的 mapping 信息,然后復制 properties 屬性再進行字段屬性類型修改
POST _reindex# 發送POST請求,進行"_reindex"索引下數據遷移操作
{
"source": {# 來源于哪個舊索引
"index": "bank",# 舊索引的索引名
"type": "account"# 如果是帶有type的,就寫上type名。沒有的話就可以不寫
},
"dest": {# 目標索引,新索引
"index": "newbank"# 新索引的名字
}
}
4、分詞
ES有個分詞器 tokenizer 能接收一個字符流,將之分割為獨立的tokens(詞元,通常是獨立的單詞),然后輸出 tokens 流。
同時ES提供了很多內置的分詞器,可以用來構建 custom analyzer(自定義分詞器)。
1)、安裝ik分詞器
注意:不要用默認 elasticsearch-plugin install xxx.zip 進行自動安裝。
要進入 https://github.com/medcl/elasticsearch-analysis-ik/releases 對應ES版本安裝
進入 ES 容器內部 plugins 目錄 docker exec -it 容器id /bin/bash wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.10.1/elasticsearch-analysis-ik-7.10.1.zip upzip 下載的文件 rm -rf *.zip mv elasticsearch/ ik 可以確認是否安裝好了分詞器 cd …/bin elasticsearch plugin list:即可列出系統的分詞器
2)、測試分詞器
POST _analyze
{
"analyzer": "ik_smart",
"text": ["我是中國人"]
}
POST _analyze
{
"analyzer": "ik_max_word",# 找到最大的單詞組合
"text": ["我是中國人"]
}
3)、自定義詞庫
修改 /usr/share/elasticsearch/plugins/ik/config 中的 IKAnalyzer.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java./dtd/properties.dtd">
<properties>
<comment>IK Analyzer 擴展配置</comment>
<!--用戶可以在這里配置自己的擴展字典 -->
<entry key="ext_dict"></entry>
<!--用戶可以在這里配置自己的擴展停止詞字典-->
<entry key="ext_stopwords"></entry>
<!--用戶可以在這里配置遠程擴展字典 -->
<entry key="remote_ext_dict">http://192.168.56.10/es/fenci.txt</entry>
<!--用戶可以在這里配置遠程擴展停止詞字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
五、ElasticSearch-Rest-Client
1、對比
1)、9300:TCP
spring-data-elasticsearch:transport-api.jar
2)、9200:HTTP
最終選擇 Elasticsearch-Rest-Client(elasticsearch-high-level-client)
2、實例
1)、導入依賴后編寫配置類
/**
* 1、導入依賴
* 2、編寫配置,給容器中注入一個 RestHighLevelClient
* 3、參照 API https://www./guide/en/elasticsearch/client/java-rest/7.x/java-rest-high.html
*/
@Configuration
public class MyElasticSearchConfig {
public static final RequestOptions COMMON_OPTIONS;
static {
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
// builder.addHeader("Authorization", "Bearer " TOKEN);
// builder.setHttpAsyncResponseConsumerFactory(
// new HttpAsyncResponseConsumerFactory
// .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));
COMMON_OPTIONS = builder.build();
}
@Bean
public RestHighLevelClient esRestClient() {
RestClientBuilder builder = null;
builder = RestClient.builder(new HttpHost("IP", 9200, "http"));
RestHighLevelClient client = new RestHighLevelClient(builder);
// RestHighLevelClient client = new RestHighLevelClient(
// RestClient.builder(
// new HttpHost("IP", 9200, "http")));
return client;
}
}
2)、測試類
@RunWith(SpringRunner.class)
@SpringBootTest
class MymallSearchApplicationTests {
@Autowired
private RestHighLevelClient client;
/**
*
*/
@ToString
@Data
static class Account {
private int account_number;
private int balance;
private String firstname;
private String lastname;
private int age;
private String gender;
private String address;
private String employer;
private String email;
private String city;
private String state;
}
/**
*
*/
@Test
public void searchIndex() throws IOException {
//1、創建檢索請求
SearchRequest searchRequest = new SearchRequest();
//指定索引
searchRequest.indices("bank");
//指定 DSL ,檢索條件
// SearchSourceBuilder searchSourceBuilder 封裝的條件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//1.1)、構造檢索條件
searchSourceBuilder.query(QueryBuilders.matchQuery("address", "mill"));
//1.2)、按照年齡的值分布進行聚合
TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);
searchSourceBuilder.aggregation(ageAgg);
//1.3)、計算平均薪資
AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");
searchSourceBuilder.aggregation(balanceAvg);
System.out.println("檢索條件:" searchSourceBuilder.toString());
searchRequest.source(searchSourceBuilder);
//2、執行檢索
SearchResponse searchResponse = client.search(searchRequest, MymallElasticSearchConfig.COMMON_OPTIONS);
//3、分析結果 searchResponse
System.out.println(searchResponse.toString());
// Map map = JSON.parseObject(searchResponse.toString(), Map.class);
//3.1)、獲取所有查到的數據
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
/**
* "_index": "bank",
* "_type": "account",
* "_id": "970",
* "_score": 5.4032025,
* "_source": {}
*/
// hit.getIndex();hit.getType();hit.getId();
String sourceAsString = hit.getSourceAsString();
Account account = JSON.parseObject(sourceAsString, Account.class);
System.out.println("account:" account);
}
//3.2)、獲取這次檢索到的分析信息
Aggregations aggregations = searchResponse.getAggregations();
// for (Aggregation aggregation : aggregations.asList()) {
// System.out.println("當前聚合:" aggregation.getName());
// }
Terms ageAgg1 = aggregations.get("ageAgg");
for (Terms.Bucket bucket : ageAgg1.getBuckets()) {
String keyAsString = bucket.getKeyAsString();
System.out.println("年齡:" keyAsString "==>" bucket.getDocCount());
}
Avg balanceAvg1 = aggregations.get("balanceAvg");
System.out.println("平均薪資:" balanceAvg1.getValue());
}
/**
* 測試存儲數據到 ES
* 也可以進行更新操作
*/
@Test
public void indexData() throws IOException {
IndexRequest indexRequest = new IndexRequest("users");
indexRequest.id("1");//數據的id
// indexRequest.source("username","PrPrPr","age",22,"gender","女");
User user = new User();
user.setUserName("PrPrPr");
user.setAge(23);
user.setGender("女");
String jsonString = JSON.toJSONString(user);
indexRequest.source(jsonString, XContentType.JSON);//要保存的內容
//執行操作
IndexResponse index = client.index(indexRequest, MymallElasticSearchConfig.COMMON_OPTIONS);
//提取有用的響應數據
System.out.println(index);
}
}
六、安裝 nginx
隨便啟動一個 nginx 實例,只是為了復制出配置(如果沒有該鏡像,也會先下載鏡像后啟動容器實例)
[root@localhost mydata]# mkdir nginx
[root@localhost mydata]# ls
elasticsearch mysql mysql8 nginx redis
[root@localhost mydata]# docker run -p 80:80 --name nginx -d nginx:1.19
Unable to find image 'nginx:1.19' locally
1.19: Pulling from library/nginx
a076a628af6f: Already exists
0732ab25fa22: Pull complete
d7f36f6fe38f: Pull complete
f72584a26f32: Pull complete
7125e4df9063: Pull complete
Digest: sha256:10b8cc432d56da8b61b070f4c7d2543a9ed17c2b23010b43af434fd40e2ca4aa
Status: Downloaded newer image for nginx:1.19
227cc822c4679c2f6de23bbc6cdd4b27c7555214a959e31bc2aff0ef4c8df0c4
將容器內的配置文件拷貝到當前目錄【別忘了命令后的點,并且 nginx 和點之間有個空格】
[root@localhost mydata]# ll nginx/
total 0
[root@localhost mydata]# docker container cp nginx:/etc/nginx .
[root@localhost mydata]# ll nginx/
total 36
drwxr-xr-x. 2 root root 26 Feb 27 16:39 conf.d
-rw-r--r--. 1 root root 1007 Dec 15 13:59 fastcgi_params
-rw-r--r--. 1 root root 2837 Dec 15 13:59 koi-utf
-rw-r--r--. 1 root root 2223 Dec 15 13:59 koi-win
-rw-r--r--. 1 root root 5231 Dec 15 13:59 mime.types
lrwxrwxrwx. 1 root root 22 Dec 15 13:59 modules -> /usr/lib/nginx/modules
-rw-r--r--. 1 root root 643 Dec 15 13:59 nginx.conf
-rw-r--r--. 1 root root 636 Dec 15 13:59 scgi_params
-rw-r--r--. 1 root root 664 Dec 15 13:59 uwsgi_params
-rw-r--r--. 1 root root 3610 Dec 15 13:59 win-utf
[root@localhost mydata]# docker stop nginx
nginx
[root@localhost mydata]# docker rm nginx
nginx
修改文件 nginx 名稱為 conf,并把這個 conf 文件夾移動到 /mydata/nginx 下
[root@localhost mydata]# mv nginx conf
[root@localhost mydata]# ls
conf elasticsearch mysql mysql8 redis
[root@localhost mydata]# mkdir nginx
[root@localhost mydata]# mv conf nginx/
[root@localhost mydata]# ls
elasticsearch mysql mysql8 nginx redis
docker run -p 80:80 --name nginx -v /mydata/nginx/html:/usr/share/nginx/html -v /mydata/nginx/logs:/var/log/nginx -v /mydata/nginx/conf:/etc/nginx -d nginx:1.19
來源:https://www./content-4-874951.html