初學者都能學會的ElasticSearch入門實戰

發表於2022-03-23

大家好,我是咔咔 不期速成,日拱一卒

專案中準備使用ElasticSearch,之前只是對ElasticSearch有過簡單的瞭解沒有系統的學習,本系列文章將從基礎的學習再到深入的使用。

咔咔之前寫了一份死磕MySQL文章,如今再入一個系列玩轉ElasticSearch。

本期文章會帶給大家學習ElasticSearch的基礎入門,先把基礎學會再深入學習更多的知識點。

一、基本概念

文件(Document)

ElasticSearch是面向文件的,文件是所有可搜尋資料的最小單位,例如MySQL的一條資料記錄

文件會被序列化成為json格式,儲存在ElasticSearch中

每個文件都有一個唯一ID,例如MySQL中的主鍵ID

JSON文件

一篇文件包括了一系列的欄位,例如資料中的一條記錄

json文件,格式靈活,不需要預先定義格式

在上期文章中把csv檔案格式檔案通過Logstash轉化為json儲存到ElasticSearch中

文件的後設資料

index :文件所屬的索引名

type:文件所屬型別名

id:文件唯一ID

source:文件的原始JSON資料

version:文件的版本資訊

score:相關性分數

索引

索引是文件的容器,是一類文件的結合,每個索引都有自己的mapping定義,用於定義包含的文件的欄位和型別

每個索引都可以定義mapping,setting,mapping是定義欄位型別,setting定義不同的資料分佈

{
  "movies" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "@version" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "genre" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "id" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "title" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "year" : {
          "type" : "long"
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1641637408626",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "gf0M2BgnStGZZHsIJD6otQ",
        "version" : {
          "created" : "7010099"
        },
        "provided_name" : "movies"
      }
    }
  }
}

Type

7.0之前,一個Index可以設定多個type,所以當時大多數資料顯示的都是type型別與資料庫的表

7.0之後,一個索引只能建立一個type“_doc”

若不好理解,可以對比MySQL類比一下

Databases ElasticSearch
Table Index(Type)
Row Document
Column Filed
Schema Mapping
Sql Dsl

節點

節點是一個ElasticSearch的例項,本質上就是java的一個程式,一臺機器可以執行多個ElasticSearch程式,但生產環境下還是建議一臺伺服器執行一個ElasticSearch例項

每個節點都有名字,通過配置檔案配置,或者啟動時 -E node.name=node1

每個節點在啟動後,會分配一個UID,儲存在data目錄下

主節點:master

預設情況下任何一個叢集中的節點都有可能被選為主節點,職責是建立索引、刪除索引、跟蹤叢集中的節點、決定分片分配給相應的節點。索引資料和搜尋查詢操作會佔用大量的記憶體、cpu、io資源。因此,為了保證一個叢集的穩定性,應該主動分離主節點跟資料節點。

資料節點:data

看名字就知道是儲存索引資料的節點,主要用來增刪改查、聚合操作等。資料節點對記憶體、cpu、io要求比較高,在優化的時候需要注意監控資料節點的狀態,當資源不夠的時候,需要在叢集中新增新的節點。

負載均衡節點:client

該節點只能處理路由請求,處理搜尋,分發索引等操作,該節點類似於Nginx的負載均衡處理,獨立的客戶端節點在一個比較大的叢集中是非常有用的,它會協調主節點、資料節點、客戶端節點加入叢集的狀態,根據叢集的狀態可以直接路由請求。

預處理節點:ingest

在索引資料之前可以先對資料做預處理操作,所有節點其實預設都是支援ingest操作的,也可以專門將某個節點配置為ingest節點。

分片

分片分為主分片,副本分片

主分片:用以解決資料水平擴充套件的問題,將資料分佈到叢集內的所有節點上,一個分片是一個執行的Lucene(搜尋引擎)例項,主分片數在建立時指定,後續不允許修改,除非Reindex

副本:用以解決資料高可用的問題,可以理解為主分片的拷貝,增加副本數,還可以在一定程度上提高服務的可用性。

在生產環境中分片的設定有何影響

分片數設定過小會導致無法增加節點實現水平擴充套件,單個分片資料量太大,導致資料重新分配耗時。假設你給索引設定了三個主分片 ,這時你給叢集加了幾個例項,索引也只能在三臺伺服器上

分片數設定過大導致搜尋結果相關性打分,影響統計結果的準確性,單個節點上過多的分片,會導致資源浪費,同時也會影響效能

從ElasticSearch7.0開始,預設的主分片設定為1,解決了over-sharding的問題

檢視叢集健康狀態

執行介面

get _cluster/health

green:主分片與副本都正常分配

yellow:主分片全部正常分配,有副本分片未能正常分配

red:有主分片未能分配,當伺服器的磁碟容量超過85%時建立了一個索引

二、Result Api

介面 作用
get movies 檢視索引相關資訊
get movies/_count 檢視索引的文件總數
post movies/_search 檢視前10條文件
get /_cat/indices/movies?v&s=index 獲取索引狀態
get /_cat/indices?v&health=green 檢視狀態為綠色的索引
get /_cat/indices?v&s=docs.count:desc 根據文件資料倒序
get /_cat/indices/kibana*?pri&v&h=health,index,pri,rep,docs,count,mt 檢視索引具體欄位
get /_cat/indices?v&h=i,tm&s=tm:desc 檢視索引所佔的記憶體
get _cluster/health 檢視叢集健康狀態

三、文件的基本CRUD操作

create 一個文件

支援自動生成文件ID和指定文件ID兩種方式

通過呼叫post /movies/_doc 系統會自動生成文件ID

使用http put movies/_create/1 建立時,url中顯示指定_create ,如果該id的文件已經存在,操作失敗

Index 文件

Index和Create區別在於,如果文件不存在,就索引新的文件。否則現有文件會被刪除,新的文件被索引並且版本資訊+1

可以看到之前的文件已經被更新為最新的niuniu,是因為之前就存在文件id=1,並且能看到版本資訊也加了1

update 文件

update方法不會刪除原有文件,而是實現真正的資料更新

get 一個文件

檢索文件找到,返回狀態碼200,文件元資訊,這裡需要注意一下版本資訊,同一個id的文件,即被刪除版本號也會不斷增加

找不到文件,返回狀態碼404

Bulk Api

支援在一次Api呼叫中,對不同的索引進行操作,支援index、create、update、delete

可以在url中指定index,也可以在請求的payload中進行

操作中單條操作失敗,不會影響其它繼續操作,並且返回結果包括了每一條操作執行的結果

多索引bulk批量操作案例:

post _bulk
{"index":{"_index" : "test1","_id" : "1"}}
{"name":"kaka_bulk"}
{"delete":{"_index":"test1","_id":"2"}}
{"create":{"_index":"test2","_id":"3"}}
{"name":"kaka_create"}
{"update":{"_id":"1","_index":"test1"}}
{"doc":{"name":"kaka_bulk"}}

返回結果

{
  "took" : 165,
  "errors" : false,
  "items" : [
    {
      "index" : {
        "_index" : "test1",
        "_type" : "_doc",
        "_id" : "1",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 0,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "delete" : {
        "_index" : "test1",
        "_type" : "_doc",
        "_id" : "2",
        "_version" : 1,
        "result" : "not_found",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 1,
        "_primary_term" : 1,
        "status" : 404
      }
    },
    {
      "create" : {
        "_index" : "test2",
        "_type" : "_doc",
        "_id" : "3",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 0,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "update" : {
        "_index" : "test1",
        "_type" : "_doc",
        "_id" : "1",
        "_version" : 1,
        "result" : "noop",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "status" : 200
      }
    }
  ]
}

這裡需要大家注意:bulk api 對json語法有嚴格的要求,每個json串不能換行,只能放一行,同時一個json和另一個json串之間必須有一個換行。

單索引bulk批量操作

如果操作的是同一個索引時,bulk語句還可以變化為以下方式

post test1/_bulk
{"index":{"_id" : "1"}}
{"name":"kaka_bulk"}
{"delete":{"_id":"2"}}
{"create":{"_id":"3"}}
{"name":"kaka_create"}
{"update":{"_id":"1"}}
{"doc":{"name":"kaka_bulk"}}

單條的返回結果可以自己嘗試一下,可以看到單索引bulk跟多索引bulk之間的區別顯而易見。

bulk size的最佳大小

bulk request 會載入到記憶體裡,如果太大的話,效能反而會下降,因此需要不斷嘗試最佳的bulk size,大小最好控制在5~15MB即可,至於條數需要根據當下資料量再調整。

批量讀取_mget

道理跟MySQL都一樣,只要是批量在一定合理的範圍內都會減少網路連線所產生的開銷,從而提高效能

需要注意批量獲取每個json之間是需要逗號隔開的,否則會報json解析異常

get /_mget
{
  "docs": [
    {"_index":"test","_id":"1"},
    {"_index":"movies","_id":"2"}
    ]
}

批量搜尋_msearch

post kibana_sample_data_ecommerce/_msearch
{}
{"query":{"match_all":{}},"size":1}
{"index":"kibana_smaple_sample_data_flights"}
{"query":{"match_all":{}},"size":1}

常見的錯誤狀態

問題 原因
無法連線 網路故障、叢集異常
連線無法關閉 網路故障、節點出錯
429 叢集過於繁忙
4xx 請求體格式錯誤
500 叢集內部錯誤

四、倒排索引

倒排索引是由單詞詞典、倒排列表兩部分組成,單詞詞典記錄的所有文件的單詞,記錄單詞倒排列表的關聯關係

倒排列表記錄了單詞對應的文件結合,由倒排索引項組成,分別為文件ID、詞頻TF、位置、偏移

案例:

文件ID 文件內容
1 kaka ElasticSearch
2 ElasticSearch kaka
3 ElasticSearch niuniu

倒排列表就為:

文件ID 詞頻 位置 偏移量
1 1 1 <10,23>
2 1 0 <0,13>
3 1 0 <0,13>

ElasticSearch可以為json文件中的每個欄位設定自己的倒排索引,也可以指定某些欄位不做倒排索引

若不做倒排索引,雖可以節省儲存空間,但欄位無法被搜尋

五、使用Analyzer進行分詞

首先你得知道什麼是分詞:Analysis把全文字轉換為一系列單詞的過程叫做分詞

Analysis通過Analyzer實現的,可以通過ElasticSearch內建的分析器、或使用定製分析器

分詞器除了寫入時轉換此條,查詢query時也需要用相同的分析器對查詢語句進行分析

案例:ElasticSearch kaka

通過分詞就轉化為 elasticSearch和kaka,這裡需要注意的是通過分詞轉化後把單詞的首字母變為小寫

Analyzer的組成

Character Fiters :針對原始文字處理,例如去除html

Tokenizer : 按照規則切分單詞

Token Filter : 將切分的單詞進行加工,轉為小寫,刪除stopwords並增加同義詞

ElasticSearch的內建分詞器

# Standard Analyzer - 預設分詞器,按詞切分,小寫處理
# 只做單詞分割、並且把單詞轉為小寫
get _analyze
{
  "analyzer":"standard",
  "text":"If you don't expect quick success, you'll get a pawn every day"
}

# Simple Analyzer - 按照非字母切分(符號被過濾),小寫處理
# 按照非字母切分例如字母與字母之間的——,非字母的都被去除例如下邊的 2
get _analyze
{
  "analyzer" :"simple",
  "text":"3 If you don't expect quick success, you'll get a pawn every day kaka-niuniu"
}

# Whitespace Analyzer - 按照空格切分,不轉小寫
# 僅僅是根據空格切分,再無其它
get _analyze
{
  "analyzer":"whitespace",
  "text":"3 If you don't expect quick success, you'll get a pawn every day"
}

# Stop Analyzer - 小寫處理,停用詞過濾(the,a, is)
# 按照非字母切分例如字母與字母之間的——,非字母的都被去除例如下邊的 2
# 相比Simple Analyze,會把the,a,is等修飾性詞語去除
get _analyze
{
  "analyzer":"stop",
  "text":"4 If you don't expect quick success, you'll get a pawn every day"
}

# Keyword Analyzer  - 不分詞,直接將輸入當作輸出
# 不做任何分詞,直接把輸入的輸出,假如你不想使用任何分詞時就可以使用這個
get _analyze
{
  "analyzer":"keyword",
  "text":"5 If you don't expect quick success, you'll get a pawn every day"
}

# Patter Analyzer  - 正規表示式,預設\W+(非字元分隔)
# 通過正規表示式進行分詞,預設是\W+,非字元的符號進行分割
get _analyze
{
  "analyzer":"pattern",
  "text":"6 If you don't expect quick success, you'll get a pawn every day"
}

# Language 一提供了30多種常見語言的分詞器
# 通過不同語言進行分詞
# 會把複數轉為單數  ,會把單詞的ing去除
get _analyze
{
  "analyzer":"english",
  "text":"7 If you don't expect quick success, you'll get a pawn every day kakaing kakas"
}

# 中文分詞器
# 這個需要安裝  
# 執行: ./bin/elasticsearch-plugin install analysis-icu
# 重啟:nohup ./bin/elasticsearch > /dev/null 2>&1 &
get _analyze
{
  "analyzer":"icu_analyzer",
  "text":"你好,我是咔咔"
}

其它中文分詞

用的最多的IK分詞,只是自定義詞庫,支援熱更新分詞字典

清華大學自然語言一套分詞器Thulac

六、Search Api

通過Url query 實現搜尋

例如:

get /movies/_search?q=2012&df=title&sort=year:desc

q:指定查詢語句,使用Query String Syntax

df:查詢欄位,不指定時,會對所有欄位進行查詢

sort:排序、from和size用於分頁

Profile:可以檢視查詢是如果被執行的

指定欄位查詢、泛查詢

指定欄位查詢就是加上df即可、泛查詢什麼都不加,看案例

通過下圖右側資訊可得知,指定欄位查詢的是title中存在2012的資料

同樣也可以這樣來寫指定欄位查詢

get /movies/_search?q=2012&df=title
{
  "profile":true
}

通過下圖右側可得知,泛查詢則是在所有欄位中查詢存在2012的數

分組與引號查詢

若你查詢值為Beautiful Mind 則等效於Beautiful OR Mind ,類似於MySQL中的or語句,意思為查詢的欄位中包含 Beautiful 或者 Mind 都會被查詢出來

若你查詢值為"Beautiful Mind" 則等效於Beautiful AND Mind ,類似於MySQL中的and語句,意思為查詢的欄位中不僅要包含Beautiful 而且還需要包含 Mind ,跟MySQL中不同的是順序也不能變

注意:這裡你乍一眼看過去沒啥區別, 其實區別就在於有無引號

# PhraseQuery

# 需要欄位title中存在beautiful 和 mind,並且兩者的順序不能亂

# "description" : """title:"beautiful mind""""

get /movies/_search?q=title:"Beautiful Mind"
{
  "profile":"true"
}


# TermQuery

# 需要欄位title中出現beautiful 或 mind 都可以

# "type" : "BooleanQuery",
# "description" : "title:beautiful title:mind",

get /movies/_search?q=title:(Beautiful Mind)
{
  "profile":"true"
}

布林操作

可以使用AND / OR / NOT 或者 && / || / ! 這裡你會發現使用的都是大寫,+表示must(必須存在),-表示not mast(必須不存在)接下來看案例

# title 裡邊必須有beautiful 和 mind
# "description" : "+title:beautiful +title:mind"
get /movies/_search?q=title:(Beautiful AND Mind)
{
  "profile":"true"
}


# title裡邊包含beautiful  必須沒有mind
# "description" : "title:beautiful -title:mind"
get /movies/_search?q=title:(Beautiful NOT Mind)
{
  "profile":"true"
}


# title裡包含beautiful ,必須也包含mind
# "description" : "title:beautiful +title:mind"
get /movies/_search?q=title:(Beautiful  %2BMind)
{
  "profile":"true"
}

範圍查詢、萬用字元查詢、模糊匹配

# year年份大於1996的電影
# 注意一下[] 為閉區間   {}為開區間
# "description" : "year:[1997 TO 9223372036854775807]"
get /movies/_search?q=year:>1996
{
  "profile":"true"
}

# title 中存在b的資料
# "description" : "title:b*"
get /movies/_search?q=title:b*
{
  "profile":"true"
}

# 對於模糊匹配還是非常有必要的,因為會存在一起使用者會輸錯單詞,我們就可以給做近似度匹配
# "description" : "(title:beautiful)^0.875"
get /movies/_search?q=title:beautifl~1
{
  "profile":"true"
}

七、Request Body Search

在日常開發過程中,最經常用的還是在Request Body中做,接下來跟著咔咔的例項一點點走

正常查詢

sort :需要排序的欄位

source:查那些欄位

from:頁數

size:每頁數量

post movies/_search
{
  "profile":"true",
  "sort":[{"year":"desc"}],
  "_source":["year"],
  "from":0,
  "size":2,
  "query":{
    "match_all": {}
  }
}

指令碼欄位

這個應用場景跟咔咔近期做的外幣功能是非吻合,每筆合同都有自己不同的匯率,要算出這筆合同金額是多少

post /movies/_search
{
  "script_fields":{
    "new_field":{
      "script":{
        "lang":"painless",
        "source":"doc['year'].value+'年'"
      }
    }
  },
  "query":{
    "match_all": {}
  }
}

這個案例就是把當前資料的year 拼上 “年” 組成的新欄位然後返回,返回結果如下

    {
        "_index" : "movies",
        "_type" : "_doc",
        "_id" : "3844",
        "_score" : 1.0,
        "fields" : {
          "new_field" : [
            "1989年"
          ]
        }
      } 

從上面的結果可以看到只返回了指令碼欄位,沒有返回原始欄位,那如何讓原始欄位也跟著一起返回呢?

只需要在request body中加上_source即可,當然也可以查詢指定欄位"_source":["id","title"]

post /movies/_search
{
  "_source":"*",
  "script_fields":{
    "new_field":{
      "script":{
        "lang":"painless",
        "source":"doc['year'].value+'年'"
      }
    }
  },
  "query":{
    "match_all": {}
  }
}

檢視返回結果

    {
        "_index" : "movies",
        "_type" : "_doc",
        "_id" : "3843",
        "_score" : 1.0,
        "_source" : {
          "year" : 1983,
          "@version" : "1",
          "genre" : [
            "Horror"
          ],
          "id" : "3843",
          "title" : "Sleepaway Camp"
        },
        "fields" : {
          "new_field" : [
            "1983年"
          ]
        }
      }

查詢表示式Match

# title中包含sleepaway 或者 camp 即可
# 可以看到跟 url 的get /movies/_search?q=title:(Beautiful Mind) 分組查詢返回結果是一致的
# "description" : "title:sleepaway title:camp"
get /movies/_doc/_search
{
  "query":{
    "match":{
      "title":"Sleepaway Camp"
    }
  },
  "profile":"true"
}

# title中必須包含sleepaway 和 camp  並且順序不能亂
# 可以看到跟 url 的get /movies/_search?q=title:(Beautiful AND Mind)是一致的
# "description" : "+title:sleepaway +title:camp"
get /movies/_doc/_search
{
  "query":{
    "match":{
      "title":{
        "query":"Sleepaway Camp",
        "operator":"AND"
      }
    }
  },
  "profile":"true"
}

# title 中查詢Sleepaway 和 Camp中間可以有一個任意值插入
# get /movies/_search?q=title:beautifl~1
# "description" : """title:"sleepaway camp"~1"""
get /movies/_doc/_search
{
  "query":{
    "match_phrase":{
      "title":{
        "query":"Sleepaway Camp",
        "slop":1
      }
    }
  },
  "profile":"true"
}

八、 Query String 和 Simple Query String

# Query String 中可以使用and跟url 的query string一樣
# title 中必須存在sleepaway 和 camp 即可
# 跟url的 get /movies/_search?q=title:(Beautiful Mind) 一致
# "description" : "+title:sleepaway +title:camp"
post /movies/_search
{
  "query":{
    "query_string":{
      "default_field":"title",
      "query":"Sleepaway AND Camp"
    }
  },
  "profile":"true"
}

# simple_query_string 不支援and的使用,可以看到是把and當做一個詞來進行查詢
# title 中存在sleepaway 或 camp 即可
# "description" : "title:sleepaway title:and title:camp"
post /movies/_search
{
  "query":{
    "simple_query_string": {
      "query""Sleepaway AND Camp",
      "fields": ["title"]
    }
  },
  "profile":"true"
}

# 如果想讓simple_query_string 執行布林操作,則需要給加上default_operator
# title中必須存在sleepaway 和 camp 即可
# "description" : "+title:sleepaway +title:camp"
post /movies/_search
{
  "query":{
    "simple_query_string": {
      "query""Sleepaway Camp",
      "fields": ["title"],
      "default_operator""AND"
    }
  },
  "profile":"true"
}

九、Mapping和常見欄位型別

什麼是Mapping

Mapping類似於資料庫中的schema,主要包括定義索引的欄位名稱,定義欄位的資料型別,配置倒排索引設定

什麼是Dynamic Mapping

Mapping有一個屬性為dynamic,其定義瞭如何處理新增文件中包含的新增欄位,其有三個值可選預設為true

true:一旦有新增欄位的文件寫入,Mapping也同時被更新

false:Mapping不會被更新並且新增的欄位也不會被索引,但是資訊會出現在_source中

strict:文件寫入失敗

常見型別

Json型別 ElasticSearch型別
字串 日期格式為data、浮點數為float、整數為long、設定為text並且增加keyword子欄位
布林值 boolean
浮點數 float
整數 long
物件 object
陣列 取第一個非空數值的型別所定
控制 忽略
put kaka/_doc/1
{
  "text":"kaka",
  "int":10,
  "boole_text":"false",
  "boole":true,
  "float_text":"1.234",
  "float":1.234,
  "loginData":"2005-11-24T22:20"
}

# 獲取索引kaka的mapping
get kaka/_mapping

返回結果,從結果中可得知如果是false或者true在引號裡邊就是text型別需要注意這一點就行

{
  "kaka" : {
    "mappings" : {
      "properties" : {
        "boole" : {
          "type" : "boolean"
        },
        "boole_text" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "float" : {
          "type" : "float"
        },
        "float_text" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "int" : {
          "type" : "long"
        },
        "loginData" : {
          "type" : "date"
        },
        "text" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

自定義Mapping

設定欄位不被索引

設定欄位不被索引使用index,只需要給欄位在加一個index:false即可,同時注意一下mapping的設定格式

按照咔咔給的步驟走,你會得到一個這樣的錯誤Cannot search on field [mobile] since it is not indexed,意思就是不能搜尋沒有索引的欄位

put kaka
{
  "mappings":{
    "properties":{
      "firstName":{
        "type":"text"
      },
      "lastName":{
        "type":"text"
      },
      "mobile":{
        "type":"text",
        "index":false
      }
    }
  }
}

post /kaka/_doc/1
{
  "firstName":"kaka",
  "lastName":"Niu",
  "mobile":"123456"
}

get /kaka/_search
{
  "query":{
    "match": {
      "mobile":"123456"
    }
  }
}

設定copy_to

設定方式如下,copy_to設定後再搜尋時可以直接使用你定義的欄位進行搜尋

put kaka
{
  "mappings":{
    "properties":{
      "firstName":{
        "type":"text",
        "copy_to":"allSearch"
      },
      "lastName":{
        "type":"text",
        "copy_to":"allSearch"
      }
    }
  }
}

為了方便檢視,這裡咔咔再插入兩條資料

post /kaka/_doc/1
{
  "fitstName":"kaka",
  "lastName":"niuniu"
}

post /kaka/_doc/2
{
  "fitstName":"kaka",
  "lastName":"kaka niuniu"
}

進行查詢,返回的只有id為2的這條資料,所以說使用copy_to後,代表著所有欄位中都包含搜尋的詞

post /kaka/_search
{
  "query":{
    "match":{
      "allSearch":"kaka"
    }
  },
  "profile":"true"
}

十、自定義分詞器

分詞器是由Character Fiters、Tokenizer、Token Filter組成的

Character Filters 主要是對文字的替換、增加、刪除,可以配置多個Character Filters ,需要注意的是設定後會影響Tokenizer的position、offset資訊

Character Filters 自帶的有 HTMl strip 去除html標籤、Mapping 字串的替換、Pattern replace 正則匹配替換

Tokenizer 處理的就是分詞,內建了非常多的分詞詳細可以在第二期文章中檢視

Token Filters 是將Tokenizer 分詞後的單詞進行增加、修改、刪除,例如進行轉為lowercase小寫字母、stop去除修飾詞、synonym近義詞等

自定義Character Filters

# Character Fiters之html的替換
# 會把text中的html標籤都會去除掉
post /_analyze
{
  "tokenizer":"keyword",
  "char_filter":["html_strip"],
  "text":"<span>咔咔閒談</span>"
}

# Character Fiters之替換值
# 會把text中的 i 替換為 kaka、hope 替換為 wish
post /_analyze
{
  "tokenizer":"keyword",
  "char_filter":[
    {
      "type":"mapping",
      "mappings":["i => kaka","hope => wish"]
    }
    ],
  "text":"I hope,if you don't expect quick success, you'll get a pawn every day."
}

# Character Fiters之正規表示式
# 使用正規表示式來獲取域名資訊
post /_analyze
{
  "tokenizer":"keyword",
  "char_filter":[
    {
      "type":"pattern_replace",
      "pattern":"http://(.*)",
      "replacement":"$1"
    }
    ],
    "text":"http://www.kakaxiantan.com"
}

自定義Token Filters

現在用的分詞器是whitespace,這個分詞器是把詞使用空格 隔開,但是現在還想讓詞變小寫並過濾修飾詞,應該怎麼做呢?

post /_analyze
{
  "tokenizer":"whitespace",
  "filter":["stop","lowercase"],
  "text":"If on you don't expect quick success, you'll get a pawn every day"
}

為了不佔地方,只複製出了代表性的返回結果

{
  "tokens" : [
    {
      "token" : "if",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "you",
      "start_offset" : 6,
      "end_offset" : 9,
      "type" : "word",
      "position" : 2
    }
  ]
}

實戰自定義分詞

本節開篇就知道analyze是通過Character Fiters、Tokenizer、Token Filter組成的,那麼在自定義時這三個都是可以自定義的

自定義分詞必存在analyzer、tokenizer、char_filter、filter

這部分的定義都是需要在下面定義好規則,否則無法使用,詳細定義程式碼往下拉看完整版本即可

對這個配置不要死記硬背使用的多了自然就會記住

# 實戰自定義analyze
put kaka
{
  "settings":{
    "analysis":{
      "analyzer":{
        "my_custom_analyzer":{
          "type":"custom",
          "char_filter":[
            "emoticons"
          ],
          "tokenizer":"punctuation",
          "filter":[
            "lowercase",
            "englist_stop"
          ]
        }
      },
      "tokenizer":{
        "punctuation":{
          "type":"keyword"
        }
      },
      "char_filter":{
        "emoticons":{
          "type":"mapping",
          "mappings":[
            "123 => Kaka",
            "456 => xian tan"
          ]
        }
      },
      "filter":{
        "englist_stop":{
          "type":"stop",
          "stopwords":"_english_"
        }
      }
    }
  }
}

# 執行自定義的分詞
post /kaka/_analyze
{
  "analyzer":"my_custom_analyzer",
  "text":" 123 456"
}

# 返回結果,把字母大寫轉為小寫不做分詞
{
  "tokens" : [
    {
      "token" : " kaka xian tan",
      "start_offset" : 0,
      "end_offset" : 8,
      "type" : "word",
      "position" : 0
    }
  ]
}

十一、Index Template

在一個新索引新建並插入文件後,會使用預設的setting、mapping,如果你有設定settings、mappings會覆蓋預設的settings、mappings配置

# 建立索引並插入文件
post /kaka/_doc/1
{
  "gongzhonghao":"123"
}

# 獲取settings、mappings
get /kaka

以下這個配置,就是預設配置

# 返回的settings、mappings
{
  "kaka" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "gongzhonghao" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1642080577305",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "JJWsGYcrTam0foEQxuZqGQ",
        "version" : {
          "created" : "7010099"
        },
        "provided_name" : "kaka"
      }
    }
  }
}

接下來建立一個自己的模板

# 設定一個只要是test開頭的索引都能使用的模板,在這個模板中我們將字串中得數字也轉為了long型別,而非text
put /_template/kaka_tmp
{
  "index_patterns":["test*"],
  "order":1,
  "settings":{
    "number_of_shards":1,
    "number_of_replicas":2
  },
  "mappings":{
   # 讓時間不解析為date型別,返回是text型別
    "date_detection":false,
    # 讓雙引號下的數字解析為long型別,而非text型別
    "numeric_detection":true
  }
}

建立索引

post /test_kaka/_doc/1
{
  "name":"123",
  "date":"2022/01/13"
}

get /test_kaka

返回結果

{
  "test_kaka" : {
    "aliases" : { },
    "mappings" : {
      "date_detection" : false,
      "numeric_detection" : true,
      "properties" : {
        "date" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "name" : {
          "type" : "long"
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1642081053006",
        "number_of_shards" : "1",
        "number_of_replicas" : "2",
        "uuid" : "iCcaa_8-TXuymhfzQi31yA",
        "version" : {
          "created" : "7010099"
        },
        "provided_name" : "test_kaka"
      }
    }
  }
}

堅持學習、堅持寫作、堅持分享是咔咔從業以來所秉持的信念。願文章在偌大的網際網路上能給你帶來一點幫助,我是咔咔,下期見。

相關文章