ES(Elasticsearch)的应用与代码示例

[复制链接]
发表于 2025-8-26 13:00:38 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
Elasticsearch应用与代码示例技术文章大纲

一、弁言


  • Elasticsearch在现代化应用中的焦点作用
  • 典型应用场景分析(日记分析/全文检索/数据聚合)
二、环境准备(前提条件)


  • Elasticsearch 8.x集群部署要点
  • IK中文分词插件配置指南
  • Ingest Attachment插件安装说明
三、焦点代码结构剖析
  1. src/main/
  2. ├── java/
  3. │   ├── config/
  4. │   │   └── ElasticSearchConfig.java
  5. │   ├── controller/
  6. │   │   └── ElasticSearchController.java
  7. │   ├── service/
  8. │   │   ├── ElasticSearchService.java
  9. │   │   └── ElasticSearchServiceImpl.java
  10. │   ├── model/
  11. │   │   ├── FileData.java
  12. │   │   ├── Attachment.java
  13. │   │   └── SearchResult.java
  14. │   ├── dto/
  15. │   │   └── WarningInfoDto.java
  16. │   └── util/
  17. │       └── ElasticSearchUtils.java
复制代码
四、焦点组件实现(含代码示例)

1. 配置中心(ElasticSearchConfig.java)
application.properties or yml
  1. es.uri=192.168.1.1
  2. es.port=9200
  3. es.username=""
  4. es.password=""
复制代码
  1. package com.zbxsoft.wds.config;
  2. import co.elastic.clients.elasticsearch.ElasticsearchClient;
  3. import co.elastic.clients.json.JsonpMapper;
  4. import co.elastic.clients.json.jackson.JacksonJsonpMapper;
  5. import co.elastic.clients.transport.ElasticsearchTransport;
  6. import co.elastic.clients.transport.rest_client.RestClientTransport;
  7. import com.alibaba.fastjson.parser.deserializer.JSONPDeserializer;
  8. import com.baomidou.mybatisplus.core.toolkit.StringUtils;
  9. import com.fasterxml.jackson.databind.ObjectMapper;
  10. import org.apache.http.HttpHost;
  11. import org.apache.http.auth.AuthScope;
  12. import org.apache.http.auth.UsernamePasswordCredentials;
  13. import org.apache.http.client.CredentialsProvider;
  14. import org.apache.http.impl.client.BasicCredentialsProvider;
  15. import org.elasticsearch.client.RestClient;
  16. import org.elasticsearch.client.RestClientBuilder;
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.boot.context.properties.ConfigurationProperties;
  19. import org.springframework.context.annotation.Bean;
  20. import org.springframework.context.annotation.Configuration;
  21. /**
  22. * es配置
  23. */
  24. @Configuration
  25. @ConfigurationProperties(prefix = "es")
  26. public class ElasticSearchConfig{
  27.     public String getUri() {
  28.         return uri;
  29.     }
  30.     public void setUri(String uri) {
  31.         this.uri = uri;
  32.     }
  33.     public Integer getPort() {
  34.         return port;
  35.     }
  36.     public void setPort(Integer port) {
  37.         this.port = port;
  38.     }
  39.     public String getPassword() {
  40.         return password;
  41.     }
  42.     public void setPassword(String password) {
  43.         this.password = password;
  44.     }
  45.     public String getUsername() {
  46.         return username;
  47.     }
  48.     public void setUsername(String username) {
  49.         this.username = username;
  50.     }
  51.     private String uri;
  52.     private Integer port;
  53.     private String password;
  54.     private String username;
  55.     @Bean
  56.     public ElasticsearchClient elasticsearchClient(@Autowired(required = false) JsonpMapper jsonpMapper, @Autowired(required = false) ObjectMapper objectMapper) {
  57.         // 解析hostlist配置信息
  58.         // 创建HttpHost数组,其中存放es主机和端口的配置信息
  59.         HttpHost[] httpHostArray = new HttpHost[1];
  60.         httpHostArray[0] = new HttpHost(uri,Integer.valueOf(port));
  61.         RestClientBuilder builder = RestClient.builder(httpHostArray);
  62.         final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
  63.         if(StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)){
  64.             credentialsProvider.setCredentials(AuthScope.ANY,
  65.                     new UsernamePasswordCredentials(username, password));
  66.             RestClient client = RestClient.builder(new HttpHost(uri,Integer.valueOf(port)))
  67.                         .build();
  68.             ElasticsearchTransport transport = new RestClientTransport(client, new JacksonJsonpMapper());
  69.             return new ElasticsearchClient(transport);
  70.         }
  71.         RestClient restClient = builder.build();
  72.         ElasticsearchTransport transport = null;
  73.         if (jsonpMapper != null) {
  74.             transport = new RestClientTransport(restClient, jsonpMapper);
  75.         } else if (objectMapper != null) {
  76.             transport = new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper));
  77.         } else {
  78.             transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
  79.         }
  80.         // Create the transport with a Jackson mapper
  81.         // And create the API client
  82.         return new ElasticsearchClient(transport);
  83.     }
  84. }
复制代码
2. 数据处理模型(FileData.java)
  1. package com.zbxsoft.wds.config;
  2. import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  3. import com.zbxsoft.wds.fileupload.Attachment;
  4. import lombok.Data;
  5. import org.springframework.data.elasticsearch.annotations.Document;
  6. import org.springframework.data.elasticsearch.annotations.Field;
  7. import org.springframework.data.elasticsearch.annotations.FieldType;
  8. import java.io.Serializable;
  9. import java.util.Objects;
  10. @Data
  11. @Document(indexName = "file_data", createIndex = false)
  12. @JsonIgnoreProperties(ignoreUnknown = true)
  13. public class FileData implements Serializable {
  14.     @Field(name = "file_id",type = FieldType.Text)
  15.     private String file_id;
  16.     @Field(name = "file_type",type = FieldType.Text)
  17.     private String file_type;
  18.     @Field(name = "file_name",type = FieldType.Text)
  19.     private String file_name;
  20.     @Field(name = "file_url",type = FieldType.Text)
  21.     private String file_url;
  22.     @Field(name = "file_size",type = FieldType.Text)
  23.     private String file_size;
  24.     @Field(name = "group_file_id",type = FieldType.Text)
  25.     private String group_file_id;
  26.     @Field(name = "file_suffix",type = FieldType.Text)
  27.     private String file_suffix;
  28.     @Field(name = "file_dir_name",type = FieldType.Text)
  29.     private String file_dir_name;
  30.     //保存时使用
  31.     @Field(name = "attachment.content",type = FieldType.Text)
  32.     private String content;
  33.     //检索时使用
  34.     private Attachment attachment;
  35. }
  36. }
复制代码
3. 附件剖析组件(Attachment.java)
  1. package com.zbxsoft.wds.fileupload;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import lombok.ToString;
  6. import java.io.Serializable;
  7. @Data
  8. @AllArgsConstructor
  9. @NoArgsConstructor
  10. @ToString
  11. public class Attachment implements Serializable {
  12.     private String date;
  13.     private String content_type;
  14.     private String author;
  15.     private String language;
  16.     private String title;
  17.     private String content;
  18.     private String content_length;
  19. }
  20. }
复制代码
4. 服务层实现(ElasticSearchService.java)
  1. package com.zbxsoft.wds.fileupload;
  2. import com.baomidou.mybatisplus.core.metadata.IPage;
  3. import com.zbxsoft.wds.config.FileData;
  4. import org.springframework.http.HttpEntity;
  5. import org.springframework.web.multipart.MultipartFile;
  6. import java.io.IOException;
  7. import java.util.List;
  8. import java.util.Map;
  9. public interface ElasticsearchService {
  10.     HttpEntity<?> createFileIndex(MultipartFile file) throws IOException;
  11.     String createFileIndex(FileData fileData) throws IOException;
  12.     Map<String,Double> queryWord(String keyword) throws IOException;
  13.     IPage<SearchResult> queryWord(WarningInfoDto warningInfoDto) throws IOException;
  14.     List<String> getAssociationalWordOther(WarningInfoDto warningInfoDto);
  15.     String updateFileIndex(String id, FileData fileData) throws IOException;
  16. }
复制代码
工具类(ElasticSearchUtils.java)
  1. package com.zbxsoft.wds.config;
  2. import cn.hutool.extra.spring.SpringUtil;
  3. import co.elastic.clients.elasticsearch.ElasticsearchClient;
  4. import co.elastic.clients.elasticsearch._types.Result;
  5. import co.elastic.clients.elasticsearch._types.SortOptions;
  6. import co.elastic.clients.elasticsearch._types.SortOrder;
  7. import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
  8. import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
  9. import co.elastic.clients.elasticsearch._types.mapping.DateProperty;
  10. import co.elastic.clients.elasticsearch._types.mapping.Property;
  11. import co.elastic.clients.elasticsearch._types.mapping.TextProperty;
  12. import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
  13. import co.elastic.clients.elasticsearch._types.query_dsl.Query;
  14. import co.elastic.clients.elasticsearch.core.*;
  15. import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
  16. import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
  17. import co.elastic.clients.elasticsearch.core.search.TotalHits;
  18. import co.elastic.clients.elasticsearch.core.search.TotalHitsRelation;
  19. import co.elastic.clients.elasticsearch.core.search.TrackHits;
  20. import co.elastic.clients.elasticsearch.indices.*;
  21. import com.baomidou.mybatisplus.core.toolkit.StringUtils;
  22. import lombok.extern.slf4j.Slf4j;
  23. import java.io.IOException;
  24. import java.io.StringReader;
  25. import java.lang.annotation.Annotation;
  26. import java.lang.reflect.Field;
  27. import java.util.*;
  28. import java.util.stream.Collectors;
  29. @Slf4j
  30. public class ElasticSearchUtils<T> {
  31.     public static ElasticsearchClient elasticsearchClient;
  32.     public String index;
  33.     public Class obj;
  34.     public String idField;
  35.     public Set<String> fields;
  36.     static {
  37.         ElasticSearchConfig elasticSearchConfig = SpringUtil.getBean(ElasticSearchConfig.class);
  38. //        elasticsearchClient = elasticSearchConfig.elasticsearchClient();
  39.     }
  40.     /**
  41.      * 获取id字段
  42.      *
  43.      * @return
  44.      */
  45.     private String getIdValue(T t) throws Exception {
  46.         Field field = t.getClass().getDeclaredField(idField);
  47.         field.setAccessible(true);
  48.         Object object = field.get(t);
  49.         return object.toString();
  50.     }
  51.     /**
  52.      * 判断索引是否存在
  53.      *
  54.      * @return
  55.      */
  56.     public Boolean getIndex() {
  57.         try {
  58.             GetIndexRequest getIndexRequest = GetIndexRequest.of(builder -> builder.index(index));
  59.             GetIndexResponse getIndexResponse = elasticsearchClient.indices().get(getIndexRequest);
  60.             log.info("getIndexResponse:{}", getIndexResponse);
  61.             return true;
  62.         } catch (IndexOutOfBoundsException | IOException e) {
  63.             log.info("getIndexResponse:{}", e.getMessage());
  64.             return false;
  65.         }
  66.     }
  67.     public void deleteIndex() throws IOException {
  68.         //1.创建索引请求
  69.         DeleteIndexRequest request = DeleteIndexRequest.of(builder -> builder.index(index));
  70.         //2.执行创建请求
  71.         DeleteIndexResponse delete = elasticsearchClient.indices().delete(request);
  72.         //如果为true就删除了
  73.         log.info("DeleteIndexRequest:{}", delete);
  74.     }
  75.     /**
  76.      * 插入数据
  77.      *
  78.      * @throws IOException
  79.      */
  80.     public void push(T t) throws Exception {
  81.         String id = getIdValue(t);
  82.         IndexRequest<T> indexRequest = IndexRequest.of(b -> b
  83.                         .index(index)
  84.                         .id(id)
  85.                         .document(t)
  86.                 //刷新可以立刻搜索到,消耗性能
  87.                 /*.refresh(Refresh.True)*/);
  88.         elasticsearchClient.index(indexRequest);
  89.     }
  90.     /**
  91.      * 索引信息查询
  92.      *
  93.      * @throws IOException
  94.      */
  95.     public T query(String id) throws IOException {
  96.         GetResponse<T> response = elasticsearchClient.get(g -> g
  97.                         .index(index)
  98.                         .id(id)
  99.                 , obj);
  100.         if (response.found()) {
  101.             return response.source();
  102.         }
  103.         return null;
  104.     }
  105.     /**
  106.      * 索引信息查询
  107.      *
  108.      * @throws IOException
  109.      */
  110.     public HitsMetadata<T> queryList(int page, int pageSize, Query query, SortOptions... sortOptions) throws IOException {
  111.         SearchResponse<T> search = elasticsearchClient.search(
  112.                 s -> s.index(index)
  113.                         .query(query)
  114.                         .trackTotalHits(TrackHits.of(i -> i.enabled(true)))
  115.                         .sort(Arrays.asList(sortOptions))
  116.                         .from((page - 1) * pageSize).size(pageSize)
  117.                 , obj);
  118.         return search.hits();
  119.     }
  120.     /**
  121.      * 删除文档
  122.      *
  123.      * @throws IOException
  124.      */
  125.     public boolean del(String id) throws IOException {
  126.         DeleteResponse delete = elasticsearchClient.delete(
  127.                 d -> d.index(index)
  128.                         .id(id));
  129.         Result result = delete.result();
  130.         return "deleted".equals(result.jsonValue()) | "not_found".equals(result.jsonValue());
  131.     }
  132.     /**
  133.      * 批量
  134.      *
  135.      * @throws IOException
  136.      */
  137.     public Map<String, String> batchDel(Set<String> ids) throws Exception {
  138.         BulkRequest.Builder br = new BulkRequest.Builder();
  139.         for (String id : ids) {
  140.             br.operations(op -> op.delete(d -> d.index(index).id(id)));
  141.         }
  142.         return requestBulk(br);
  143.     }
  144.     /**
  145.      * 批量
  146.      *
  147.      * @throws IOException
  148.      */
  149.     public Map<String, String> batchAdd(List<T> list) throws Exception {
  150.         BulkRequest.Builder br = new BulkRequest.Builder();
  151.         for (T t : list) {
  152.             String idValue = getIdValue(t);
  153.             br.operations(op -> op
  154.                     .index(idx -> idx
  155.                             .index(index)
  156.                             .id(idValue)
  157.                             .document(t)
  158.                     )
  159.             );
  160.         }
  161.         return requestBulk(br);
  162.     }
  163.     /**
  164.      * 处理批量请求
  165.      *
  166.      * @param br
  167.      * @return
  168.      * @throws IOException
  169.      */
  170.     private Map<String, String> requestBulk(BulkRequest.Builder br) throws IOException {
  171.         //刷新可以立刻搜索到,消耗性能
  172.         //br.refresh(Refresh.True);
  173.         BulkResponse result = elasticsearchClient.bulk(br.build());
  174.         System.out.println(result);
  175.         Map<String, String> returnResult = new HashMap<>();
  176.         if (result.errors()) {
  177.             returnResult = result.items().stream()
  178.                     .filter(e -> e.error() != null)
  179.                     .collect(Collectors.toMap(BulkResponseItem::id, b -> b.error().reason()));
  180.         }
  181.         return returnResult;
  182.     }
  183.     /**
  184.      * 分组
  185.      * @param map
  186.      * @return
  187.      * @throws IOException
  188.      */
  189.     public Map<String, Aggregate> buildAggregate(Map<String, Aggregation> map) throws IOException {
  190.         SearchResponse<T> search = elasticsearchClient.search(
  191.                 s -> s.index(index).size(0)
  192.                         .aggregations(map), obj);
  193.         return search.aggregations();
  194.     }
  195.     public Long queryCount(Query query) throws IOException {
  196.         CountResponse search = elasticsearchClient.count(s -> s.index(index).query(query));
  197.         return search.count();
  198.     }
  199. }
复制代码
服务层实现(ElasticSearchServiceImpl.java)
  1. package com.zbxsoft.wds.fileupload;
  2. import cn.hutool.core.collection.CollectionUtil;
  3. import cn.hutool.core.io.FileUtil;
  4. import cn.hutool.core.lang.UUID;
  5. import co.elastic.clients.elasticsearch.ElasticsearchClient;
  6. import co.elastic.clients.elasticsearch.core.*;
  7. import co.elastic.clients.elasticsearch.core.search.*;
  8. import com.aliyun.oss.ServiceException;
  9. import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  10. import com.baomidou.mybatisplus.core.metadata.IPage;
  11. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  12. import com.google.common.collect.Lists;
  13. import com.google.common.collect.Maps;
  14. import com.zbxsoft.wds.aneo4j.TextAnalysis;
  15. import com.zbxsoft.wds.aneo4j.rep.CustomNeo4jRepository;
  16. import com.zbxsoft.wds.filequery.FileQuery;
  17. import com.zbxsoft.wds.mapper.FileQueryMapper;
  18. import com.zbxsoft.wds.config.FileData;
  19. import com.zbxsoft.xwk.vo.R;
  20. import lombok.extern.slf4j.Slf4j;
  21. import org.apache.commons.io.IOUtils;
  22. import org.apache.commons.lang3.StringUtils;
  23. import org.apache.pdfbox.pdmodel.PDDocument;
  24. import org.apache.pdfbox.text.PDFTextStripper;
  25. import org.jetbrains.annotations.NotNull;
  26. import org.springframework.beans.factory.annotation.Autowired;
  27. import org.springframework.beans.factory.annotation.Value;
  28. import org.springframework.core.io.ResourceLoader;
  29. import org.springframework.data.util.Pair;
  30. import org.springframework.http.HttpEntity;
  31. import org.springframework.stereotype.Service;
  32. import org.springframework.web.multipart.MultipartFile;
  33. import java.io.File;
  34. import java.io.FileInputStream;
  35. import java.io.IOException;
  36. import java.net.URL;
  37. import java.util.*;
  38. @Service
  39. @Slf4j
  40. public class ElasticsearchServiceImpl implements ElasticsearchService {
  41.     @Autowired
  42.     private ElasticsearchClient client;
  43.     @Autowired
  44.     private ResourceLoader resourceLoader;
  45.     @Autowired
  46.     protected FileQueryMapper fileQueryMapper;
  47.     @Value("${file.server.address}")
  48.     private String fileServerAddress;
  49.     @Autowired
  50.     private CustomNeo4jRepository customNeo4jRepository;
  51.     /**
  52.      * 创建索引
  53.      * @param fileData 文件数据
  54.      * @return 索引id
  55.      * @throws IOException exp
  56.      */
  57.     public String createFileIndex(FileData fileData) throws IOException {
  58.         String path = fileData.getFile_url();
  59.         String url = fileServerAddress + path;
  60.         log.info("已上传文件地址:" + url);
  61.         String fileName = FileUtil.getName(url);
  62.         // 上传并返回新文件名称
  63.         String prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
  64.         File file = File.createTempFile(fileName, prefix);
  65.         IOUtils.copy(new URL(url),file);
  66.         try (PDDocument document = PDDocument.load(file)) {
  67.             //提取pdf中的文本内容
  68.             PDFTextStripper pdfTextStripper = new PDFTextStripper();
  69.             String text = pdfTextStripper.getText(document);
  70.             //该主键 在es dm 中用作数据唯一id, 在neo4j中作为实体的type
  71.             String id = fileData.getFile_id();
  72.             //提取文章中的关键词
  73.             List<String> keywords = TextAnalysis.getKeywords(text);
  74.             if (CollectionUtil.isNotEmpty(keywords)) {
  75.                 //构建知识图谱
  76.                 customNeo4jRepository.buildArticleKeywordGraph(id, keywords);
  77.                 //更新keywords字段 file_id 就是数据库中存在的主键
  78.                 updateKeywords(id, String.join(",", keywords));
  79.             }
  80.         } catch (IOException e) {
  81.             log.error("提取文本或创建关键词图谱失败,异常信息: {}", e.toString());
  82.         }
  83.         //最后建立该文章的索引
  84.         String _docId = createIndex(file,fileData);
  85.         return _docId;
  86.     }
  87.     /**
  88.      * 更新文件对象的keyword
  89.      * @param id 主键
  90.      * @param keyword 关键词
  91.      */
  92.     private void updateKeywords(String id,String keyword){
  93.         LambdaUpdateWrapper<FileQuery> updateWrapper = new LambdaUpdateWrapper<>();
  94.         updateWrapper.eq(FileQuery::getFileQueryId,id).set(FileQuery::getKeyword,keyword);
  95.         fileQueryMapper.update(updateWrapper);
  96.     }
  97.     /**
  98.      * 创建索引
  99.      * @param file 文件
  100.      * @param fileData 文件对象
  101.      * @return 索引id
  102.      */
  103.     private String createIndex(File file, FileData fileData) {
  104.         try {
  105.             byte[] bytes = getContent(file);
  106.             String base64 = Base64.getEncoder().encodeToString(bytes);
  107.             fileData.setContent(base64);
  108.             IndexRequest<Object> requestData = IndexRequest.of(i -> i.index("file_data").pipeline("attachment").id(fileData.getFile_id()).document(fileData));
  109.             IndexResponse indexResponse = client.index(requestData);
  110.             log.info("indexResponse:" + indexResponse);
  111.             return indexResponse.id();
  112.         } catch (IOException e) {
  113.             log.error("文件上传异常,异常信息: {}", e.toString());
  114.             return "";
  115.         }
  116.     }
  117.     /**
  118.      * 更新索引,在更新文件的时候发生
  119.      * @param id 索引id 也是关系库主键
  120.      * @param fileData 文件对象
  121.      * @return
  122.      * @throws IOException
  123.      */
  124.     @Override
  125.     public String updateFileIndex(String id, FileData fileData) throws IOException {
  126.         //直接删除索引, 重建
  127.         DeleteRequest deleteRequest = DeleteRequest.of(s -> s.index("file_data").id(id));
  128.         client.delete(deleteRequest);
  129.         String _docId = createFileIndex(fileData);
  130.         return _docId;
  131.     }
  132.     /**
  133.      * 文件转base64
  134.      *
  135.      * @param file 文件对象
  136.      * @return buffer
  137.      * @throws IOException
  138.      */
  139.     private byte[] getContent(File file) throws IOException {
  140.         long fileSize = file.length();
  141.         if (fileSize > Integer.MAX_VALUE) {
  142.             log.info("file too big...");
  143.             return null;
  144.         }
  145.         FileInputStream fi = new FileInputStream(file);
  146.         byte[] buffer = new byte[(int) fileSize];
  147.         int offset = 0;
  148.         int numRead = 0;
  149.         while (offset < buffer.length
  150.                 && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {
  151.             offset += numRead;
  152.         }
  153.         // 确保所有数据均被读取
  154.         if (offset != buffer.length) {
  155.             throw new ServiceException("Could not completely read file "
  156.                     + file.getName());
  157.         }
  158.         return buffer;
  159.     }
  160.     /**
  161.      * 给定keyword 检索数据
  162.      * @param keyword
  163.      * @return
  164.      * @throws IOException
  165.      */
  166.     public Map<String,Double> queryWord(String keyword) throws IOException {
  167.         SearchResponse<FileData> response = doQuery(keyword);
  168.         log.info("检索结果: {}", response.hits().hits());
  169.         List<Pair<String,Double>> idList = Lists.newArrayList();
  170.         Map<String,Double> idsWithScore = Maps.newHashMap();
  171.         List<Hit<FileData>> hits = response.hits().hits();
  172.         for (Hit<FileData> hit : hits) {
  173.             Double score = hit.score();
  174.             String id = hit.id();
  175.             score = score == null ? 0.0d : score;
  176.             idsWithScore.put(id,score);
  177.         }
  178.         log.info("查询数据: {}", idsWithScore);
  179.         return idsWithScore;
  180.     }
  181.     /**
  182.      * es attachment.content 检索封装
  183.      * @param keyword 关键词
  184.      * @return 检索结果
  185.      * @throws IOException exp
  186.      */
  187.     private SearchResponse<FileData> doQuery(String keyword) throws IOException {
  188.         Map<String, HighlightField> map = new HashMap<>();
  189.         HighlightField build = new HighlightField.Builder().preTags("").postTags("").build();
  190.         map.put("file_name",build);
  191.         map.put("attachment.content",HighlightField.of(hf -> hf.preTags("").postTags("").numberOfFragments(4)));
  192.         Highlight highlight = Highlight.of(h -> h
  193.                 .type(HighlighterType.of(ht -> ht
  194.                         .builtin(BuiltinHighlighterType.Unified)))
  195.                 .fields(map)
  196.                 .fragmentSize(50).numberOfFragments(5));
  197.         //索引 file_name 分词器 为ik_max_word 颗粒度较细  而attachment.content 使用ik_smart分词器 , 颗粒度相对粗一点
  198.         SearchResponse<FileData> response = client.search(s -> s
  199.                         .index("file_data")
  200.                         .highlight(highlight)
  201.                         .query(q -> q
  202.                                 .bool(b -> b
  203.                                         .should(sh -> sh
  204.                                                 .match(t -> t
  205.                                                         .field("file_name").query(keyword)))
  206.                                         .should(sh -> sh
  207.                                                 .match(t -> t
  208.                                                         .field("attachment.content").query(keyword)))
  209.                                 )),
  210.                 FileData.class);
  211.         return response;
  212.     }
  213.     /**
  214.      * 高亮分词搜索其它类型文档
  215.      *
  216.      * @param warningInfoDto
  217.      * @return
  218.      */
  219.     public IPage<SearchResult> queryWord(WarningInfoDto warningInfoDto) throws IOException {
  220.         //分页
  221.         SearchResponse<FileData> response = doQuery(warningInfoDto.getKeyword());
  222.         //手动创建分页对象
  223.         IPage<SearchResult> warningInfoIPage = getFileDataIPage(warningInfoDto, response);
  224.         return warningInfoIPage;
  225.     }
  226.     /**
  227.      * 获取检索数据, 分页
  228.      * @param warningInfoDto
  229.      * @param response
  230.      * @return
  231.      */
  232.     @NotNull
  233.     private static IPage<SearchResult> getFileDataIPage(WarningInfoDto warningInfoDto, SearchResponse<FileData> response) {
  234.         List<Hit<FileData>> hits = response.hits().hits();
  235.         TotalHits total = response.hits().total();
  236.         List<SearchResult> resultList = new LinkedList<>();
  237.         //处理返回内容
  238.         for (Hit<FileData> hit : hits) {
  239.             Map<String, List<String>> map = hit.highlight();
  240.             List<String> highLightWords = Lists.newArrayList();
  241.             map.forEach((k,v) -> highLightWords.addAll(v));
  242.             FileData fileData = hit.source();
  243.             SearchResult searchResult = new SearchResult(fileData,highLightWords);
  244.             resultList.add(searchResult);
  245.         }
  246. //        //设置一个最后需要返回的实体类集合
  247.         //手动分页返回信息
  248.         IPage<SearchResult> warningInfoIPage = new Page<>();
  249.         assert total != null;
  250.         warningInfoIPage.setTotal(total.value());
  251.         warningInfoIPage.setRecords(resultList);
  252.         warningInfoIPage.setCurrent(warningInfoDto.getPageIndex());
  253.         warningInfoIPage.setSize(warningInfoDto.getPageSize());
  254.         warningInfoIPage.setPages(warningInfoIPage.getTotal() % warningInfoDto.getPageSize());
  255.         return warningInfoIPage;
  256.     }
  257.     /**
  258.      * 文档信息关键词联想(根据输入框的词语联想文件名称)
  259.      *
  260.      * @param warningInfoDto
  261.      * @return
  262.      */
  263.     public List<String> getAssociationalWordOther(WarningInfoDto warningInfoDto) {
  264. //        //需要查询的字段
  265. //        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
  266. //                .should(QueryBuilders.matchBoolPrefixQuery("fileName", warningInfoDto.getKeyword()));
  267. //        //contentType标签内容过滤
  268. //        boolQueryBuilder.must(QueryBuilders.termsQuery("contentType", warningInfoDto.getContentType()));
  269. //        //构建高亮查询
  270. //        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
  271. //                .withQuery(boolQueryBuilder)
  272. //                .withHighlightFields(
  273. //                        new HighlightBuilder.Field("fileName")
  274. //                )
  275. //                .withHighlightBuilder(new HighlightBuilder().preTags("<span style='color:red'>").postTags("</span>"))
  276. //                .build();
  277. //        //查询
  278. //        SearchHits<FileData> search = null;
  279. //        try {
  280. //            search = client.search(searchQuery);
  281. //        } catch (Exception ex) {
  282. //            ex.printStackTrace();
  283. //            throw new ServiceException(String.format("操作错误,请联系管理员!%s", ex.getMessage()));
  284. //        }
  285. //        //设置一个最后需要返回的实体类集合
  286. //        List<String> resultList = new LinkedList<>();
  287. //        //遍历返回的内容进行处理
  288. //        for (org.springframework.data.elasticsearch.core.SearchHit<FileInfo> searchHit : search.getSearchHits()) {
  289. //            //高亮的内容
  290. //            Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
  291. //            //将高亮的内容填充到content中
  292. //            searchHit.getContent().setFileName(highlightFields.get("fileName") == null ? searchHit.getContent().getFileName() : highlightFields.get("fileName").get(0));
  293. //            if (highlightFields.get("fileName") != null) {
  294. //                resultList.add(searchHit.getContent().getFileName());
  295. //            }
  296. //        }
  297. //        //list去重
  298. //        List<String> newResult = null;
  299. //        if (!FastUtils.checkNullOrEmpty(resultList)) {
  300. //            if (resultList.size() > 9) {
  301. //                newResult = resultList.stream().distinct().collect(Collectors.toList()).subList(0, 9);
  302. //            } else {
  303. //                newResult = resultList.stream().distinct().collect(Collectors.toList());
  304. //            }
  305. //        }
  306.         return new ArrayList<>();
  307.     }
  308.     /**
  309.      * 上传文件并进行文件内容识别上传到es 测试使用
  310.      * @param file
  311.      * @return
  312.      */
  313.     public HttpEntity<?> createFileIndex(MultipartFile file) throws IOException {
  314.         //http://192.168.100.243//file/wds/fileQuery/2024-12/1734319529179/downloadFile.pdf
  315. //        InputStream inputStream = resourceLoader.getResource(url).getInputStream();
  316.         String fileName = file.getName();
  317.         String prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
  318.         File tempFile = File.createTempFile(fileName, prefix);
  319.         // 上传文件路径
  320.         file.transferTo(tempFile);
  321.         FileData fileData = new FileData();
  322.         fileData.setFile_url("http://localhost:9000/bucket/p2");
  323.         fileData.setFile_id(UUID.fastUUID().toString(true));
  324.         fileData.setFile_name("张飞吃豆芽333.pdf");
  325.         fileData.setFile_suffix(".pdf");
  326.         fileData.setFile_type("pdf");
  327.         fileData.setFile_size("44");
  328.         fileData.setFile_dir_name("p2");
  329.         fileData.setGroup_file_id("g1");
  330.         String _docId = createIndex(tempFile,fileData);
  331.         return StringUtils.isBlank(_docId) ? R.badRequest("文件上传异常") : R.success("文件上传成功");
  332.     }
  333. }
复制代码
五、数据传输

1. 前端哀求数据传输(WarningInfoDto.java)
  1. package com.zbxsoft.wds.fileupload;
  2. import lombok.Data;
  3. import java.util.List;
  4. /**
  5. * 前端请求数据传输
  6. * WarningInfo
  7. * @author luoY
  8. */
  9. @Data
  10. public class WarningInfoDto{
  11.     /**
  12.      * 页数
  13.      */
  14.     private Integer pageIndex;
  15.     /**
  16.      * 每页数量
  17.      */
  18.     private Integer pageSize;
  19.     /**
  20.      * 查询关键词
  21.      */
  22.     private String keyword;
  23.     /**
  24.      * 内容类型
  25.      */
  26.     private List<String> contentType;
  27.     /**
  28.      * 用户手机号
  29.      */
  30.     private String phone;
  31. }
  32. }
复制代码
2. 文档全文检索接口(ElasticSearchController.java)
  1. package com.zbxsoft.wds.fileupload;
  2. import com.baomidou.mybatisplus.core.metadata.IPage;
  3. import com.zbxsoft.wds.common.R;
  4. import org.springframework.http.HttpEntity;
  5. import org.springframework.web.bind.annotation.*;
  6. import org.springframework.web.multipart.MultipartFile;
  7. import javax.annotation.Resource;
  8. import javax.servlet.http.HttpServletRequest;
  9. import java.io.IOException;
  10. import java.util.List;
  11. /**
  12. * es搜索引擎
  13. *
  14. * @author luoy
  15. */
  16. @RestController
  17. @RequestMapping("es")
  18. public class ElasticsearchController {
  19.     @Resource
  20.     private ElasticsearchService elasticsearchService;
  21.     @PostMapping("/uploadFile")
  22.     public HttpEntity<?> uploadFile(@RequestParam(required = false) MultipartFile file) throws IOException {
  23.         return elasticsearchService.createFileIndex(file);
  24.     }
  25.     /**
  26.      * 告警信息关键词联想
  27.      *
  28.      * @param warningInfoDto
  29.      * @return
  30.      */
  31.     @PostMapping("getAssociationalWordDoc")
  32.     public HttpEntity<?> getAssociationalWordDoc(@RequestBody WarningInfoDto warningInfoDto, HttpServletRequest request) {
  33.         List<String> words = elasticsearchService.getAssociationalWordOther(warningInfoDto);
  34.         return R.list(words);
  35.     }
  36.     /**
  37.      * 告警信息高亮分词分页查询
  38.      *
  39.      * @param warningInfoDto
  40.      * @return
  41.      */
  42.     @PostMapping("queryHighLightWordDoc")
  43.     public HttpEntity<?> queryHighLightWordDoc(@RequestBody WarningInfoDto warningInfoDto,HttpServletRequest request) throws IOException {
  44.         IPage<SearchResult> warningInfoListPage = elasticsearchService.queryWord(warningInfoDto);
  45.         return R.entity(warningInfoListPage);
  46.     }
  47. }
复制代码
3. 数据返回(SearchResult.java)
  1. package com.zbxsoft.wds.fileupload;
  2. import com.zbxsoft.wds.config.FileData;
  3. import lombok.AllArgsConstructor;
  4. import lombok.Data;
  5. import lombok.NoArgsConstructor;
  6. import lombok.ToString;
  7. import java.io.Serializable;
  8. import java.util.List;
  9. @Data
  10. @AllArgsConstructor
  11. @NoArgsConstructor
  12. @ToString
  13. public class SearchResult implements Serializable {
  14.     private FileData fileData;
  15.     private List<String> highLightWords;
  16. }
复制代码
六、高级功能实现


  • 多条件复合查询构建本领
  • 聚合统计实现方案
  • 搜索高亮显示配置
七、性能优化建议


  • 索引分片策略设计
  • 查询DSL优化本领
  • 批量操作最佳实践
八、总结与展望


  • 当前架构优势分析
  • 后续演进方向建议
(注:各代码示例需配合完备的类定义和import语句,实际开辟中需补充异常处理、日记纪录等生产级代码要素)
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表