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

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

1:Linux安装ES

下载安装:
参考文章:连接1

连接2

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命令时输入密码后提示权限限制,确认密码是正确的
即:

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

#浏览器可直接访问

xpack.security.http.ssl:
enabled: false

末尾添加:

http.cors.enabled: true
http.cors.allow-origin: "*"
http.cors.allow-headers: Authorization

修改配置:jvm.options

-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

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

配置ES基础信息:

es:
  ip: 192.34.21.129
  port: 9200
  username: elskib
  password: qwe123

添加Java配置:

@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)
                        )
        );
    }

添加方法的实现类:


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

  • 添加依赖:
        <!-- 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>
  • 添加配置
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;
    }
}

  • 实现类

索引操作

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();
    }
}

文档类

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;
    }
}

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

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)及自定义词库

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/447988.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

校园小情书微信小程序源码 | 社区小程序前后端开源 | 校园表白墙交友小程序

项目描述&#xff1a; 校园小情书微信小程序源码 | 社区小程序前后端开源 | 校园表白墙交友小程序 功能介绍&#xff1a; 表白墙 卖舍友 步数旅行 步数排行榜 情侣脸 漫画脸 个人主页 私信 站内消息 今日话题 评论点赞收藏 服务器环境要求&#xff1a;PHP7.0 MySQL5.7 效果…

【三十】springboot项目上高并发解决示例

互相交流入口地址 整体目录&#xff1a; 【一】springboot整合swagger 【二】springboot整合自定义swagger 【三】springboot整合token 【四】springboot整合mybatis-plus 【五】springboot整合mybatis-plus 【六】springboot整合redis 【七】springboot整合AOP实现日志操作 【…

c++ primer plus 笔记 第十六章 string类和标准模板库

string类 string自动调整大小的功能&#xff1a; string字符串是怎么占用内存空间的&#xff1f; 前景&#xff1a; 如果只给string字符串分配string字符串大小的空间&#xff0c;当一个string字符串附加到另一个string字符串上&#xff0c;这个string字符串是以占用…

并发容器介绍(二)

并发容器介绍&#xff08;二&#xff09; 文章目录 并发容器介绍&#xff08;二&#xff09;BlockingQueueBlockingQueue 简介ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue ConcurrentSkipListMap 文章来自Java Guide 用于学习如有侵权&#xff0c;立即删除 Bl…

大模型字典中加入特殊字符

大模型字典中加入特殊字符 在微调大模型的时候会遇到添加特殊字符&#xff0c;例如在微调多轮的数据的时候需要加入人和机器等特殊标识字符&#xff0c;如用这个特殊字符表示人&#xff0c;用这个特殊字符表示机器&#xff0c;从而实现了人机对话。一般在大模型中base字典中不…

二次供水无人值守解决方案

二次供水无人值守解决方案 二次供水系统存在一定的管理难题和技术瓶颈&#xff0c;如设备老化、维护不及时导致的水质安全隐患&#xff0c;以及如何实现高效运行和智能化管理等问题。在一些地区&#xff0c;特别是老旧小区或农村地区&#xff0c;二次供水设施建设和改造滞后&a…

【go语言开发】redis简单使用

本文主要介绍redis安装和使用。首先安装redis依赖库&#xff0c;这里是v8版本&#xff1b;然后连接redis&#xff0c;完成基本配置&#xff1b;最后测试封装的工具类 文章目录 安装redis依赖库连接redis和配置工具类封装代码测试 欢迎大家访问个人博客网址&#xff1a;https://…

初学Vue——Vue路由

0 什么是Vue路由 类似于Html中的超链接(<a>)一样&#xff0c;可以跳转页面的一种方式。 前端路由&#xff1a;URL中hash(#号之后的内容)与组件之间的对应关系&#xff0c;如下图&#xff1a; 当我们点击左侧导航栏时&#xff0c;浏览器的地址栏会发生变化&#xff0c;路…

hutool,真香!

大家好&#xff0c;我是苏三&#xff0c;又跟大家见面了。 前言 今天给大家介绍一个能够帮助大家提升开发效率的开源工具包&#xff1a;hutool。 Hutool是一个小而全的Java工具类库&#xff0c;通过静态方法封装&#xff0c;降低相关API的学习成本&#xff0c;提高工作效率&…

IOT的发展历程及其优势——青创智通

工业互联网-物联网-设备改造-IOT-青创智通 ​随着科技的不断发展&#xff0c;物联网&#xff08;IoT&#xff09;已经逐渐成为了我们生活中不可或缺的一部分。IoT是指通过互联网将各种物理设备连接起来&#xff0c;实现设备之间的数据交换和智能化控制。IoT的发展不仅改变了我们…

四管齐下 共建发展 | 七巧低代码助力零售行业打造一体化协同解决方案

行业背景 随着互联网和移动技术的普及&#xff0c;零售行业的销售渠道日趋多元化和融合化&#xff0c;传统线下渠道和新兴线上渠道相互竞争和协作&#xff0c;形成了新零售和全渠道的格局。快消品新零售模式下&#xff0c;企业需要通过数字化和智能化的手段&#xff0c;实现对…

Flask python 开发篇:项目布局

一、背景简介 Flask应用程序可以像单个文件一样简单。就像上一篇简单实现一个接口一样&#xff0c;所有的东西都在一个python文件内&#xff1b; 然而&#xff0c;当项目越来越大的时候&#xff0c;把所有代码放在单个文件中就有点不堪重负了。 Python 项目使用 包 来管理代码…

携手亚信安慧AntDB,在数智化浪潮中乘风破浪

随着大数据时代的到来&#xff0c;对数据库的需求愈发强烈。在这一背景下&#xff0c;国产数据库逐渐崭露头角&#xff0c;亚信安慧AntDB作为重要的代表产品之一正积极参与到激烈的市场竞争中。亚信安慧AntDB不仅追求技术的革新和突破&#xff0c;同时也致力于满足用户日益增长…

【Python】conda 命令报错解决:Example: conda --no-plugins install <package>

目录 报错效果&#xff1a;解决方法总结 欢迎关注 『Python』 系列&#xff0c;持续更新中 欢迎关注 『Python』 系列&#xff0c;持续更新中 报错效果&#xff1a; An unexpected error has occurred. Conda has prepared the above report. If you suspect this error is bei…

OD_2024_C卷_200分_9、园区参观路径【JAVA】【动态规划】

package odjava;import java.util.Scanner;public class 九_园区参观路径 {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt(); // 长 -> 行数int m sc.nextInt(); // 宽 -> 列数int[][] matrix new int[n][m]; // 地图…

HAproxy反向代理与负载均衡

目录 一、HAproxy介绍 1. 概述 2. 关于4/7层负载均衡 2.1 无负载均衡 2.1.1 图示 2.1.2 说明 2.2 四层负载均衡 2.2.1 图示 2.2.2 说明 2.3 七层负载 2.3.1 图示 2.3.2 说明 3. 特性 4. HAProxy负载均衡常见策略 5. 处理模式 二、HAproxy安装 1. yum安装 2. 第…

算法---双指针练习-7(三数之和)

三数之和 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;三数之和 2. 讲解算法原理 首先对输入的数组进行排序&#xff0c;以便后续使用双指针法。初始化一个空的二维向量 ret&#xff0c;用于存储结果。使用一个循环遍历数组中的每个元素&#xff…

Spark性能优化指南——高级篇

调优概述 有的时候&#xff0c;我们可能会遇到大数据计算中一个最棘手的问题——数据倾斜&#xff0c;此时Spark作业的性能会比期望差很多。数据倾斜调优&#xff0c;就是使用各种技术方案解决不同类型的数据倾斜问题&#xff0c;以保证Spark作业的性能。 数据倾斜发生时的现…

【Idea】八种Debug模式介绍

1.行断点 在对应的代码行左侧边栏点击鼠标左键&#xff0c;会出现一个红色圆圈&#xff0c;以debug模式执行时当代码运行到此处则会停止&#xff0c;并可以查询相关上下文参数 2.方法断点 在方法左侧点击创建断点,在方法进入时会停止&#xff0c;同时可以右键断点&#xff0c;…

Jenkins Pipeline实现Golang项目的CI/CD

Jenkins Pipeline实现Golang项目的CI/CD 背景 最近新增了一个Golang实现的项目&#xff0c;需要接入到现有的流水线架构中。 流程图 这边流程和之前我写过的一篇《基于Jenkins实现的CI/CD方案》差不多&#xff0c;不一样的是构建现在是手动触发的&#xff0c;没有配置webho…