ElasticSearch常规操作

导入数据到索引库

0.学习目标

  • 理解索引库数据结构设计
  • 能够把商品数据导入到索引库
  • 能实现搜索补全功能
  • 能实现基本搜索功能

1.ElasticSearch的starter(了解)

ElasticSearch的异步API代码编写比较复杂,因此我们需要封装成工具,方便后期的使用。而且最好与SpringBoot整合,完成自动配置的starter。最终达成与MybatisPlus一样的效果:

  • 用户定义接口并继承我们的接口,获取CRUD的方法
  • 我们实现接口中的方法
  • 通过动态代理来代理用户接口,并注入Spring容器
  • 实现自动配置

我们大概会经过这样几个步骤来完成:

  • 定义工具类
  • 与spring整合
  • 形成starter

1.1.搭建工程

SpringBoot的starter结构一般如下:

  • spring-boot-xxx-autoconfigure:实现自动配置的代码、工具类、配置类等
  • spring-boot-starter-xxx:管理spring-boot-xxx-autoconfigure及其它所需依赖的版本

因此我们如果要自定义工具类,并且实现starter,项目结构也是如此,包含:

  • leyou-starters:父工程,起聚合作用,方便开发
    • elastic-spring-boot-autoconfigure:工具类、配置类等
    • elastic-spring-boot-starter:引入spring-boot-xxx-autoconfigure及其它所需依赖

1.1.1.创建父工程:

创建一个普通maven工程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ENqnXVyj-1614752234688)(assets/image-20200517111029233.png)]

位置:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YLDWSCJd-1614752234690)(assets/image-20200517111050389.png)]

pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.leyou</groupId>
    <artifactId>leyou-starters</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <packaging>pom</packaging>

    <properties>
        <spring.boot.version>2.1.12.RELEASE</spring.boot.version>
        <elasticsearch.version>7.4.2</elasticsearch.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

1.1.2.AutoConfigure

然后是搭建AutoConfigure模块。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QVlWOUc2-1614752234691)(assets/image-20200517111202030.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Izpl0io-1614752234693)(assets/image-20200517111215955.png)]

坐标:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MCt1aX7A-1614752234694)(assets/image-20200517111222004.png)]

依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leyou-starters</artifactId>
        <groupId>com.leyou</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>elastic-spring-boot-autoconfigure</artifactId>

    <dependencies>
        <!--springboot 自动配置基本依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>${spring.boot.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <version>${spring.boot.version}</version>
            <optional>true</optional>
        </dependency>
        <!--elasticsearch依赖-->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>${elasticsearch.version}</version>
            <scope>compile</scope>
        </dependency>
        <!--JSON依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10.3</version>
            <scope>compile</scope>
        </dependency>
        <!--通用模块-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
            <scope>compile</scope>
        </dependency>
        <!--响应式API 依赖-->
        <dependency>
            <groupId>io.projectreactor.netty</groupId>
            <artifactId>reactor-netty</artifactId>
            <version>0.8.15.RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <!--日志相关-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
            <version>2.11.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jul-to-slf4j</artifactId>
            <version>1.7.30</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

1.1.3.Starter模块

然后是starter模块,与AutoConfigure模块一样,是一个普通maven模块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ScgyYhbP-1614752234695)(assets/image-20200517111258427.png)]

依赖如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leyou-starters</artifactId>
        <groupId>com.leyou</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>elastic-spring-boot-starter</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.leyou</groupId>
            <artifactId>elastic-spring-boot-autoconfigure</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
    </dependencies>
</project>

1.2.定义工具类

课程中为了时间考虑,我们只把核心需要的几个功能封装,并不追求功能的完整性,包括下列方法:

  • 创建索引库和映射
  • 删除索引库
  • 新增文档
  • 查询文档
  • 删除文档
  • 搜索并实现分页、高亮、排序
  • 自动补全查询

因为包含分页查询,因此需要一个DTO,代表分页结果,

我们在com.leyou.starter.elastic.entity包下定义一个类:

package com.leyou.starter.elastic.entity;

import java.util.List;


public class PageInfo<T> {
   
   
    private long total;
    private List<T> content;

    public PageInfo() {
   
   
    }

    public PageInfo(long total, List<T> content) {
   
   
        this.total = total;
        this.content = content;
    }

    public long getTotal() {
   
   
        return total;
    }

    public void setTotal(long total) {
   
   
        this.total = total;
    }

    public List<T> getContent() {
   
   
        return content;
    }

    public void setContent(List<T> content) {
   
   
        this.content = content;
    }
}

1.2.1.定义工具接口

首先我们在com.leyou.starter.elastic.repository包中定义一个接口,声明需要的方法:

package com.leyou.starter.elastic.repository;

import com.leyou.starter.elastic.dto.PageInfo;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 定义了操作Elasticsearch的CRUD的功能 <br/>
 * 泛型说明 <br/>
 * T:实体类类型
 * ID:实体类中的id类型
 */
public interface Repository<T, ID> {
   
   
    /**
     * 创建索引库
     *
     * @param source setting和mapping的json字符串
     * @return 是否创建成功
     */
    Boolean createIndex(String source);

    /**
     * 删除当前实体类相关的索引库
     *
     * @return 是否删除成功
     */
    Boolean deleteIndex();

    /**
     * 新增数据
     *
     * @param t 要新增的数据
     * @return 是否新增成功
     */
    boolean save(T t);

    /**
     * 批量新增
     *
     * @param iterable 要新增的数据结婚
     * @return 是否新增成功
     */
    boolean saveAll(Iterable<T> iterable);

    /**
     * 根据id删除数据
     *
     * @param id id
     * @return 是否删除成功
     */
    boolean deleteById(ID id);

    /**
     * 异步功能,根据id查询数据
     *
     * @param id id
     * @return 包含实体类的Mono实例
     */
    Mono<T> queryById(ID id);


    /**
     * 根据{@link SearchSourceBuilder}查询数据,返回分页结果{@link PageInfo},其中的数据已经高亮处理
     *
     * @param sourceBuilder 查询条件构建器
     * @return 结果处理器处理后的的数据
     */
    Mono<PageInfo<T>> queryBySourceBuilderForPageHighlight(SearchSourceBuilder sourceBuilder);

    /**
     * 根据指定的prefixKey对单个指定suggestField 做自动补全,返回推荐结果的列表{@link List}
     * @param suggestField 补全字段
     * @param prefixKey 关键字
     * @return 返回推荐结果列表{@link List}
     */
    Mono<List<String>> suggestBySingleField(String suggestField, String prefixKey);
}

1.2.2.定义实现类

我们在com.leyou.starter.elastic.repository包中定义实现类,暂时不实现代码:

package com.leyou.starter.elastic.repository;



public class RepositoryHandler<T, ID> implements Repository<T, ID> {
   
   

    /**
     * Elasticsearch的客户端
     */
    private final RestHighLevelClient client;
    /**
     * 索引库名称
     */
    private String indexName;

    public RepositoryHandler(RestHighLevelClient client, String indexName){
   
   
        this.client = client;
        this.indexName = indexName;
    }

    // ...
}

解读:

通过构造函数我们注入了两个属性:

  • RestHighLevelClient client:操作elasticsearch需要用到的客户端
  • String indexName:要操作的索引库名称

1.2.3.创建和删除索引库

先来实现索引库操作:

@Override
public Boolean createIndex(String source) {
   
   
    try {
   
   
        // 发起请求,准备创建索引库
        CreateIndexResponse response = client.indices().create(
            new CreateIndexRequest(indexName).source(source, XContentType.JSON),
            RequestOptions.DEFAULT);
        // 返回执行结果
        return response.isAcknowledged();
    } catch (IOException e) {
   
   
        throw new RuntimeException(e);
    }
}

@Override
public Boolean deleteIndex() {
   
   
    try {
   
   
        // 发起请求,删除索引库
        AcknowledgedResponse response = client.indices()
            .delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT);
        // 返回执行结果
        return response.isAcknowledged();
    } catch (IOException e) {
   
   
        throw new RuntimeException(e);
    }
}

这两个都是同步操作,没有调用异步的API。

1.2.4.新增文档

新增文档代码如下:

@Override
public boolean save(T t) {
   
   
    try {
   
   
        // 从对象中获取id
        String id = getID(t);
        // 把对象转为JSON
        String json = toJson(t);
        // 准备请求
        IndexRequest request = new IndexRequest(indexName)
            .id(id)
            .source(json, XContentType.JSON);
        // 发出请求
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        // 判断是否有失败
        return response.getShardInfo().getFailed() == 0;
    } catch (IOException e) {
   
   
        throw new RuntimeException(e);
    }
}

注意,新增的时候需要做两件事情:

  • 获取文档数据中的ID属性:这里通过一个getID()方法来获取,这个方法暂时空实现

    private String getID(T t) {
         
         
        // TODO 待完成
    	return "";
    }
    
  • 将文档转为JSON格式:这里通过Jackson的ObjectMapper来封装方法toJson()

    private static final ObjectMapper mapper = new ObjectMapper();
    
    private String toJson(Object o) {
         
         
        try {
         
         
            return mapper.writeValueAsString(o);
        } catch (JsonProcessingException e) {
         
         
            throw new RuntimeException(e);
        }
    }
    
    private T fromJson(String json) {
         
         
        try {
         
         
            return mapper.readValue(json, clazz);
        } catch (IOException e) {
         
         
            throw new RuntimeException(e);
        }
    }
    

1.2.5.批量新增

批量新增时利用BulkRequest,把多个IndexRequest封装,然后一次请求中发出,代码如下:

@Override
public boolean saveAll(Iterable<T> iterable) {
   
   
    // 创建批处理请求
    BulkRequest request = new BulkRequest();
    // 遍历要处理的文档集合,然后创建成IndexRequest,逐个添加到BulkRequest中
    iterable.forEach(t -> request.add(new IndexRequest(indexName).id(getID(t)).source(toJson(t), XContentType.JSON)));
    try {
   
   
        // 发送批处理请求
        BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
        // 判断结果
        if(bulkResponse.status() != RestStatus.OK){
   
   
            return false;
        }
        if(bulkResponse.hasFailures()){
   
   
            throw new RuntimeException(bulkResponse.buildFailureMessage());
        }
        return true;
    } catch (IOException e) {
   
   
        throw new RuntimeException(e);
    }
}

1.2.6.删除文档

这里采用根据id删除文档的方式:

@Override
public boolean deleteById(ID id) {
   
   
    try {
   
   
        // 准备请求
        DeleteRequest request = new DeleteRequest(indexName, id.toString());
        // 发出请求
        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
        // 判断是否有失败
        return response.getShardInfo().getFailed() == 0;
    } catch (IOException e) {
   
   
        throw new RuntimeException(e);
    }
}

1.2.7.根据id查询文档

查询业务采用异步操作,并将结果用Mono封装:

@Override
public Mono<T> queryById(ID id) {
   
   
    // 通过Mono.create函数来构建一个Mono,sink用来发布查询到的数据或失败结果
    
### Elasticsearch 常规操作指南 #### 1. 安装与启动 安装 Elasticsearch 需要先下载对应版本的软件包,解压后配置 `elasticsearch.yml` 文件中的基本参数,例如集群名称、节点名称以及绑定地址等。完成配置后,在命令行运行以下命令即可启动服务: ```bash bin/elasticsearch ``` 此过程需注意硬件资源分配,尤其是 JVM 的堆内存大小设置,默认值可能不足以支持大规模数据处理[^1]。 #### 2. 创建索引 创建索引时可以指定映射(mapping)、分片数量以及其他高级选项。以下是通过 REST API 创建名为 `my_index` 的索引示例: ```json PUT /my_index { "settings": { "number_of_shards": 3, "number_of_replicas": 2 }, "mappings": { "properties": { "field1": { "type": "text" } } } } ``` 上述代码定义了一个具有三个主分片和两个副本的索引,并设置了字段 `field1` 的类型为文本。 #### 3. 数据写入 向 Elasticsearch 中插入文档可以通过 POST 请求实现。假设目标索引已存在,则可通过如下方式新增一条记录到 `_doc/1` 路径下: ```json POST /my_index/_doc/1 { "field1": "example value" } ``` 每条文档都应拥有唯一 ID 或由系统自动生成。 #### 4. 查询检索 查询功能是 Elasticsearch 的核心之一。简单匹配所有条件可采用以下形式: ```json GET /my_index/_search { "query": { "match_all": {} } } ``` 对于复杂需求则推荐构建布尔逻辑组合或其他特定类型的子句来精确控制返回结果集。 #### 5. 刷新机制优化 为了提高性能表现,默认策略会延迟一定时间段才执行实际物理层面的数据同步动作;如果应用程序频繁更新少量新内容却立即读取的话可能会遇到不一致现象。此时可以根据实际情况调整 refresh_interval 参数或者手动触发即时生效的操作[^2]: ```json POST /_refresh ``` 以上命令适用于全局范围内的所有打开状态下的 indices 同步最新改动至可见视图之中去。 #### 6. 版本选择建议 当面临不同发行版之间的抉择困境时,综合考量稳定性因素之后得出结论认为 v7.10 是一个折衷平衡点既保留了较新的特性又兼顾长期维护周期较长的优势特点因此被广泛采纳作为生产环境部署的标准方案之一[^4]。 #### 7. Prometheus 监控集成 借助第三方工具如 Elasticsearch Exporter 可以轻松搭建起针对 ES 集群健康状况监测体系架构从而更好地掌握整体运作态势以便及时发现问题所在并采取相应措施加以解决提升运维效率降低风险隐患发生概率[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值