首页 > 数据库 >ElasticSearch第1讲(4万字详解 Linux下安装、原生调用、API调用超全总结、Painless、IK分词器、4种和数据库同步方案、高并发下一致性解决方案、Kibana、 ELK)

ElasticSearch第1讲(4万字详解 Linux下安装、原生调用、API调用超全总结、Painless、IK分词器、4种和数据库同步方案、高并发下一致性解决方案、Kibana、 ELK)

时间:2024-07-26 13:25:25浏览次数:19  
标签:index 调用 超全 Painless id 索引 params 文档 response

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的概念去辅助记忆。

ESMySQL备注
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],是中国共产党的诞生地。上海市入围世界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服务已启动。


防火墙开启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          

标签:index,调用,超全,Painless,id,索引,params,文档,response
From: https://blog.csdn.net/weixin_42100387/article/details/140061843

相关文章

  • delphi调用Java类
    1、jdk的安装:https://www.cnblogs.com/ljk2008/p/18324820。2、jvm.dll:如果提示找不到JVM.dll需要在classpath、path中分别添加jvm.dll的路径。3、用到三方库:https://github.com/aleroot/DelphiJNI.git。4、JAVA类(CLASS、JAR)要放置在对应的文件夹内,如:packagecom.com1.com2;对......
  • 使用pybind11封装c++的dll,供python调用
    用pip安装好pybind11 文件清单,都写在一个目录里//文件名:add.cppextern"C"doubleadd(doublein1,doublein2){returnin1+in2;}//文件名:sub.cppextern"C"doublesub(doublein1,doublein2){returnin1-in2;}//文件名:mul.cppextern"......
  • 实现一个自己的OpenFeign 远程调用验证协议--Springboot 自定义拦截器验证请求合法性-
    Springboot如何实现一个自定义的拦截器处理系统发出的请求以及接收到的请求(实现一个用于feign远程调用验证的简单协议)文章目录Springboot如何实现一个自定义的拦截器处理系统发出的请求以及接收到的请求(实现一个用于feign远程调用验证的简单协议)**实现Feign拦截器的意......
  • 利用Java调用人脸身份证比对接口
    一、什么是人脸身份证比对接口?人脸身份证比对接口是一种特定的API接口服务,主要用于将提供的人脸图片和对应的身份证照片/号码进行比对,以此验证其身份。这种接口的功能基于复杂的人脸识别技术,一般通过使用人工智能和深度学习算法来实现。它在许多需要实名身份验证的行业中有......
  • 嵌入式学习--DAY10:函数的调用
    一、函数参数和函数的值1.在定义函数中指定的形参,在未出现函数调用时,它们并不占用内存中的存储单元,只有在发生函数调用时,函数中的形参才会被分配内存单元。在调用结束后,形参所占的内存单元也会被释放。2.实参可以是常量、变量或表达式。在被定义的函数中,必须指定形参的类型,实......
  • SpringBootApplication入口调用service类方法
    要在publicstaticvoidmain(String[]args)中调用Service的方法,需要在Application类中手动获取Spring容器,并从中获取Service的实例。示例如下:启动入口程序@SpringBootApplicationpublicclassRouteApplication{publicstaticvoidmain(String[]args){......
  • 在 Python Notebook 中调用 `subprocess` 具有与 `!` shell 不同的 `$PATH`
    我正在IPython笔记本中交互地开发一个包装类。这个包装类调用用java编写的命令行程序,因此我需要访问用于编译该程序的相同版本的java运行时。但是,我注意到在笔记本中使用方便的!运算符,生成的shell实例与在我的终端中使用zsh时不同。这得到了确......
  • 调用后端接口返回导出表格
    //fetch('/record/export/report',{//method:'POST',//指定请求方法为POST//headers:{//'Content-Type':'application/json',//设置请求头,指明发送的是JSON格式的数据//......
  • 无法在我的 apache 服务器内对 Flask 应用程序(用于 MySQL 连接)进行远程 api 调用
    我有一个在AlmaLinux上运行的apache服务器。我有Flask代码设置来接受来自远程连接的API调用。所以我的API调用命中了Flask,然后它连接到MySQL数据库。当我尝试在服务器内部本地运行此数据库连接代码时,它工作正常。但是当我尝试通过远程API调用来访问Flask应......
  • 在C++里如何释放内存的时候不调用对象的析构函数?
    今天,看到一个有趣的面试题,问题是:在C++里如何释放内存的时候不调用对象的析构函数?之所以有趣,是因为这个问题违反了C++中资源管理的RAII(资源获取即初始化),它要求资源的释放应当和对象的生命周期紧密相关。在正常情况下,当对象离开其作用域时,它的析构函数被调用,以释放它所管理的......