Elasticsearch 的详细介绍

Elasticsearch是非常强大的一个开源的分布式搜索和分析引擎,是一个Nosql数据库,可以帮助我们从海量数据中快速找到需要的内容。

es的底层是使用倒排索引来实现的,那么什么是倒排索引呢,接下来简单介绍一下:

倒排索引是指根据内容来查找id。

以传统的MySQL数据库为例,要查询一条数据信息,通常是先找到这个数据的id,在顺应的找到这个数据,那么这样就是正向索引或者说普通索引

如(select * from user  where  title  like  '%手机%')这条查询语句会在user表中,逐条的查看数据,看相应的数据是否符合条件

id      title                         price

1         小米手机                  3499

2         华为手机                 4966

3         小米手环                 299

4         华为小米充电器        999 

但是,倒排索引不同。它是根据字段来查询id;

将我们要查询的字段单独列出来,并指定对应的id

title                               id

小米手机                      1

华为手机                       2

小米手环                       3

华为小米充电器            4

查询时,直接查询title字段,如果字段匹配,那么就可以得到相对应的id。

在查询的过程中,我们还可以将字段按照语义进行划分,划分形成的词语我们称为“词条”

词条                id

小米                 1,3,4

华为                  2,4

手机                 1,2

手环                  3

充电器              4

我要查询华为手机,那么,符合条件的id有1、2、2、4。我们可以看到id为2出现了两次,相关度最高,那么我们就会把id为2的数据放在查询结果的最前面:2、1、4。这样极大的提高了我们在搜索时的效率,不用再逐条数据的查找。

接下来,我们来查看一下es中的相应概念,并于MySQL数据库进行对比。

可以看到es中的属性是可以与MySQL中的属性一一对映的,这意味着我们完全可以参照原有的MySQL的表建立一个es的索引。并在es中映射出MySQL表中的字段。然后,就可以使用es来完成MySQL中难以查询的局部搜索了。

es不允许修改已经创建好的索引库,因为已经创建好的索引库mapper映射已经固定好的。所以es不允许我们再去修改创建好的索引。(这点与MySQL是不同的,MySQL即使表已经创建,还是可以修改表的结构)但是我们却可以在已经创建好的索引中在添加新的mapper映射,这个是允许的。

Elasticsearch环境的搭建

本次使用docker进行es环境的部署,需要先安装好docker的运行环境:

安装docker容器

docker run -d \
    --name es \
    -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
    -e "discovery.type=single-node" \
    -v es-data:/usr/share/elasticsearch/data \
    -v es-plugins:/usr/share/elasticsearch/plugins \
    --privileged \
    --network es-net \
    -p 9200:9200 \
    -p 9300:9300 \
elasticsearch:7.12.1

* `-e "cluster.name=es-docker-cluster"`:设置集群名称
* `-e "http.host=0.0.0.0"`:监听的地址,可以外网访问
* `-e "ES_JAVA_OPTS=-Xms512m -Xmx512m"`:内存大小
* `-e "discovery.type=single-node"`:非集群模式
* `-v es-data:/usr/share/elasticsearch/data`:挂载逻辑卷,绑定es的数据目录
* `-v es-logs:/usr/share/elasticsearch/logs`:挂载逻辑卷,绑定es的日志目录
* `-v es-plugins:/usr/share/elasticsearch/plugins`:挂载逻辑卷,绑定es的插件目录
* `--privileged`:授予逻辑卷访问权
* `--network es-net` :加入一个名为es-net的网络中
* `-p 9200:9200`:端口映射配置

部署kibana:

kibana可以给我们提供一个elasticsearch的可视化界面,便于我们学习。

docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601  \
kibana:7.12.1

安装ik分词器:

# 进入容器内部
docker exec -it elasticsearch /bin/bash

# 在线下载并安装
./bin/elasticsearch-plugin  install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip

#退出
exit
#重启容器
docker restart elasticsearch

访问kibana:

http://ip:5601

spring boot整合Elasticsearch

spring boot整合Elasticsearch有两种常见的方式,

一种是spring boot官方整合的spring-boot-starter--dataelasticsearch,这种整合的是es早期的操作方式,使用的客户端被称为Low Level Client,这种客户端操作方式性能方面略显不足。

于是ES官方开发了全新的客户端操作方式,称为High Level Client。(这也是es官方推荐使用的JavaApi)高级别客户端与ES版本同步更新,但是springboot最初整合ES的时候使用的是低级别客户端,所以在企业开发中通常需要更换成高级别的客户端模式。

Spring Boot 与 Elasticsearch 的整合方式主要有两种:使用 Spring Data Elasticsearch 或者使用 Elasticsearch 官方提供的 Java API。

1、使用 Spring Data Elasticsearch

Spring Data Elasticsearch 是 Spring Data 项目的一部分,它提供了对 Elasticsearch 数据库的访问支持。通过 Spring Data Elasticsearch,你可以使用简单的 Java 对象来进行 Elasticsearch 操作,并且可以自动生成相应的 Elasticsearch 查询语句,从而简化开发过程。

在 Spring Boot 中使用 Spring Data Elasticsearch 需要导入 spring-boot-starter-data-elasticsearch 依赖,并且配置相关属性,例如 Elasticsearch 的地址、端口等。然后就可以在代码中使用 ElasticsearchRepository 进行数据存储和检索操作。具体实现详见 Spring Data Elasticsearch 官方文档。

<dependency>
         <groupId>org.springframework.boot</groupId>-->
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>-->
     </dependency>

在配置文件中配置es的连接地址:

spring:
  data:
    elasticsearch:
      cluster-nodes: localhost:9200


 

初始化和关闭客户端对象:

早期使用ElasticsearchTemplate来操作es,但是使用的客户端被称为Low Level Client,这种客户端操作方式性能方面略显不足,于是ES开发了全新的客户端操作方式,称为High Level Client。高级别客户端与ES版本同步更新,但是springboot最初整合ES的时候使用的是低级别客户端,所以企业开发需要更换成高级别的客户端模式。下面使用高级别客户端方式进行springboot整合ES,操作步骤如下:

Spring Boot会自动根据配置文件中的参数来初始化Elasticsearch客户端对象。您可以直接注入RestHighLevelClient 来使用它,Spring会负责管理客户端的初始化和关闭。

import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.stereotype.Service;

@Service
public class MyElasticsearchService {

    private final RestHighLevelClient elasticsearchClient;

    public MyElasticsearchService(RestHighLevelClient elasticsearchClient) {
        this.elasticsearchClient = elasticsearchClient;
    }

    // 其他业务操作
    // ...
}

在上述代码中,我们在MyElasticsearchService中注入了RestHighLevelClient。Spring Boot会自动查找配置并创建Elasticsearch客户端,然后将其注入到MyElasticsearchService类中。当应用程序关闭时,Spring Boot会自动关闭Elasticsearch客户端。

确保Elasticsearch服务器在配置的节点上运行,并且应用程序能够连接到Elasticsearch服务器。整合完成后,您可以在MyElasticsearchService中使用elasticsearchClient来执行各种Elasticsearch操作。

通过使用Spring Data Elasticsearch Starter,您可以避免直接使用低级别的Elasticsearch客户端,而是利用Spring Boot的自动配置来简化初始化和关闭操作。这使得整合Elasticsearch变得更加容易和高效。

2、使用 Elasticsearch 官方提供的 Java API

Elasticsearch 官方提供了用于 Java 开发的 Elasticsearch Java API,你可以使用这个 API 直接与 Elasticsearch 进行交互。它提供了完整的 Elasticsearch REST API 功能,并且支持各种查询和聚合操作。

在 Spring Boot 中使用 Elasticsearch Java API 需要导入 Elasticsearch 官方提供的 Java 客户端依赖,然后根据需要编写相应的 Java 代码来进行 Elasticsearch 操作。这种方式更加灵活,但需要更多的代码编写和维护工作。

1、导入相应的依赖:

<dependency>
   <groupId>org.elasticsearch.client</groupId>
   <artifactId>elasticsearch-rest-high-level-client</artifactId>
   <version>7.12.1</version>
</dependency>
<properties>
      <java.version>17</java.version>
<!--导入es的版本-->
      <elasticsearch.version>7.12.1</elasticsearch.version>
   </properties>

2、初始化RestHighLevelClient,将它注册为一个bean,用于以后的使用:

@Configuration
public class ESConfig {
    
@Bean
    public RestHighLevelClient restHighLevelClient(){

    RestClientBuilder builder = RestClient.builder(new HttpHost(
            "192.168.231.110",
            9200));
    return new RestHighLevelClient(builder);
}

}

3、使用RestHighLevelClient完成一些简单的操作:

(1)、完成索引的创建:(我直接在test中进行测试了,我在测试时直接将异常抛出了,实际中应该用try catch捕获)

@Autowired
   private RestHighLevelClient restClientBuilder;


   @Test
   void createIndex() throws IOException {
   restClientBuilder.indices().create(new CreateIndexRequest("hotel")//s索引名称
          .source(Custom.INDEX_NAME,XContentType.JSON)//创建索引的语句,使用json传递
         , RequestOptions.DEFAULT);//选择默认的请求

   }

在kibana控制台中可以看到索引已经创建成功了:

(2)、索引的删除:

@Test
void deleteIndex() throws IOException {
   restClientBuilder.indices().
         delete(new DeleteIndexRequest("hotel"),RequestOptions.DEFAULT);
   }

(3)判断索引是否存在:

@Test
   void existIndex() throws IOException {
   GetIndexRequest request = new GetIndexRequest("hotel");
   boolean exists = restClientBuilder.indices().exists(request, RequestOptions.DEFAULT);
   System.out.println(exists?"索引存在":"索引不存在");

}

(4)完成一个文档的新增:

@Test
   void saveDoc() throws IOException {
//从数据库中查询一条数据,放到es中
   TbHotel tbHotel = tbHotelMapper.selectById(39141);
   TbHotelEs tbHotelEs=new TbHotelEs(tbHotel);
//新建一个IndexRequest对象,指定索引名和文档id(文档id不指定会默认生成)
   IndexRequest request = new IndexRequest("hotel").id(tbHotelEs.getId());
request.source(JSON.toJSONString(tbHotelEs),XContentType.JSON);
   restClientBuilder.index(request,RequestOptions.DEFAULT);
}

(5)完成文档的查询:

@Test
   void getDoc() throws IOException {

//创建GetRequest对象,指定索引名和查询文档id
   GetRequest request=new GetRequest("hotel","39141");
// 发送请求
   GetResponse documentFields = restClientBuilder.get(request, RequestOptions.DEFAULT);
//    解析结果
   String sourceAsString = documentFields.getSourceAsString();
   System.out.println(sourceAsString);
}

(6)文档的更新:

es中文档的更新有两种方式,全部更新和局部更新

全部更新:再次写入一样的文档id,就会删除旧文档,添加新文档。写法与新增没有区别

局部更新:只更新部分字段,我演示方法二:

@Test
   void UpdateDoc() throws IOException {
   UpdateRequest updateRequest=new UpdateRequest("hotel","39141");
   updateRequest.doc(
         "name","源神酒店",
         "city","洛阳"
   );
//    更新文档
   restClientBuilder.update(updateRequest,RequestOptions.DEFAULT);

   }

(7)文档的删除:

@Test
   void deleteDoc() throws IOException {

   DeleteRequest deleteRequest=new DeleteRequest("hotel","39141");
   restClientBuilder.delete(deleteRequest,RequestOptions.DEFAULT);
}

(8)批量导入文档:

@Test
void bulkDoc() throws IOException {
//       创建BulkRequest对象,用于批量操作
   BulkRequest bulkRequest=new BulkRequest();
// 查询数据库中所有数据
   List<TbHotel> tbHotels = tbHotelMapper.selectList(null);
   for (TbHotel tbHotel:tbHotels) {
      TbHotelEs tbHotelEs=new TbHotelEs(tbHotel);
      IndexRequest request = new IndexRequest("hotel").id(tbHotelEs.getId());
//    将新增的doc数据添加到BulkRequest对象
      bulkRequest.add(request);
   }
// 执行批量操作
   restClientBuilder.bulk(bulkRequest,RequestOptions.DEFAULT);
}

(9)根据字段查询:

@Test
   void searchDoc() throws IOException {
   SearchRequest searchRequest=new SearchRequest("hotel");

   SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
//查询name中包含"如家"的酒店
   sourceBuilder.query(QueryBuilders.matchQuery("name","如家"));
   searchRequest.source(sourceBuilder);
//返回一个search对象,这个search对象中就封装了查询到的json字符
   SearchResponse search = restClientBuilder.
         search(searchRequest, RequestOptions.DEFAULT);
//打印search
   System.out.println(search);


}

总体来说:

       使用 Spring Data Elasticsearch 可以更快速地实现 Elasticsearch 的集成和数据操作,而使用 Elasticsearch Java API 则更加灵活,可以更加自由地控制 Elasticsearch 操作。选择哪种方式,应根据具体的业务需求和开发团队的技术特长进行决策。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐