ElasticSearchLinux安装和springboot整合的记录和遇到的问题

前面整合遇到的一些问题有的记录在下面了,有的当时忘了记录下来,希望下面的能帮到你们

1:Linux安装ES

下载安装:

参考文章:连接1

连接2

css 复制代码
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.12.2-linux-x86_64.tar.gz

解压:tar -zxvf elasticsearch-8.12.2-linux-x86_64.tar.gz

这里注意,在bin目录下启动报错,因为不能通过root账户启动,需要新建账户

创建新用户给ES使用:adduser els

修改密码:echo 123456 | passwd --stdin els 或者:sudo passwd newpassword

然后将解压的es 目录赋予新创建的用户

sudo chown -R els:els /home/cdszwy/elasticsearch/elasticsearch-8.12.2

切换到新创建用户

su - els

然后进入解压目录的/bin 执行 ./elasticsearch -d 运行es

然后 执行curl http://127.0.0.1:9200显示

先要在防火墙打开该端口才能访问

安装遇到的问题:

  • Password: su: Permission denied

添加用户修改密码后,切换到新用户环境,再切换到root环境提示异常:Password: su: Permission denied

使用su命令时输入密码后提示权限限制,确认密码是正确的

即:

css 复制代码
su root
Password:
su: permission denied

解决:

1.改变用户分组,将用户添加进wheel分组

#语法

#usermod [-G] [GroupName] [UserName]

usermod -G wheel username

2.修改/etc/pam.d/su

注释行:auth required pam_wheel.so use_uid

3:查看当前用户分组:

#语法

#id username

id els

#执行结果如下

uid=1001(els) gid=1001(els) groups=1001(els),10(wheel)

  • vm.max_map_count [65530] is too low

解决vm.max_map_count [65530] is too low问题

编辑 /etc/sysctl.conf 文件来永久更改该值。在文件的末尾添加下面一行并保存:vm.max_map_count=262144

然后可以通过运行以下命令重新加载配置文件:sudo sysctl -p

修改ES的配置:

修改配置:elasticsearch.yml

绑定到0.0.0.0,允许任何ip来访问

network.host: 0.0.0.0

#浏览器可直接访问

css 复制代码
xpack.security.http.ssl:
enabled: false

末尾添加:

css 复制代码
http.cors.enabled: true
http.cors.allow-origin: "*"
http.cors.allow-headers: Authorization

修改配置:jvm.options

css 复制代码
-Xms1g
-Xmx1g

设置用户名密码:

在bin目录下执行:./elasticsearch-reset-password -u elskib -i

添加新用户给kibana使用

添加用户:./elasticsearch-users useradd elskib

分配绝俗: ./elasticsearch-users roles -a superuser elskib

账号:elskib

密码:qwe123

在启动ES

使用命令查看进程:

查看是否成功:ps -ef|grep elastic

IK分词器安装:https://github.com/infinilabs/analysis-ik/releases

下载再执行

在bin目录下执行命令:./elasticsearch-plugin install file:///home/cdszwy/elasticsearch/elasticsearch-analysis-ik-8.12.2.zip

2.springboot 整合ES

这里有一个区别:下面会有两种快速接口调用ES,分别是:使用RestHighLevelClient,使用ElasticsearchClient,先讲使用RestHighLevelClient

使用RestHighLevelClient

  • 引入依赖
css 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

配置ES基础信息:

css 复制代码
es:
  ip: 192.34.21.129
  port: 9200
  username: elskib
  password: qwe123

添加Java配置:

css 复制代码
@Configuration
public class ElasticSearchConfig {

    @Value("${es.ip}")
    private String esIp;

    @Value("${es.port}")
    private Integer port;

    @Value("${es.username}")
    private String username;

    @Value("${es.password}")
    private String password;

    /**
     * 注册 rest高级客户端
     * @date   2024/3/5 16:01
     **/
    @Bean
    public RestHighLevelClient restHighLevelClient() {
        // 创建CredentialsProvider,并设置用户名密码
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,
                new UsernamePasswordCredentials(username,password));
        // 创建RestHighLevelClient实例,并设置CredentialsProvider
        return new RestHighLevelClient(
                RestClient.builder(new HttpHost(esIp, port, "http"))
                        .setHttpClientConfigCallback(httpAsyncClientBuilder ->
                                httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
                        )
        );
    }

添加方法的实现类:

css 复制代码
import cn.test.vo.DocumentInfoPageVo;
import cn.test.vo.DocumentInfoVo;
import cn.hutool.json.JSONUtil;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

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

/**
 * Description: 多条件操作文档
 * @version 1.0
 * @ClassName RestHighLevelServiceImpl
 * @date 2024/3/7 11:12
 **/
@Service
public class RestHighLevelServiceImpl {

    @Autowired
    private RestHighLevelClient restHighLevelClient;


    /**
     * 判断索引是否存在
     */
    public Boolean indexExists(String indexName) throws Exception {
        GetIndexRequest request = new GetIndexRequest(indexName);
        return restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
    }

    /**
     * 创建 ES 索引
     */
    public CreateIndexResponse createIndex(String indexName, Settings.Builder settings, XContentBuilder mappings) throws Exception {
        //将 Settings 和 Mappings 封装到一个Request 对象中
        CreateIndexRequest request = new CreateIndexRequest(indexName)
                .settings(settings)
                .mapping(mappings);

        //通过 client 对象去连接ES并执行创建索引
        return restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
    }

    /**
     * 批量创建 ES 文档
     */
    public IndexResponse createDoc(String indexName, String id, String json) throws Exception {
        //准备一个Request对象
        IndexRequest request = new IndexRequest(indexName);
        //手动指定ID
        request.id(id);
        request.source(json, XContentType.JSON);
        //request.opType(DocWriteRequest.OpType.INDEX); 默认使用 OpType.INDEX,如果 id 重复,会进行  覆盖更新, resp.getResult().toString() 返回 UPDATE
        //request.opType(DocWriteRequest.OpType.CREATE); 如果ID重复,会报异常 =>  document already exists

        //通过 Client 对象执行添加
        return restHighLevelClient.index(request, RequestOptions.DEFAULT);
    }

    /**
     * 批量创建 ES 文档
     *
     * @param jsonMap Key = id,Value =  json
     */
    public BulkResponse batchCreateDoc(String indexName, Map<String, String> jsonMap) throws Exception {
        //准备一个Request对象
        BulkRequest bulkRequest = new BulkRequest();
        for (String id : jsonMap.keySet()) {
            IndexRequest request = new IndexRequest(indexName)
                    //手动指定ID
                    .id(id)
                    .source(jsonMap.get(id), XContentType.JSON);
            bulkRequest.add(request);
        }

        //通过 Client 对象执行添加
        return restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
    }

    /**
     * 自动补全 根据用户的输入联想到可能的词或者短语
     *
     * @param indexName 索引名称
     * @param field     搜索条件字段
     * @param keywords  搜索关键字
     * @param size      匹配数量
     * @return
     * @throws Exception
     */
    public List<String> suggest(String indexName, String field, String keywords, int size) throws Exception {
        //定义返回
        List<String> suggestList = new ArrayList<>();
        //构建查询请求
        SearchRequest searchRequest = new SearchRequest(indexName);
        //通过查询构建器定义评分排序
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
        //构造搜索建议语句,搜索条件字段
        CompletionSuggestionBuilder completionSuggestionBuilder = new CompletionSuggestionBuilder(field);
        //搜索关键字
        completionSuggestionBuilder.prefix(keywords);
        //去除重复
        completionSuggestionBuilder.skipDuplicates(true);
        //匹配数量
        completionSuggestionBuilder.size(size);
        searchSourceBuilder.suggest(new SuggestBuilder().addSuggestion("article-suggest", completionSuggestionBuilder));
        //article-suggest为返回的字段,所有返回将在article-suggest里面,可写死,sort按照评分排序
        searchRequest.source(searchSourceBuilder);
        //定义查找响应
        SearchResponse suggestResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //定义完成建议对象
        CompletionSuggestion completionSuggestion = suggestResponse.getSuggest().getSuggestion("article-suggest");
        List<CompletionSuggestion.Entry.Option> optionsList = completionSuggestion.getEntries().get(0).getOptions();
        //从optionsList取出结果
        if (!CollectionUtils.isEmpty(optionsList)) {
            optionsList.forEach(item -> suggestList.add(item.getText().toString()));
        }
        return suggestList;
    }

    /**
     * 前缀查询,可以通过一个关键字去指定一个Field的前缀,从而查询到指定的文档
     */
    public SearchResponse prefixQuery(String indexName, String searchField, String searchKeyword) throws Exception {

        //创建Request对象
        SearchRequest request = new SearchRequest(indexName);

        //XX开头的关键词查询
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(QueryBuilders.prefixQuery(searchField, searchKeyword));
        request.source(builder);
        //执行查询
        return restHighLevelClient.search(request, RequestOptions.DEFAULT);
    }

    /**
     * 通过 QueryBuilder 构建多字段匹配如:QueryBuilders.multiMatchQuery("人工智能","title","content")
     * multi_match => https://www.cnblogs.com/vipsoft/p/17164544.html
     */
    public SearchResponse search(String indexName, QueryBuilder query, int currPage, int pageSize) throws Exception {
        SearchRequest request = new SearchRequest(indexName);
        SearchSourceBuilder builder = new SearchSourceBuilder();
        int start = (currPage - 1) * pageSize;
        builder.from(start);
        builder.size(pageSize);
        builder.query(query);
        request.source(builder);
        return restHighLevelClient.search(request, RequestOptions.DEFAULT);
    }




    /**
     * Description:根据条件查询文档信息
     * 分页问题:《不能实现跳转分页》
     * @date   2024/3/7 11:19
     * @param  indexName 索引名称
     * @param  queryBuilder 查询条件
     * @param  pageSize 每页数量
     * @param  page 页码
     * @param  scrollId 下一页的请求ID
     * @return List<DocumentInfo> 返回所有文档信息
     **/
//    @Override
    public Object getDocPageList(String indexName, QueryBuilder queryBuilder, Integer pageSize, Integer page, String scrollId) throws IOException {
        List<Object> list = new ArrayList<>();
        //指定scroll信息,2分钟过期
        //失效时间为2min
        Scroll scroll = new Scroll(TimeValue.timeValueMinutes(2));
        Long totalCount = null;
        //首次查询scrollId为空
        if(StringUtils.isEmpty(scrollId)){
            //指定搜索索引
            SearchRequest searchRequest = new SearchRequest(indexName);
            //封存快照
            searchRequest.scroll(scroll);
            //指定条件对象
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            //指定查询条件
            sourceBuilder.size(pageSize);
            sourceBuilder.sort("reportDate", SortOrder.DESC);
            sourceBuilder.query(queryBuilder);
            searchRequest.source(sourceBuilder);
            //参数 1:搜索请求对象 参数2: 请求配置对象 返回值:查询结果对象
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //计算总数量
            totalCount = searchResponse.getHits().getTotalHits().value;
//            //得到总页数
//            page = (int) Math.ceil((float) totalCount / 2);
            //多次遍历分页,获取结果
            scrollId = searchResponse.getScrollId();
            SearchHit[] hits = searchResponse.getHits().getHits();
            for (SearchHit hit : hits) {
                System.out.println("id: "+hit.getId()+" source: "+hit.getSourceAsString());
                list.add(JSONUtil.toBean(JSONUtil.parseObj(hit.getSourceAsString()), Object.class));
            }
        } else {
            System.out.println("第二次查询");
            //TODO 只能连续分页查询,不能进行分页跳转查询
            //获取到该id
            SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
            searchScrollRequest.scroll(scroll);
            SearchResponse response = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
            //打印数据
            SearchHits searchHits = response.getHits();
            scrollId = response.getScrollId();

            for (SearchHit hit : searchHits) {
                System.out.println("id: "+hit.getId()+" source: "+hit.getSourceAsString());
                list.add(JSONUtil.toBean(JSONUtil.parseObj(hit.getSourceAsString()), Object.class));
            }
        }
        Object vo = new Object();
        vo.setTotal(totalCount);
//        vo.setScrollId(scrollId);
        vo.setList(list);
        return vo;
    }
}

这里的Object可以替换为你的实体类,想要解析的对象

使用ElasticsearchClient

  • 添加依赖:
css 复制代码
        <!-- Elasticsearch8.12版本(Java API Client),使用ElasticsearchClient-->
        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
            <version>8.12.2</version>
            <exclusions>
                <exclusion>
                    <artifactId>jakarta.json-api</artifactId>
                    <groupId>jakarta.json</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>8.12.2</version>
        </dependency>

        <dependency>
            <groupId>jakarta.json</groupId>
            <artifactId>jakarta.json-api</artifactId>
            <version>2.1.1</version>
        </dependency>
  • 添加配置
css 复制代码
package cn.test.configs;

import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

/**
 * Description: es配置累
 * @version 1.0
 * @ClassName ElasticSearchConfig
 * @date 2024/3/5 15:58
 **/
@Configuration
public class ElasticSearchConfig {

    @Value("${es.ip}")
    private String esIp;

    @Value("${es.port}")
    private Integer port;

    @Value("${es.username}")
    private String username;

    @Value("${es.password}")
    private String password;


    @Bean
    public ElasticsearchClient esRestClientWithCred(){
        final String scheme = "http";
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        // 配置连接ES的用户名和密码,如果没有用户名和密码可以不加这一行
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
        RestClientBuilder restClientBuilder = RestClient.builder(new HttpHost(esIp, port, scheme))
                .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                    @Override
                    public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
                        return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                    }
                });
        RestClient restClient = restClientBuilder.build();
        ElasticsearchTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        return new ElasticsearchClient(transport);
    }

    /**
     * 异步方式
     *
     * @return
     */
    @Bean
    public ElasticsearchAsyncClient elasticsearchAsyncClient() {
        HttpHost[] httpHosts = toHttpHost();
        RestClient restClient = RestClient.builder(httpHosts).build();
        RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        return new ElasticsearchAsyncClient(transport);
    }


    /**
     * 解析配置的字符串hosts,转为HttpHost对象数组
     *
     * @return
     */
    private HttpHost[] toHttpHost() {
        if (!StringUtils.hasLength(esIp)) {
            throw new RuntimeException("invalid elasticsearch configuration. elasticsearch.hosts不能为空!");
        }

        // 多个IP逗号隔开
        String[] hostArray = esIp.split(",");
        HttpHost[] httpHosts = new HttpHost[hostArray.length];
        HttpHost httpHost;
        for (int i = 0; i < hostArray.length; i++) {
            String[] strings = hostArray[i].split(":");
            httpHost = new HttpHost(strings[0], port, "http");
            httpHosts[i] = httpHost;
        }

        return httpHosts;
    }
}
  • 实现类

索引操作

css 复制代码
import cn.test.service.ElasticSearchIndexService;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.util.ObjectBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.HashMap;
import java.util.function.Function;

/**
 * Description: ES索引操作
 * @version 1.0
 * @ClassName ElasticSearchIndexServiceImpl
 * @date 2024/3/6 14:05
 **/
@Service
@Slf4j
public class ElasticSearchIndexServiceImpl implements ElasticSearchIndexService {

    @Autowired
    private ElasticsearchClient elasticsearchClient;


    /**
     * Description:根据索引名称创建索引
     * @date   2024/3/6 14:09
     * @param  name 索引名称
     **/
    @Override
    public void createIndex(String name) throws IOException {
        //ApplicationContext applicationContext;
        CreateIndexResponse response = elasticsearchClient.indices().create(c -> c.index(name));
        log.info("createIndex方法,acknowledged={}", response.acknowledged());
    }

    /**
     * Description:索引创建
     * @date   2024/3/6 14:15
     * @param  name 索引名称
     * @param  settingFn 索引属性设置
     * @param  mappingFn mapping属性设置
     **/
    @Override
    public void createIndex(String name,
                            Function<IndexSettings.Builder, ObjectBuilder<IndexSettings>> settingFn,
                            Function<TypeMapping.Builder, ObjectBuilder<TypeMapping>> mappingFn) throws IOException {
        CreateIndexResponse response = elasticsearchClient
                .indices()
                .create(c -> c
                        .index(name)
                        .settings(settingFn)
                        .mappings(mappingFn)
                );
        log.info("createIndex方法,acknowledged={}", response.acknowledged());
    }

    /**
     * Description:根据索引名称删除索引
     * @date   2024/3/6 14:10
     * @param  name 索引名称
     **/
    @Override
    public void deleteIndex(String name) throws IOException {
        DeleteIndexResponse response = elasticsearchClient.indices().delete(c -> c.index(name));
        log.info("deleteIndex方法,acknowledged={}", response.acknowledged());
    }

    /**
     * Description:根据索引名称设置mapping
     * @date   2024/3/6 14:12
     * @param  name 索引名称
     * @param  propertyMap 属性
     **/
    @Override
    public void updateIndexProperty(String name, HashMap<String, Property> propertyMap) throws IOException {
        PutMappingResponse response = elasticsearchClient.indices()
                .putMapping(typeMappingBuilder ->
                        typeMappingBuilder
                                .index(name)
                                .properties(propertyMap)
                );
        log.info("updateIndexMapping方法,acknowledged={}", response.acknowledged());
    }

    /**
     * Description:获取所有索引信息
     * @date   2024/3/6 14:13
     **/
    @Override
    public GetIndexResponse getIndexList() throws IOException {
        //使用 * 或者 _all都可以
        GetIndexResponse response = elasticsearchClient.indices().get(builder -> builder.index("_all"));
        log.info("getIndexList方法,response.result()={}", response.result().toString());
        return response;
    }

    /**
     * Description:根据索引名称获取所有详情
     * @date   2024/3/6 14:13
     * @param  name 索引名称
     * @return GetIndexResponse 返回信息
     **/
    @Override
    public GetIndexResponse getIndexDetail(String name) throws IOException {
        GetIndexResponse response = elasticsearchClient.indices().get(builder -> builder.index(name));
        log.info("getIndexDetail方法,response.result()={}", response.result().toString());
        return response;
    }

    /**
     * Description:根据索引名称判断索引是否存在
     * @date   2024/3/6 14:13
     * @param  name 索引名称
     * @return boolean 返回存在的布尔值
     **/
    @Override
    public boolean indexExists(String name) throws IOException {
        return elasticsearchClient.indices().exists(b -> b.index(name)).value();
    }
}

文档类

css 复制代码
package cn.goktech.workbench.service.impl;

import cn.test.configs.PublicConfig;
import cn.test.entity.docmanagement.po.DocumentInfo;
import cn.test.entity.docmanagement.vo.DocumentInfoPageVo;
import cn.test.entity.docmanagement.vo.DocumentInfoVo;
import cn.test.service.ElasticSearchDocService;
import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;

/**
 * Description: es操作文档的接口
 * @version 1.0
 * @ClassName ElasticSearchDocServiceImpl
 * @date 2024/3/6 17:44
 **/
@Service
@Slf4j
public class ElasticSearchDocServiceImpl implements ElasticSearchDocService {

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private ElasticsearchAsyncClient elasticsearchAsyncClient;


    /**
     * 新增一个文档并返回ID
     * @param indexName 索引名称
     * @param document 文档对象
     * @return IndexResponse
     * @throws Exception 抛出操作异常
     */
    @Override
    public String createByFluentDsl(String indexName, DocumentInfo document) throws Exception {
        return  elasticsearchClient.index(idx -> idx
                .index(indexName)
                .id(document.getDocId())
                .document(document)).id();
    }


    /**
     * 新增一个文档
     * @param indexName 索引名称
     * @param document 文档对象 需要的字段自己创建一个实体类
     * @return IndexResponse
     * @throws Exception 抛出操作异常
     */
    @Override
    public String createByBuilderPattern(String indexName, DocumentInfo document) throws Exception {
        IndexRequest.Builder<Object> indexReqBuilder = new IndexRequest.Builder<>();
        indexReqBuilder.index(indexName);
        indexReqBuilder.id(document.getDocId());
        indexReqBuilder.document(document);
        return elasticsearchClient.index(indexReqBuilder.build()).id();
    }

    /**
     * 用JSON字符串创建文档
     * @param indexName 索引名称
     * @param idxId 索引id
     * @param jsonContent json字符串
     * @return IndexResponse
     * @throws Exception 抛出操作异常
     */
    @Override
    public String createByJson(String indexName, String idxId, String jsonContent) throws Exception {
        return elasticsearchClient.index(i -> i
                .index(indexName)
                .id(idxId)
                .withJson(new StringReader(jsonContent))).id();
    }

    /**
     * 异步新增文档
     * @param indexName 索引名称
     * @param idxId 索引id
     * @param document 文档内容 需要的字段自己创建一个实体类
     * @param action 属性
     */
    @Override
    public void createAsync(String indexName, String idxId, DocumentInfo document, BiConsumer<IndexResponse, Throwable> action) {
        elasticsearchAsyncClient.index(idx -> idx
                .index(indexName)
                .id(idxId)
                .document(document)
        ).whenComplete(action);
    }

    /**
     * 批量增加文档
     * @param indexName 索引名称
     * @param documents 要增加的对象集合 需要的字段自己创建一个实体类
     * @return 批量操作的结果
     * @throws Exception 抛出操作异常
     */
    @Override
    public BulkResponse bulkCreate(String indexName, List<DocumentInfo> documents) throws Exception {
        BulkRequest.Builder br = new BulkRequest.Builder();
        //可以将 Object定义为一个文档基类。比如 ESDocument类
        // 将每一个product对象都放入builder中
        documents.stream().forEach(esDocument -> br
                .operations(op -> op
                        .index(idx -> idx
                                .index(indexName)
                                .id(esDocument.getDocId())
                                .document(esDocument))));
        return elasticsearchClient.bulk(br.build());
    }

    /**
     * 根据文档id查找文档
     * @param indexName 索引名称
     * @param docId 文档id
     * @return Object类型的查找结果
     * @throws IOException 抛出操作异常
     */
    @Override
    public Object getById(String indexName, String docId) throws IOException {
        GetResponse<Object> response = elasticsearchClient.get(g -> g
                        .index(indexName)
                        .id(docId),
                Object.class);
        return response.found() ? response.source() : null;
    }

    /**
     * 根据文档id查找文档,返回类型是ObjectNode
     * @param indexName 索引名称
     * @param docId 文档id
     * @return ObjectNode类型的查找结果
     * @throws IOException 抛出操作异常
     */
    @Override
    public ObjectNode getObjectNodeById(String indexName, String docId) throws IOException {
        GetResponse<ObjectNode> response = elasticsearchClient.get(g -> g
                        .index(indexName)
                        .id(docId),
                ObjectNode.class);
        return response.found() ? response.source() : null;
    }

    /**
     * 根据文档id删除文档
     * @param indexName 索引名称
     * @param docId 文档id
     * @return Object类型的查找结果
     * @throws IOException 抛出操作异常
     */
    @Override
    public Boolean deleteById(String indexName, String docId) throws IOException {
        DeleteResponse delete = elasticsearchClient.delete(d -> d
                .index(indexName)
                .id(docId));
        return delete.forcedRefresh();
    }

    /**
     * 批量删除文档
     * @param indexName 索引名称
     * @param docIds 要删除的文档id集合
     * @return BulkResponse
     * @throws Exception 抛出操作异常
     */
    @Override
    public BulkResponse bulkDeleteByIds(String indexName, List<String> docIds) throws Exception {
        BulkRequest.Builder br = new BulkRequest.Builder();
        // 将每一个对象都放入builder中
        docIds.stream().forEach(id -> br
                .operations(op -> op
                        .delete(d -> d
                                .index(indexName)
                                .id(id))));
        return elasticsearchClient.bulk(br.build());
    }


    /**
     * Description:修改文档信息
     * @date   2024/3/7 10:09
     * @param  indexName 索引名称
     * @param  documentInfo 文档信息 需要的字段自己创建一个实体类
     **/
    @Override
    public void updateDocById(String indexName, DocumentInfo documentInfo) throws IOException {
        final UpdateResponse<DocumentInfo> response = elasticsearchClient.update(builder -> builder
                .index(indexName)
                .id(documentInfo.getDocId())
                .doc(documentInfo), DocumentInfo.class);
        System.err.println(response.shards().successful());
    }

    public void getDocPageList(String indexName, Integer pageNum, Integer pageSize) throws IOException {
        /*
         * 分页查询,查询所有
         * sort 排序
        */
        final SearchResponse<DocumentInfo> response = elasticsearchClient.search(builder ->
                        builder.index(indexName)
                                .query(q->q.matchAll(v->v))
                                .size(pageSize)
                                .from(pageNum)
                                .sort(builder1 -> builder1.field(f->f.field("reportDate").order(SortOrder.Desc)))
                , DocumentInfo.class);
        List<Hit<DocumentInfo>> hitList = response.hits().hits();
        System.err.println(hitList);
    }

    public void getDocPageListBySearchWords(String indexName, Integer pageNum, Integer pageSize, String searchWords) throws IOException {
        /*
         * 分页查询,可根据输入文字搜索
         * sort 排序
         */
        final SearchResponse<DocumentInfoVo> response = elasticsearchClient.search(builder ->
                        builder.index(indexName)
                                .query(q->q.multiMatch(v->v.query(searchWords).fields("docTitle", "docContent")))
                                .size(pageSize)
                                .from(pageNum)
                                .sort(builder1 -> builder1.field(f->f.field("reportDate").order(SortOrder.Desc)))
                , DocumentInfoVo.class);
        System.err.println(response);
    }

    public void getDocPageListByTitle(String indexName, Integer pageNum, Integer pageSize, String type) throws IOException {
        /*
         * 分页查询,可根据类型搜索
         * query 查询条件
         * sort 排序
         */
        final SearchResponse<DocumentInfoVo> response = elasticsearchClient.search(builder ->
                        builder.index(indexName)
                                .query(q->q.term(t ->t.field("docType.keyword").value(type)))
                                .size(pageSize)
                                .from(pageNum)
                                .sort(builder1 -> builder1.field(f->f.field("reportDate").order(SortOrder.Desc)))
                , DocumentInfoVo.class);
        System.err.println(response);
        List<Hit<DocumentInfoVo>> hitList = response.hits().hits();
    }


    /**
     * Description:TODOmethod
     * @date   2024/3/8 10:26
     * @param  indexName 索引名称
     * @param  pageNum 页码
     * @param  pageSize 每页数量
     * @param  queryList 查询条件query
     * @return DocumentInfoPageVo 返回分页数据
     * @throws IOException 抛出操作异常
     **/
    @Override
    public DocumentInfoPageVo getDocPageList(String indexName, Integer pageNum, Integer pageSize, List<Query> queryList) throws IOException {
        List<DocumentInfoVo> list = new ArrayList<>();
        SearchResponse<DocumentInfoVo> response = elasticsearchClient.search(s -> s
                        .index(indexName)
                        .query(q -> q.bool(b -> b.must(queryList)))
                        //高亮
                        .highlight(h -> h
                                .preTags("<span>")
                                .postTags("</span>")
                                .requireFieldMatch(false)
                                .fields("docTitle", hf -> hf)
                                .fields("docContent", hf -> hf))
                        //排序
                        .sort(sort -> sort.field(f -> f.field("reportDate").order(SortOrder.Desc)))
//                        //查询字段过滤
//                        .source(sc -> sc.filter(f -> f.includes(sources)))
                        //分页
                        .from((pageNum - 1) * pageSize)
                        .size(pageSize),
                DocumentInfoVo.class
        );
        DocumentInfoPageVo vo = new DocumentInfoPageVo();
        vo.setTotal(response.hits().total() != null ? response.hits().total().value() : PublicConfig.ZERO);

        System.err.println(response);

        List<Hit<DocumentInfoVo>> hitList = response.hits().hits();
        for (Hit<DocumentInfoVo> hit : hitList) {
            DocumentInfoVo infoVo = hit.source();
            if(Objects.nonNull(infoVo)){
                Map<String, List<String>> map = hit.highlight();
                //设置返回高亮的字段进行原字段替换
                infoVo.setDocTitle(CollectionUtils.isEmpty(map.get("docTitle")) ? infoVo.getDocTitle() : map.get("docTitle").get(PublicConfig.ZERO));
                infoVo.setDocContent(CollectionUtils.isEmpty(map.get("docContent")) ? infoVo.getDocContent() : map.get("docContent").get(PublicConfig.ZERO));
                list.add(infoVo);
            }
        }
        vo.setList(list);
        return vo;
    }
}

这里我想在分页多条件查询,使用方式:

css 复制代码
try {
            //sources 为你过滤的字段,也就是你想要查询出来的字段,配置了那些字段才会返回那些字段
//            List<String> sources = new ArrayList<>();
            //queryList 为你想要的查询条件封装,每种查询方式的条件query不一样
            List<Query> queryList = new ArrayList<>();
            //根据文档类型筛选查询
            if(StringUtils.isNotEmpty(dto.getTypeName())){
                Query query = TermQuery.of(m -> m.field("docType.keyword").value(dto.getTypeName()))._toQuery();
                queryList.add(query);
            }
            //根据输入内容查询文档标签和文档内容
            if(StringUtils.isNotEmpty(dto.getSearchWord())){
                Query query = MultiMatchQuery.of(v -> v.query(dto.getSearchWord()).fields("docTitle", "docContent"))._toQuery();
                queryList.add(query);
            }
            //根据时间范围查询
            if(StringUtils.isNotEmpty(dto.getStartDate()) && StringUtils.isNotEmpty(dto.getEndDate())){
                long startTime = DateUtil.parse(dto.getStartDate()).getTime();
                Query startQuery = RangeQuery.of(r -> r.field("reportDate")
                        .gte(JsonData.of(startTime))
                )._toQuery();
                queryList.add(startQuery);
                long endTime = DateUtil.parse(dto.getEndDate()).getTime();
                Query endQuery = RangeQuery.of(r -> r.field("reportDate")
                        .lte(JsonData.of(endTime))
                )._toQuery();
                queryList.add(endQuery);
            }
            DocumentInfoPageVo pageVo = elasticSearchDocService.getDocPageList(DOC_INDEX, dto.getPage(), dto.getLimit(), queryList);
            return new PageResult<>(pageVo.getList(), pageVo.getTotal());

上面是我的分页查询,这里docType.keyword是为了匹配查询,如mysql中的 name=张三,我这里的docType在ES文档中的type就是keyword,MultiMatchQuery这个是我多字段分词查询,后面是范围查询。

上面这块可以看文章:这里讲了很多知识

文章查询并高亮显示可查看此文章

Elasticsearch Java API

ES的term查询无返回结果

学习和整合时推荐
ElasticSearch 所有知识总结

ElasticSearch(提高篇)

中文分词器(IK Analyzer)及自定义词库

以上简洁整理只是记录下,便于后期使用,希望对大家有所帮助

相关推荐
odng1 分钟前
IDEA自己常用的几个快捷方式(自己的习惯)
java·ide·intellij-idea
CT随9 分钟前
Redis内存碎片详解
java·开发语言
brrdg_sefg18 分钟前
gitlab代码推送
java
hanbarger41 分钟前
mybatis框架——缓存,分页
java·spring·mybatis
cdut_suye1 小时前
Linux工具使用指南:从apt管理、gcc编译到makefile构建与gdb调试
java·linux·运维·服务器·c++·人工智能·python
苹果醋31 小时前
2020重新出发,MySql基础,MySql表数据操作
java·运维·spring boot·mysql·nginx
小蜗牛慢慢爬行1 小时前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate
azhou的代码园1 小时前
基于JAVA+SpringBoot+Vue的制造装备物联及生产管理ERP系统
java·spring boot·制造
wm10432 小时前
java web springboot
java·spring boot·后端
smile-yan2 小时前
Provides transitive vulnerable dependency maven 提示依赖存在漏洞问题的解决方法
java·maven