随手记录第四话 -- elasticsearch基于docker安装以及SpringBoot集成使用

本文只要记录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的过期时间

旧书不厌百回读,熟读精思子自知

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值