本文只要记录elasticsearch8.2.0的安装和常用api使用。
1.elasticsearch介绍
Elasticsearch 是一个分布式的开源搜索和分析引擎,适用于所有类型的数据,包括文本、数字、地理空间、结构化和非结构化。
Elasticsearch 是一个基于 Lucene 库的搜索引擎。它提供了一个分布式、支持多租户的全文搜索引擎,具有 HTTP Web 接口和无模式 JSON 文档。Elasticsearch 是用 Java 开发的,并在 Apache 许可证下作为开源软件发布。官方客户端在 Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby 和许多其他语言中都是可用的。
自我理解:
一个索引对应一个表,可以将表数据存储进去,相当于将其他服务的表数据备份了一份到分布式中间件,其他服务需要根据条件或者根据关键字搜索该表数据时就可以使用es.
例如:在订单服务订单列表中需要搜索用户昵称包含’xx’的用户,这个时候就可以用到elasticsearch搜索引擎了。
2.docker安装elasticsearch
环境准备
centos7,docker,镜像elasticsearch:8.2.0和kibana:8.2.0
2.1 elasticsearch.yml
配置文件elasticsearch.yml,可以直接外网访问,要不然需要https
cluster.name: "docker-cluster"
network.host: 0.0.0.0
xpack.security.enabled: true
xpack.security.http.ssl.enabled: false
xpack.license.self_generated.type: basic
2.2 docker-compose.yml
version: '3.7'
services:
es01:
image: elasticsearch:8.2.0
container_name: es01
ports:
- "9200:9200"
- "9300:9300"
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms128m -Xmx256m
volumes:
- ./data:/usr/share/elasticsearch/data
- ./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
networks:
- my-net
#控制台
kibana:
image: kibana:8.2.0
container_name: kibana
environment:
- elasticsearch.hosts=http://es01:9200
depends_on:
- es01
ports:
- "5601:5601"
networks:
- my-net
networks:
#新增的网络 内部服务名调用
my-net:
external: true
2.3 启动
- 启动镜像详情
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aa8bdc03b942 elasticsearch:8.2.0 "/bin/tini -- /usr/l…" 7 hours ago Up 7 hours 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp es01
58bf5591d120 kibana:8.2.0 "/bin/tini -- /usr/l…" 7 hours ago Up 7 hours 0.0.0.0:5601->5601/tcp kibana
- data目录无权限问题
java.io.UncheckedIOException: java.nio.file.NoSuchFileException: /usr/share/elasticsearch
执行 chmod 777 data
- kibana启动成功日志
[2022-08-16T09:17:12.013+00:00][INFO ][plugins.monitoring.monitoring] config sourced from: production cluster
[2022-08-16T09:17:15.719+00:00][INFO ][http.server.Kibana] http server running at http://0.0.0.0:5601
3.访问
访问都会要求输入账号密码,有两种方式设置,一种是通过配置文件,一种是直接通过容器内部的指令设置
3.1 指令设置密码
docker exec -it es01 bash
# 创建内置的用户 elastic,kibana等用户都会创建,按照步骤一直输入密码即可
elasticsearch-setup-passwords interactive
创建密码完成之后即可登录
3.2 elasticsearch访问
浏览器查看一下http://ip:9200
,输入账号密码elastic/123456
(上面命令设置的密码),出现下面内容即表示es安装成功
3.3 控制台kibana
http://ip:5601
,第一次进去会让输token,直接点手动配置
修改一下默认的地址,注意一下协议
这里连接成功后会到管理控制台界面,会要求登录kibana_system
用户,密码也是上面指令设置好的密码
点击配置后,如果弹出验证窗口,则需要去日志中查看一下验证码
查看kibana的日志
[2022-08-16T09:52:10.231+00:00][INFO ][plugins-system.preboot] Setting up [1] plugins: [interactiveSetup]
[2022-08-16T09:52:10.236+00:00][INFO ][preboot] "interactiveSetup" plugin is holding setup: Validating Elasticsearch connection configuration…
[2022-08-16T09:52:10.331+00:00][INFO ][root] Holding setup until preboot stage is completed.
i Kibana has not been configured.
Go to http://0.0.0.0:5601/?code=297775 to get started.
297775 就是对应的验证码,如果直接使用该url也是可以省略这一步的,验证码应该是检查连接那一步生成的
设置成功才会到登录elastic的界面,和9200端口的账号密码一致elastic/123456
3.4 kibana控制台dev-tool使用
rest风格的url,参数都有提示,但还是要看下具体API,可以查看help指令怎么使用
3.5 重置密码
elastic重置密码
elasticsearch容器的bin目录下执行
elasticsearch-reset-password -u 需要重置密码的用户名
4.elasticsearch 常用Api
4.1 查看所有索引
GET /_cat/indices?v
4.2 查看指定条数的数据,不传size默认10条
get /test_index/_search
{
"query":{
"match_all": {}
},
"size":20
}
4.3 分词功能模糊匹配
这里会分词为最好的、java
,也就是name包含了这两个内容的都会返回
get /test_index/_search
{
"query":{
"match": {
"name":"最好的java"
}
}
}
4.4 不分词查询
也是模糊匹配,但是不分词
get /test_index/_search
{
"query":{
"match_phrase": {
"name":"最好的语言java"
}
}
}
4.5 多个字段查询
类似多个字段合并查询
get /test_index/_search
{
"query":{
"multi_match": {
"query":"老北京信",
"fields": ["addr","info"]
}
}
}
//返回结果
{
"_index" : "test_index",
"_id" : "24",
"_score" : 0.60023105,
"_source" : {
"name" : "世界上最好的语言java",
"age" : 24,
"addr" : "老北京5",
"info" : "老北京信息5"
}
},
{
"_index" : "test_index",
"_id" : "15",
"_score" : 0.12947136,
"_source" : {
"name" : "最好的人来了",
"age" : 15,
"addr" : "老北京人家",
"info" : "老人家信息"
}
4.6 大于小于查询
age大于等于21且小于等于25
get /test_index/_search
{
"query":{
"range": {
"age":{
"gte": 21,
"lte": 25
}
}
}
}
4.7 and或者or
get /test_index/_search
{
"query": {
"bool": {
//should表示或 must表示and
"must": [{
"match": {
"name": "语言"
}
},
{
"match": {
"age": 20
}
}
]
}
}
}
常用的就这些了,其他的等用到再去看api吧,接下来看下springboot集成
5.springboot集成elasticsearch8
基于spring-boot-starter-parent 2.6.8,elasticsearch-rest-client 8.2.0
5.1 依赖
如果使用全家桶包spring-boot-starter-data-elasticsearch
最新的只支持到7.15.2
,本次演示的是8版本,只能分开使用了
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.2.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>8.2.0</version>
</dependency>
<-- 默认的版本是1.x的 不支持了 需要手动引入-->
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.1.1</version>
</dependency>
5.2 初始化es客户端
为了方便地址和密码什么的直接硬编码了
@Bean
public ElasticsearchClient restHighLevelClient() {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
//设置账号密码
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "123456"));
RestClient restClient = RestClient.builder(
new HttpHost("192.168.0.221", 9200, "http"))
.setHttpClientConfigCallback(httpClientBuilder ->
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider))
.build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
ElasticsearchClient client = new ElasticsearchClient(transport);
return client;
}
//注解使用
@Autowired
ElasticsearchClient elasticsearchClient;
5.3 put数据到es
@GetMapping("/put")
public String putData() throws Exception {
List<String> strings = Arrays.asList("java", "python", "php", "go", "vue", "html");
//单独插入
User user = new User();
user.setName("最好的人来了");
user.setAge(15);
user.setAddr("老北京人家");
user.setInfo("老人家信息");
IndexResponse response = elasticsearchClient.index(a -> a
.index("test_index")
.id("15").document(user));
log.info("response:{}", response.seqNo());
//批量插入
List<User> users = new ArrayList<>();
for (int i = 1; i < 6; i++) {
User userBatch = new User();
userBatch.setName("世界上最好的语言" + strings.get(new Random().nextInt(strings.size())));
userBatch.setAge(19 + i);
userBatch.setAddr("老北京" + i);
userBatch.setInfo("老北京信息" + i);
users.add(userBatch);
}
List<BulkOperation> bulkOperationArrayList = new ArrayList<>();
//遍历添加到bulk中
for (User u : users) {
bulkOperationArrayList.add(BulkOperation.of(o -> o.index(i -> i.id(u.getAge() + "").document(u))));
}
BulkResponse bulkResponse = elasticsearchClient.bulk(b -> b.index("test_index").operations(bulkOperationArrayList));
log.info("batch:{}", bulkResponse.took());
return "success";
}
调用接口,查看输出日志
c.example.demo.controller.EsController : response:0
c.example.demo.controller.EsController : batch:1
5.4 get es数据
@GetMapping("/get")
public String getData() throws Exception {
//根据查询条件搜索 term 不使用分词器精确查找
SearchResponse<JSONObject> search = elasticsearchClient.search(s -> s
.index("test_index")
.query(q -> q.matchPhrase(t -> t
.field("info").query("人家信息")
)).from(0).size(3)
.sort(f -> f.field(o -> o.field("age").order(SortOrder.Desc))),
JSONObject.class);
List<Hit<JSONObject>> hits = search.hits().hits();
Iterator<Hit<JSONObject>> iterator = hits.iterator();
while (iterator.hasNext()) {
Hit<JSONObject> decodeBeanHit = iterator.next();
JSONObject docJson = decodeBeanHit.source();
log.info("response_matchphrase:{}", docJson);
}
//分词模糊数据
SearchResponse<JSONObject> response = elasticsearchClient.search(s -> s
.index("test_index")
.query(q -> q.match(t -> t
.field("name").query("最好的python")
)),
JSONObject.class);
iterator = response.hits().hits().iterator();
while (iterator.hasNext()) {
Hit<JSONObject> decodeBeanHit = iterator.next();
JSONObject docJson = decodeBeanHit.source();
log.info("response_match:{}", docJson);
}
//查询范围数据
response = elasticsearchClient.search(s -> s
.index("test_index")
.query(q -> q.range(t ->
t.field("age").gte(JsonData.of(23))
)),
JSONObject.class);
iterator = response.hits().hits().iterator();
while (iterator.hasNext()) {
Hit<JSONObject> decodeBeanHit = iterator.next();
JSONObject docJson = decodeBeanHit.source();
log.info("response_range:{}", docJson);
}
//根据id搜索
GetResponse<JSONObject> getResponse = elasticsearchClient.get(s -> s
.index("test_index").id("24"), JSONObject.class);
log.info("response_id:{}", getResponse.source());
//查询and多条件匹配数据
SearchResponse<JSONObject> response1 = elasticsearchClient.search(s -> s
.index("test_index")
.query(q -> q.bool(t ->
t.must(m -> {
m.match(a -> a.field("name").query("语言go"));
m.match(a -> a.field("age").query(24));
return m;
})
)),
JSONObject.class);
iterator = response1.hits().hits().iterator();
while (iterator.hasNext()) {
Hit<JSONObject> decodeBeanHit = iterator.next();
JSONObject docJson = decodeBeanHit.source();
log.info("response_and:{}", docJson);
}
return "success";
}
分别包含了精确查找、模糊查询、范围查询、根据id查询、and多条件匹配
。请求接口,查看日志
#使用分词器精确查找info包含人家信息
response_matchphrase:{"name":"最好的人来了","addr":"老北京人家","age":15,"info":"老人家信息"}
#模糊匹配name包含 '最好的'或者'python'
response_match:{"name":"最好的人来了","addr":"老北京人家","age":15,"info":"老人家信息"}
response_match:{"name":"世界上最好的语言html","addr":"老北京1","age":20,"info":"老北京信息1"}
response_match:{"name":"世界上最好的语言python","addr":"老北京2","age":21,"info":"老北京信息2"}
response_match:{"name":"世界上最好的语言php","addr":"老北京3","age":22,"info":"老北京信息3"}
response_match:{"name":"世界上最好的语言vue","addr":"老北京4","age":23,"info":"老北京信息4"}
response_match:{"name":"世界上最好的语言go","addr":"老北京5","age":24,"info":"老北京信息5"}
#范围查询 age大于等于23
response_range:{"name":"世界上最好的语言vue","addr":"老北京4","age":23,"info":"老北京信息4"}
response_range:{"name":"世界上最好的语言go","addr":"老北京5","age":24,"info":"老北京信息5"}
#根据id=24查询 存数据是id设置为age
response_id:{"name":"世界上最好的语言go","addr":"老北京5","age":24,"info":"老北京信息5"}
#多条件查询 name包含了`语言go`和age=24
response_and:{"name":"世界上最好的语言go","addr":"老北京5","age":24,"info":"老北京信息5"}
5.5 删除某条数据
@GetMapping("/delete")
public String delete(@RequestParam String id) throws Exception {
DeleteResponse deleteResponse = elasticsearchClient.delete(s -> s.index("test_index").id(id));
log.info("result:{}", deleteResponse.result());
return "";
}
这个就不多做解释了,至于删库自行百度吧
6.总结
查询的核心语句在于下面这句,根据前面的api操作文档应该都能推演出来。
query(q -> q.bool(t ->
t.must(m -> {
m.match(a -> a.field("name").query("语言go"));
m.match(a -> a.field("age").query(24));
return m;
})
)),
不得不说,这编程式是真的难写!吐了
还有很多坑没记录了,数据库数据同步的就不说了,别家去看吧
如果还有其他的更好的方式实现的,欢迎评论区留言哦
以上就是本章的全部内容了。
上一篇:随手记录第三话 --你见过哪些神乎其乎的存储方式?
下一篇:随手记录第五话 – SpringCache搭配Redis的实现以及设置每个key的过期时间
旧书不厌百回读,熟读精思子自知