目錄:
- ElasticSearch叢集特性
- es部署安裝,要踩的坑少不了
- ElasticSearch CRUD操作
- ElasticSearch Search操作
- ElasticSearch 分詞器analyzer
ElasticSearch,叢集特性
ES的叢集是基于Master Slave架構的
叢集中一個節點被選舉為主節點後,
它将負責管理叢集範圍内的所有變更,例如增加、删除索引,或者增加、删除節點等;
而主節點并不需要涉及到文檔級别的變更和搜尋等操作,是以當叢集隻擁有一個主節點的情況下,即使流量的增加它也不會成為瓶頸。 任何節點都可以成為主節點;
作為使用者,我們可以将請求發送到 叢集中的任何節點 ,包括主節點。 每個節點都知道任意文檔所處的位置,并且能夠将我們的請求直接轉發到存儲我們所需文檔的節點。 無論我們将請求發送到哪個節點,它都能負責從各個包含我們所需文檔的節點收集回資料,并将最終結果傳回給用戶端。
分片
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_add-an-index.html https://www.elastic.co/guide/cn/elasticsearch/guide/current/routing-value.html#routing-valueElasticsearch 是利用分片将資料分發到叢集内各處的。分片是資料的容器,文檔儲存在分片内,分片又被配置設定到叢集内的各個節點裡。 當你的叢集規模擴大或者縮小時, Elasticsearch 會自動的在各節點中遷移分片,使得資料仍然均勻分布在叢集裡;
一個分片可以是 主 分片或者 副本 分片。 索引内任意一個文檔都歸屬于一個主分片,是以主分片的數目決定着索引能夠儲存的最大資料量。
技術上來說,一個主分片最大能夠存儲 Integer.MAX_VALUE - 128 個文檔,但是實際最大值還需要參考你的使用場景:包括你使用的硬體, 文檔的大小和複雜程度,索引和查詢文檔的方式以及你期望的響應時長。
一個副本分片隻是一個主分片的拷貝。副本分片作為硬體故障時保護資料不丢失的備援備份,并為搜尋和傳回文檔等讀操作提供服務。
在索引建立的時候就已經确定了主分片數,但是副本分片數可以随時修改。
注:索引建立的時候就需要确定好主分片的數量,因為索引一旦建立完成後,主分片的數量将不可以再變更,隻能動态的修改副本分片數,而副本分片數隻能提供搜尋和傳回文檔等讀操作時提供相關的服務,是以通過增加副本數可以增加叢集讀操作的可用性;但是對于索引中資料的存儲,還是隻能儲存在主分片上,是以,合理的确定主分片的值,對于後續的叢集的擴充是很有必要和好處的;
建立資料時的路由政策
當索引一個文檔的時候,文檔會被存儲到一個主分片中。 Elasticsearch 如何知道一個文檔應該存放到哪個分片中呢?當我們建立文檔時,它如何決定這個文檔應當被存儲在分片 1 還是分片 2 中呢
首先這肯定不會是随機的,否則将來要擷取文檔的時候我們就不知道從何處尋找了。實際上,這個過程是根據下面這個公式決定的:shard = hash(routing) % number_of_primary_shards
routing 是一個可變值,預設是文檔的 _ id ,也可以設定成一個自定義的值。 routing 通過 hash 函數生成一個數字,然後這個數字再除以 number_of_primary_shards (主分片的數量)後得到 餘數 。這個分布在 0 到 number_of_primary_shards-1 之間的餘數,就是我們所尋求的文檔所在分片的位置;
這就解釋了為什麼我們要在建立索引的時候就确定好主分片的數量 并且永遠不會改變這個數量:因為如果數量變化了,那麼所有之前路由的值都會無效,文檔也再也找不到了。
那麼前期如何更好的設計好我們的ES叢集的主分片數,來保證可以支撐後續的業務呢?
詳情可以參考:資料模組化-擴容設計章節
https://www.elastic.co/guide/cn/elasticsearch/guide/current/scale.html所有的文檔 API( get 、 index 、 delete 、 bulk 、 update 以及 mget )都接受一個叫做 routing的路由參數 ,通過這個參數我們可以自定義文檔到分片的映射。一個自定義的路由參數可以用來確定所有相關的文檔——例如所有屬于同一個使用者的文檔——都被存儲到同一個分片中。除了索引建立時不支援路由外,其他的資料導入,搜尋等操作都可以指定路由進行操作;索引建立肯定是由主節點來操作的
原創聲明:作者:Arnold.zhao 部落格園位址:https://www.cnblogs.com/zh94ES部署安裝,要踩的坑少不了
此處直接使用的是elasticsearch-7.3.2的版本,是以提示:Elasticsearch的未來版本将需要Java 11;您的Java版本從[/opt/package/jdk1.8.0_241/jre]不滿足此要求,此處直接注釋掉了本地環境變量對JDK的映射,直接使用es自帶的java啟動就行
future versions of Elasticsearch will require Java 11; your Java version from [/opt/package/jdk1.8.0_241/jre] does not meet this requirement
如下所示:為了安全考慮es不支援直接使用root使用者啟動
[2020-06-15T10:24:11,746][WARN ][o.e.b.ElasticsearchUncaughtExceptionHandler] [VM_0_5_centos] uncaught exception in thread [main]
org.elasticsearch.bootstrap.StartupException: java.lang.RuntimeException: can not run elasticsearch as root
此處建立一個新的elsearch使用者進行啟動
建立elsearch使用者組 以及elsearch使用者
groupadd elsearch
useradd elsearch -g elsearch -p elasticsearch
将對應的elasticsearch-7.3.2目錄授權給elsearch
chown -R elsearch:elsearch /opt/shengheApp/elasticsearch/elasticsearch-7.3.2
切換使用者進行es的啟動
su elsearch #切換賬戶
cd elasticsearch/bin #進入你的elasticsearch目錄下的bin目錄
./elasticsearch
# 指定jvm記憶體啟動,
ES_JAVA_OPTS="-Xms512m" ./bin/elasticsearch
背景啟動模式
./elasticsearch -d
修改ES配置為所有IP都可以通路,network.host修改為 0.0.0.0
network.host: 0.0.0.0
修改完IP配置項後,啟動将會異常,提示如下:
[VM_0_5_centos] publish_address {172.17.0.5:9300}, bound_addresses {[::]:9300}
[2020-06-15T10:46:53,894][INFO ][o.e.b.BootstrapChecks ] [VM_0_5_centos] bound or publishing to a non-loopback address, enforcing bootstrap checks
ERROR: [3] bootstrap checks failed
[1]: initial heap size [536870912] not equal to maximum heap size [1073741824]; this can cause resize pauses and prevents mlockall from locking the entire heap
[2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
[3]: the default discovery settings are unsuitable for production use; at least one of [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured
解決方式如下:
解決第一個警告:
由于我本機記憶體不足是以啟動的時候,直接指定了JVM參數進行了啟動:ES_JAVA_OPTS="-Xms512m" ./bin/elasticsearch
但是:bin/elasticsearch在啟動的時候會去加載config/jvm.options 下的jvm配置,由于jvm.options下Xms和Xmx配置為1g,是以啟動的時候提示沖突了,初始堆大小512M和最大堆大小1g,不比對;解決方式是,啟動的時候将指令更改為: ES_JAVA_OPTS="-Xms512m -Xmx512M" ./bin/elasticsearch , 或者直接修改jvm.options中Xms和Xmx的大小,然後直接 ./bin/elasticsearch啟動也行
解決第二個警告:
臨時提高了vm.max_map_count的大小,此操作需要root權限:
sysctl -w vm.max_map_count=262144
sysctl -a|grep vm.max_map_count # 設定完成後可以通過該指令檢視是否設定成功
永久修改vm.max_map_count的大小:
vi /etc/sysctl.conf 添加下面配置: vm.max_map_count=655360 并執行指令: sysctl -p 然後,重新啟動elasticsearch,即可啟動成功。
解決第三個警告:
叢集節點問題,我們現在隻啟動一個節點測試,是以修改目前節點的名稱為:node-1,然後配置cluster.initial_master_nodes 初始化節點為自身的node-1節點即可;
配置elasticsearch.yml檔案如下:
node.name: node-1
cluster.initial_master_nodes: ["node-1"]
然後重新開機es即可
ElasticSearch,CRUD操作
索引受檔案系統的限制。僅可能為小寫字母,不能下劃線開頭。同時需遵守下列規則:
不能包括 , /, *, ?, ", <, >, |, 空格, 逗号, #
7.0版本之前可以使用冒号:,但不建議使用并在7.0版本之後不再支援
不能以這些字元 -, _, + 開頭
不能包括 . 或 …
長度不能超過 255 個字元
以上這些命名限制是因為當Elasticsearch使用索引名稱作為磁盤上的目錄名稱,這些名稱必須符合不同作業系統的約定。
我猜想未來可能會放開這些限制,因為我們使用uuid關聯索引放在磁盤上,而不使用索引名稱。
類型
類型名稱可以包括除了null的任何字元,不能以下劃線開頭。7.0版本之後不再支援類型,預設為_doc.
基于ES7.0+版本
1、
新增一個文檔
PUT twitter/_doc/1
{
"user": "GB",
"uid": 1,
"city": "Beijing",
"province": "Beijing",
"country": "China"
}
如上,建立一個twitter的索引,并且建立一個叫做_doc的type,并插入一個文檔1,(在ES7中,一個index隻能有一個type,如果建立多個type将會提示異常,預設情況下因為隻能建立一個type,是以預設叫做_doc即可;)
新插入的資料一般不會實時參與到搜尋中,可以通過調用如下接口,使ES強勢進行一次refersh操作;(除此之外在建立index時也可以設定referch的周期,預設為1,也就是每一秒重新整理一下新的資料到索引中,)
2、
新增資料并實時重新整理到索引中
PUT twitter/_doc/1?refresh=true
{
"user": "GB",
"uid": 1,
"city": "Beijing",
"province": "Beijing",
"country": "China"
}
預設情況下執行(上述1的)dsl語句, 第一步判斷是确定插入的文檔是否指定id,如果沒有指定id,系統會預設生成一個唯一id,上述我們指定了id為(1),是以插入es時,将按照我們指定的id進行插入,
并且,如果目前該Id是在es中已經存在的,那麼此時将會進行第二次判斷,檢查插入時是否指定了_version,如果插入時沒有指定_version,那麼對于已有的doc資料,_version會進行遞增,并将文檔進行更新覆寫,如果插入時指定了_version,那麼判斷目前所指定的_version于現有的文檔的_version是否相等,相等則覆寫,不相等則插入失敗(注意:這裡則類似于一個樂觀鎖的操作了,如果有類似的資料插入時的場景通過該_version則可以實作一個樂觀鎖的操作);
除此之外,在插入資料時,如果不想做修改操作,那麼可以使用
使用create類型,表示隻新增,資料存在時不做修改操作
PUT twitter/_doc/1?optype=create
或者
PUT twitter/_create/1
這兩個文法都表示使用類型為create的方式來建立文檔,如果目前文檔已經存在,則直接報錯,不會進行覆寫更新;
optype的類型有兩個,index和create,我們預設使用 PUT twitter/_doc/1 建立資料時,其實就等價于 PUT twitter/_doc/1?optype=index
使用post新增文檔
在上面,特意為我們的文檔配置設定了一個ID。其實在實際的應用中,這個并不必要。相反,當我們配置設定一個ID時,在資料導入的時候會檢查這個ID的文檔是否存在,如果是已經存在,那麼就更新器版本。如果不存在,就建立一個新的文檔。如果我們不指定文檔的ID,轉而讓Elasticsearch自動幫我們生成一個ID,這樣的速度更快。在這種情況下,我們必須使用POST,而不是PUT
使用POST請求不指定ID時,es自動對應生成ID
POST twitter/_doc
{
"user": "GB",
"uid": 1,
"city": "Beijing",
"province": "Beijing",
"country": "China"
}
GET資料
擷取twitter索引文檔為1的資料
GET twitter/_doc/1
擷取twitter索引文檔為1的資料,并且隻傳回這個文檔的 _source 部分
GET twitter/_doc/1/_source
隻擷取source的部分字段
GET twitter/_doc/1?_source=city,age,province
_MGET資料
擷取多個文檔的資料
GET _mget
{
"docs": [
{
"_index": "twitter",
"_id": 1
},
{
"_index": "twitter",
"_id": 2
}
]
}
擷取多個文檔的資料,并且隻傳回部分字段
GET _mget
{
"docs": [
{
"_index": "twitter",
"_id": 1,
"_source":["age", "city"]
},
{
"_index": "twitter",
"_id": 2,
"_source":["province", "address"]
}
]
}
直接擷取id為1和2的資料,(簡化後的寫法)
GET twitter/_doc/_mget
{
"ids": ["1", "2"]
}
修改文檔(全量修改,指定字段修改,先查詢再修改)
使用PUT的方式預設就會進行資料的更新添加,這個上述也都提到過,但是使用PUT的方式是全量更新,如果那些字段不指定的話,将會更新為一個空值;
PUT twitter/_doc/1
{
"user": "GB",
"uid": 1,
"city": "北京",
"province": "北京",
"country": "中國",
"location":{
"lat":"29.084661",
"lon":"111.335210"
}
}
是以可以使用POST的方式進行更新,隻需要将待修改的字段列出來即可;
POST twitter/_update/1
{
"doc": {
"city": "成都",
"province": "四川"
}
}
先查詢再更新_update_by_query
POST twitter/_update_by_query
{
"query": {
"match": {
"user": "GB"
}
},
"script": {
"source": "ctx._source.city = params.city;ctx._source.province = params.province;ctx._source.country = params.country",
"lang": "painless",
"params": {
"city": "上海",
"province": "上海",
"country": "中國"
}
}
}
修改一個文檔,如果目前文檔不存在則新增該文檔
doc_as_upsert參數檢查具有給定ID的文檔是否已經存在,并将提供的doc與現有文檔合并。 如果不存在具有給定ID的文檔,則會插入具有給定文檔内容的新文檔。
下面的示例使用doc_as_upsert合并到ID為3的文檔中,或者如果不存在則插入一個新文檔:
POST /catalog/_update/3
{
"doc": {
"author": "Albert Paro",
"title": "Elasticsearch 5.0 Cookbook",
"description": "Elasticsearch 5.0 Cookbook Third Edition",
"price": "54.99"
},
"doc_as_upsert": true
}
檢查文檔是否存在
HEAD twitter/_doc/1
删除一個文檔
DELETE twitter/_doc/1
搜尋并删除_delete_by_query
POST twitter/_delete_by_query
{
"query": {
"match": {
"city": "上海"
}
}
}
_bulk批量操作
使用_bulk可以執行批量的資料插入,批量的資料更新,批量的資料删除,
批量的資料插入,使用index類型,表示存在即更新,不存在則新增
POST _bulk
{ "index" : { "_index" : "twitter", "_id": 1} }
{"user":"雙榆樹-張三","message":"今兒天氣不錯啊,出去轉轉去","uid":2,"age":20,"city":"北京","province":"北京","country":"中國","address":"中國北京市海澱區","location":{"lat":"39.970718","lon":"116.325747"}}
{ "index" : { "_index" : "twitter", "_id": 2 }}
{"user":"東城區-老劉","message":"出發,下一站雲南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中國","address":"中國北京市東城區台基廠三條3号","location":{"lat":"39.904313","lon":"116.412754"}}
批量資料插入,使用 create類型,id不存在則插入,存在則抛異常不作任何操作
POST _bulk
{ "create" : { "_index" : "twitter", "_id": 1} }
{"user":"雙榆樹-張三","message":"今兒天氣不錯啊,出去轉轉去","uid":2,"age":20,"city":"北京","province":"北京","country":"中國","address":"中國北京市海澱區","location":{"lat":"39.970718","lon":"116.325747"}}
批量資料删除 ,delete類型
POST _bulk
{ "delete" : { "_index" : "twitter", "_id": 1 }}
批量資料更新
POST _bulk
{ "update" : { "_index" : "twitter", "_id": 2 }}
{"doc": { "city": "長沙"}}
系統指令
檢視ES資訊
GET /
關閉索引(關閉索引後,将阻止讀/寫操作)
POST twitter/_close
開啟索引
POST twitter/_open
當機索引(當機索引後,該索引将阻止寫操作)
POST twitter/_freeze
索引當機後,搜尋時需加上ignore_throttled=false參數來進行搜尋
POST twitter/_search?ignore_throttled=false
索引解凍
POST twitter/_unfreeze
ElasticSearch,Search操作
query進行全局搜尋,aggregation可以進行全局的資料統計和分析
搜尋所有文檔
搜尋該cluster下的所有index,預設傳回10個
GET /_search = GET /_all/_search
GET /_search?size=20
同時對多個index進行搜尋
POST /index1,index2,index3/_search
針對所有以index為開頭的索引來進行搜尋,但是排除index3索引
POST /index*,-index3/_search
隻搜尋索引名為twitter的索引
GET twitter/_search
搜尋後設定隻傳回指定的字段
使用_source表示隻傳回 user,和city字段
GET twitter/_search
{
"_source": ["user", "city"],
"query": {
"match_all": {
}
}
}
設定_source 為false表示不傳回任何的_source資訊
GET twitter/_search
{
"_source": false,
"query": {
"match": {
"user": "張三"
}
}
}
使用通配符的方式表示隻傳回 user*以及location*的資料,但是對于*.lat字段則不進行傳回
GET twitter/_search
{
"_source": {
"includes": [
"user*",
"location*"
],
"excludes": [
"*.lat"
]
},
"query": {
"match_all": {}
}
}
創造傳回字段script_fields
當我們的想要擷取的field可能在_source裡根本沒有時,那麼我們可以使用script field來生成這些field;
GET twitter/_search
{
"query": {
"match_all": {}
},
"script_fields": {
"years_to_100": {
"script": {
"lang": "painless",
"source": "100-doc['age'].value"
}
},
"year_of_birth":{
"script": "2019 - doc['age'].value"
}
}
}
傳回結果是:
"hits" : [
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"fields" : {
"years_to_100" : [
80
],
"year_of_birth" : [
1999
]
}
},
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"fields" : {
"years_to_100" : [
70
],
"year_of_birth" : [
1989
]
}
},
...
]
必須注意的是這種使用script的方法來生成查詢的結果對于大量的文檔來說,可能會占用大量資源。
match和term的差別解釋
term是代表完全比對,也就是精确查詢,搜尋前不會再對所要搜尋的詞進行分詞拆解。
https://www.jianshu.com/p/d5583dff4157而使用match進行搜尋的時候,會先将所要搜尋的詞進行分詞拆分,拆完後再來進行比對;
建立一個索引資料結構mapping
後續的示範都是基于如下的結構進行的示範;
PUT twitter/_mapping
{
"properties": {
"address": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"age": {
"type": "long"
},
"city": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"country": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"location": {
"type": "geo_point"
},
"message": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"province": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"uid": {
"type": "long"
},
"user": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
查詢資料(Match query 搜尋詞分詞比對)
搜尋 twitter索引中user字段為“朝陽區-老賈”的詞;(注意,此處我們使用的是match,也就是會将我們的“朝陽區-老賈”按照預設分詞器 分詞後再進行相關搜尋比對)
GET twitter/_search
{
"query": {
"match": {
"user": "朝陽區-老賈"
}
}
}
當我們使用上述的 match query 進行查詢的時候,預設的操作是OR的關系,比如上述的DSL語句實際上于以下語句是對等的:
GET twitter/_search
{
"query": {
"match": {
"user": {
"query": "朝陽區-老賈",
"operator": "or"
}
}
}
}
預設的match query的操作就是or的關系:上述的dsl語句的查詢結果是任何的文檔隻要比對:
“朝”,“陽”,“區”,“老”及“賈”這5個字中的任何一個字,則都将會被比對到;
預設情況下我們指定搜尋的詞,如果不指定分詞器的話,也就是使用的預設分詞器,預設分詞器在進行中文分詞的時候,就隻是會把對應的中文詞進行一個一個拆開進行分詞,是以我們上述由于是使用的match,檢索的“朝陽區-老賈”是以分詞後的結果就是“朝“陽”區”“老”賈”;是以隻要是文檔user中存在這幾個詞中的任何一個,則都會被檢索出來
設定最少比對的詞的數量minimum_should_match
使用minimum_should_match來設定至少比對的索引詞term,也就是說我們的搜尋結果中:
至少要比對到:
“朝”,“陽”,“區”,“老”及“賈這5個中的3個字才可以
GET twitter/_search
{
"query": {
"match": {
"user": {
"query": "朝陽區-老賈",
"operator": "or",
"minimum_should_match": 3
}
}
}
}
更改為and關系的match query
預設情況下我們的match query是or的關系,這個上述已經說明過了,不過我們也可以動态的将其更改為and的關系,比如,如下的dsl語句:
GET twitter/_search
{
"query": {
"match": {
"user": {
"query": "朝陽區-老賈",
"operator": "and"
}
}
}
}
更改為and關系後,也就是每個分詞後的結果都是and的關系,及我們的分詞結果
“朝”,“陽”,“區”,“老”“賈”這幾個詞之間都是and關系,也就是說,我們的搜尋結果中是必須要包含這幾個詞,才可以;
這種寫法,其實和直接使用 term 很相似,因為使用term的搜尋詞,則都不會進行分詞處理,預設是必須精确比對,是以對于上述的這種使用場景,直接使用term效率會更高一些,省略了match的分詞這個步驟,并且結果也都是擷取精确比對後的結果;
Multi_query(比對多個字段)
在上述的搜尋中,我們都是特别的指明了個一個user字段來進行的搜尋查詢,但是在實際使用中,我們可能并不知道那個字段含有這個關鍵詞,是以對于這種情況下則可以使用multi_query來進行搜尋;
GET twitter/_search
{
"query": {
"multi_match": {
"query": "朝陽",
"fields": [
"user",
"address^3",
"message"
],
"type": "best_fields"
}
}
}
上述同時對三個fields: user,adress及message進行搜尋,
同時對address含有 “朝陽” 的文檔的分數進行3倍的權重;
權重的作用是為了計算傳回結果的相似度的值,比如此處對“address”進行了3倍的權重,那麼此時如果是address中包含“朝陽時”所對應的傳回結果的相似度就相對最高,所對應的排名的順序就越靠前;
預設情況下如果不使用order by進行排序的話,則全部是按照相似度的高低來進行排序的;
Prefix query(隻比對字首)
傳回在提供的字段中包含特定字首的文檔。(是隻比對字首,)
GET twitter/_search
{
"query": {
"prefix": {
"user": {
"value": "朝"
}
}
}
}
Term query(精确比對)
Term query會在給定字段中進行精确的字詞比對,搜尋前不會再對搜尋詞進行分詞拆解。
GET twitter/_search
{
"query": {
"term": {
"user.keyword": {
"value": "朝陽區-老賈"
}
}
}
}
Term query 是精确比對一個字詞,如果是比對多個字詞,則将會無效,如下所示:
查詢“朝陽區“空格”老賈“ 因為有空格的存在,是以預設情況下這是兩個詞了,是以對于搜尋的結果,
可能會是無效的(未驗證,待驗證具體效果是否真的如此)
GET twitter/_search
{
"query": {
"term": {
"user.keyword": {
"value": "朝陽區 老賈"
}
}
}
}
Terms query(多個詞同時精确比對)
是以,對于兩個詞的精确比對,則應該是使用 terms
使用terms進行比對,預設情況下是精确比對對應的多個詞,并且是OR的關系;也就是說:隻要比對“朝陽區”或者“老賈”則都算是比對完成;
是以,此處使用term解決了多精确比對的問題,但是如果是對于要精确比對到“朝陽區 老賈”這樣一個詞的場景的話,則使用Terms也是不合适的,此時則需要使用boot query進行must 的 term and term的判斷了;
是以其實這也就是一個對應的場景問題了,如果在錄入這個資料的時候是按照“朝陽區-老賈”而不是空格來錄入的話,那麼查詢起來則也就友善很多了,但其實不同的query查詢器,對應的應用場景則也是各有好處和優劣了;
GET twitter/_search
{
"query": {
"terms": {
"user.keyword": [
"朝陽區",
"老賈
]
}
}
}
city在我們的mapping中是一個multi-field項。它既是text也是keyword類型。對于一個keyword類型的項來說,這個項裡面的所有字元都被當做一個字元串。它們在建立文檔時,不需要進行index。keyword字段用于精确搜尋,aggregation和排序(sorting),是以我們此處也是使用的term來進行的比對;
bool query(複合查詢)
符合查詢通過将上述的多個查詢方式組合起來,進而形成更大的複雜的查詢邏輯,
bool query的查詢格式一般情況下是:
must: 必須比對。貢獻算分 (多個term之間是 and 關系)
must_not:過濾子句,必須不能比對,但不貢獻算分 (and 關系)
should: 選擇性比對,至少滿足一條。貢獻算分 (多個term之間是or的關系)
filter: 過濾子句,必須比對,但不貢獻算分(filter 确定是否包含在檢索結果中,後續再針對性說明)
POST _search
{
"query": {
"bool" : {
"must" : {
"term" : { "user" : "kimchy" }
},
"filter": {
"term" : { "tag" : "tech" }
},
"must_not" : {
"range" : {
"age" : { "gte" : 10, "lte" : 20 }
}
},
"should" : [
{ "term" : { "tag" : "wow" } },
{ "term" : { "tag" : "elasticsearch" } }
],
"minimum_should_match" : 1,
"boost" : 1.0
}
}
}
表示查詢,user字段既包含 朝陽區 又包含老賈的詞,然後進行傳回
GET twitter/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"user": "朝陽區"
}
},
{
"match": {
"user": "老賈"
}
}
]
}
}
}
以下dsl的意思是,age必須是30歲,但是如果文檔裡含有“Hanppy birthday”,相關性會更高,那麼搜尋得到的結果會排在前面;
GET twitter/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"age": "30"
}
}
],
"should": [
{
"match_phrase": {
"message": "Happy birthday"
}
}
]
}
}
}
如果是在不使用must,must_not以及filter的情況下,直接使用should進行比對,則是表示 or 的關系;一個或者更多的should必須有一個比對才會有搜尋結果;
query range(範圍查詢)
查詢年齡介于30到40歲的文檔資料
GET twitter/_search
{
"query": {
"range": {
"age": {
"gte": 30,
"lte": 40
}
}
}
}
查詢字段是否存在(query exists)
如果文檔裡隻要city這個字段不為空,那麼就會被傳回。反之,如果一個文檔裡city這個字段是空的,那麼就不會傳回。
GET twitter/_search
{
"query": {
"exists": {
"field": "city"
}
}
}
比對短語(query match_phrase)
query match_phrase 要求所有的分詞必須同時出現在文檔中,同時位置必須緊鄰一緻,
使用slop 1表示Happy和birthday之前是可以允許一個 詞 的差别。
GET twitter/_search
{
"query": {
"match_phrase": {
"message": {
"query": "Happy birthday",
"slop": 1
}
}
},
"highlight": {
"fields": {
"message": {}
}
}
}
Profile API
Profile API是調試工具。 它添加了有關執行的詳細資訊搜尋請求中的每個元件。 它為使用者提供有關搜尋的每個步驟的洞察力請求執行并可以幫助确定某些請求為何緩慢。
GET twitter/_search
{
"profile": "true",
"query": {
"match": {
"city": "北京"
}
}
}
在上面,我們加上了"profile":"true"後,除了顯示搜尋的結果之外,還顯示profile的資訊:
"profile" : {
"shards" : [
{
"id" : "[ZXGhn-90SISq1lePV3c1sA][twitter][0]",
"searches" : [
{
"query" : [
{
"type" : "BooleanQuery",
"description" : "city:北 city:京",
"time_in_nanos" : 1390064,
"breakdown" : {
"set_min_competitive_score_count" : 0,
"match_count" : 5,
"shallow_advance_count" : 0,
"set_min_competitive_score" : 0,
"next_doc" : 31728,
"match" : 3337,
"next_doc_count" : 5,
"score_count" : 5,
"compute_max_score_count" : 0,
"compute_max_score" : 0,
"advance" : 22347,
"advance_count" : 1,
"score" : 16639,
"build_scorer_count" : 2,
"create_weight" : 342219,
"shallow_advance" : 0,
"create_weight_count" : 1,
"build_scorer" : 973775
},
"children" : [
{
"type" : "TermQuery",
"description" : "city:北",
"time_in_nanos" : 107949,
"breakdown" : {
"set_min_competitive_score_count" : 0,
"match_count" : 0,
"shallow_advance_count" : 3,
"set_min_competitive_score" : 0,
"next_doc" : 0,
"match" : 0,
"next_doc_count" : 0,
"score_count" : 5,
"compute_max_score_count" : 3,
"compute_max_score" : 11465,
"advance" : 3477,
"advance_count" : 6,
"score" : 5793,
"build_scorer_count" : 3,
"create_weight" : 34781,
"shallow_advance" : 18176,
"create_weight_count" : 1,
"build_scorer" : 34236
}
},
{
"type" : "TermQuery",
"description" : "city:京",
"time_in_nanos" : 49929,
"breakdown" : {
"set_min_competitive_score_count" : 0,
"match_count" : 0,
"shallow_advance_count" : 3,
"set_min_competitive_score" : 0,
"next_doc" : 0,
"match" : 0,
"next_doc_count" : 0,
"score_count" : 5,
"compute_max_score_count" : 3,
"compute_max_score" : 5162,
"advance" : 15645,
"advance_count" : 6,
"score" : 3795,
"build_scorer_count" : 3,
"create_weight" : 13562,
"shallow_advance" : 1087,
"create_weight_count" : 1,
"build_scorer" : 10657
}
}
]
}
],
"rewrite_time" : 17930,
"collector" : [
{
"name" : "CancellableCollector",
"reason" : "search_cancelled",
"time_in_nanos" : 204082,
"children" : [
{
"name" : "SimpleTopScoreDocCollector",
"reason" : "search_top_hits",
"time_in_nanos" : 23347
}
]
}
]
}
],
"aggregations" : [ ]
}
]
}
從上面我們可以看出來,這個搜尋是搜尋了“北”及“京”,而不是把北京作為一個整體來進行搜尋的。我們可以在以後的文檔中可以學習使用中文分詞器來進行分詞搜尋。有興趣的同學可以把上面的搜尋修改為city.keyword來看看。
filter查詢和query查詢的不同:
https://blog.csdn.net/laoyang360/article/details/80468757如何在搜尋的時候指定分詞器
如何指定分詞器進行字段的使用,
如何調整java rest client api的線程數,還有全局client api的異常處理的捕獲,逾時時間等
es 的叢集負載方式,節點,副本等,以及節點掉線後的資料恢複等(主要是全部節點都掉線後,如何恢複資料?這個應該也不是重點)另外是,如何進行es叢集的遷移,比如把現有叢集的資料遷移到另外一個叢集中,比如更新等操作時,這些可以在elastic 官網上關于es的2.x的版本介紹中有相關的叢集等運維等的說明;
還有垃圾回收器等的說明,這個在文檔中:《不要觸碰這些配置中都有詳細的說明》但這些也都的确是需要了解并熟悉的;
https://www.elastic.co/guide/cn/elasticsearch/guide/current/dont-touch-these-settings.htmlElasticSearch,分詞器analyzer
ElasticSearch3 分詞器 analyzer
analyzer分析器将輸入的字元流分解為token的過程,主要是發生在兩個場合:
1、在index建立索引的時候(也就是在向index中建立文檔資料的時候)
2、在search的時候,也就是在搜尋時,分析需要搜尋的詞語;

analyzer 是es在文檔存儲之前對文檔正文内容執行的過程,以添加到反向索引中;在将文檔添加到索引之前,es會為每個待分析的字段執行對應的analyzer步驟;
如:我們此時定制化一個新的analyzer,分别是由:character 過濾器,标準的tokenizer及token filter組成;
下圖表達了一段原始文本,在經過analyzer分析時的整個過程;
最後在經過了一系列分析後,将所對應的分析後的結果,則添加到對應的反向索引中,如圖1的步驟所示;
analyzer分析器的組成
analyzer分析器,常見的分為三個部分組成:
- Char Filter:字元過濾器的工作是執行清除任務,例如剝離HTML标記。
- Tokenizer:下一步是将文本拆分為稱為标記的術語。 這是由tokenizer完成的。 可以基于任何規則(例如空格)來完成拆分
- Token Filter:一旦建立了token,它們就會被傳遞給token filter,這些過濾器會對token進行規範化。 Token filter可以更改token,删除術語或向token添加術語。
很重要哦:Elasticsearch已經提供了比較豐富的analyzer分析器。我們可以自己建立自己的token analyzer,甚至可以利用已經有的char filter,tokenizer及token filter來重新組合成一個新的analyzer,并可以對文檔中的每一個字段分别定義自己的analyzer。
在預設情況下,ES中所使用的分析器是standard analyzer分析器;
standard analyzer分析器所使用的特征是:
1、沒有Char Filter
2、使用 standard tokonzer
3、把對應字元串轉換為小寫,同時有選擇性的删除一些stop words(停頓詞);預設情況下stop words 為 none,及不過濾任何的stop words(停頓詞)
下面另外一張圖說明了,不同的analyzers分析器,對于token的拆分情況: