ElasticSearch第1講(4萬字詳解 Linux下安裝、原生呼叫、API呼叫超全總結、Painless、IK分詞器、4種和資料庫同步方案、高併發下一致性解決方案、Kibana、 ELK)

小松聊PHP进阶發表於2024-07-26

ElasticSearch

  • 官方文件:https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html
  • 非官方中文文件:https://learnku.com/docs/elasticsearch73/7.3
  • 極簡概括:基於Apache Lucene構建開源的分散式搜尋引擎。
  • 解決問題:MySQL like中文全文搜尋不走索引,或大資料搜尋效能低下的問題。
  • 適用場景:
    • 大資料檢索:在大資料量的查詢場景下,ES查詢效能依然保持優勢,常用於替代MySQL由於效能不足而做一些複雜的查詢。
    • 大資料開發:大資料開發幾乎離不開Spark、Fink、Hadoop、ElasticSearch、MySQL、Redis、ZooKeeper這些元件。
    • ELK結合:ES結合LK作為ELK(Elasticsearch(搜尋), Logstash(採集轉換), Kibana(分析))組合,可用於實時監控、分析和視覺化大量日誌和事件資料,如系統日誌、應用程式日誌、網路流量日誌等。
  • 優點:
    • 跨平臺:元件支援在Linux、Windows、MacOS上執行。
    • 查詢效能優異:在超大資料量的查詢場景下,ES查詢效能依然保持優勢。
    • 支援全文檢索:替代MySQL中文全文檢索不走索引的查詢弱項。
    • 生態繁榮:是面向開發者的主流的搜尋引擎,文件,解決方案,疑難雜症,非0day漏洞,基本都有成熟的解決方案。
    • 支援分散式:每個ES節點,都可以執行一部分搜尋任務,然後將結果合併。累加的算力效果如虎添翼。
    • 支援複雜查詢:支援,模糊匹配,範圍查詢,布林搜尋。
  • 缺點:
    • ES沒有事務機制,對於MySQL的合作呢,也是最終一致性,所以強一致性的搜尋環境下並不適用,推薦Redis。
    • json請求體父子格式反人類:果然技術厲害的程式設計師往往不會是一個好的產品經理。
    • json響應體格式反人類,按照["成功或失敗的code", "data資料", "msg補充說明"]這種格式返回就好了。
    • PHP API經常性異常:APi介面,寫操作失敗返回false也行,非要返回異常,異常若沒有處理,會中斷程式執行。
    • 查詢方式受mapping限制:相比於MySQL,哪怕是個數字,都可以用like強制查詢,但是ES不行。
  • 同類元件:Apache Solr、Apache Lucene、Algolia、Sphinx、XunSearch。

正排索引和倒排索引

ES用的倒排索引演算法。正倒兩種索引都是用於快速檢索資料的實現方案,我沒有太官方的解釋,所以舉例說明:

  • 正排索引:有一個文章表,有文章id、標題、詳情3個欄位,透過文章列表功能獲取文章,透過id作為索引值獲取文章內容,這是很普遍的業務邏輯。想要搜尋包含指定關鍵詞的文章,資料庫就需要對文章的標題和內容逐一做對比,因為不走索引,資料量不大還好,資料量一大效能降低。
  • 倒排索引:用於加速文字的檢索,文章內容利用分詞器拆分,將拆分好的關鍵詞與文章id做關聯,然後儲存。類比MySQL表的兩個列,一列是關鍵詞,另一列是包含這個關鍵詞的文章id,多個倒排索引資料集組成一個倒排表。再查詢時,不需要針對資料來源本身做查詢,而是變成了,關鍵詞為xxx的id為多少。

分詞

分詞就是把字串拆分成有用的關鍵詞,用於提供高質量搜尋的資料來源。

  • 對英文:分詞直接用空格就行,I love you,可直接利用空格分成3個詞,對中文顯然不適用。
  • 對中文:例如“今天溫度很高”,能用的詞彙可以拆分成“今天”、“溫度”、“很高”,可程式不知道怎麼拆分,若拆分為“今天溫”、“天溫”、“”度很”這樣的關鍵詞就顯得很怪異。
    所以也就誕生了語法分析+字典的解決方案,用人工干涉+詞典的方式實現分詞器的邏輯。
    至於利用NLP語義分析,上下文預測,的AI模式,不屬於ES的範疇,不展開。
  • 若搜尋關鍵詞為語句或短語:需要利用TF-IDF和BM25演算法(等更高階的演算法),先對句子進行分詞,然後根據這多個分詞的再對結果集進行分詞查詢,然後評分,組合,最終返回結果。

安裝ES 8.14.1

  • 系統配置,用於開啟防火牆,建立使用者,和大資料情況下提升效能。
Java寫的元件吃記憶體,建議VM虛擬機器記憶體設定大一點,系統設定為1G記憶體。

開兩個埠,並重啟防火牆
firewall-cmd --add-port=9200/tcp --zone=public --permanent
firewall-cmd --add-port=9300/tcp --zone=public --permanent
systemctl restart firewalld

新建一個es使用者,以非root形式執行,否則執行es會報錯,java.lang.RuntimeException: can not run elasticsearch as root
useradd -M es
passwd es 密碼為123456

vim  /etc/security/limits.conf
文末新增兩行配置,最佳化檔案描述符軟硬限制,對提高效能非常重要,檔案描述符用於標識和管理每個程序都可以開啟檔案的數量
es soft nofile 65536
es hard nofile 65536

vim /etc/security/limits.d/20-nproc.conf
文末新增兩行配置,最佳化檔案描述符軟硬限制,對提高效能非常重要,檔案描述符用於標識和管理每個程序都可以開啟檔案的數量
es soft nofile 65536
es hard nofile 65536

vim /etc/sysctl.conf
定義系統中可以同時開啟的最大檔案描述符數量。
fs.file-max=655350
定義Linux核心中程序可以擁有的最大記憶體對映區域數量
vm.max_map_count=262144

重啟
sysctl -p
  • 安裝相關
下載tar包並解壓,這個包地址來源於官網,並非java原始碼包
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.14.1-linux-x86_64.tar.gz
tar zxf elasticsearch-8.14.1-linux-x86_64.tar.gz


更改所屬的使用者和使用者組
chown -R es:es elasticsearch-8.14.1


切換使用者
su es


啟動ES,如果發現報錯,請清空bin目錄同級的data目錄
./bin/elasticsearch


啟動後,直到看到如下字樣,說明能成功啟動,但是輸入它生成的使用者名稱密碼,登不進去
然後Ctrl + C強制停止,因為啟動一次之後,config/elasticsearch.yml配置檔案,會發生變化,這一步不可少
Elasticsearch security features have been automatically configured!


登不進去,那就改配置
vim config/elasticsearch.yml

把91~103行的true全部改為false,如下,注意配置格式,key: value之間要留出空格,否則ES不識別對應的值。
# Enable security features
xpack.security.enabled: false

xpack.security.enrollment.enabled: false

# Enable encryption for HTTP API client connections, such as Kibana, Logstash, and Agents
xpack.security.http.ssl:
  enabled: false
  keystore.path: certs/http.p12

# Enable encryption and mutual authentication between cluster nodes
xpack.security.transport.ssl:
  enabled: false


儲存退出後,清除初始化的data資料
rm -rf elasticsearch-8.14.1/data/*


再次執行,並使其後臺執行
./bin/elasticsearch -d


檢視程序,確定ES是否成功執行
ps aux | grep elastic
es        49044 30.2 64.3 8291804 640416 pts/0  Sl   05:08   0:26 /test/elasticsearch-8.14.1/jdk/bin/java -Des.networkaddress.cache.ttl=60 -Des.networkaddress.cache.negative.ttl=10 -Djava.security.manager=allow -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -XX:-OmitStackTraceInFastThrow -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j2.formatMsgNoLookups=true -Djava.locale.providers=SPI,COMPAT --add-opens=java.base/java.io=org.elasticsearch.preallocate --add-opens=org.apache.lucene.core/org.apache.lucene.store=org.elasticsearch.vec --enable-native-access=org.elasticsearch.nativeaccess -XX:ReplayDataFile=logs/replay_pid%p.log -Djava.library.path=/test/elasticsearch-8.14.1/lib/platform/linux-x64:/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib -Djna.library.path=/test/elasticsearch-8.14.1/lib/platform/linux-x64:/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib -Des.distribution.type=tar -XX:+UnlockDiagnosticVMOptions -XX:G1NumCollectionsKeepPinned=10000000 -XX:+UseG1GC -Djava.io.tmpdir=/tmp/elasticsearch-13971958964404181235 --add-modules=jdk.incubator.vector -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:HeapDumpPath=data -XX:ErrorFile=logs/hs_err_pid%p.log -Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,level,pid,tags:filecount=32,filesize=64m -Xms389m -Xmx389m -XX:MaxDirectMemorySize=204472320 -XX:G1HeapRegionSize=4m -XX:InitiatingHeapOccupancyPercent=30 -XX:G1ReservePercent=15 --module-path /test/elasticsearch-8.14.1/lib --add-modules=jdk.net --add-modules=ALL-MODULE-PATH -m org.elasticsearch.server/org.elasticsearch.bootstrap.Elasticsearch
es        49075  0.0  0.0  55180   880 pts/0    Sl   05:09   0:00 /test/elasticsearch-8.14.1/modules/x-pack-ml/platform/linux-x86_64/bin/controller
es        49230  0.0  0.0 112828   968 pts/0    R+   05:10   0:00 grep elastic


訪問:
http://IP:9200/

設定密碼(推薦新增)

上文配置的是沒有密碼的方案,倘若伺服器IP和埠對外暴露,這不是一種安全的行為。
注意,要部署叢集,各個節點密碼應當一致。

注意配置格式,key: value之間要留出空格,否則ES不識別對應的值。
vim es根目錄/config/elasticsearch.yml
修改以下配置
xpack.security.enabled: true

非root使用者下啟動es
./bin/elasticsearch -d
啟動一個互動式命令列介面,從而設定密碼,期間的幾個互動,全部設定為123456
./bin/elasticsearch-setup-passwords interactive

預設使用者名稱:elastic
密碼:123456

概念輔助類比

ES中有些新的概念,可透過MySQL的概念去輔助記憶。

ES MySQL 備註
Index(索引) 庫表 /
Type(型別) 7及以上的版本被移除,原先是對標MySQL表的理念,後來發現這對於ES並非必須,就移除了
Documents(文件) 行資料 /
Fields(欄位) 欄位 /
Mapping(對映) 表結構 /
Shards(分片) 分表 顧名思義,當資料量太大單個節點都裝不下的時候,就拆分到其它節點上

預設頁說明

  • 預設頁:
    GET請求IP:9200/
{
    "name": "lnmp",
    "cluster_name": "elasticsearch",
    "cluster_uuid": "k61PBMDqTKO31rZeV-ENGA",
    "version": {
        "number": "8.14.1",
        "build_flavor": "default",
        "build_type": "tar",
        "build_hash": "93a57a1a76f556d8aee6a90d1a95b06187501310",
        "build_date": "2024-06-10T23:35:17.114581191Z",
        "build_snapshot": false,
        "lucene_version": "9.10.0",
        "minimum_wire_compatibility_version": "7.17.0",
        "minimum_index_compatibility_version": "7.0.0"
    },
    "tagline": "You Know, for Search"
}

"name": "lnmp":系統標識
"cluster_name": "elasticsearch":Elasticsearch 叢集的名稱為 “elasticsearch”。
"cluster_uuid": "k61PBMDqTKO31rZeV-ENGA":Elasticsearch叢集的唯一識別符號。
"version":版本資訊:
"number": 版本號
"build_flavor": "default":構建的型別,這裡是預設的。
"build_type": "tar":構建型別為 tar 包。
"build_hash": "93a57a1a76f556d8aee6a90d1a95b06187501310":構建的雜湊值,用於唯一標識這個特定的構建。
"build_date": "2024-06-10T23:35:17.114581191Z":構建的日期和時間。
"build_snapshot": false:表示這個構建不是一個快照版本。
"lucene_version": "9.10.0":基於Lucene 9.10.0的版本。
"minimum_wire_compatibility_version": "7.17.0":最低相容的網路傳輸版本。
"minimum_index_compatibility_version": "7.0.0":最低相容的索引版本。
"tagline": "You Know, for Search":Elasticsearch 的標語,說明其用途是進行搜尋。

索引增刪查操作

  • 建立索引:
    PUT請求 IP:9200/索引名
{
	"acknowledged": true,
	"shards_acknowledged": true,
	"index": "zs_index"
}

"acknowledged": true:指示請求是否被成功接受和處理。
"shards_acknowledged": true:指示所有分片是否已經確認請求。
"index": "zs_index":這表示操作涉及的索引名稱為 “zs_index”。
  • 建立索引:
    重複建立,報錯說明:
{
	"error": {
		"root_cause": [
			{
				"type": "resource_already_exists_exception",
				"reason": "index [zs_index/dCMAgdlqTeaihB4JSH1gNw] already exists",
				"index_uuid": "dCMAgdlqTeaihB4JSH1gNw",
				"index": "zs_index"
			}
		],
		"type": "resource_already_exists_exception",
		"reason": "index [zs_index/dCMAgdlqTeaihB4JSH1gNw] already exists",
		"index_uuid": "dCMAgdlqTeaihB4JSH1gNw",
		"index": "zs_index"
	},
	"status": 400
}

"error":這個物件包含了發生的錯誤資訊。
"root_cause":根本原因的陣列,指示導致問題的具體原因。
"type": "resource_already_exists_exception":錯誤的型別,表示嘗試建立的索引已經存在。
"reason": "index [zs_index/dCMAgdlqTeaihB4JSH1gNw] already exists":錯誤的詳細原因,指明索引 “zs_index” 和其唯一識別符號 “dCMAgdlqTeaihB4JSH1gNw” 已經存在。
"index_uuid": "dCMAgdlqTeaihB4JSH1gNw":已存在索引的 UUID。
"index": "zs_index":已存在索引的名稱。
"type": "resource_already_exists_exception":總體錯誤型別,與根本原因相同。
"reason": "index [zs_index/dCMAgdlqTeaihB4JSH1gNw] already exists":再次指明索引已經存在的原因。
"index_uuid": "dCMAgdlqTeaihB4JSH1gNw":重複指定已存在索引的 UUID。
"index": "zs_index":重複指定已存在索引的名稱。
"status": 400:HTTP 狀態碼,表示客戶端請求錯誤
  • 檢視索引:
    GET請求 IP:9200/索引名
{
	"zs_index": {
		"aliases": {},
		"mappings": {},
		"settings": {
			"index": {
				"routing": {
					"allocation": {
						"include": {
							"_tier_preference": "data_content"
						}
					}
				},
				"number_of_shards": "1",
				"provided_name": "zs_index",
				"creation_date": "1719699272706",
				"number_of_replicas": "1",
				"uuid": "dCMAgdlqTeaihB4JSH1gNw",
				"version": {
					"created": "8505000"
				}
			}
		}
	}
}

"aliases": {}:索引的別名列表為空,表示該索引當前沒有別名。
"mappings": {}:索引的對映為空物件,即沒有定義特定的欄位對映。
"settings":索引的設定資訊:
"index":
"routing":
"allocation":
"include":
"_tier_preference": "data_content":指定索引分配時偏好的資料內容層級。
"number_of_shards": "1":該索引被分成了一個分片。
"provided_name": "zs_index":索引的提供的名稱為 “zs_index”。
"creation_date": "1719699272706":索引的建立日期的時間戳形式。
"number_of_replicas": "1":該索引有一個副本。
"uuid": "dCMAgdlqTeaihB4JSH1gNw":索引的唯一識別符號 UUID。
"version":
"created": "8505000":索引的版本資訊,表示索引在 Elasticsearch 版本 “8505000” 中建立。
  • 檢視所有索引:
    GET請求 IP:9200/_cat/indices?v
health status index    uuid                   pri rep docs.count docs.deleted store.size pri.store.size dataset.size
yellow open   zs_index dCMAgdlqTeaihB4JSH1gNw   1   1          0            0       249b           249b         249b

health: 索引的健康狀態,此處為 “yellow”,表示所有預期的分片都可用,但副本尚未分配。
status: Elasticsearch 的狀態指示符,這裡是 “open”,表示索引是開啟狀態,可以接收讀寫操作。
index: 索引名。
uuid: 索引的唯一識別符號。
pri: 主分片數為 1,即索引被分成了一個主分片。
rep: 副本數為 1,表示每個主分片有一個副本。
docs.count: 文件數量為 0,當前索引中的文件總數。
docs.deleted: 已刪除的文件數量為 0。
store.size: 儲存大小為 249b,索引佔用的物理儲存空間。
pri.store.size: 主分片的儲存大小,也是 249b。
dataset.size: 資料集大小為 249b,即索引的資料集大小。
  • 刪除索引 DELETE方式 IP:9200/索引名
{
	"acknowledged": true
}

返回true表示成功執行。

文件增刪改查操作

  • 增文件(資料):
    方式1:POST請求 IP:9200/索引名/_doc/可選引數,資料唯一標識
    方式2:PUT請求 IP:9200/索引名/_create/必填唯一識別符號 由於方式2的put請求是冪等,所以再次請求會報錯
這是存入的資料
{
    "id":1,
    "content":"C是世界上最好的程式語言"
}

這是返回的資料,若使用者指定id,則id處顯示的是使用者指定的id
{
	"_index": "zs_index",
	"_id": "0mMsZpABZdTHCHXLZQhu",
	"_version": 1,
	"result": "created",
	"_shards": {
		"total": 2,
		"successful": 1,
		"failed": 0
	},
	"_seq_no": 0,
	"_primary_term": 1
}
"_index": "zs_index": 表示文件被新增到了名為zs_index的索引中。
"_id": "0mMsZpABZdTHCHXLZQhu": 是新新增的文件的ID。在Elasticsearch中,每個文件都有一個唯一的ID,用於唯一標識和檢索該文件。
"_version": 1: 表示該文件的版本號是1。每當文件被更新時,版本號會增加,這有助於跟蹤文件的更改歷史。
"result": "created": 表示操作的結果是建立了一個新的文件。
"_shards": 這個欄位提供了關於索引操作的分片資訊。
"total": 2: 表示總共有2個分片參與了這次索引操作(通常是一個主分片和其副本)。
"successful": 1: 表示有1個分片成功完成了索引操作。在yellow健康狀態的索引中,這通常意味著主分片成功了,但副本分片可能還沒有資料(因為它是yellow狀態,副本可能還沒有分配或同步)。
"failed": 0: 表示沒有分片失敗。
"_seq_no": 0: 是文件在Lucene段中的序列號,用於在內部跟蹤文件的版本和順序。
"_primary_term": 1: 主要術語(primary term)是與_seq_no一起使用的,用於確保文件版本的一致性,特別是在主節點更換時。
  • 改文件(資料):
    方式1(用於覆蓋老資料):POST請求 IP:9200/索引名/_doc/唯一標識
    方式2(用於覆蓋老資料):PUT請求 IP:9200/索引名/_doc/唯一標識
    方式3(用於修改區域性資料):POST請求 IP:9200/索引名/_update/唯一標識
方式1,若有id號,再次執行增文件操作,可自動將create操作程式設計update操作。
更新資料
{
    "id":1,
    "content":"C是世界上最好的程式語言"
}
方式2,請求內容同方式1

方式3,因為要修改區域性資料,所以必須告知ES修改那塊的區域性資料,以下:第一層花括號和doc是固定格式。
{
    "doc" : {
        "content": "C是最好的程式語言"
    }
}



3種方式的響應格式一致:
{
	"_index": "zs_index",
	"_id": "1",
	"_version": 17,
	"result": "updated",
	"_shards": {
		"total": 2,
		"successful": 1,
		"failed": 0
	},
	"_seq_no": 24,
	"_primary_term": 1
}

"_index": "zs_index": 表示被更新的文件位於名為zs_index的索引中。
"_id": "1": 是被更新的文件的唯一ID。
"_version": 17: 表示該文件的版本號已更新為17。版本號在每次更新時增加,用於跟蹤文件的變化歷史。
"result": "updated": 表示更新操作已成功執行,文件被更新了。
"_shards": 提供了關於更新操作涉及的分片資訊。

"total": 2: 表示總共有2個分片參與了更新操作(通常是一個主分片和其副本)。
"successful": 1: 表示有1個分片成功完成了更新操作。在yellow健康狀態的索引中,這意味著主分片成功了,但副本分片可能尚未同步資料。
"failed": 0: 表示沒有分片失敗。
"_seq_no": 24: 是文件在Lucene段中的序列號,用於內部跟蹤文件版本和順序。

"_primary_term": 1: 主要術語(primary term)與_seq_no一起使用,確保文件版本的一致性,特別是在主節點更換時。
  • 查詢單條資料:
    GET請求 IP:9200/索引名/_doc/唯一標識
{
	"_index": "zs_index",
	"_id": "1",
	"_version": 8,
	"_seq_no": 14,
	"_primary_term": 1,
	"found": true,
	"_source": {
		"id": 1,
		"content": "C是世界上最好的程式語言"
	}
}


"_seq_no": 14: 是文件在Lucene段中的序列號,用於內部跟蹤文件版本和順序。
"_primary_term": 1: 主要術語(primary term)與_seq_no一起使用,確保文件版本的一致性,尤其是在主節點更換時。
"found": true: 表示Elasticsearch成功找到了指定ID的文件,若為false,表示未找到。
"_source": 包含了文件的實際內容。
  • 查詢多條資料:
    GET請求 IP:9200/索引名/_search
{
	"took": 137,
	"timed_out": false,
	"_shards": {
		"total": 1,
		"successful": 1,
		"skipped": 0,
		"failed": 0
	},
	"hits": {
		"total": {
			"value": 8,
			"relation": "eq"
		},
		"max_score": 1,
		"hits": [
			{
				"_index": "zs_index",
				"_id": "1",
				"_score": 1,
				"_source": {
					"id": 1,
					"content": "C是世界上最好的程式語言"
				}
			},
			{
				"_index": "zs_index",
				"_id": "02M0ZpABZdTHCHXLjAgN",
				"_score": 1,
				"_source": {
					"id": 1,
					"content": "C是世界上最好的程式語言"
				}
			}
		]
	}
}

"took": 137: 表示搜尋操作耗費了137毫秒。
"timed_out": false: 表示搜尋操作未超時。
"_shards": 提供了關於搜尋操作涉及的分片資訊。

"total": 1: 表示總共有1個分片參與了搜尋操作。
"successful": 1: 表示所有參與的分片都成功完成了搜尋。
"skipped": 0: 表示沒有分片被跳過。
"failed": 0: 表示沒有分片失敗。
"hits": 包含了搜尋結果的詳細資訊。

"total": {"value": 8, "relation": "eq"}: 表示符合搜尋條件的文件總數為8個。
"value": 8: 具體的文件數。
"relation": "eq": 表示與總數值相等,即已經獲取了所有匹配的文件。
"hits"陣列: 包含了每個匹配文件的詳細資訊。

每個文件物件包括了:
"_index": "zs_index": 文件所屬的索引名稱。
"_id": 文件的唯一ID。
"_score": 1: 文件的匹配分數,此處為1(最高分)。
"_source": 包含了文件的實際內容。
  • 刪除資料
    DELETE請求 IP:9200/索引名/_doc/唯一標識
{
	"_index": "zs_index",
	"_id": "1",
	"_version": 24,
	"result": "not_found",
	"_shards": {
		"total": 2,
		"successful": 1,
		"failed": 0
	},
	"_seq_no": 31,
	"_primary_term": 1
}
"result": "not_found": 表示更新操作未找到指定的文件,若是deleted,表示成功刪除。
_shards": 提供了關於更新操作涉及的分片資訊。

"total": 2: 表示總共有 2 個分片參與了更新操作(通常是一個主分片和其副本)。
"successful": 1: 表示有 1 個分片成功完成了更新操作。在索引狀態為 yellow 時,這可能意味著主分片成功了,但副本分片可能尚未同步資料。
"failed": 0: 表示沒有分片失敗。
"_seq_no": 31: 是文件在 Lucene 段中的序列號,用於內部跟蹤文件版本和順序。

"_primary_term": 1: 主要術語(primary term)與 _seq_no 一起使用,確保文件版本的一致性,特別是在主節點更換時。

文件複雜查詢操作

  • 透過關鍵詞查詢:
    方式1:GET請求 IP:9200/索引名/_search?q=文件欄位名:要搜尋的關鍵字
    方式2:GET請求 IP:9200/索引名/_search
    並新增請求body{ "query":{ "match": { "文件欄位名":"要搜尋的關鍵字" } } }
{
	"took": 8,
	"timed_out": false,
	"_shards": {
		"total": 1,
		"successful": 1,
		"skipped": 0,
		"failed": 0
	},
	"hits": {
		"total": {
			"value": 6,
			"relation": "eq"
		},
		"max_score": 0.074107975,
		"hits": [
			{
				"_index": "zs_index",
				"_id": "0mMsZpABZdTHCHXLZQhu",
				"_score": 0.074107975,
				"_source": {
					"id": 1,
					"content": "C是世界上最好的程式語言"
				}
			}
		]
	}
}

took: 查詢花費的時間,單位為毫秒。在這個例子中,值為8,表示查詢執行花費了8毫秒時間。

timed_out: 表示查詢是否超時。在這個例子中,值為false,表示查詢未超時。

_shards: 分片相關資訊,包括:

total: 總分片數,這裡是1個分片。
successful: 成功的分片數,這裡是1個分片。
skipped: 被跳過的分片數,這裡是0個分片。
failed: 失敗的分片數,這裡是0個分片。
hits: 查詢命中的結果集資訊,包含:

total: 總命中數,這裡是6。
max_score: 結果集中最高得分,這裡是0.074107975。
hits: 包含具體的命中文件陣列。
每個文件包含以下資訊:
_index: 文件所在的索引。
_id: 文件的唯一識別符號。
_score: 文件的得分。
_source: 儲存實際資料的欄位。
  • 分頁查詢:
    GET請求 IP:9200/索引名/_search
    body體新增{ "query": { "match": { "文件欄位名":"要搜尋的關鍵字" } }, "from":0, "size":2 }
    其中,from為起始位置偏移量,size為每頁顯示的條數。
    from演算法:(頁碼 -1)* size = form。
    第1頁:(1 - 1)* 2 = 0,所以from為0。
    第2頁:(2 - 1)* 2 = 2,所以from為2。
    響應結果同上。

  • 只顯示資料的部分欄位:
    GET請求 IP:9200/索引名/_search
    body體新增_source項即可{ "query": { "match": { "文件欄位名":"要搜尋的關鍵字" } }, "_source":["id"] }
    響應結果同上。

  • 排序:
    GET請求 IP:9200/索引名/_search
    body體新增sort項即可 { "query": { "match": { "文件欄位名":"要搜尋的關鍵字" } }, "sort":{ "排序的欄位名":{ "order":"asc" } } }
    注意,這個將要排序的欄位,可以不被展示出來也能排序(_source控制項)
    響應結果同上。

  • 多條件and或or查詢,區間查詢
    GET請求 IP:9200/索引名/_search
    如下,需新增以下body,表示查詢content欄位為C語言和(&&)C++語言(C++語言會被拆分),並且content>1(隨意測試)的資料。
    若替換must為should,則表示或(or)之意。

{
	"query": {
		"bool": {
			"must": [
				{
					"match": {
						"content": "C語言"
					}
				},
				{
					"match": {
						"content": "C++語言"
					}
				}
			],
			"filter": {
				"range": {
					"content": {
						"gt": 1
					}
				}
			}
		}
	}
}

響應結果同上。

  • 全文精準匹配
    GET請求 IP:9200/索引名/_search
    仍需新增如下body{ "query":{ "match_phrase" :{ "欄位名":"要搜尋的關鍵字" } } }
    響應結果同上。
  • 查詢到的結果高亮顯示
    GET請求 IP:9200/索引名/_search
    仍需新增如下body{ "query":{ "match_phrase" :{ "欄位名":"要搜尋的關鍵字" } }, "highlight":{ "fields":{ "欄位名":{} } } }
    響應結果同上。

聚合查詢

  • 求指定欄位平均值
    由於聚合函式過多,逐一說明會讓篇幅變的很長,因此推薦看官方文件:https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics.html
    GET請求 IP:9200/索引名/_search
    仍需新增如下body{ "aggs" : { "id_group_avg" : { "avg" : { "field" : "欄位名" } } }, "size":0 }
    其中,id_group_avg為自定義名稱,size:0表示去掉對文件資料的返回。
{
	"took": 35,
	"timed_out": false,
	"_shards": {
		"total": 1,
		"successful": 1,
		"skipped": 0,
		"failed": 0
	},
	"hits": {
		"total": {
			"value": 6,
			"relation": "eq"
		},
		"max_score": null,
		"hits": []
	},
	"aggregations": {
		"id_group_avg": {
			"value": 1
		}
	}
}

took: 查詢花費的時間,單位是毫秒。這裡是 35 毫秒。

timed_out: 查詢是否超時。這裡顯示為 false,表示查詢在規定時間內完成。

_shards: 這個物件提供關於查詢在分片上的執行情況的詳細資訊:

total: 總分片數。
successful: 成功完成查詢的分片數。
skipped: 跳過的分片數。
failed: 查詢失敗的分片數。
在這個例子中,總分片數為 1,且成功完成了查詢。

hits: 包含有關查詢匹配的文件資訊:

total: 文件匹配的總數。
value: 匹配的文件數,這裡是 6。
relation: 匹配關係,這裡是 “eq” 表示精確匹配。
max_score: 最高得分,如果不需要計算得分則為 null。
hits: 實際匹配的文件陣列。在這個例子中是空的,因為沒有具體的文件資料。
aggregations: 聚合結果資訊:

id_group_avg: 聚合名稱,這裡的值為 1。具體的聚合結果會根據你的查詢和聚合定義而有所不同。

分詞與不分詞的控制

這塊由於涉及到欄位的改動,所以需要重新建立索引,並且新增了對映(mapping)的概念

重新建立一個people索引
PUT請求 IP:9200/people
再次請求,新增對映
IP:9200/people/_mapping

{
    "properties" :{
        "name" : {
            "type":"text",
            "index":true
        },
        "sex" : {
            "type":"keyword",
            "index":true
        },
        "tel" : {
            "type":"keyword",
            "index":false
        }
    }
}
上方的index指的是是否為這條資料新增索引。
type是索引型別,text代表支援分詞查詢(MySQL like '%kw%'),keyword代表不可分詞查詢 (MySQL = 'kw')。

然後新增三條資料
PUT IP:9200/people/_create/1
{
    "name":"張三",
    "sex":"男性",
    "tel":"18888888888"
}
PUT IP:9200/people/_create/2
{
    "name":"李四",
    "sex":"女性",
    "tel":"16666666666"
}
PUT IP:9200/people/_create/3
{
    "name":"王五",
    "sex":"男性",
    "tel":"18866668888"
}

搜尋
GET IP:9200/people/_search
{
    "query" :{
        "match" :{
            "sex" : "男" 把性去掉,搜尋不到資料
        }
    }
}
GET IP:9200/people/_search
{
    "query" :{
        "match" :{
            "name" : "張" 把三去掉,可以搜尋到資料
        }
    }
}
GET IP:9200/people/_search
{
    "query" :{
        "match" :{
            "tel" : "188" 若輸入手機號前3位,則搜不到資料,輸入完整的手機號,則可以搜尋到資料
        }
    }
}

PHP Api呼叫

官方文件:https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/getting-started-php.html#_installation
某些ES Api(例如建立索引)不能重複執行,重複執行會報錯,所以在執行寫操作的上游做判斷,或者使用try catch。

composer require elasticsearch/elasticsearch
推薦安裝symfony/var-dumper,用於dd()或dump()執行,美化輸出。


新建PHP檔案,以下程式碼資料為公共部分。
include './vendor/autoload.php';
use Elastic\Elasticsearch\ClientBuilder;
//連線ES
$client = ClientBuilder::create()->setHosts(['192.168.0.183:9200'])->build();
//若es有密碼,則需要新增一個setBasicAuthentication()方法。
$client = ClientBuilder::create()->setHosts(['192.168.0.183:9200'])->setBasicAuthentication('elastic', '123456')->build();

PHP ES Api針對Index增刪改查

  • 建立
返回bool
$response = $client->indices()->create([
    'index' => 'php_index'
]);
$response->asBool();
  • 查詢 判斷索引是否存在
返回bool
$response = $client->indices()->exists(['index' => 'php_index']);
dd($response->asBool());
  • 查詢 檢視索引相關資訊
返回array
$response = $client->indices()->get(['index' => 'php_index']);
dd($response->asArray());
  • 刪除
返回bool
$response = $client->indices()->delete(['index' => 'php_index']);
dd($response->asBool());
  • 修改
    索引作為基礎性的資料支撐,一般不做改動。

PHP ES Api針對Mapping增刪改查

  • 型別可參考官方文件:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html
  • 增 建立索引後
返回bool
$params = [
    'index' => 'php_index',
    'body' => [
        'properties' => [
            'name' => [
                'type' => 'text',
            ],
        ]
    ]
];
$response = $client->indices()->putMapping($params);

dd($response->asBool());
  • 增 建立索引時
返回bool
$params = [
    'index' => 'php_index',
    'body' => [
        'mappings' => [
            'properties' => [
                'title' => [
                    'type' => 'text',
                ],
                'content' => [
                    'type' => 'text',
                ],
            ]
        ]
    ]
];
$response = $client->indices()->create($params);
dd($response->asBool());
  • 查 所有索引
返回陣列
$response = $client->indices()->getMapping();
dd($response->asArray());
  • 查 指定索引
返回陣列
$response = $client->indices()->getMapping(['index' => 'php_index']);
dd($response->asArray());

  • 請直接刪除索引。

  • 請重新建立索引,在新索引基礎上做對映的修改。

PHP ES Api針對Doc增刪改

  • 索引與對映如下:
準備四個直轄市的名稱,簡介,人口和麵積大小。
$params = [
    'index' => 'php_index',
    'body' => [
        'mappings' => [
            'properties' => [
                'city' => [
                    'type' => 'keyword',
                ],

                'description' => [
                    'type' => 'text',
                ],

                'population' => [
                    'type' => 'integer'
                ],

                'area' => [
                    'type' => 'integer'
                ],
            ]
        ]
    ]
];

$response = $client->indices()->create($params);
dd($response->asArray());
  • 增 單條 請記憶這4個直轄市的資料儲存格式,下文基本每個演示都要用
    一級陣列下有個id屬性,若省去,ES會預設給這條資料加一個id。不推薦。推薦使用MySQL的資料id作為ES的id。
返回bool
$params = [
    'index' => 'php_index',
    'id'   => 1,
    'body' => [
        'id'          => 1,
        'city'        => '北京市',
        'description' => '北京市(Beijing),簡稱“京”,古稱燕京、北平,是中華人民共和國首都、直轄市、國家中心城市、超大城市, 國務院批覆確定的中國政治中心、文化中心、國際交往中心、科技創新中心, 中國歷史文化名城和古都之一,世界一線城市',
        'population'  => '2186',
        'area'        => '16411',
    ]
];
$response = $client->index($params);
dd($response->asBool());

再增加3條資料
$params = [
    'index' => 'php_index',
    'id'   => 2,
    'body' => [
        'id'          => 2,
        'city'        => '上海市',
        'description' => '上海市(Shanghai City),簡稱“滬” ,別稱“申”,中華人民共和國直轄市、國家中心城市、超大城市、上海大都市圈核心城市、國家歷史文化名城 [206],是中國gcd的誕生地。上海市入圍世界Alpha+城市, 基本建成國際經濟、金融、貿易、航運中心,形成具有全球影響力的科技創新中心基本框架。截至2022年12月底,上海市轄16個區,107個街道、106個鎮、2個鄉。',
        'population'  => '2487',
        'area'        => '6341',
    ]
];
$params = [
    'index' => 'php_index',
    'id'   => 3,
    'body' => [
        'id'          => 3,
        'city'        => '天津市',
        'description' => '天津市(Tianjin City),簡稱“津”,別稱津沽、津門,是中華人民共和國省級行政區、直轄市、國家中心城市、超大城市 [222],地處中華人民共和國華北地區,海河流域下游,東臨渤海,北依燕山,西靠首都北京市,其餘均與河北省相鄰。截至2023年10月,天津市共轄16個區。',
        'population'  => '1364',
        'area'        => '11966',
    ]
];
$params = [
    'index' => 'php_index',
    'id'   => 4,
    'body' => [
        'id'          => 4,
        'city'        => '重慶市',
        'description' => '重慶市,簡稱“渝”, 別稱山城、江城,是中華人民共和國直轄市、國家中心城市、超大城市,國務院批覆的國家重要中心城市之一、長江上游地區經濟中心, 國際消費中心城市,全國先進製造業基地、西部金融中心、西部科技創新中心、 國際性綜合交通樞紐城市和對外開放門戶,轄38個區縣',
        'population'  => '3191',
        'area'        => '82400',
    ]
];
  • 增 多條
返回陣列
//假設MySQL查詢出來的資料如下
$mysql_data = [
    [
        'id'          => 1024,
        'city'        => 'xx市',
        'description' => 'xxxx',
        'population'  => '6666',
        'area'        => '6666',
    ],
    [
        'id'          => 1025,
        'city'        => 'yy市',
        'description' => 'yyyy',
        'population'  => '8888',
        'area'        => '8888',
    ]
];

//由於ES插入的要求,需要將插入資料的格式轉化,為此可以封裝一個方法
function esBatchInsert($index_name, $mysql_data) {
    $params = [];
    foreach($mysql_data as $v) {
        $params['body'][] = ['index' => ['_index' => $index_name, '_id' => $v['id']],];
        $params['body'][] = $v;
    }
    return $params;
}

$response = $client->bulk(esBatchInsert('php_index', $mysql_data));
dd($response->asArray());
可根據返回的資料再次迴圈,排查失敗掉的漏網之魚
  • 刪 單條
返回bool
$params = [
    'index' => 'php_index',
    'id'    => '1025'
];
$response = $client->delete($params);
dd($response->asBool());
  • 刪 多條
方式1:
返回mixed
for($i = 1000; $i < 1050; $i++) { //模擬要刪除這些資料
    $params = [
        'index' => 'php_index',
        'id'    => $i
    ];

    if(! $client->exists($params)->asBool()) {
        continue;
    }

    $response = $client->delete($params)->asBool();
    if(! $response) {
        //若刪除失敗,請新增其它操作,記錄日誌或存入佇列,進行重試或者人工介入
    }
}

方式2:
返回mixed
for($i = 1000; $i < 1050; $i++) { //模擬要刪除這些資料
    $params['body'][] = [
        'delete' => [
            '_index' => 'php_index',
            '_id' => $i,
        ]
    ];
}

$response = $client->bulk($params)->asArray();

if ($response['errors']) {
    foreach ($response['items'] as $item) {
        if (isset($item['delete']['status']) && ($item['delete']['status'] != 200)) {
            //若刪除失敗,請新增其它操作,記錄日誌或存入佇列,進行重試或者人工介入
        }
    }
} else {
    echo "批次刪除成功!";
}
  • 刪 文件的某個欄位
返回bool
$params = [
    'index' => 'php_index',
    'id' => 1,
    'body' => [
        'script' => [
            'source' => 'ctx._source.remove(params.field)',
            'params' => [
                'field' => '要刪除的欄位名'
            ]
        ]
    ]
];

$response = $client->update($params);
dd($response->asBool());
  • 改 直接修改
返回bool
$params = [
    'index' => 'php_index',
    'id'    => 1,
    'body'  => [
        'doc' => [
            'city' => '北京' //這裡是要修改的欄位,把北京市改為北京
        ]
    ]
];

$response = $client->update($params);
dd($response->asBool());
  • 改 自增
返回bool
官方文件演示有誤,請按照以下正確寫法。
$params = [
    'index' => 'php_index',
    'id'    => 1,
    'body'  => [
        'script' => [
        	//表示式
            'source' => 'ctx._source.population += params.population', //給北京人口加4萬,population為自定義文件欄位,其餘字元固定寫法。
            //表示式所使用的變數
            'params' => [
                'population' => 4
            ],
        ],
    ]
];
$response = $client->update($params);
dd($response->asBool());
  • 改 若文件不存在,則插入
$params = [
    'index' => 'php_index',
    'id'    => 60, //若id對應的文件不存在,則利用upsert段的資料,重新生成一個id為60的文件。
    'body'  => [
        'doc' => [
            'city' => '臺北市'
        ],
        'upsert' => [
            'append_field' => 1
        ],
    ]
];

$response = $client->update($params);
dd($response->asBool());
  • 改 批次
//假設以下資料時資料表中查詢出來的欄位,要修改以下內容
$mysql_data = [
    ['id' => 1, 'city' => '北京'],
    ['id' => 2, 'city' => '上海'],
];

//可以封裝一個方法,格式化資料
function esBatchUpdate($index_name, $update_data) {
    if(! $update_data) {
        return [];
    }

    $arr = [];
    foreach($update_data as $v) {
        $arr[] = ['update' => ['_index' => $index_name, '_id' => $v['id']]];
        unset($v['id']);
        $arr[] = ['doc' => $v];
    }
    return ['body' => $arr];
}


$response = $client->bulk(esBatchUpdate('php_index', $mysql_data));
$response = $response->asArray();

//處理
if ($response['errors']) {
    foreach ($response['items'] as $item) {
        if (isset($item['update']['status']) && ($item['update']['status'] != 200)) {
            //若刪除失敗,請新增其它操作,記錄日誌或存入佇列,進行重試或者人工介入
        }
    }
} else {
    echo "批次刪除成功!";
}
  • 改 追加新的欄位
$params = [
    'index' => 'php_index',
    'id' => '1',
    'body' => [
        'doc' => [
            'new_field' => 'new_value'
        ],
    ]
];

$response = $client->update($params);

  • 改 刪除某些欄位
返回bool
$params = [
    'index' => 'php_index',
    'id' => 1,
    'body' => [
        'script' => [
            'source' => 'ctx._source.remove(params.field)',
            'params' => [
                'field' => '要刪除的欄位名'
            ]
        ]
    ]
];

$response = $client->update($params);
dd($response->asBool());

PHP ES Api針對Doc高階查詢

查詢關鍵詞官方文件:https://www.elastic.co/guide/en/elasticsearch/reference/current/term-level-queries.html

  • 指定id查詢
返回string
$params = [
    'index' => 'php_index',
    'id'    => 1,
];

$response =  $client->get($params);
echo $response->asString();
得到以下結果
{
    "_index": "php_index",
    "_id": "1",
    "_version": 1,
    "_seq_no": 0,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "id": 1,
        "city": "北京市",
        "description": "北京市(Beijing),簡稱“京”,古稱燕京、北平,是中華人民共和國首都、直轄市、國家中心城市、超大城市, 國務院批覆確定的中國政治中心、文化中心、國際交往中心、科技創新中心, 中國歷史文化名城和古都之一,世界一線城市",
        "population": "2186",
        "area": "16411"
    }
}
  • 查詢全部
返回array
$response['hits']['total']['value']可獲取條數
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'match_all' => new StdClass
        ]
    ]
];

$response = $client->search($params);

dd($response->asArray());
  • 指定指定部分id的資料。
返回陣列
$response['hits']['total']['value']可獲取條數
$params = [
    'index' => 'php_index',
    'body' => [
        'query' => [
            'ids' => [
                'values' => [1, 2]
            ]
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());
  • 分頁查詢
返回陣列
//傳輸的頁碼
$page = 2;
$size = 2;

//偏移量演算法
$offset = ($page -1 ) * $size;

$params = [
    'index' => 'php_index',
    'body' => [
        'from' => $offset,
        'size' => $size,
        // 可以新增其他查詢條件
        'query' => [
            'match_all' => new \stdClass()
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());
  • 返回指定欄位
返回陣列
$params = [
    'index' => 'php_index',
    'body' => [
        '_source' => ['description'], //自定義欄位
        'query' => [
            'match_all' => new \stdClass()
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());
  • 判斷是否存在
返回bool
$params = [
    'index' => 'php_index',
    'id'    => 10
];
$response = $client->exists($params);
  • 獲取條數
返回int
$params = [
    'index' => 'php_index',
    'body' => [
        'query' => [
            'match_all' => new StdClass
        ]
    ]
];

$response = $client->count($params);
dd($response->asArray()['count'] ?? 0);
  • 高亮查詢(類比百度詞條對關鍵字的標紅行為)
返回string
echo "<style>em{color:red}</style>";
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'match' => [
                'description' => '北京' //返回該欄位含有北京或北或京的文字。
            ]
        ],
        'highlight' => [
            'fields' => [
                'city' => ['pre_tags' => ['<em>'], 'post_tags' => ['</em>'],], //配置要高亮的欄位
                'description' => ['pre_tags' => ['<em>'], 'post_tags' => ['</em>'],] //配置要高亮的欄位
            ]
        ]
    ]
];

$response = $client->search($params);
print_r($response->asString());
返回格式如下,具體要用那個欄位,看具體需求
<style>em{color:red}</style>
{
  "took":13,
  "timed_out":false,
  "_shards":{
    "total":1,
    "successful":1,
    "skipped":0,
    "failed":0
  },
  "hits":{
    "total":{
      "value":2,
      "relation":"eq"
    },
    "max_score":2.9070516,
    "hits":[
      {
        "_index":"php_index",
        "_id":"1",
        "_score":2.9070516,
        "_source":{
          "id":1,
          "city":"北京",
          "description":"北京市(Beijing),簡稱“京”,古稱燕京、北平,是中華人民共和國首都、直轄市、國家中心城市、超大城市, 國務院批覆確定的中國政治中心、文化中心、國際交往中心、科技創新中心, 中國歷史文化名城和古都之一,世界一線城市",
          "population":2198,
          "area":"16411",
          "new_field":"new_value"
        },
        "highlight":{
          "description":["<em>北</em><em>京</em>市(Beijing),簡稱“<em>京</em>”,古稱燕<em>京</em>、<em>北</em>平,是中華人民共和國首都、直轄市、國家中心城市、超大城市, 國務院批覆確定的中國政治中心、文化中心、國際交往中心、科技創新中心, 中國歷史文化名城和古都之一"]
        }
      },
      {
        "_index":"php_index",
        "_id":"3",
        "_score":2.5460577,
        "_source":{
          "id":3,
          "city":"天津市",
          "description":"天津市(Tianjin City),簡稱“津”,別稱津沽、津門,是中華人民共和國省級行政區、直轄市、國家中心城市、超大城市 [222],地處中華人民共和國華北地區,海河流域下游,東臨渤海,北依燕山,西靠首都北京市,其餘均與河北省相鄰。截至2023年10月,天津市共轄16個區。",
          "population":"1364",
          "area":"11966"
        },
        "highlight":{
          "description":["天津市(Tianjin City),簡稱“津”,別稱津沽、津門,是中華人民共和國省級行政區、直轄市、國家中心城市、超大城市 [222],地處中華人民共和國華<em>北</em>地區,海河流域下游,東臨渤海,<em>北</em>依燕山,西靠首都<em>北</em><em>京</em>市",",其餘均與河<em>北</em>省相鄰。"]
        }
      }
    ]
  }
}
  • 限量 可參考分頁邏輯(類比MySQL limit)
返回array
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'match_all' => new stdClass
        ],
        'from'  => 0,
        'size'  => 1,
    ]
];

$response = $client->search($params);
dd($response->asArray());
  • 定值查詢 (類比MySQL wher filed = ‘kw’)
    keyword 或 integer 等非分詞欄位:可用 term 精確匹配。如果欄位是 text 型別,那麼 term 查詢無法找到預期的匹配結果。
    text 型別並且你想要精確匹配,可以使用 match_phrase 查詢
方式1 針對integer欄位的精準匹配
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'term' => [
                'city' => '北京市' //北京或北或京無法查詢出指定資料
            ]
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());
  • 分詞查詢(類比MySQL where filed like '%kw%' or filed like '%k%' or filed like '%w%')
方式1
返回array
這種方式僅支援text型別
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'match' => [
                'description' => '北京'
            ]
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());

方式2
返回array
非text型別,可手動分詞
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'bool' => [
                'should' => [ //or
                    [
                        'match' => ['city' => '北京']
                    ],
                    [
                        'match' => ['city' => '北京市']
                    ]
                ],
                'minimum_should_match' => 1
                //minimum_should_match 設定為 1,表示至少需要匹配一個 should 子句中的條件
            ]
        ]
    ]
];
$response = $client->search($params);
dd($response->asArray());
  • 模糊匹配 (類比MySQL where filed like '%kw%')wildcard效能可能不如其它型別的查詢,如match查詢,因為wildcard查詢需要對每個文件的欄位值進行模式匹配
方式1,針對keyword mapping
返回array
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'wildcard' => [
                'city' => '*北京*' //*表示任意字元,?表示任意一個字元
            ]
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());

方式2,針對text mapping,並非嚴格意義上的MySQL where filed like  '%kw%',而是 where filed like '%kw%' or filed like '%k%' or filed like '%w%'
返回array
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'match' => [
                'description' => '北京'
            ]
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());
  • 字首查詢 (類比MySQL where filed like 'kw%')針對keyword型別的欄位有效
返回array
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'prefix' => [
                'city' => '北'
            ]
        ]
    ]
];

$response = $client->search($params);
  • 字尾查詢 (類比MySQL where filed like '%kw')針對keyword欄位有效
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'wildcard' => [
                'city' => '*京市'
            ]
        ]
    ]
];

$response = $client->search($params);
  • 區間查詢(類比MySQL where field <、<=、>、>=、between)
返回array
<是lt、<=是lte、>是gt、>=是gte
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'range' => [
                'area' => [ //面積大於1000平方千米的城市
                    'gt' => 1000
                ]
            ]
        ]
    ]
];
$response = $client->search($params);
dd($response->asArray());

返回array
between
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'range' => [
                'area' => [ //獲取面積大於1000平方千米,但在10000平方千米以內的城市資料
                    'gt' => 1000,
                    'lt' => 10000,
                ]
            ]
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());
  • 正則匹配(類比MySQL where field regexp 'xxx')針對keyword欄位有效
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'regexp' => [
                'city' => '.*北京.*' //搜尋包含北京關鍵字的欄位
            ]
        ]
    ]
];
$response = $client->search($params);
dd($response->asArray());

.*: 匹配任意數量的任意字元
.: 匹配任意單個字元。
*: 匹配前面的元素零次或多次。
+: 匹配前面的元素一次或多次。
?: 匹配前面的元素零次或一次。
^: 匹配字串的開頭。
$: 匹配字串的結尾。
[...]: 匹配方括號中的任意字元。
{n}: 匹配前面的元素恰好 n 次。
{n,}: 匹配前面的元素至少 n 次。
{n,m}: 匹配前面的元素至少 n 次,但不超過 m 次。
  • 取反查詢(類比MySQL where filed != 'kw')針對text型別的欄位無效
返回bool
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'bool' => [
                'must_not' => [
                    'term' => [
                        'city' => '北京市' //返回不是北京市的資料
                    ]
                ]
            ]
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());

$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'bool' => [
                'must_not' => [
                    'range' => [
                        'area' => [ //面積不小於1000平方千米的城市
                            'lt' => 1000
                        ]
                    ]
                ]
            ]
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());
  • 多條件and查詢(類比MySQL where expression1 and expression2)
返回array
$params = [
    'index' => 'php_index',
    'body' => [
        'query' => [
            'bool' => [
                'must' => [ //返回city欄位是北京市,並且描述帶有首都的資料
                    ['term' => ['city' => '北京市']],
                    ['match' => ['description' => '首都']],
                ]
            ]
        ]
    ]
];
$response = $client->search($params);
dd($response->asArray());
  • 多條件or查詢(類比MySQL where expression1 or expression2)
返回array
$params = [
    'index' => 'php_index',
    'body' => [
        'query' => [
            'bool' => [
                'should' => [ //查詢城市名北京市,或者描述含有滬的描述內容
                    ['term' => ['city' => '北京市']],
                    ['match' => ['description' => '滬']],
                ]
            ]
        ]
    ]
];
$response = $client->search($params);
dd($response->asArray());
  • and 和 or 共同使用
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'bool' => [ //查詢城市名為北京市或上海市,並且描述帶有京字的資料
                'must' => [
                    [
                        'bool' => [
                            'should' => [
                                ['term' => ['city' => '北京市']],
                                ['term' => ['city' => '上海市']]
                            ]
                        ]
                    ],
                    ['match' => ['description' => '京']]
                ]
            ]
        ]
    ]
];
$response = $client->search($params);
dd($response->asArray());
  • 排序(類比MySQL Order By)
單欄位排序
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'match_all' => new StdClass,
        ],
        'sort' => [ //四個直轄市資料按照區域大小排名
            ['area' => ['order' => 'asc']] //asc或desc
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());

多欄位排序
$params = [
    'index' => 'php_index',
    'body'  => [
        'query' => [
            'match_all' => new StdClass,
        ],
        'sort' => [ //區域按照降序,人口按照升序排,條件不會衝突,回想MySQL order by那樣,合併處理。
            ['area' => ['order' => 'asc']], //asc或desc
            ['population' => ['order' => 'desc']], //asc或desc
        ]
    ]
];
  • 聚合函式(類比MySQL聚合函式)
返回bool
$params = [
    'index' => 'php_index',
    'body'  => [
        'size' => 0,  // 設定為0表示不返回實際的文件,僅返回聚合結果
        'aggs' => [
            'population_data' => [ //這個key為自定義名稱
                'avg' => [ //返回4個直轄市平均人口
                    'field' => 'population'
                ]
            ]
        ]
    ]
];

$response = $client->search($params);
dd($response->asArray());

avg : 平均值
sum :總和
min : 最小值
max :最大值
沒有count。
  • 分組(類比MySQL Group By)
返回string
$params = [
    'index' => 'php_index',
    'body'  => [
        'size' => 0,  // 不返回文件,只返回聚合結果
        'aggs' => [
            'city_group' => [ //自定義名稱
                'terms' => [
                    'field' => 'city',
                    'size'  => 10  // 聚合結果的數量限制
                ]
            ]
        ]
    ]
];

$response = $client->search($params)->asArray();
$aggregations = $response['aggregations']['city_group']['buckets'];

foreach ($aggregations as $bucket) {
    echo "城市名:" . $bucket['key'] . " - 本組組對應的數量:" . $bucket['doc_count'] . "\n";
}

城市名:上海市 - 本組組對應的數量:1
城市名:北京市 - 本組組對應的數量:1
城市名:天津市 - 本組組對應的數量:1
城市名:重慶市 - 本組組對應的數量:1
  • 合併(類比MySQL union)
    用PHP array_merge實現吧,這對於ES不適用。
  • 指定資料靠前(類比競價排名)
返回array
個人還是推薦使用自定義欄位,因為['hits']['_score']欄位得出來分數不可控。

搜尋城市,原先是北京靠前,現在透過修改權重,使其上海靠前
$params = [
    'index' => 'php_index',
    'body' => [
        'query' => [
            'function_score' => [
                'query' => [
                    'bool' => [
                        'should' => [
                            ['term' => ['city' => '北京市']],
                            ['match' => ['description' => '滬']]
                        ]
                    ]
                ],
                'functions' => [
                    [
                        'filter' => [
                            'match' => ['description' => '滬']
                        ],
                        'weight' => 2  // 增加包含“滬”的文件的權重
                    ]
                ],
                'boost_mode' => 'sum'
            ]
        ],
        'sort' => [
            '_score' => [
                'order' => 'desc'  // 按照得分降序排序
            ]
        ]
    ]
];
$response = $client->search($params);
dd($response->asArray());

boost_mode設定了如何將查詢的基礎得分(由 query 部分確定)與功能得分(由 functions 部分計算)進行組合。以下是幾種常用的 boost_mode 設定:
multiply: 基礎得分與功能得分相乘。
replace: 功能得分替代基礎得分。
sum: 基礎得分與功能得分相加。
avg: 基礎得分與功能得分的平均值。
max: 取基礎得分與功能得分中的最大值。

Painless

  • 極簡概括:是一種簡單、安全的、服務於Elasticsearch的指令碼語言。類比Redis或Nginx中的Lua,某些元件嵌入指令碼語言用於實現複雜的邏輯,這並不罕見。
  • 官方文件:https://www.elastic.co/guide/en/elasticsearch/painless/current/painless-guide.html
  • 使用場景:針對ES,例如上文在更新文件中,請求文件script段中的source段,都用的painless表示式。
  • 額外補充:由於painless語法內容過多,且比較簡單,整個記錄下來需要2萬字,成本問題,因此讀者推薦看手冊。
  • 簡單舉例:
//counter子對岸自增
ctx._source.counter += params.count

//if else 判斷
if (ctx._source.someField > 10) {
    ctx._source.anotherField = ctx._source.someField * params.multiplier;
} else {
    ctx._source.anotherField = params.defaultValue;
}

IK中問分詞與高階索引建立

  • 使用理由:ES預設的分詞器對中文不友好,英文分詞器會把中文每個字分開,因此需要專門的中文分詞器。
  • 分詞器的服務物件是對映,而不是索引。
  • 安裝:
關閉ES

執行以下程式碼,注意版本號的問題
bin/elasticsearch-plugin install https://get.infini.cloud/elasticsearch/analysis-ik/8.14.1
進入互動介面輸入Y。

之後啟動ES
  • ik_max_word與ik_smart分詞精度控制演示:
演示分詞:
GET IP:9200/_analyze
傳入以下內容
{
    "text":"射鵰英雄傳",
    "analyzer":"ik_smart"
}
返回
{
	"tokens": [
		{
			"token": "射鵰英雄傳",
			"start_offset": 0,
			"end_offset": 5,
			"type": "CN_WORD",
			"position": 0
		}
	]
}


若使用ik_max_word
{
    "text":"射鵰英雄傳",
    "analyzer":"ik_max_word"
}
則返回
{
	"tokens": [
		{
			"token": "射鵰英雄傳",
			"start_offset": 0,
			"end_offset": 5,
			"type": "CN_WORD",
			"position": 0
		},
		{
			"token": "射鵰",
			"start_offset": 0,
			"end_offset": 2,
			"type": "CN_WORD",
			"position": 1
		},
		{
			"token": "英雄傳",
			"start_offset": 2,
			"end_offset": 5,
			"type": "CN_WORD",
			"position": 2
		},
		{
			"token": "英雄",
			"start_offset": 2,
			"end_offset": 4,
			"type": "CN_WORD",
			"position": 3
		},
		{
			"token": "傳",
			"start_offset": 4,
			"end_offset": 5,
			"type": "CN_CHAR",
			"position": 4
		}
	]
}


  • 配置自定義分詞:
有些場景,有很多的專業用語,但是IK分詞器把它拆分開,就顯得不是很精準,因此可以新增自定義分詞解決。

vim ES安裝目錄/config/analysis-ik/IKAnalyzer.cfg.xml
在<entry key="ext_dict"></entry>的雙標籤中間寫入檔名,例如
<entry key="ext_dict">self_words.dic</entry>

vim self_words.dic
逐行新增自定義詞彙

重啟ES。
  • PHP使用:
返回bool
$params = [
    'index' => 'test_index',
    'body' => [
        'settings' => [
            'analysis' => [
                'analyzer' => [
                    'analyzer_ik_max_word' => [
                        'type' => 'ik_max_word' //ik分詞器內建關鍵配置,更多的分詞結果
                    ],
                    'analyzer_ik_smart' => [
                        'type' => 'ik_smart' //ik分詞器內建關鍵配置,更快的分詞結果
                    ]
                ]
            ]
        ],
        'mappings' => [
            'properties' => [
                'content' => [
                    'type' => 'text',
                    'analyzer' => 'analyzer_ik_smart',  // 設定索引時的分詞器
                    'search_analyzer' => 'analyzer_ik_smart' // 設定搜尋時的分詞器
                ]
            ]
        ]
    ]
];

$response = $client->indices()->delete(['index' => 'test_index']);
dump($response->asBool());
  • 進階用法,新增過濾(不生效):
返回bool
這裡嘗試建立了一個更復雜的索引,新增了過濾器,但是不生效,不知道是那裡的問題。
如下,按照以下索引的配置,過濾後的結果,應當是"C世界上最好程式語言",然後再分詞,可卻不生效。
GET IP:9200/test_index/_analyze
{
    "analyzer":"self_ik_max_word",
    "text" : "PHP是世界上最好的程式語言"
}


$params = [
    'index' => 'test_index',  // 指定要建立的索引名稱
    'body' => [
        'settings' => [  // 配置索引的設定
            'analysis' => [  // 分析器設定
                'char_filter' => [  // 字元過濾器設定
                    'self_char_filter' => [  // 自定義字元過濾器名稱
                        'type' => 'mapping',  // 過濾器型別為對映
                        'mappings' => ['PHP => C']  // 替換分詞的字元
                    ]
                ],
                'filter' => [  // 過濾器設定
                    'self_filter' => [  // 自定義停用詞過濾器名稱
                        'type' => 'stop',  // 過濾器型別為停用詞
                        'stopwords' => ['是', '的']  // 停用詞列表
                    ]
                ],
                'analyzer' => [  // 分析器設定
                    'self_ik_max_word' => [  // IK 分詞器名稱
                        'type' => 'ik_max_word',  // 使用 IK 分詞器的最大分詞模式
                        'char_filter' => ['html_strip', 'self_char_filter'], // html_strip過濾器會把html標籤忽略,但html跳脫字元仍舊生效(&nbsp;仍舊是空格),且會把<br/>轉化為\n
                        'filter' => ['lowercase', 'self_filter'] //lowercase過濾器是將大寫字母變為小寫
                    ],
                    'self_ik_smart' => [  // IK 分詞器名稱
                        'type' => 'ik_smart',  // 使用 IK 分詞器的快速分詞模式
                        'char_filter' => ['html_strip', 'self_char_filter'],  // html_strip過濾器會把html標籤忽略,但html跳脫字元仍舊生效(&nbsp;仍舊是空格),且會把<br/>轉化為\n
                        'filter' => ['lowercase', 'self_filter'] //lowercase過濾器是將大寫字母變為小寫
                    ]
                ]
            ]
        ],
        'mappings' => [  // 配置索引的對映
            'properties' => [  // 文件欄位的屬性設定
                'content' => [  // 文件中的欄位名稱
                    'type' => 'text',  // 欄位型別為文字
                    'analyzer' => 'self_ik_max_word',  // 設定索引時的分詞器
                    'search_analyzer' => 'self_ik_max_word' // 設定搜尋時的分詞器
                ]
            ]
        ]
    ]
];
$response = $client->indices()->create($params);
dd($response->asBool());

ELK

  • 概念:ES結合LK作為ELK(Elasticsearch(搜尋), Logstash(採集轉換), Kibana(分析))組合,可用於實時監控、分析和視覺化大量日誌和事件資料,如系統日誌、應用程式日誌、網路流量日誌等。
  • 構成
    • Elasticsearch:一個分散式搜尋引擎,提供強大的搜尋功能和實時的資料分析能力。
    • Logstash:一個資料處理管道,用於收集、解析和轉發日誌資料。
    • Kibana:一個資料視覺化工具,幫助使用者透過圖形化介面檢視和分析 Elasticsearch 中的資料。
  • 作用:
    • 日誌管理:集中化日誌收集:透過Logstash收集來自不同系統和應用的日誌,統一儲存在Elasticsearch中。
    • 日誌分析:利用Kibana對日誌資料進行實時分析和視覺化,幫助發現系統問題和異常。
    • 實時監控:跟蹤應用程式的效能指標,實時檢視應用的健康狀況。
    • 效能瓶頸檢測:透過分析日誌資料,識別和解決效能瓶頸。
    • 安全事件分析:監控和分析系統中的安全事件,檢測異常行為。
    • 合規性審計:記錄和分析系統日誌,以滿足合規性要求。
    • 資料視覺化:透過Kibana建立各種圖表和儀表盤,幫助業務分析師理解資料趨勢和模式。
    • 使用者行為分析:分析使用者的操作日誌,最佳化使用者體驗和產品設計。
    • 伺服器監控:跟蹤伺服器的效能指標,如CPU使用率、記憶體使用情況和磁碟空間。
    • 應用狀態監控:監控應用程式的執行狀態和日誌,以確保正常執行。
    • 問題診斷:利用Elasticsearch儲存的日誌資料,快速定位和解決系統故障。
    • 根因分析:分析相關日誌,幫助找到問題的根本原因。
  • 對於PHP而言:幾乎用不到,這是Java和大資料方向的。

Kibana

  • 極簡概括:開源的視覺化控制ES的元件。
  • 官方文件:https://www.elastic.co/guide/en/kibana/current/index.html
  • 安裝:
保證ES服務已啟動。


防火牆開啟5601埠
firewall-cmd --add-port=5601/tcp --zone=public --permanent
systemctl restart firewalld


下載與解壓
curl -O https://artifacts.elastic.co/downloads/kibana/kibana-8.14.1-linux-x86_64.tar.gz
tar zxf kibana-8.14.1-linux-x86_64.tar.gz


許可權配置
chown -R es:es kibana-8.14.1

切換使用者
su es


kibana不支援elastic使用者,所以需要建立新使用者,並賦予超級管理員角色,並賦予kibana_system角色
elasticsearch-users useradd zs
elasticsearch-users roles -a superuser zs
少了這一步報錯,讓我搞了4個小時。
elasticsearch-users roles -a kibana_system zs


修改配置
vim kibana-8.14.1/config/kibana.yml
elasticsearch.username: "zs"
elasticsearch.password: "123456"
elasticsearch.hosts: ["ES IP:9200"]
i18n.locale: "zh-CN"


啟動
kibana-8.14.1/bin/kibana


過2分鐘後,訪問http://IP:5601

4種和資料庫同步方案

  • 不妨先講一講業務層是怎麼使用ES的讀功能的:
    以電商系統為例,用到ES的原因,一個是商品數量龐大,一個是分詞有助於展示更好的結果,上架的商品因為關鍵詞誤差搜不到,這就是損失。
    例如商品列表資料的展示,可將價格,名稱,描述,主圖片,標籤,id等其他資料存入ES,然後展示。
    當使用者點選某個商品時,根據id進行雜湊運算,獲取商品資料在那個MySQL分表中,利用id主鍵索引極速查詢的特性,快速獲取商品資料。
  • 同步雙寫:MySQL和ES同步更新
    • 優點:實現簡單,實時性高。
    • 缺點:耦合度高,其中一個元件異常可能會影響另一個。
  • 非同步雙寫:先同步MySQL,再用MQ同步ES。
    • 優點:優雅,由於MQ(非Redis實現的MQ)具有高可用機制,因此ES消費失敗可以重試。
    • 缺點:多了一個MQ,就多了一層運維成本。有延遲。
  • 自動化任務,定時遍歷SQL:用時間戳做識別符號,用於區分哪些資料未同步,沒有同步就用指令碼定時同步到ES。
    • 優點:業務邏輯層不需要額外的針對ES做寫操作。
    • 缺點:實時性不夠,對MySQL壓力大。
  • 使用Canal基於Binlog進行接近實時的同步,使用Canal監聽MySQL Binlog,並部署同步ES資料的指令碼,從而自動化保持同步。也可直接利用Canal同步ES。相關連結:https://github.com/alibaba/canal/wiki/Sync-ES
    • 優點:實時性高,對業務層程式碼無侵入。
    • 缺點:多了一個Canal,就多了一層運維成本。

高併發下ES本身一致性解決方案

  • 問題:與上文講的資料庫一致性,不是一個東西。這裡講的是併發下ES本身更新資料導致的一致性問題。例如併發過來的兩個請求,查詢到結果是10,都想要-1,等兩個執行完畢後,結果不是8而是9,那麼就出現了資料一致性問題。
  • ES之外的解決方案:分散式鎖。或非分散式環境下程式語言自帶的具有排它性的鎖。
  • ES樂觀鎖解決方案1:
背景:先建立一個num_test索引,並新增名為num的int型別的對映。並插入一條資料。
流程:當進行資料更新時,先做一次查詢(get方法,不是search方法),獲取相關的_primary_term,_seq_no值。
當更新資料時,新增對應的版本號,如果ES檢測到版本號不對,則會報錯,如下:
$params = [
    'index' => 'num_test',
    'id'    => 1,
    'body'  => [
        'doc' => [
            'num' => 10
        ]
    ],
    'if_seq_no' => 3, // 使用序列號
    'if_primary_term' => 1, // 使用主分片術語
];
try {
    $response = $client->update($params);
} catch (\Exception $exception) {
   echo '出錯了,這裡重試查詢後再更新,或者記錄錯誤等其它操作。。。'
}
  • ES樂觀鎖解決方案2(不生效,請勿使用):
$params = [
    'index' => 'num_test',
    'id'    => 1,
    'body'  => [
        'doc' => [
            'num' => 1800
        ]
    ],
    'version' => 40, // 提供外部版本號
    'version_type' => 'external' // 使用外部版本號
];
try {
    $response = $client->update($params); //版本不生效的方案,不推薦使用
} catch (\Exception $exception) {
    dump('出錯了,這裡進行重試,或者記錄錯誤,等其它操作');
}
  • ES應對高併發寫的報錯問題(和上文內容不是一回事):ES針對大量的併發過來的寫請求,ES支援的並不好,ES底層採用樂觀鎖的形式,這會導致ES內部在頻繁併發寫入時內部維護版本號衝突,也就是說更新前查詢出來的版本號,比當前實際的版本號小(被其它併發過來的請求增加了版本號),那就會報錯,這也就是所謂的ES報版本衝突的錯誤的問題,對於這種場景,可新增重試次數,和業務層的異常獲取作為兜底策略。重試程式碼示例如下:
$params = [
    'index' => 'index',
    'id' => '10',
    'body' => [
        'doc' => [
            'field1' => 'new value1',
            'field2' => 'new value2'
        ]
    ],
    'retry_on_conflict' => 3 // 設定重試次數
];

try {
    $response = $client->update($params);
} catch (Exception $e) {
    // 處理異常,可以選擇記錄日誌或執行其它操作,這個catch是用來重試3次還報錯的兜底策略。
}

為什麼不用ES替代MySQL

  • ES沒有MySQL的事務機制,高可用無法保證。
  • ES沒有MySQL的關係型側重,MySQL有強大的關聯策略,MySQL join多張表時,ES需要手動實現。
  • ES的定位是快速索引快速查詢,並非有過多高可用儲存的機制,還是需要配合MySQL使用。

EQL

  • 極簡概括:Event Query Language用於在ES中進行事件資料查詢的類SQL語言。
  • 解決問題:為了更方便地分析時間序列資料和事件流資料,特別適用於安全事件、日誌資料和監控資料的分析。
  • 棄用原因:多用於快速除錯。畢竟ES不是MySQL,SQL API 並不是ES中所有功能的完整替代品,有些複雜的查詢和功能可能需要使用原生的ES查詢 DSL(ES領域或問題域設計的程式語言或語法)。
  • 簡單示例:要查詢索引下的一條資料
POST IP:9200/_sql?format=json //型別可未txt,用製表符更直觀的展示
{
  "query": "SELECT * FROM php_index WHERE city = '北京市'"
}

結果:
{
	"columns": [
		{
			"name": "_boost",
			"type": "float"
		},
		{
			"name": "area",
			"type": "integer"
		},
		{
			"name": "city",
			"type": "keyword"
		},
		{
			"name": "description",
			"type": "text"
		},
		{
			"name": "id",
			"type": "long"
		},
		{
			"name": "population",
			"type": "integer"
		}
	],
	"rows": [
		[
			null,
			16411,
			"北京市",
			"北京市(Beijing),簡稱“京”,古稱燕京、北平,是中華人民共和國首都、直轄市、國家中心城市、超大城市, 國務院批覆確定的中國政治中心、文化中心、國際交往中心、科技創新中心, 中國歷史文化名城和古都之一,世界一線城市",
			1,
			2186
		]
	]
}
  • 演示2:查詢所有索引:
POST IP:9200/_sql?format=txt
{
  "query": "show tables"
}


    catalog    |                       name                       |     type      |     kind      
---------------+--------------------------------------------------+---------------+---------------
zs_es_cluster  |.alerts-default.alerts-default                    |VIEW           |ALIAS          
zs_es_cluster  |.alerts-ml.anomaly-detection-health.alerts-default|VIEW           |ALIAS          
zs_es_cluster  |.alerts-ml.anomaly-detection.alerts-default       |VIEW           |ALIAS          
zs_es_cluster  |.alerts-observability.apm.alerts-default          |VIEW           |ALIAS          
zs_es_cluster  |.alerts-observability.logs.alerts-default         |VIEW           |ALIAS          
zs_es_cluster  |.alerts-observability.metrics.alerts-default      |VIEW           |ALIAS          
zs_es_cluster  |.alerts-observability.slo.alerts-default          |VIEW           |ALIAS          
zs_es_cluster  |.alerts-observability.threshold.alerts-default    |VIEW           |ALIAS          
zs_es_cluster  |.alerts-observability.uptime.alerts-default       |VIEW           |ALIAS          
zs_es_cluster  |.alerts-security.alerts-default                   |VIEW           |ALIAS          
zs_es_cluster  |.alerts-stack.alerts-default                      |VIEW           |ALIAS          
zs_es_cluster  |.alerts-transform.health.alerts-default           |VIEW           |ALIAS          
zs_es_cluster  |.kibana-observability-ai-assistant-conversations  |VIEW           |ALIAS          
zs_es_cluster  |.kibana-observability-ai-assistant-kb             |VIEW           |ALIAS          
zs_es_cluster  |.siem-signals-default                             |VIEW           |ALIAS          
zs_es_cluster  |my_index                                          |TABLE          |INDEX          
zs_es_cluster  |num_test                                          |TABLE          |INDEX              
zs_es_cluster  |php_index                                         |TABLE          |INDEX          
zs_es_cluster  |test_index                                        |TABLE          |INDEX          
zs_es_cluster  |zs_index                                          |TABLE          |INDEX          

相關文章