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

来源:https://www.cnblogs.com/phpphp/p/18324399
-Advertisement-
Play Games

ElasticSearch 官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html 非官方中文文檔:https://learnku.com/docs/elasticsearch ...


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":{ "欄位名":{} } } }
    響應結果同上。

聚合查詢

{
	"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增刪改查

返回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
個人還是推薦使用自

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • “10000條”問題(個人稱謂) 癥狀: 在數據量不大的情況下,可能還會使用from + size的傳統分頁方式,但是數量受限,只能取前10000條的數據。 緣由:ES限值10000條,是ES團隊挑選一個不大不小的數作為閾值,為了避免深度分頁的策略。 調整:max_result_window 用於控 ...
  • 解鎖 SQL Server 2022的時間序列數據功能 SQL Server2022在處理時間序列數據時,SQL Server 提供了一些優化和功能,比如 DATE_BUCKET 函數、視窗函數(如 FIRST_VALUE 和 LAST_VALUE)以及其他時間日期函數,以便更高效地處理時間序列數據 ...
  • 寫在前面 大家好,不知道前面的20題大家寫的怎麼樣,前面分享的20題是SQL中查詢的基礎題型,這部分被稱為DQL部分,是每個學習MySQL必須要學會的部分,下麵就讓我來介紹MySQL中的其他部分。 回顧DQL部分 先介紹一下sql語句的語法和執行順序(序號代表順序由1~9): select 查詢列表 ...
  • DML(數據定義語言) 插入語句 方式一:經典的插入語句 * 語法: insert into 表名(列名,...) values(值1,...); 方式二: 語法: insert into 表名 set 列名=值,列名=值,...... 方式一和方式二對比: 方式一支持插入多行,方式二不支持; 方式 ...
  • 一、MySQL數據結構 InnoDB引擎 MySQL預設引擎是InnoDB引擎,這個引擎的主要特點是支持事務和行鎖, 數據結構 2.1 二叉樹(二叉查找樹) 二叉樹是一種特殊的樹,二叉樹中每個節點的度都不能大於2,就是說每個節點最多只能有左右兩個子節點 當我們像二叉查找樹儲存數據的時候,是安裝從大到 ...
  • 工作常用SQL Excel生成SQL 這個好用 ="insert into t_gk_mapping(id,gk_project_name,gk_project_code,main_project_name,main_project_code) values ('"&J2&"','"&I2&"',' ...
  • 2024年7月24日,由數據猿主辦,IDC協辦,新華社中國經濟信息社、上海大數據聯盟、上海市數商協會、上海超級計算中心作為支持單位,舉辦“數智新質·力拓未來 2024企業數智化轉型升級發展論壇——暨AI大模型趨勢論壇”數據猿“年中·特別策劃季——數智化轉型升級”主題策劃活動。 在這場備受矚目的盛會上 ...
  • 前置銜接文章:ElasticSearch第1講(4萬字詳解 Linux下安裝、原生調用、API調用超全總結、Painless、IK分詞器、4種和資料庫同步方案、高併發下一致性解決方案、Kibana、 ELK) ElasticSearch集群 極簡概括:多個ES節點組成的一個系統。 解決問題: 防止單 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...