1

1、Elasticsearch安装包下载

官网下载:https://www.elastic.co/cn/downloads/past-releases#elasticsearch
mac版本:elasticsearch-7.6.0

2、解压、配置

/elasticsearch-7.6.0/config/elasticsearch.yml文件增加配置

# 日志路径
path.logs: /xxx/xxx
xpack.ml.enabled: false

bin目录下启动

./elasticsearch

3、spring boot结合Elasticsearch

pom.xml文件,spring boot依赖冲突,需要使用正确ES版本

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.java</groupId>
    <artifactId>java</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>java</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
        <!--    启动时不扫描测试类    -->
        <skipTests>true</skipTests>
        <!--    指定elasticsearch版本    -->
        <elasticsearch.version>7.15.2</elasticsearch.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--        mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.7</version>
        </dependency>

        <!--        mysql-->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.33</version>
        </dependency>

<!--        elasticsearch-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            <version>2.6.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-elasticsearch</artifactId>
            <version>4.3.3</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--        lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.28</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.7.18</version>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

4、配置ElasticsearchConfig

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ElasticsearchConfig {

    @Bean(destroyMethod = "close")
    public RestHighLevelClient restHighLevelClient() {
        RestHighLevelClient client = new RestHighLevelClient(
//                可以配置多个
                RestClient.builder(
                        new HttpHost("127.0.0.1", 9200, "http")
//                        , new HttpHost("127.0.0.1", 9201, "http")
                ));
        return client;
    }
}

5、实体类

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.util.Date;

@Data
@Document(indexName = "blog")
public class Blog {
    //此项作为id,不会写到_source里边。
    @Id
    private Long blogId;

    @Field(name = "title", type = FieldType.Text)
    private String title;

}

6、继承ElasticsearchRepository方法

import com.java.dao.es.Blog;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface BlogRepository extends ElasticsearchRepository<Blog, Long> {
}

7、ES方法详情

RestHighLevelClient
RestHighLevelClient 是 Elasticsearch 官方提供的 Java High Level REST Client 的一部分,用于在 Java 应用程序中与 Elasticsearch 集群进行交互。这个客户端提供了比低级 REST 客户端更高级别的抽象,使得与 Elasticsearch 的交互更加简单和直观。

RestHighLevelClient 提供了多种方法来执行不同的操作,如索引文档、搜索文档、更新文档、删除文档等。以下是一些常用的 RestHighLevelClient 方法及其用途的概述:

索引文档
index(IndexRequest request, RequestOptions options):将一个文档索引到指定的索引中。

搜索文档
search(SearchRequest searchRequest, RequestOptions options):执行一个搜索请求并返回 SearchResponse。
searchAsync(SearchRequest searchRequest, RequestOptions options, ActionListener<SearchResponse> listener):异步执行搜索请求。

获取文档
get(GetRequest getRequest, RequestOptions options):从索引中获取一个文档。

更新文档
update(UpdateRequest request, RequestOptions options):更新索引中的文档。

删除文档
delete(DeleteRequest request, RequestOptions options):从索引中删除一个文档。

创建索引
虽然 RestHighLevelClient 没有直接的方法来创建索引(因为索引的创建通常是通过发送一个包含索引定义的 PUT 请求到 /<index> 端点来完成的),但你可以使用 indices().create(CreateIndexRequest request, RequestOptions options) 方法,这是通过 IndicesClient 接口提供的,而 RestHighLevelClient 提供了对它的访问。

删除索引
同样,RestHighLevelClient 没有直接的方法来删除索引,但你可以通过 indices().delete(DeleteIndexRequest request, RequestOptions options) 方法来做到这一点。

其他操作
indices(): 返回 IndicesClient 实例,用于执行与索引相关的操作,如获取索引信息、删除索引、创建索引等。
cluster(): 返回 ClusterClient 实例,用于执行与集群相关的操作,如获取集群健康状态、节点信息等。

QueryBuilders方法

        QueryBuilders方法
        /**
         * 匹配所有文档(Match All Query)
         * QueryBuilders.matchAllQuery();
         *
         *  匹配查询(Match Query)
         *  fieldName 字段中搜索包含 textToSearch 的文档。
         *  QueryBuilders.matchQuery("fieldName", "textToSearch");
         *
         * 布尔查询(Boolean Query)
         * 布尔查询允许你组合多个查询子句,如 must(必须匹配)、should(应该匹配,但不是必须的)、must_not(必须不匹配)。
         * QueryBuilders.boolQuery()
         *     .must(QueryBuilders.matchQuery("field1", "value1"))
         *     .should(QueryBuilders.matchQuery("field2", "value2"))
         *     .mustNot(QueryBuilders.matchQuery("field3", "value3"));
         *
         * 范围查询(Range Query)
         * 这会找到 age 字段值在10(包含)到20(不包含)之间的文档。
         * QueryBuilders.rangeQuery("age")
         *     .from(10)
         *     .to(20)
         *     .includeLower(true)
         *     .includeUpper(false);
         *
         *  术语查询(Term Query)
         *  术语查询用于精确值匹配,不分析查询字符串。
         *  QueryBuilders.termQuery("status", "active");
         *
         *  前缀查询(Prefix Query)
         *  这会在 fieldName 字段中搜索以 prefixText 开头的文档。
         *  QueryBuilders.prefixQuery("fieldName", "prefixText");
         *
         *  模糊查询(Fuzzy Query)
         *  这会在 fieldName 字段中搜索与 textToSearch 相似的文档,fuzziness 参数定义了相似度级别。
         *  QueryBuilders.fuzzyQuery("fieldName", "textToSearch")
         *     .fuzziness(Fuzziness.TWO);
         *
         *  通配符查询(Wildcard Query)
         *  这会在 fieldName 字段中搜索以 text 开头的文档。
         *  QueryBuilders.wildcardQuery("fieldName", "text*");
         *
         *  嵌套查询(Nested Query)  对于嵌套对象,你需要使用 nestedQuery。
         *  这会在嵌套文档 path_to_nested_doc 中搜索 field 字段值为 value 的文档,并指定了评分模式为 Avg。
         *  QueryBuilders.nestedQuery(
         *     "path_to_nested_doc",
         *     QueryBuilders.matchQuery("path_to_nested_doc.field", "value"),
         *     ScoreMode.Avg
         * );
         *
         */

8、 ES示例

import com.java.dao.es.Blog;
import com.java.mapper.es.BlogRepository;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("/es")
public class EsCrudController {

    @Autowired
    private BlogRepository blogRepository;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @GetMapping("/searchQueryBuilders")
    public Object searchQueryBuilders() throws IOException {
        SearchRequest searchRequest = new SearchRequest("blog");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


        /**
         * 匹配所有文档(Match All Query)
         * QueryBuilders.matchAllQuery();
         *
         *  匹配查询(Match Query)
         *  fieldName 字段中搜索包含 textToSearch 的文档。
         *  QueryBuilders.matchQuery("fieldName", "textToSearch");
         *
         * 布尔查询(Boolean Query)
         * 布尔查询允许你组合多个查询子句,如 must(必须匹配)、should(应该匹配,但不是必须的)、must_not(必须不匹配)。
         * QueryBuilders.boolQuery()
         *     .must(QueryBuilders.matchQuery("field1", "value1"))
         *     .should(QueryBuilders.matchQuery("field2", "value2"))
         *     .mustNot(QueryBuilders.matchQuery("field3", "value3"));
         *
         * 范围查询(Range Query)
         * 这会找到 age 字段值在10(包含)到20(不包含)之间的文档。
         * QueryBuilders.rangeQuery("age")
         *     .from(10)
         *     .to(20)
         *     .includeLower(true)
         *     .includeUpper(false);
         *
         *  术语查询(Term Query)
         *  术语查询用于精确值匹配,不分析查询字符串。
         *  QueryBuilders.termQuery("status", "active");
         *
         *  前缀查询(Prefix Query)
         *  这会在 fieldName 字段中搜索以 prefixText 开头的文档。
         *  QueryBuilders.prefixQuery("fieldName", "prefixText");
         *
         *  模糊查询(Fuzzy Query)
         *  这会在 fieldName 字段中搜索与 textToSearch 相似的文档,fuzziness 参数定义了相似度级别。
         *  QueryBuilders.fuzzyQuery("fieldName", "textToSearch")
         *     .fuzziness(Fuzziness.TWO);
         *
         *  通配符查询(Wildcard Query)
         *  这会在 fieldName 字段中搜索以 text 开头的文档。
         *  QueryBuilders.wildcardQuery("fieldName", "text*");
         *
         *  嵌套查询(Nested Query)  对于嵌套对象,你需要使用 nestedQuery。
         *  这会在嵌套文档 path_to_nested_doc 中搜索 field 字段值为 value 的文档,并指定了评分模式为 Avg。
         *  QueryBuilders.nestedQuery(
         *     "path_to_nested_doc",
         *     QueryBuilders.matchQuery("path_to_nested_doc.field", "value"),
         *     ScoreMode.Avg
         * );
         *
         */

        RangeQueryBuilder query = QueryBuilders.rangeQuery("blogId").from(10).to(13).includeLower(true).includeUpper(true);


        searchSourceBuilder.query(query);
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        SearchHits hits = searchResponse.getHits();
        List<String> results = new ArrayList<>();

        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();
            results.add(sourceAsString);
        }
        return results;
    }


    @GetMapping("/searchSelect")
    public Object searchSelect() throws IOException {
        SearchRequest searchRequest = new SearchRequest("blog");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 构建 Term Query
        MatchPhraseQueryBuilder query = QueryBuilders.matchPhraseQuery("title", "Spring Data ElasticSearch学习教程1");
        searchSourceBuilder.query(query);
        searchRequest.source(searchSourceBuilder);

        // 发送请求并处理响应
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        SearchHits hits = searchResponse.getHits();
        List<String> results = new ArrayList<>();
        for (SearchHit hit : hits) {
            String jsonString = hit.getSourceAsString();
            // 这里你可以使用Jackson或其他JSON库来将jsonString转换为YourEntity对象
            // 或者,如果你已经配置了Spring Data Elasticsearch的实体映射,你可能不需要这样做
            // 这里仅作为示例,我们直接添加jsonString到结果列表中
            log.info("获取数据:" + jsonString);
            results.add(jsonString);
            // 实际上,你可能需要这样做:YourEntity entity = objectMapper.readValue(jsonString, YourEntity.class);
        }
        return results;
    }


    /**
     * 添加单个文档
     *
     * @return
     */
    @GetMapping("addDocument")
    public Blog addDocument() {
        Long id = 1L;
        Blog blog = new Blog();
        blog.setBlogId(id);
        blog.setTitle("Spring Data ElasticSearch学习教程" + id);
        blog.setContent("这是添加单个文档的实例" + id);
        blog.setAuthor("Tony");
        blog.setCategory("ElasticSearch");
        blog.setCreateTime(new Date());
        blog.setStatus(1);
        blog.setSerialNum(id.toString());

        return blogRepository.save(blog);
    }


    /**
     * 添加多个文档
     *
     * @param count
     * @return
     */
    @GetMapping("/addDocuments/{count}")
    public Object addDocuments(@PathVariable Integer count) {
        long startTime = System.currentTimeMillis();
        List<Blog> blogs = new ArrayList<>();
        for (int i = 1; i <= count; i++) {
            Long id = (long) i;
            Blog blog = new Blog();
            blog.setBlogId(id);
            blog.setTitle("Spring Data ElasticSearch学习教程" + id);
            blog.setContent("这是添加单个文档的实例" + id);
            blog.setAuthor("Tony");
            blog.setCategory("ElasticSearch");
            blog.setCreateTime(new Date());
            blog.setStatus(1);
            blog.setSerialNum(id.toString());
            blogs.add(blog);
        }
        Iterable<Blog> saveAll = blogRepository.saveAll(blogs);
        long endTime = System.currentTimeMillis();
        log.info("执行时间:" + (endTime - startTime) / 1000 + " 秒");
        return saveAll
                ;
    }


    /**
     * 修改单个文档
     * 跟新增是同一个方法。若id已存在,则修改。
     * 无法只修改某个字段,只能覆盖所有字段。若某个字段没有值,则会写入null。
     *
     * @return 成功写入的数据
     */
    @PostMapping("editDocument")
    public Blog editDocument() {
        Long id = 1L;
        Blog blog = new Blog();
        blog.setBlogId(id);
        blog.setTitle("Spring Data ElasticSearch学习教程" + id);
        blog.setContent("这是修改单个文档的实例" + id);
        // blog.setAuthor("Tony");
        // blog.setCategory("ElasticSearch");
        // blog.setCreateTime(new Date());
        // blog.setStatus(1);
        // blog.setSerialNum(id.toString());

        return blogRepository.save(blog);
    }

    /**
     * 查找单个文档
     *
     * @param id
     * @return
     */
    @GetMapping("findById/{id}")
    public Blog findById(@PathVariable Long id) {
        return blogRepository.findById(id).get();
    }

    /**
     * 删除单个文档
     *
     * @param id
     * @return
     */
    @GetMapping("deleteDocument/{id}")
    public String deleteDocument(@PathVariable Long id) {
        blogRepository.deleteById(id);
        return "success";
    }

    /**
     * 删除所有文档
     *
     * @return
     */
    @GetMapping("deleteDocumentAll")
    public String deleteDocumentAll() {
        blogRepository.deleteAll();
        return "success";
    }

    /**
     * 查询所有文档
     */
    @GetMapping("/findAll")
    public Object findAll(){
        return blogRepository.findAll();
    }
}


LLL_
15 声望3 粉丝