Просмотр исходного кода

恢复 elasticsearch, 但在 @SpringBootApplication 中使用 exclude = ElasticsearchClientAutoConfiguration.class 排除 elasticsearch 的自动配置

reghao 7 месяцев назад
Родитель
Сommit
328003a9ca
20 измененных файлов с 1534 добавлено и 97 удалено
  1. 2 2
      search/search-service/pom.xml
  2. 2 1
      search/search-service/src/main/java/cn/reghao/tnb/search/app/SearchApplication.java
  3. 122 0
      search/search-service/src/main/java/cn/reghao/tnb/search/app/es/DocumentService.java
  4. 80 0
      search/search-service/src/main/java/cn/reghao/tnb/search/app/es/ElasticService.java
  5. 131 0
      search/search-service/src/main/java/cn/reghao/tnb/search/app/es/IndexService.java
  6. 83 0
      search/search-service/src/main/java/cn/reghao/tnb/search/app/es/MappingService.java
  7. 313 0
      search/search-service/src/main/java/cn/reghao/tnb/search/app/es/QueryService.java
  8. 434 0
      search/search-service/src/main/java/cn/reghao/tnb/search/app/es/SearchService.java
  9. 73 0
      search/search-service/src/main/java/cn/reghao/tnb/search/app/es/VideoTextDocument.java
  10. 165 0
      search/search-service/src/main/java/cn/reghao/tnb/search/app/es/VideoTextQuery.java
  11. 26 0
      search/search-service/src/main/java/cn/reghao/tnb/search/app/es/WenshuSearch.java
  12. 12 12
      search/search-service/src/main/java/cn/reghao/tnb/search/app/log/NginxLogDocument.java
  13. 41 41
      search/search-service/src/main/java/cn/reghao/tnb/search/app/log/NginxLogSearch.java
  14. 8 8
      search/search-service/src/main/java/cn/reghao/tnb/search/app/log/NginxLogService.java
  15. 14 13
      search/search-service/src/main/java/cn/reghao/tnb/search/app/rpc/DataSearchServiceImpl.java
  16. 8 7
      search/search-service/src/main/java/cn/reghao/tnb/search/app/service/WenshuSearchService.java
  17. 5 5
      search/search-service/src/main/java/cn/reghao/tnb/search/app/service/WenshuService.java
  18. 3 3
      search/search-service/src/test/java/ElasticTest.java
  19. 3 3
      search/search-service/src/test/java/NginxLogTest.java
  20. 9 2
      zzz/run_thirdparty.sh

+ 2 - 2
search/search-service/pom.xml

@@ -113,7 +113,7 @@
         </dependency>
 
         <!-- elasticsearch -->
-        <!--<dependency>
+        <dependency>
             <groupId>co.elastic.clients</groupId>
             <artifactId>elasticsearch-java</artifactId>
             <version>7.17.18</version>
@@ -148,7 +148,7 @@
             <groupId>jakarta.json</groupId>
             <artifactId>jakarta.json-api</artifactId>
             <version>2.0.1</version>
-        </dependency>-->
+        </dependency>
 
         <dependency>
             <groupId>org.springframework.cloud</groupId>

+ 2 - 1
search/search-service/src/main/java/cn/reghao/tnb/search/app/SearchApplication.java

@@ -2,13 +2,14 @@ package cn.reghao.tnb.search.app;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientAutoConfiguration;
 import org.springframework.context.annotation.ComponentScan;
 
 /**
  * @author reghao
  * @date 2025-04-28 09:50:58
  */
-@SpringBootApplication
+@SpringBootApplication(exclude = ElasticsearchClientAutoConfiguration.class)
 @ComponentScan({"cn.reghao.tnb.search", "cn.reghao.tnb.common"})
 public class SearchApplication {
     public static void main(String[] args) {

+ 122 - 0
search/search-service/src/main/java/cn/reghao/tnb/search/app/es/DocumentService.java

@@ -0,0 +1,122 @@
+package cn.reghao.tnb.search.app.es;
+
+import cn.reghao.tnb.search.app.log.model.NginxLog;
+import cn.reghao.tnb.search.app.model.po.Wenshu;
+import cn.reghao.jutil.jdk.id.SnowFlake;
+import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.Result;
+import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery;
+import co.elastic.clients.elasticsearch.core.*;
+import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2025-03-12 10:45:58
+ */
+@Slf4j
+@Service
+public class DocumentService {
+    private final ElasticsearchClient esClient;
+    private final SnowFlake idGenerator;
+
+    public DocumentService(ElasticService elasticService) {
+        this.esClient = elasticService.getElasticsearchClient();
+        this.idGenerator = new SnowFlake(1, 1);
+    }
+
+    public void addDocument(String indexName, NginxLog product) throws IOException {
+        IndexResponse indexResponse = esClient.index(i -> i.index(indexName).id(product.getId()).document(product));
+        log.info("add one document result: {}", indexResponse.result().jsonValue());
+    }
+
+    public void batchAddDocument(String indexName, List<NginxLog> nginxLogs) throws IOException {
+        List<BulkOperation> bulkOperations = new ArrayList<>();
+        nginxLogs.forEach(p -> bulkOperations.add(BulkOperation.of(b -> b.index(c -> c.id(p.getId()).document(p)))));
+        BulkResponse bulkResponse = esClient.bulk(s -> s.index(indexName).operations(bulkOperations));
+        //bulkResponse.items().forEach(b -> log.info("bulk response result = {}", b.result()));
+        //log.error("bulk response.error() = {}", bulkResponse.errors());
+    }
+
+    public void batchAddWenshu(String indexName, List<Wenshu> wenshuList) throws IOException {
+        List<BulkOperation> bulkOperations = new ArrayList<>();
+        wenshuList.forEach(p -> bulkOperations.add(BulkOperation.of(b -> b.index(c -> c.id(p.getId()).document(p)))));
+        BulkResponse bulkResponse = esClient.bulk(s -> s.index(indexName).operations(bulkOperations));
+//        bulkResponse.items().forEach(b -> log.info("bulk response result = {}", b.result()));
+        //log.error("bulk response.error() = {}", bulkResponse.errors());
+    }
+
+    public void saveAll(String index, List<NginxLog> nginxLogs) throws IOException {
+        for (NginxLog nginxLog : nginxLogs) {
+            IndexResponse response = esClient.index(i -> i.index(index).document(nginxLog).id("" + idGenerator.nextId()));
+            Result result = response.result();
+            System.out.println(result.jsonValue());
+        }
+    }
+
+    public void updateDocument(String indexName, NginxLog product) throws IOException {
+        UpdateResponse<NginxLog> updateResponse = esClient.update(s -> s.index(indexName).id(product.getId()).doc(product), NginxLog.class);
+        log.info("update doc result: {}", updateResponse.result());
+    }
+
+    public void update(String index, NginxLog nginxLog) throws IOException {
+        String id = nginxLog.getId();
+        IndexResponse response = esClient.index(i -> i.index(index).document(nginxLog).id(id));
+        Result result = response.result();
+        System.out.println(result.jsonValue());
+    }
+
+    public void deleteDocument(String indexName, String id) {
+        try {
+            DeleteResponse deleteResponse = esClient.delete(s -> s.index(indexName).id(id));
+            log.info("del doc result: {}", deleteResponse.result());
+        } catch (IOException e) {
+            log.error("del doc failed, error: ", e);
+        }
+    }
+
+    /**
+     * 删除索引下的所有文档
+     *
+     * @param
+     * @return
+     * @date 2025-03-13 16:26:20
+     */
+    public void deleteAllDocument(String indexName) {
+        try {
+            DeleteByQueryRequest deleteByQueryRequest = DeleteByQueryRequest.of(s -> s.index(indexName)
+                    .query(m -> m.matchAll(new MatchAllQuery.Builder().build()))
+            );
+            DeleteByQueryResponse deleteByQueryResponse = esClient.deleteByQuery(deleteByQueryRequest);
+            log.info("del doc result: {}", deleteByQueryResponse.total());
+        } catch (IOException e) {
+            log.error("del doc failed, error: ", e);
+        }
+    }
+
+    public void batchDeleteDocument(String indexName, List<String> ids) {
+        List<BulkOperation> bulkOperations = new ArrayList<>();
+        ids.forEach(a -> bulkOperations.add(BulkOperation.of(b -> b.delete(c -> c.id(a)))));
+        try {
+            BulkResponse bulkResponse = esClient.bulk(a -> a.index(indexName).operations(bulkOperations));
+            bulkResponse.items().forEach(a -> log.info("batch del result: {}", a.result()));
+            log.error("batch del bulk resp errors: {}", bulkResponse.errors());
+        } catch (IOException e) {
+            log.error("batch del doc failed, error: ", e);
+        }
+    }
+
+    public void delete(ElasticsearchAsyncClient client, String index) throws IOException {
+        int id = 1;
+        client.delete(i -> i.index(index).id("" + id)).whenComplete((success, failure)->{
+            System.out.println(success.index());
+            System.out.println(success.version());
+        });
+    }
+}

+ 80 - 0
search/search-service/src/main/java/cn/reghao/tnb/search/app/es/ElasticService.java

@@ -0,0 +1,80 @@
+package cn.reghao.tnb.search.app.es;
+
+import cn.reghao.tnb.search.app.config.ElasticProperties;
+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.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestClientBuilder;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author reghao
+ * @date 2025-03-12 10:41:52
+ */
+@Service
+public class ElasticService {
+    private final ElasticsearchClient esClient;
+
+    public ElasticService(ElasticProperties elasticProperties) {
+        String host = elasticProperties.getHost();
+        int port = elasticProperties.getPort();
+        String username = elasticProperties.getUsername();
+        String password = elasticProperties.getPassword();
+        this.esClient = getElasticsearchClient(host, port, username, password);
+    }
+
+    private ElasticsearchClient getElasticsearchClient(String host, int port, String username, String password) {
+        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
+
+        // 创建 SSL 上下文
+        /*SSLContextBuilder sslBuilder = SSLContexts.custom().loadTrustMaterial(null, (chain, authType) -> true);
+        final SSLContext sslContext = sslBuilder.build();*/
+
+        HttpHost httpHost = new HttpHost(host, port);
+        // 创建 low-level client
+        RestClientBuilder builder = RestClient.builder(httpHost)
+                .setHttpClientConfigCallback(httpClientBuilder ->
+                        httpClientBuilder
+                                // 超时 10 分钟
+                                .setConnectionTimeToLive(600, TimeUnit.SECONDS)
+                                .setDefaultCredentialsProvider(credentialsProvider))
+                                /*.setSSLContext(sslContext)
+                                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE))*/;
+        RestClient restClient = builder.build();
+
+        // 创建一个 Transport 通信和一个 JacksonJsonpMapper 序列化实例
+        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
+        // 得到一个 es 客户端
+        ElasticsearchClient client = new ElasticsearchClient(transport);
+        return client;
+    }
+
+    private ElasticsearchAsyncClient getElasticsearchAsyncClient(String host, int port) {
+        //创建 low-level client
+        RestClient restClient = RestClient.builder(new HttpHost(host, port)).build();
+        //创建一个Transport通信 和 一个JacksonJsonpMapper序列化实例
+        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
+
+        //得到一个es客户端
+        ElasticsearchAsyncClient client = new ElasticsearchAsyncClient(transport);
+        return client;
+    }
+
+    public ElasticsearchClient getElasticsearchClient() {
+        return this.esClient;
+    }
+
+    public void close() {
+    }
+}

+ 131 - 0
search/search-service/src/main/java/cn/reghao/tnb/search/app/es/IndexService.java

@@ -0,0 +1,131 @@
+package cn.reghao.tnb.search.app.es;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.analysis.Analyzer;
+import co.elastic.clients.elasticsearch._types.analysis.StandardAnalyzer;
+import co.elastic.clients.elasticsearch._types.mapping.IntegerNumberProperty;
+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.elasticsearch.indices.get_mapping.IndexMappingRecord;
+import co.elastic.clients.transport.endpoints.BooleanResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author reghao
+ * @date 2025-03-12 10:45:52
+ */
+@Slf4j
+@Service
+public class IndexService {
+    private final ElasticsearchClient esClient;
+
+    public IndexService(ElasticService elasticService) {
+        this.esClient = elasticService.getElasticsearchClient();
+    }
+
+    public void createIndex(String indexName, Map<String, Property> propertyMap) throws IOException {
+        String type = "_doc";
+        ExistsRequest existsRequest = new ExistsRequest.Builder()
+                .index(indexName)
+                .local(false)
+                .build();
+        BooleanResponse exists = esClient.indices().exists(existsRequest);
+        if(exists.value()) {
+            log.info("索引 {} 已存在!", indexName);
+            return;
+        }
+
+        // 设置分词
+        IndexSettingsAnalysis indexSettingsAnalysis = new IndexSettingsAnalysis.Builder()
+                .analyzer("StandardAnalyzer", new Analyzer.Builder().standard(new StandardAnalyzer.Builder().build()).build())
+                .build();
+
+        IndexSettings indexSettings = new IndexSettings.Builder()
+                //.analysis(indexSettingsAnalysis)
+                .numberOfShards("10")
+                .numberOfReplicas("10")
+                //.refreshInterval(Time.of(t -> 5))
+                .build();
+        TypeMapping typeMapping = new TypeMapping.Builder()
+                .properties(propertyMap)
+                .build();
+        CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder()
+                .index(indexName)
+                .settings(indexSettings)
+                .mappings(typeMapping)
+                //.aliases("ngxlog", new Alias.Builder().isWriteIndex(true).build())
+                .build();
+        CreateIndexResponse createIndexResponse = esClient.indices().create(createIndexRequest);
+        if (createIndexResponse.acknowledged()) {
+            log.info("索引 {} 创建成功!", indexName);
+        }
+    }
+
+    public void updateMapping(String indexName) throws IOException {
+        Map<String, Property> propertyMap = new HashMap<>();
+        Property intProp = Property.of(builder -> builder.integer(IntegerNumberProperty.of(pro -> pro.index(true))));
+        propertyMap.put("member", intProp);
+
+        /*Property keywordProp = Property.of(builder -> builder.keyword(KeywordProperty.of(pro -> pro.index(true))));
+        propertyMap.put("requestMethod", keywordProp);*/
+
+        /*Property textProp = Property.of(builder -> builder.text(TextProperty.of(pro -> pro.index(true).analyzer("ik_max_word"))));
+        propertyMap.put("requestUri", textProp);*/
+
+        PutMappingRequest putMappingRequest = PutMappingRequest.of(m -> m.index(indexName).properties(propertyMap));
+        PutMappingResponse putMappingResponse = esClient.indices().putMapping(putMappingRequest);
+        boolean acknowledged = putMappingResponse.acknowledged();
+        log.info("update mappings ack: {}", acknowledged);
+    }
+
+    public void deleteIndex(String indexName) throws IOException {
+        ExistsRequest existsRequest = new ExistsRequest.Builder()
+                .index(indexName)
+                .local(false)
+                .build();
+        BooleanResponse exists = esClient.indices().exists(existsRequest);
+        if(!exists.value()) {
+            log.info("索引 {} 不存在!", indexName);
+            return;
+        }
+
+        DeleteIndexResponse response = esClient.indices().delete(i -> i.index(indexName));
+        if (!response.acknowledged()) {
+
+        }
+    }
+
+    public void getMapping(String indexName) throws IOException {
+        GetMappingRequest request = GetMappingRequest.of(builder -> builder.index(indexName));
+        GetMappingResponse response = esClient.indices().getMapping(request);
+        Map<String, IndexMappingRecord> result = response.result();
+        log.info("{} mapping message: {}", indexName, result);
+    }
+
+    public void getIndex(String indexName) throws IOException {
+        List<String> indexNames = new ArrayList<>();
+        indexNames.add(indexName);
+        GetIndexRequest request = GetIndexRequest.of(builder -> builder.index(indexNames));
+        GetIndexResponse getIndexResponse = esClient.indices().get(request);
+        Map<String, IndexState> result = getIndexResponse.result();
+        result.entrySet().forEach(entry -> {
+            log.info("key: {}, value: {}", entry.getKey(), entry.getValue());
+        });
+    }
+
+    public void existIndex() throws IOException {
+        List<String> indexName = new ArrayList<>();
+        indexName.add("user");
+        ExistsRequest request = ExistsRequest.of(builder -> builder.index(indexName));
+        BooleanResponse response = esClient.indices().exists(request);
+        log.info("exist: {}", response.value());
+    }
+}

+ 83 - 0
search/search-service/src/main/java/cn/reghao/tnb/search/app/es/MappingService.java

@@ -0,0 +1,83 @@
+package cn.reghao.tnb.search.app.es;
+
+import cn.reghao.tnb.search.app.model.po.VideoText;
+import co.elastic.clients.elasticsearch._types.mapping.*;
+import org.springframework.stereotype.Service;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author reghao
+ * @date 2025-03-12 11:11:07
+ */
+@Service
+public class MappingService {
+    Property booleanProp = Property.of(builder -> builder.boolean_(BooleanProperty.of(pro -> pro.index(true))));
+    Property intProp = Property.of(builder -> builder.integer(IntegerNumberProperty.of(pro -> pro.index(true))));
+    Property longProp = Property.of(builder -> builder.long_(LongNumberProperty.of(pro -> pro.index(true).store(true))));
+    Property doubleProp = Property.of(builder -> builder.double_(DoubleNumberProperty.of(pro -> pro.index(true).store(true))));
+    Property dateProp = Property.of(builder -> builder.date(DateProperty.of(pro -> pro.index(true))));
+    Property keywordProp = Property.of(builder -> builder.keyword(KeywordProperty.of(pro -> pro.index(true))));
+    Property textProp = Property.of(builder -> builder.text(TextProperty.of(pro -> pro.index(true))));
+    Property textPropIk = Property.of(builder -> builder.text(TextProperty.of(pro -> pro.index(true).analyzer("ik_max_word"))));
+    Property textKeywordProp = new Property(new TextProperty.Builder().index(true).fields("raw", keywordProp).store(true).build());
+
+    public Map<String, Property> getPropertyMapWithNginxLog(Class<?> clazz) {
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            String name = field.getName();
+            Class<?> type = field.getType();
+        }
+
+        Map<String, Property> propertyMap = new HashMap<>();
+        propertyMap.put("id", keywordProp);
+        propertyMap.put("timeIso8601", dateProp);
+        propertyMap.put("remoteAddr", keywordProp);
+        propertyMap.put("request", textKeywordProp);
+        propertyMap.put("status", intProp);
+        propertyMap.put("requestMethod", keywordProp);
+        propertyMap.put("bodyBytesSent", intProp);
+        propertyMap.put("requestTime", doubleProp);
+        propertyMap.put("upstreamResponseTime", keywordProp);
+        propertyMap.put("upstreamAddr", keywordProp);
+        propertyMap.put("host", keywordProp);
+        propertyMap.put("url", textKeywordProp);
+        propertyMap.put("httpXForwardedFor", textKeywordProp);
+        propertyMap.put("httpReferer", textKeywordProp);
+        propertyMap.put("httpUserAgent", textKeywordProp);
+        return propertyMap;
+    }
+
+    public Map<String, Property> getPropertyMapByWenshu() {
+        Map<String, Property> propertyMap = new HashMap<>();
+        propertyMap.put("id", keywordProp);
+        propertyMap.put("originalUrl", keywordProp);
+        propertyMap.put("caseId", textPropIk);
+        propertyMap.put("caseName", textPropIk);
+        propertyMap.put("court", keywordProp);
+        propertyMap.put("region", keywordProp);
+        propertyMap.put("caseType", keywordProp);
+        propertyMap.put("caseTypeId", intProp);
+        propertyMap.put("judgmentDate", keywordProp);
+        propertyMap.put("judgeDate", keywordProp);
+        propertyMap.put("publicDate", keywordProp);
+        propertyMap.put("parties", textPropIk);
+        propertyMap.put("cause", textPropIk);
+        propertyMap.put("legalBasis", textPropIk);
+        propertyMap.put("fullText", textPropIk);
+        return propertyMap;
+    }
+
+    public Map<String, Property> getVideoTextPropertyMap() {
+        String className = VideoText.class.getSimpleName();
+        Map<String, Property> propertyMap = new HashMap<>();
+        propertyMap.put("id", keywordProp);
+        propertyMap.put("title", textPropIk);
+        propertyMap.put("description", textPropIk);
+        propertyMap.put("scope", intProp);
+        propertyMap.put("publishTime", longProp);
+        return propertyMap;
+    }
+}

+ 313 - 0
search/search-service/src/main/java/cn/reghao/tnb/search/app/es/QueryService.java

@@ -0,0 +1,313 @@
+package cn.reghao.tnb.search.app.es;
+
+import cn.reghao.tnb.content.api.constant.PostScope;
+import cn.reghao.tnb.search.app.model.vo.ElasticQuery;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.FieldValue;
+import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
+import co.elastic.clients.elasticsearch._types.query_dsl.Query;
+import co.elastic.clients.elasticsearch._types.query_dsl.QueryStringQuery;
+import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
+import co.elastic.clients.elasticsearch.core.SearchRequest;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.search.Highlight;
+import co.elastic.clients.elasticsearch.core.search.HighlightField;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author reghao
+ * @date 2025-03-12 11:16:48
+ */
+@Slf4j
+@Service
+public class QueryService<T> {
+    private final ElasticsearchClient esClient;
+
+    public QueryService(ElasticService elasticService) {
+        this.esClient = elasticService.getElasticsearchClient();
+    }
+
+    public Page<T> queryWithHighlight(ElasticQuery elasticQuery, Class<T> clazz) {
+        int pageSize = elasticQuery.getPageSize();
+        int pageNumber = elasticQuery.getPageNumber();
+        String indexName = elasticQuery.getIndexName();
+        List<String> queryFiledNames = elasticQuery.getQueryFiledNames();
+        List<String> highlightFieldNames = elasticQuery.getHighlightFiledNames();
+
+        String queryString = elasticQuery.getQueryString();
+
+        // 1.构建查询的对象
+        QueryStringQuery stringQuery = new QueryStringQuery.Builder()
+                // 查询的字段
+                .fields(queryFiledNames)
+                .query(queryString)
+                .build();
+        Query query = new Query.Builder()
+                .queryString(stringQuery)
+                .build();
+
+        Query query1 = new Query.Builder()
+                .bool(BoolQuery.of(b -> b
+                        .must(m -> m.term(t -> t.field("vip").value(FieldValue.of(""))))
+                        .must(m -> m.queryString(stringQuery))))
+                .build();
+
+        String highlightFieldName = highlightFieldNames.get(0);
+        // 2.高亮显示
+        HighlightField highlightField = new HighlightField.Builder()
+                .matchedFields(highlightFieldName)
+                .preTags("<span style=\"color:red\">")
+                .postTags("</span>")
+                .build();
+        Highlight highlight = new Highlight.Builder()
+                .fields(highlightFieldName, highlightField)
+                .requireFieldMatch(false)
+                .build();
+
+        // 3.搜索请求
+        int start = (pageNumber-1)*pageSize;
+        SearchRequest searchRequest = new SearchRequest.Builder()
+                .index(indexName)
+                .from(start)
+                .size(pageSize)
+                .query(query)
+                .highlight(highlight)
+                .build();
+        try {
+            SearchResponse<T> searchResponse = esClient.search(searchRequest, clazz);
+            HitsMetadata<T> hitsMetadata = searchResponse.hits();
+            long total = hitsMetadata.total().value();
+            List<T> list = hitsMetadata.hits().stream().map(mapper -> {
+                Map<String, List<String>> highlightMap = mapper.highlight();
+                String highlightStr = "";
+                if (!highlightMap.isEmpty()) {
+                    highlightStr = mapper.highlight()
+                            .get(highlightFieldName)
+                            .get(0);
+                }
+
+                T t = mapper.source();
+                try {
+                    Field field = clazz.getDeclaredField(highlightFieldName);
+                    field.setAccessible(true);
+
+                    if (t == null) {
+                        Object object = clazz.getDeclaredConstructors()[0].newInstance();
+                        field.set(object, highlightStr);
+                    } else {
+                        field.set(t, highlightStr);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                return mapper.source();
+            }).collect(Collectors.toList());
+
+            //return list;
+            PageRequest pageRequest = PageRequest.of(pageNumber-1, pageSize);
+            return new PageImpl<>(list, pageRequest, total);
+        } catch (IOException e) {
+            log.error("search By Query Highlight error", e);
+        }
+
+        return Page.empty();
+    }
+
+    public Page<T> queryByPage(String index, String queryString, int pn, int ps, Class<T> clazz) {
+        String highlightFieldName = "caseName";
+        List<String> otherFiledNames = List.of("caseName", "cause", "parties");
+
+        // 1.构建查询的对象
+        QueryStringQuery stringQuery = new QueryStringQuery.Builder()
+                // 查询的字段
+                .fields(highlightFieldName, otherFiledNames.toArray(new String[0]))
+                .query(queryString)
+                .build();
+        Query query = new Query.Builder()
+                .queryString(stringQuery)
+                .build();
+
+        // 2.高亮显示
+        HighlightField highlightField = new HighlightField.Builder()
+                .matchedFields(highlightFieldName)
+                .preTags("<span style=\"color:red\">")
+                .postTags("</span>")
+                .build();
+        Highlight highlight = new Highlight.Builder()
+                .fields(highlightFieldName, highlightField)
+                .requireFieldMatch(false)
+                .build();
+
+        // 3.搜索请求
+        int start = (pn-1)*ps;
+        SearchRequest searchRequest = new SearchRequest.Builder()
+                .index(index)
+                .from(start)
+                .size(ps)
+                //.query(query)
+                //.highlight(highlight)
+                .build();
+        try {
+            SearchResponse<T> searchResponse = esClient.search(searchRequest, clazz);
+            HitsMetadata<T> hitsMetadata = searchResponse.hits();
+            long total = hitsMetadata.total().value();
+            List<T> list = hitsMetadata.hits().stream().map(mapper -> {
+                Map<String, List<String>> highlightMap = mapper.highlight();
+                String highlightStr = "";
+                if (!highlightMap.isEmpty()) {
+                    highlightStr = mapper.highlight()
+                            .get(highlightFieldName)
+                            .get(0);
+                }
+
+                T t = mapper.source();
+                try {
+                    Field field = clazz.getDeclaredField(highlightFieldName);
+                    field.setAccessible(true);
+
+                    if (t == null) {
+                        Object object = clazz.getDeclaredConstructors()[0].newInstance();
+                        field.set(object, highlightStr);
+                    } else {
+                        field.set(t, highlightStr);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                return mapper.source();
+            }).collect(Collectors.toList());
+
+            //return list;
+            PageRequest pageRequest = PageRequest.of(pn-1, ps);
+            return new PageImpl<>(list, pageRequest, total);
+        } catch (IOException e) {
+            log.error("search By Query Highlight error", e);
+        }
+
+        return Page.empty();
+    }
+
+    public Page<T> queryWithHighlight(String indexName, String fieldName, String fieldValue, int pn, int ps, Class<T> clazz) {
+        List<Integer> scopes = List.of();
+        int scope1 = PostScope.PROTECT.getCode();
+        int scope2 = PostScope.PUBLIC.getCode();
+
+        QueryStringQuery stringQuery = new QueryStringQuery.Builder()
+                // 查询的字段
+                .fields(fieldName)
+                .query(fieldValue)
+                .build();
+
+        BoolQuery.Builder builder = new BoolQuery.Builder();
+        for (int scope : scopes) {
+            builder.should(m -> m.term(t -> t.field("scope").value(FieldValue.of(scope))));
+        }
+        builder.must(m -> m.queryString(stringQuery));
+        BoolQuery boolQuery = builder.build();
+        Query query = new Query.Builder()
+                .bool(boolQuery)
+                .build();
+
+        Query query1 = new Query.Builder()
+                .bool(BoolQuery.of(b -> b
+                        .must(m -> m.term(t -> t.field("scope").value(FieldValue.of(scope2))))
+                        .must(m -> m.queryString(stringQuery))))
+                .build();
+
+        String highlightFieldName = fieldName;
+        // 1.构建查询的对象
+        // 2.高亮显示
+        HighlightField highlightField = new HighlightField.Builder()
+                .matchedFields(highlightFieldName)
+                .preTags("<span style=\"color:red\">")
+                .postTags("</span>")
+                .build();
+        Highlight highlight = new Highlight.Builder()
+                .fields(highlightFieldName, highlightField)
+                .requireFieldMatch(false)
+                .build();
+
+        // 3.搜索请求
+        int start = (pn-1)*ps;
+        SearchRequest searchRequest = new SearchRequest.Builder()
+                .index(indexName)
+                .from(start)
+                .size(ps)
+                .query(query)
+                .highlight(highlight)
+                .build();
+        try {
+            SearchResponse<T> searchResponse = esClient.search(searchRequest, clazz);
+            HitsMetadata<T> hitsMetadata = searchResponse.hits();
+            long total = hitsMetadata.total().value();
+            List<T> list = hitsMetadata.hits().stream().map(mapper -> {
+                Map<String, List<String>> highlightMap = mapper.highlight();
+                String highlightStr = "";
+                if (!highlightMap.isEmpty()) {
+                    highlightStr = mapper.highlight()
+                            .get(highlightFieldName)
+                            .get(0);
+                }
+
+                T t = mapper.source();
+                try {
+                    Field field = clazz.getDeclaredField(highlightFieldName);
+                    field.setAccessible(true);
+
+                    if (t == null) {
+                        Object object = clazz.getDeclaredConstructors()[0].newInstance();
+                        field.set(object, highlightStr);
+                    } else {
+                        field.set(t, highlightStr);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                return mapper.source();
+            }).collect(Collectors.toList());
+
+            //return list;
+            PageRequest pageRequest = PageRequest.of(pn-1, ps);
+            return new PageImpl<>(list, pageRequest, total);
+        } catch (IOException e) {
+            log.error("search By Query Highlight error", e);
+        }
+
+        return Page.empty();
+    }
+
+    public T queryById(String indexName, String id, Class<T> clazz) {
+        Query query = TermQuery.of(t -> t
+                .field("id").value(FieldValue.of(id))
+        )._toQuery();
+
+        SearchRequest searchRequest = SearchRequest.of(s -> s
+                .index(indexName)
+                .query(query)
+        );
+
+        try {
+            SearchResponse<T> searchResponse = esClient.search(searchRequest, clazz);
+            List<Hit<T>> hits = searchResponse.hits().hits();
+            if (!hits.isEmpty()) {
+                return hits.get(0).source();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+}

+ 434 - 0
search/search-service/src/main/java/cn/reghao/tnb/search/app/es/SearchService.java

@@ -0,0 +1,434 @@
+package cn.reghao.tnb.search.app.es;
+
+import cn.reghao.tnb.search.app.log.model.NginxLog;
+import cn.reghao.tnb.search.app.model.po.VideoText;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.FieldValue;
+import co.elastic.clients.elasticsearch._types.ShardStatistics;
+import co.elastic.clients.elasticsearch._types.SortOrder;
+import co.elastic.clients.elasticsearch._types.aggregations.*;
+import co.elastic.clients.elasticsearch._types.query_dsl.*;
+import co.elastic.clients.elasticsearch.core.CountRequest;
+import co.elastic.clients.elasticsearch.core.CountResponse;
+import co.elastic.clients.elasticsearch.core.SearchRequest;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
+import co.elastic.clients.elasticsearch.core.search.TotalHits;
+import co.elastic.clients.elasticsearch.core.search.TotalHitsRelation;
+import co.elastic.clients.json.JsonData;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author reghao
+ * @date 2025-03-12 10:51:21
+ */
+@Slf4j
+@Service
+public class SearchService {
+    private final ElasticsearchClient esClient;
+
+    public SearchService(ElasticService elasticService) throws Exception {
+        this.esClient = elasticService.getElasticsearchClient();
+    }
+
+    public void searchOne(String indexName, String searchText) throws IOException {
+        SearchResponse<NginxLog> searchResponse = esClient.search(s -> s
+                .index(indexName)
+                // 搜索请求的查询部分(搜索请求也可以有其他组件,如聚合)
+                .query(q -> q
+                        // 在众多可用的查询变体中选择一个。我们在这里选择匹配查询(全文搜索)
+                        .match(t -> t
+                                .field("name")
+                                .query(searchText))), NginxLog.class);
+        TotalHits total = searchResponse.hits().total();
+        boolean isExactResult = total != null && total.relation() == TotalHitsRelation.Eq;
+        if (isExactResult) {
+            log.info("search has: {} results", total.value());
+        } else {
+            log.info("search more than : {} results", total.value());
+        }
+        List<Hit<NginxLog>> hits = searchResponse.hits().hits();
+        for (Hit<NginxLog> hit : hits) {
+            NginxLog source = hit.source();
+            log.info("Found result: {}", source);
+        }
+    }
+
+    public List<NginxLog> searchByPage(String indexName, int pn, String searchField, String searchText) throws IOException {
+        int ps = 100;
+        String sortField = "timeIso8601";
+        Query query = RangeQuery.of(r -> r.field("age").gte(JsonData.of(8)))._toQuery();
+        Query query11 = getOrQuery();
+        Query query1 = getAndQuery();
+        Query query12 = getTermQuery();
+
+        int start = (pn-1)*ps;
+        SearchRequest searchRequest = SearchRequest.of(s -> s
+                .index(indexName)
+                .query(query1)
+                .from(start)
+                .size(ps)
+                // 按 id 字段降序排列
+                .sort(f -> f.field(o -> o.field(sortField).order(SortOrder.Desc)))
+        );
+
+        SearchResponse<NginxLog> searchResponse = esClient.search(searchRequest, NginxLog.class);
+        /*List<Hit<NginxLog>> hits = searchResponse.hits().hits();
+        for (Hit<NginxLog> hit : hits) {
+            NginxLog product = hit.source();
+            log.info("search page result: {}", product);
+        }*/
+        return searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
+    }
+
+    public void searchAll(String indexName) throws IOException {
+        String fieldName1 = "url.raw";
+        String fieldValue1 = "/";
+
+        String fieldName2 = "requestMethod.raw";
+        String fieldValue2 = "POST";
+
+        String fieldName3 = "status";
+        String fieldValue3 = "200";
+
+        String fieldName4 = "vip";
+        Boolean fieldValue4 = false;
+
+        String keyword = "";
+        QueryStringQuery stringQuery = new QueryStringQuery.Builder()
+                // 查询的字段
+                .fields("title")
+                .query(keyword)
+                .build();
+        Query query = new Query.Builder()
+                .bool(BoolQuery.of(b -> b
+                        .must(m -> m.term(t -> t.field("vip").value(FieldValue.of(true))))
+                        .must(m -> m.queryString(stringQuery))))
+                .build();
+
+        SearchRequest searchRequest = SearchRequest.of(s -> s
+                .index(indexName)
+                .query(q -> q.bool(b -> b
+                                .must(m -> m.term(t -> t.field(fieldName4).value(FieldValue.of(fieldValue4))))
+                        //.must(m -> m.term(t -> t.field("name").value(FieldValue.of("test"))))
+                ))
+        );
+
+        String keyword1 = "隔壁";
+        SearchRequest searchRequest1 = SearchRequest.of(s -> s
+                .index(indexName)
+                .query(q -> q.bool(b -> b
+                        .must(m -> m.term(t -> t.field(fieldName4).value(FieldValue.of(fieldValue4))))
+                        .must(m -> m.queryString(t -> t.fields("title").query(keyword1)))
+                ))
+        );
+
+        SearchRequest searchRequest2 = SearchRequest.of(s -> s
+                .index(indexName)
+        );
+
+        //SearchResponse<NginxLog> searchResponse = esClient.search(searchRequest, NginxLog.class);
+        //List<NginxLog> list = searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
+        SearchResponse<VideoText> searchResponse = esClient.search(searchRequest2, VideoText.class);
+        List<VideoText> list = searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
+        System.out.println();
+    }
+
+    public void searchByField(String indexName, String fieldName, String fieldValue) throws IOException {
+        SearchRequest request = SearchRequest.of(s -> s
+                .index(indexName)
+                .query(query -> query.match(match -> match.field(fieldName).query(fieldValue)))
+        );
+
+        SearchResponse<NginxLog> searchResponse = esClient.search(request, NginxLog.class);
+        log.info("search result: {}", searchResponse);
+    }
+
+    public void search(String index) throws IOException {
+        String fieldName = "url";
+        String fieldValue = "/api";
+        // 构造查询条件
+        SearchRequest searchRequest = SearchRequest.of(s -> s.index(index)
+                /*.sort(s1 -> s1.field(f -> f.field(fieldName).order(SortOrder.Asc)))
+                .scroll(s2 -> s2.offset(0))*/
+                .from(0)
+                .size(500)
+                .query(q -> q.match(m -> m.field(fieldName).query(FieldValue.of(fieldValue))))
+        );
+
+        SearchResponse<NginxLog> searchResponse = esClient.search(searchRequest, NginxLog.class);
+        HitsMetadata<NginxLog> hitsMetadata = searchResponse.hits();
+        //得到总数
+        TotalHits totalHits = hitsMetadata.total();
+        Double maxScore = hitsMetadata.maxScore();
+        //拿到匹配的数据
+        List<Hit<NginxLog>> hits = hitsMetadata.hits();
+        //拿到_source中的数据
+        List<NginxLog> nginxLogs = hits.stream().map(Hit::source).collect(Collectors.toList());
+
+        //最大分数
+        System.out.println(searchResponse.maxScore());
+        //分片数
+        System.out.println(searchResponse.shards());
+        //是否超时
+        System.out.println(searchResponse.timedOut());
+    }
+
+    public void search() throws IOException {
+        Query query = getExistsQuery();
+
+        String index = "nginx_log";
+        SearchRequest searchRequest = new SearchRequest.Builder()
+                .index(index)
+                .query(query)
+                .build();
+        SearchResponse<NginxLog> searchResponse = esClient.search(searchRequest, NginxLog.class);
+        TotalHits totalHits = searchResponse.hits().total();
+        List<NginxLog> list = searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
+    }
+
+    public void count(String index) throws IOException {
+        Query query1 = getMatchQuery();
+        // remoteAddr 字段为空
+        Query query2 = BoolQuery.of(b ->
+                b.mustNot(m -> m.exists(t -> t.field("remoteAddr"))
+                ))._toQuery();
+        // remoteAddr 字段不为空
+        Query query3 = BoolQuery.of(b ->
+                b.must(m -> m.exists(t -> t.field("remoteAddr"))
+                ))._toQuery();
+
+        CountRequest countRequest = CountRequest.of(s -> s
+                        .index(index)
+                //.query(query3)
+        );
+
+        CountResponse countResponse = esClient.count(countRequest);
+        long total = countResponse.count();
+        ShardStatistics shardStatistics = countResponse.shards();
+        System.out.println("total -> " + total);
+    }
+
+    /**
+     * 等值查询
+     *
+     * @param
+     * @return
+     * @date 2025-03-12 13:45:17
+     */
+    public Query getTermQuery() {
+        String fieldName1 = "url";
+        String fieldValue1 = "/datareceive/ReceiveData/SendContentResult\"";
+
+        String fieldName2 = "requestMethod";
+        String fieldValue2 = "POST";
+
+        Query query = TermQuery.of(t -> t
+                .field(fieldName1).value(FieldValue.of(fieldValue1))
+        )._toQuery();
+        return query;
+    }
+
+    /**
+     * MatchQuery 搜索时, 首先会解析查询字符串, 进行分词,然后查询
+     * TermQuery 搜索时, 会根据输入的查询内容进行搜索, 并不会解析查询内容,对它分词
+     *
+     * @param
+     * @return
+     * @date 2025-03-12 15:04:22
+     */
+    public Query getMatchQuery() {
+        String fieldName = "host";
+        String searchText = "api.iquizoo.com";
+
+        Query query = MatchQuery.of(m -> m.field(fieldName).query(searchText))._toQuery();
+        return query;
+    }
+
+    /**
+     * 多值查询, 相当于 SQL 语句中的 in 查询
+     *
+     * @param
+     * @return
+     * @date 2025-03-12 14:12:38
+     */
+    public Query getTermsQuery() {
+        String fieldName = "status";
+        List<FieldValue> fieldValueList = List.of(FieldValue.of("401"), FieldValue.of("500"));
+
+        Query query = TermsQuery.of(t ->
+                t.field(fieldName).terms(TermsQueryField.of(q -> q.value(fieldValueList)))
+        )._toQuery();
+        return query;
+    }
+
+    /**
+     * 或查询, 相当于 SQL 查询
+     * SELECT * FROM test1 where (uid = 1 or uid =2) and phone = 12345678919
+     *
+     * @param
+     * @return
+     * @date 2025-03-12 13:46:26
+     */
+    public Query getOrQuery() {
+        Query query = BoolQuery.of(b ->
+                b.should(m -> m.term(t -> t.field("status").value(FieldValue.of(401))))
+                        .should(m -> m.term(t -> t.field("status").value(FieldValue.of(403))))
+                        .must(m -> m.term(t -> t.field("host").value(FieldValue.of("api.iquizoo.com"))))
+        )._toQuery();
+        return query;
+    }
+
+    public Query getAndQuery() {
+        String fieldName1 = "url.raw";
+        String fieldValue1 = "/datareceive/ReceiveData/SendContentResult";
+
+        String fieldName2 = "requestMethod";
+        String fieldValue2 = "POST";
+
+        Query query = BoolQuery.of(b -> b
+                .must(m -> m.term(t -> t.field(fieldName1).value(FieldValue.of(fieldValue1))))
+                .must(m -> m.term(t -> t.field(fieldName2).value(FieldValue.of(fieldValue2))))
+        )._toQuery();
+        return query;
+    }
+
+    /**
+     * 模糊查询, 相当于 SQL 语句中的 like 查询
+     *
+     * @param
+     * @return
+     * @date 2025-03-12 14:11:45
+     */
+    public Query getWildcardQuery() {
+        Query query = BoolQuery.of(b ->
+                b.must(m -> m.wildcard(t -> t.field("url").value("*result*")))
+        )._toQuery();
+        return query;
+    }
+
+    /**
+     * 存在查询, 相当于 SQL 语句中的 exist
+     *
+     * @param
+     * @return
+     * @date 2025-03-12 14:20:26
+     */
+    public Query getExistsQuery() {
+        // 判断字段是否存在
+        Query query = ExistsQuery.of(t -> t.field("host"))._toQuery();
+        return query;
+    }
+
+    /**
+     * 范围查询, 相当于 SQL 语句中的 > 和 <
+     * gt 是大于,lt 是小于,gte 是大于等于,lte 是小于等于
+     *
+     * @param
+     * @return
+     * @date 2025-03-12 14:21:26
+     */
+    public Query getRangeQuery() {
+        int status1 = 404;
+        int status2 = 600;
+        Query query = RangeQuery.of(t -> t.field("status").gte(JsonData.of(status1)).lte(JsonData.of(status2)))._toQuery();
+        return query;
+    }
+
+    /**
+     * 正则查询
+     *
+     * @param
+     * @return
+     * @date 2025-03-12 14:22:18
+     */
+    public Query getRegexpQuery() {
+        Query query = RegexpQuery.of(t -> t.field("host").value("api.*"))._toQuery();
+        return query;
+    }
+
+    public Query getQuery() {
+        MatchAllQuery.of(m -> m.queryName("host"))._toQuery();
+        // 一般情况下有一个单词错误的情况下,fuzzy 查询可以找到另一个近似的词来代替,主要有以下场景:
+        //
+        //修改一个单词,如:box--->fox。
+        //移除一个单词,如:black-->lack。
+        //插入一个单词,如:sic-->sick。
+        //转换两个单词顺序,如:act-->cat。
+        FuzzyQuery.of(f -> f.field("host").value("lonel"));
+        // 通过指定字段的前缀进行查询
+        Query query = PrefixQuery.of(p -> p.field("host").value("api"))._toQuery();
+        return query;
+    }
+
+    /**
+     * 聚合查询, 相当于 SQL 的 group by
+     *
+     * @param
+     * @return
+     * @date 2025-03-12 15:06:48
+     */
+    public void aggregate(String index) throws Exception {
+        String aggField1 = "status";
+        String aggField2 = "remoteAddr";
+        String aggField3 = "httpUserAgent.raw";
+        String aggField4 = "url.raw";
+        String aggField5 = "timeIso8601";
+
+        String fieldName1 = "url.raw";
+        String fieldValue1 = "/base/Device/PageList";
+
+        SearchRequest searchRequest = new SearchRequest.Builder()
+                .index(index)
+                .query(q -> q.bool(b -> b.must(m -> m.term(t -> t.field(fieldName1).value(FieldValue.of(fieldValue1))))))
+                .size(10000)
+                .aggregations("first_agg", a->a.terms(t->t.field(aggField5).size(65535)))
+                //.aggregations("agg1", a->a.histogram(t->t.field("httpUserAgent").interval(50.0)))
+                //.aggregations("agg1", a->a.sum(t->t.field("httpUserAgent")))
+                //.aggregations("second_agg", a->a.avg(t->t.field("status")))
+                .build();
+
+        SearchResponse<NginxLog> searchResponse = esClient.search(searchRequest, NginxLog.class);
+        TotalHits totalHits = searchResponse.hits().total();
+        Map<String, Aggregate> resultMap = searchResponse.aggregations();
+
+        List<Long> countList = new ArrayList<>();
+        resultMap.forEach((k, v) -> {
+            Object value = v._get();
+            if (value instanceof StringTermsAggregate) {
+                StringTermsAggregate terms = (StringTermsAggregate) value;
+                List<StringTermsBucket> list = terms.buckets().array();
+                list.forEach(bucket -> {
+                    String groupKey = (String) bucket.key()._get();
+                    long count = bucket.docCount();
+                    countList.add(count);
+                    //System.out.println(groupKey + " : " + IpTool.getLocation(groupKey) + " -> " + count);
+                    System.out.println(groupKey + " : " + count);
+                });
+                System.out.println("bucket size = " + list.size());
+            } else if (value instanceof LongTermsAggregate) {
+                LongTermsAggregate terms = (LongTermsAggregate) value;
+                List<LongTermsBucket> list = terms.buckets().array();
+                list.forEach(bucket -> {
+                    String groupKey = bucket.key();
+                    String groupKeyStr = bucket.keyAsString();
+                    long count = bucket.docCount();
+                    countList.add(count);
+                    System.out.println(groupKeyStr + " : " + count);
+                });
+            } else {
+                System.out.println(value);
+            }
+        });
+
+        System.out.println("total = " + countList.stream().mapToLong(Long::longValue).sum());
+    }
+}

+ 73 - 0
search/search-service/src/main/java/cn/reghao/tnb/search/app/es/VideoTextDocument.java

@@ -0,0 +1,73 @@
+package cn.reghao.tnb.search.app.es;
+
+import cn.reghao.tnb.search.app.model.po.VideoText;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.Result;
+import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery;
+import co.elastic.clients.elasticsearch.core.*;
+import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2025-03-12 10:45:58
+ */
+@Slf4j
+@Service
+public class VideoTextDocument {
+    private final String indexName = "video_text";
+    private final ElasticsearchClient esClient;
+
+    public VideoTextDocument(ElasticService elasticService) {
+        this.esClient = elasticService.getElasticsearchClient();
+    }
+
+    public void addOrUpdateVideoText(VideoText videoText) {
+        try {
+            IndexResponse response = esClient.index(i -> i.index(indexName).id(videoText.getId()).document(videoText));
+            Result result = response.result();
+            log.info(result.jsonValue());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void batchAddVideoText(List<VideoText> videoTextList) {
+        List<BulkOperation> bulkOperations = new ArrayList<>();
+        videoTextList.forEach(p -> bulkOperations.add(BulkOperation.of(b -> b.index(c -> c.id(p.getId()).document(p)))));
+        try {
+            BulkResponse bulkResponse = esClient.bulk(s -> s.index(indexName).operations(bulkOperations));
+            bulkResponse.items().forEach(b -> log.info("bulk response result = {}", b.result()));
+            log.error("bulk response.error() = {}", bulkResponse.errors());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void deleteVideoText(String id) {
+        try {
+            DeleteResponse response = esClient.delete(s -> s.index(indexName).id(id));
+            Result result = response.result();
+            log.info(result.jsonValue());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void deleteAllDocument() {
+        try {
+            DeleteByQueryRequest deleteByQueryRequest = DeleteByQueryRequest.of(s -> s.index(indexName)
+                    .query(m -> m.matchAll(new MatchAllQuery.Builder().build()))
+            );
+            DeleteByQueryResponse deleteByQueryResponse = esClient.deleteByQuery(deleteByQueryRequest);
+            log.info("del doc result: {}", deleteByQueryResponse.total());
+        } catch (IOException e) {
+            log.error("del doc failed, error: ", e);
+        }
+    }
+}

+ 165 - 0
search/search-service/src/main/java/cn/reghao/tnb/search/app/es/VideoTextQuery.java

@@ -0,0 +1,165 @@
+package cn.reghao.tnb.search.app.es;
+
+import cn.reghao.tnb.search.app.model.po.VideoText;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.FieldValue;
+import co.elastic.clients.elasticsearch._types.SortOrder;
+import co.elastic.clients.elasticsearch._types.query_dsl.*;
+import co.elastic.clients.elasticsearch.core.SearchRequest;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.search.Highlight;
+import co.elastic.clients.elasticsearch.core.search.HighlightField;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
+import co.elastic.clients.json.JsonData;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author reghao
+ * @date 2025-03-12 11:16:48
+ */
+@Slf4j
+@Service
+public class VideoTextQuery {
+    private final String indexName = "video_text";
+    private final int pageSize = 12;
+    private final ElasticsearchClient esClient;
+
+    public VideoTextQuery(ElasticService elasticService) {
+        this.esClient = elasticService.getElasticsearchClient();
+    }
+
+    public Page<VideoText> queryWithHighlight(String keyword, List<Integer> scopes, int pageNumber) {
+        if (pageNumber > 100) {
+            pageNumber = 100;
+        }
+
+        String fieldName = "title";
+        String fieldValue = keyword;
+        QueryStringQuery stringQuery = new QueryStringQuery.Builder()
+                // 查询的字段
+                .fields(fieldName)
+                .query(fieldValue)
+                .build();
+
+        BoolQuery.Builder builder = new BoolQuery.Builder();
+        for (int scope : scopes) {
+            // filter 表示 list 中的值全部都要满足
+            //builder.filter(m -> m.term(t -> t.field("scope").value(FieldValue.of(scope))));
+            builder.should(m -> m.term(t -> t.field("scope").value(FieldValue.of(scope))));
+        }
+        builder.minimumShouldMatch("1");
+        builder.must(m -> m.queryString(stringQuery));
+        BoolQuery boolQuery = builder.build();
+        Query query = new Query.Builder()
+                .bool(boolQuery)
+                .build();
+        Query query1 = new Query.Builder()
+                .bool(BoolQuery.of(b -> b
+                        .must(m -> m.term(t -> t.field("scope").value(FieldValue.of(1))))
+                        .must(m -> m.queryString(stringQuery))))
+                .build();
+
+        // 1.构建查询的对象
+        // 2.高亮显示
+        String highlightFieldName = fieldName;
+        HighlightField highlightField = new HighlightField.Builder()
+                .matchedFields(highlightFieldName)
+                .preTags("<span style=\"color:red\">")
+                .postTags("</span>")
+                .build();
+        Highlight highlight = new Highlight.Builder()
+                .fields(highlightFieldName, highlightField)
+                .requireFieldMatch(false)
+                .build();
+
+        String sortField = "publishTime";
+        // 3.搜索请求
+        int start = (pageNumber-1)*pageSize;
+        SearchRequest searchRequest = new SearchRequest.Builder()
+                .index(indexName)
+                .from(start)
+                .size(pageSize)
+                .query(query)
+                .highlight(highlight)
+                //.sort(f -> f.field(o -> o.field(sortField).order(SortOrder.Desc)))
+                .build();
+        try {
+            SearchResponse<VideoText> searchResponse = esClient.search(searchRequest, VideoText.class);
+            HitsMetadata<VideoText> hitsMetadata = searchResponse.hits();
+            long total = hitsMetadata.total().value();
+            List<VideoText> list = hitsMetadata.hits().stream().map(hit -> {
+                double score = hit.score() != null ? hit.score() : 0.0;
+                log.info("score -> {}", score);
+                Map<String, List<String>> highlightMap = hit.highlight();
+                String highlightStr = "";
+                if (!highlightMap.isEmpty()) {
+                    highlightStr = hit.highlight()
+                            .get(highlightFieldName)
+                            .get(0);
+                }
+
+                VideoText videoText = hit.source();
+                try {
+                    Class<?> clazz = videoText.getClass();
+                    Field field = clazz.getDeclaredField(highlightFieldName);
+                    field.setAccessible(true);
+                    field.set(videoText, highlightStr);
+                } catch (Exception e) {
+                    log.error(e.getMessage());
+                }
+                return hit.source();
+            }).collect(Collectors.toList());
+
+            if (total > pageSize*100L) {
+                total = pageSize*100L;
+            }
+
+            return new PageImpl<>(list, PageRequest.of(pageNumber-1, pageSize), total);
+        } catch (IOException e) {
+            log.error("search By Query Highlight error: {}", e.getMessage());
+        }
+
+        return Page.empty();
+    }
+
+    public List<VideoText> queryByPage(int pageNumber) {
+        Query rangeQuery = RangeQuery.of(r -> r.field("age").gte(JsonData.of(8)))._toQuery();
+        String fieldName = "host";
+        String searchText = "api.iquizoo.com";
+        Query matchQuery = MatchQuery.of(m -> m.field(fieldName).query(searchText))._toQuery();
+        Query matchAllQuery = new Query.Builder()
+                .matchAll(new MatchAllQuery.Builder().build())
+                .build();
+
+        String sortField = "publishTime";
+        int start = (pageNumber-1)*pageSize;
+        SearchRequest searchRequest = SearchRequest.of(s -> s
+                .index(indexName)
+                .query(matchAllQuery)
+                .from(start)
+                .size(pageSize)
+                // 按指定字段降序排列
+                .sort(f -> f.field(o -> o.field(sortField).order(SortOrder.Desc)))
+        );
+
+        try {
+            SearchResponse<VideoText> searchResponse = esClient.search(searchRequest, VideoText.class);
+            return searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("{}", e.getMessage());
+        }
+        return Collections.emptyList();
+    }
+}

+ 26 - 0
search/search-service/src/main/java/cn/reghao/tnb/search/app/es/WenshuSearch.java

@@ -0,0 +1,26 @@
+package cn.reghao.tnb.search.app.es;
+
+import cn.reghao.tnb.search.app.model.po.Wenshu;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author reghao
+ * @date 2025-03-17 09:49:48
+ */
+@Service
+public class WenshuSearch {
+    private QueryService<Wenshu> queryService;
+    private String index = "wenshu";
+
+    public WenshuSearch(ElasticService elasticService) {
+        this.queryService = new QueryService<>(elasticService);
+    }
+
+    public Page<Wenshu> search(String keyword, Pageable pageable) {
+        int pn = pageable.getPageNumber()+1;
+        int ps = pageable.getPageSize();
+        return Page.empty();
+    }
+}

+ 12 - 12
search/search-service/src/main/java/cn/reghao/tnb/search/app/log/NginxLogDocument.java

@@ -1,14 +1,14 @@
 package cn.reghao.tnb.search.app.log;
 
 import cn.reghao.jutil.jdk.id.SnowFlake;
-//import cn.reghao.tnb.search.app.es.ElasticService;
+import cn.reghao.tnb.search.app.es.ElasticService;
 import cn.reghao.tnb.search.app.log.model.NginxLog;
-//import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
-//import co.elastic.clients.elasticsearch.ElasticsearchClient;
-//import co.elastic.clients.elasticsearch._types.Result;
-//import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery;
-//import co.elastic.clients.elasticsearch.core.*;
-//import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
+import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.Result;
+import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery;
+import co.elastic.clients.elasticsearch.core.*;
+import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
@@ -21,9 +21,9 @@ import java.util.List;
  * @date 2025-03-12 10:45:58
  */
 @Slf4j
-//@Service
+@Service
 public class NginxLogDocument {
-    /*private final ElasticsearchClient esClient;
+    private final ElasticsearchClient esClient;
     private final SnowFlake idGenerator;
 
     public NginxLogDocument(ElasticService elasticService) {
@@ -73,13 +73,13 @@ public class NginxLogDocument {
         }
     }
 
-    *//**
+    /*
      * 删除索引下的所有文档
      *
      * @param
      * @return
      * @date 2025-03-13 16:26:20
-     *//*
+     */
     public void deleteAllDocument(String indexName) {
         try {
             DeleteByQueryRequest deleteByQueryRequest = DeleteByQueryRequest.of(s -> s.index(indexName)
@@ -110,5 +110,5 @@ public class NginxLogDocument {
             System.out.println(success.index());
             System.out.println(success.version());
         });
-    }*/
+    }
 }

+ 41 - 41
search/search-service/src/main/java/cn/reghao/tnb/search/app/log/NginxLogSearch.java

@@ -1,22 +1,22 @@
 package cn.reghao.tnb.search.app.log;
 
-//import cn.reghao.tnb.search.app.es.ElasticService;
+import cn.reghao.tnb.search.app.es.ElasticService;
 import cn.reghao.tnb.search.app.log.model.NginxLog;
-//import co.elastic.clients.elasticsearch.ElasticsearchClient;
-//import co.elastic.clients.elasticsearch._types.FieldValue;
-//import co.elastic.clients.elasticsearch._types.ShardStatistics;
-//import co.elastic.clients.elasticsearch._types.SortOrder;
-//import co.elastic.clients.elasticsearch._types.aggregations.*;
-//import co.elastic.clients.elasticsearch._types.query_dsl.*;
-//import co.elastic.clients.elasticsearch.core.CountRequest;
-//import co.elastic.clients.elasticsearch.core.CountResponse;
-//import co.elastic.clients.elasticsearch.core.SearchRequest;
-//import co.elastic.clients.elasticsearch.core.SearchResponse;
-//import co.elastic.clients.elasticsearch.core.search.Hit;
-//import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
-//import co.elastic.clients.elasticsearch.core.search.TotalHits;
-//import co.elastic.clients.elasticsearch.core.search.TotalHitsRelation;
-//import co.elastic.clients.json.JsonData;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.FieldValue;
+import co.elastic.clients.elasticsearch._types.ShardStatistics;
+import co.elastic.clients.elasticsearch._types.SortOrder;
+import co.elastic.clients.elasticsearch._types.aggregations.*;
+import co.elastic.clients.elasticsearch._types.query_dsl.*;
+import co.elastic.clients.elasticsearch.core.CountRequest;
+import co.elastic.clients.elasticsearch.core.CountResponse;
+import co.elastic.clients.elasticsearch.core.SearchRequest;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
+import co.elastic.clients.elasticsearch.core.search.TotalHits;
+import co.elastic.clients.elasticsearch.core.search.TotalHitsRelation;
+import co.elastic.clients.json.JsonData;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
@@ -31,9 +31,9 @@ import java.util.stream.Collectors;
  * @date 2025-03-12 10:51:21
  */
 @Slf4j
-//@Service
+@Service
 public class NginxLogSearch {
-    /*private final ElasticsearchClient esClient;
+    private final ElasticsearchClient esClient;
     private final String indexName = "nginx_log";
 
     public NginxLogSearch(ElasticService elasticService) throws Exception {
@@ -82,11 +82,11 @@ public class NginxLogSearch {
         );
 
         SearchResponse<NginxLog> searchResponse = esClient.search(searchRequest, NginxLog.class);
-        *//*List<Hit<NginxLog>> hits = searchResponse.hits().hits();
+        /*List<Hit<NginxLog>> hits = searchResponse.hits().hits();
         for (Hit<NginxLog> hit : hits) {
             NginxLog product = hit.source();
             log.info("search page result: {}", product);
-        }*//*
+        }*/
         return searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
     }
 
@@ -120,8 +120,8 @@ public class NginxLogSearch {
         String fieldValue = "/api";
         // 构造查询条件
         SearchRequest searchRequest = SearchRequest.of(s -> s.index(indexName)
-                *//*.sort(s1 -> s1.field(f -> f.field(fieldName).order(SortOrder.Asc)))
-                .scroll(s2 -> s2.offset(0))*//*
+                /*.sort(s1 -> s1.field(f -> f.field(fieldName).order(SortOrder.Asc)))
+                .scroll(s2 -> s2.offset(0))*/
                 .from(0)
                 .size(500)
                 .query(q -> q.match(m -> m.field(fieldName).query(FieldValue.of(fieldValue))))
@@ -178,13 +178,13 @@ public class NginxLogSearch {
         System.out.println("total -> " + total);
     }
 
-    *//**
+    /**
      * 等值查询
      *
      * @param
      * @return
      * @date 2025-03-12 13:45:17
-     *//*
+     */
     public Query getTermQuery() {
         String fieldName1 = "url";
         String fieldValue1 = "/datareceive/ReceiveData/SendContentResult\"";
@@ -198,14 +198,14 @@ public class NginxLogSearch {
         return query;
     }
 
-    *//**
+    /**
      * MatchQuery 搜索时, 首先会解析查询字符串, 进行分词,然后查询
      * TermQuery 搜索时, 会根据输入的查询内容进行搜索, 并不会解析查询内容,对它分词
      *
      * @param
      * @return
      * @date 2025-03-12 15:04:22
-     *//*
+     */
     public Query getMatchQuery() {
         String fieldName = "host";
         String searchText = "api.iquizoo.com";
@@ -214,13 +214,13 @@ public class NginxLogSearch {
         return query;
     }
 
-    *//**
+    /**
      * 多值查询, 相当于 SQL 语句中的 in 查询
      *
      * @param
      * @return
      * @date 2025-03-12 14:12:38
-     *//*
+     */
     public Query getTermsQuery() {
         String fieldName = "status";
         List<FieldValue> fieldValueList = List.of(FieldValue.of("401"), FieldValue.of("500"));
@@ -231,14 +231,14 @@ public class NginxLogSearch {
         return query;
     }
 
-    *//**
+    /**
      * 或查询, 相当于 SQL 查询
      * SELECT * FROM test1 where (uid = 1 or uid =2) and phone = 12345678919
      *
      * @param
      * @return
      * @date 2025-03-12 13:46:26
-     *//*
+     */
     public Query getOrQuery() {
         Query query = BoolQuery.of(b ->
                 b.should(m -> m.term(t -> t.field("status").value(FieldValue.of(401))))
@@ -262,13 +262,13 @@ public class NginxLogSearch {
         return query;
     }
 
-    *//**
+    /**
      * 模糊查询, 相当于 SQL 语句中的 like 查询
      *
      * @param
      * @return
      * @date 2025-03-12 14:11:45
-     *//*
+     */
     public Query getWildcardQuery() {
         Query query = BoolQuery.of(b ->
                 b.must(m -> m.wildcard(t -> t.field("url").value("*result*")))
@@ -276,27 +276,27 @@ public class NginxLogSearch {
         return query;
     }
 
-    *//**
+    /**
      * 存在查询, 相当于 SQL 语句中的 exist
      *
      * @param
      * @return
      * @date 2025-03-12 14:20:26
-     *//*
+     */
     public Query getExistsQuery() {
         // 判断字段是否存在
         Query query = ExistsQuery.of(t -> t.field("host"))._toQuery();
         return query;
     }
 
-    *//**
+    /**
      * 范围查询, 相当于 SQL 语句中的 > 和 <
      * gt 是大于,lt 是小于,gte 是大于等于,lte 是小于等于
      *
      * @param
      * @return
      * @date 2025-03-12 14:21:26
-     *//*
+     */
     public Query getRangeQuery() {
         int status1 = 404;
         int status2 = 600;
@@ -304,13 +304,13 @@ public class NginxLogSearch {
         return query;
     }
 
-    *//**
+    /**
      * 正则查询
      *
      * @param
      * @return
      * @date 2025-03-12 14:22:18
-     *//*
+     */
     public Query getRegexpQuery() {
         Query query = RegexpQuery.of(t -> t.field("host").value("api.*"))._toQuery();
         return query;
@@ -330,13 +330,13 @@ public class NginxLogSearch {
         return query;
     }
 
-    *//**
+    /**
      * 聚合查询, 相当于 SQL 的 group by
      *
      * @param
      * @return
      * @date 2025-03-12 15:06:48
-     *//*
+     */
     public void aggregate() throws Exception {
         String aggField1 = "status";
         String aggField2 = "remoteAddr";
@@ -390,5 +390,5 @@ public class NginxLogSearch {
             }
         });
         System.out.println("total = " + countList.stream().mapToLong(Long::longValue).sum());
-    }*/
+    }
 }

+ 8 - 8
search/search-service/src/main/java/cn/reghao/tnb/search/app/log/NginxLogService.java

@@ -1,12 +1,12 @@
 package cn.reghao.tnb.search.app.log;
 
 import cn.reghao.tnb.search.app.config.ElasticProperties;
-//import cn.reghao.tnb.search.app.es.*;
+import cn.reghao.tnb.search.app.es.*;
 import cn.reghao.tnb.search.app.log.model.NginxLog;
 import cn.reghao.jutil.jdk.converter.DateTimeConverter;
 import cn.reghao.jutil.jdk.serializer.JsonConverter;
 import cn.reghao.jutil.jdk.text.TextFile;
-//import co.elastic.clients.elasticsearch._types.mapping.Property;
+import co.elastic.clients.elasticsearch._types.mapping.Property;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
@@ -22,7 +22,7 @@ import java.util.*;
  * @date 2023-11-08 10:08:02
  */
 @Slf4j
-//@Service
+@Service
 public class NginxLogService {
     public List getChartData() throws ParseException {
         TextFile textFile = new TextFile();
@@ -177,7 +177,7 @@ public class NginxLogService {
     }
 
     public static void main(String[] args) throws Exception {
-        /*ElasticProperties elasticProperties = new ElasticProperties();
+        ElasticProperties elasticProperties = new ElasticProperties();
         ElasticService elasticService = new ElasticService(elasticProperties);
         IndexService indexService = new IndexService(elasticService);
         MappingService mappingService = new MappingService();
@@ -201,12 +201,12 @@ public class NginxLogService {
 //        deleteAll(index);
 //        searchService.aggregate(index);
 
-        int pn = 1;
-        *//*while (pn < 100) {
+        /*int pn = 1;
+        while (pn < 100) {
             List<NginxLog> list = searchService.searchByPage(index, pn, "", "");
             System.out.println();
             pn++;
-        }*//*
+        }*/
 
         //searchService.searchAll(index);
 //        indexService.updateMapping(index);
@@ -215,6 +215,6 @@ public class NginxLogService {
         //List<NginxLog> list = queryService.queryWithHighlight(index, queryString, pn, ps, NginxLog.class);
         //searchService.searchAll(index);
         //nginxLogSearch.aggregate();
-        //searchService.count(index);*/
+        //searchService.count(index);
     }
 }

+ 14 - 13
search/search-service/src/main/java/cn/reghao/tnb/search/app/rpc/DataSearchServiceImpl.java

@@ -3,9 +3,8 @@ package cn.reghao.tnb.search.app.rpc;
 import cn.reghao.tnb.search.api.dto.IndexCount;
 import cn.reghao.tnb.search.api.dto.VideoSummary;
 import cn.reghao.tnb.search.api.iface.DataSearchService;
-//import cn.reghao.tnb.search.app.es.ElasticService;
-//import cn.reghao.tnb.search.app.es.VideoTextDocument;
-//import cn.reghao.tnb.search.app.es.VideoTextQuery;
+import cn.reghao.tnb.search.app.es.VideoTextDocument;
+import cn.reghao.tnb.search.app.es.VideoTextQuery;
 import cn.reghao.tnb.search.app.lucene.LuceneDocument;
 import cn.reghao.tnb.search.app.lucene.LuceneIndex;
 import cn.reghao.tnb.search.app.lucene.LuceneSearch;
@@ -32,17 +31,20 @@ import java.util.stream.Collectors;
 public class DataSearchServiceImpl implements DataSearchService {
     private String indexName = "video_text";
     private int pageSize = 12;
-    //private final VideoTextDocument videoTextDocument;
     private LuceneDocument luceneDocument;
     private final LuceneIndex luceneIndex;
     private LuceneSearch luceneSearch;
-    //private final VideoTextQuery videoTextQuery;
+    private final VideoTextDocument videoTextDocument;
+    private final VideoTextQuery videoTextQuery;
     private boolean luceneEnabled = true;
 
-    public DataSearchServiceImpl(LuceneDocument luceneDocument, LuceneIndex luceneIndex, LuceneSearch luceneSearch) {
+    public DataSearchServiceImpl(LuceneDocument luceneDocument, LuceneIndex luceneIndex, LuceneSearch luceneSearch,
+                                 VideoTextDocument videoTextDocument, VideoTextQuery videoTextQuery) {
         this.luceneDocument = luceneDocument;
         this.luceneIndex = luceneIndex;
         this.luceneSearch = luceneSearch;
+        this.videoTextDocument = videoTextDocument;
+        this.videoTextQuery = videoTextQuery;
     }
 
     @Override
@@ -56,7 +58,7 @@ public class DataSearchServiceImpl implements DataSearchService {
                 e.printStackTrace();
             }
         } else {
-            //videoTextDocument.addOrUpdateVideoText(videoText);
+            videoTextDocument.addOrUpdateVideoText(videoText);
         }
     }
 
@@ -75,7 +77,7 @@ public class DataSearchServiceImpl implements DataSearchService {
                 e.printStackTrace();
             }
         } else {
-            //videoTextDocument.batchAddVideoText(videoTextList);
+            videoTextDocument.batchAddVideoText(videoTextList);
         }
     }
 
@@ -90,7 +92,7 @@ public class DataSearchServiceImpl implements DataSearchService {
                 e.printStackTrace();
             }
         } else {
-            //videoTextDocument.addOrUpdateVideoText(videoText);
+            videoTextDocument.addOrUpdateVideoText(videoText);
         }
     }
 
@@ -103,7 +105,7 @@ public class DataSearchServiceImpl implements DataSearchService {
                 e.printStackTrace();
             }
         } else {
-            //videoTextDocument.deleteVideoText(videoId);
+            videoTextDocument.deleteVideoText(videoId);
         }
     }
 
@@ -125,11 +127,10 @@ public class DataSearchServiceImpl implements DataSearchService {
             List<VideoSummary> list = pageList1.getList().stream().map(this::getVideoSummary).collect(Collectors.toList());
             pageList = PageList.pageList(pageNumber, pageSize, (int) pageList1.getTotalSize(), list);
         } else {
-            /*Page<VideoText> page = videoTextQuery.queryWithHighlight(keyword, scopes, pageNumber);
+            Page<VideoText> page = videoTextQuery.queryWithHighlight(keyword, scopes, pageNumber);
             int pageSize = page.getSize();
             List<VideoSummary> list = page.stream().map(this::getVideoSummary).collect(Collectors.toList());
-            pageList = PageList.pageList(pageNumber, pageSize, (int) page.getTotalElements(), list);*/
-            pageList = PageList.empty();
+            pageList = PageList.pageList(pageNumber, pageSize, (int) page.getTotalElements(), list);
         }
 
         return pageList;

+ 8 - 7
search/search-service/src/main/java/cn/reghao/tnb/search/app/service/WenshuSearchService.java

@@ -2,7 +2,7 @@ package cn.reghao.tnb.search.app.service;
 
 import cn.reghao.jutil.jdk.db.PageList;
 import cn.reghao.tnb.search.app.db.repository.WenshuDocRepository;
-//import cn.reghao.tnb.search.app.es.QueryService;
+import cn.reghao.tnb.search.app.es.QueryService;
 import cn.reghao.tnb.search.app.lucene.LuceneSearch;
 import cn.reghao.tnb.search.app.model.po.Wenshu;
 import cn.reghao.tnb.search.app.model.po.WenshuDoc;
@@ -21,17 +21,19 @@ import java.util.stream.Collectors;
  * @author reghao
  * @date 2025-08-21 15:22:31
  */
-//@Service
+@Service
 public class WenshuSearchService {
     private int pageSize = 10;
     private String indexName = "wenshu_lucene";
     private LuceneSearch luceneSearch;
     private WenshuDocRepository wenshuDocRepository;
-    //private final QueryService<Wenshu> queryService;
+    private final QueryService<Wenshu> queryService;
 
-    public WenshuSearchService(LuceneSearch luceneSearch, WenshuDocRepository wenshuDocRepository) {
+    public WenshuSearchService(LuceneSearch luceneSearch, WenshuDocRepository wenshuDocRepository,
+                               QueryService<Wenshu> queryService) {
         this.luceneSearch = luceneSearch;
         this.wenshuDocRepository = wenshuDocRepository;
+        this.queryService = queryService;
     }
 
     public PageList<WenshuResult> search(int pageNumber, String queryString) {
@@ -66,12 +68,11 @@ public class WenshuSearchService {
                 .highlightFiledNames(List.of("caseName", "fullText"))
                 .queryString(queryString)
                 .build();
-        /*Page<Wenshu> page = queryService.queryWithHighlight(elasticQuery, Wenshu.class);
+        Page<Wenshu> page = queryService.queryWithHighlight(elasticQuery, Wenshu.class);
         int pageSize = page.getSize();
         int total = (int) page.getTotalElements();
         List<WenshuResult> list = page.getContent().stream().map(WenshuResult::new).collect(Collectors.toList());
-        return PageList.pageList(pageNumber, pageSize, total, list);*/
-        return PageList.empty();
+        return PageList.pageList(pageNumber, pageSize, total, list);
     }
 
     public Wenshu queryById(String wenshuId, String queryString) {

+ 5 - 5
search/search-service/src/main/java/cn/reghao/tnb/search/app/service/WenshuService.java

@@ -24,7 +24,7 @@ import java.util.stream.Collectors;
  * @date 2025-08-20 17:45:35
  */
 @Slf4j
-//@Service
+@Service
 public class WenshuService {
     private String indexName = "wenshu_lucene";
     private SnowFlake idGenerator = new SnowFlake(1, 1);
@@ -32,8 +32,8 @@ public class WenshuService {
     private LuceneIndex luceneIndex;
     private LuceneDocument luceneDocument;
 
-    public WenshuService(/*WenshuDocRepository wenshuDocRepository,*/ LuceneIndex luceneIndex, LuceneDocument luceneDocument) {
-        //this.wenshuDocRepository = wenshuDocRepository;
+    public WenshuService(WenshuDocRepository wenshuDocRepository, LuceneIndex luceneIndex, LuceneDocument luceneDocument) {
+        this.wenshuDocRepository = wenshuDocRepository;
         this.luceneIndex = luceneIndex;
         this.luceneDocument = luceneDocument;
     }
@@ -195,7 +195,7 @@ public class WenshuService {
                 .map(wenshuLucene -> luceneDocument.getDocumentByWenshuLucene(wenshuLucene))
                 .collect(Collectors.toList());
         try {
-            //wenshuDocRepository.saveAll(wenshuDocList);
+            wenshuDocRepository.saveAll(wenshuDocList);
             luceneIndex.createIndex(indexName, luceneDocumentList);
             log.info("index {} documents...", wenshuList.size());
         } catch (Exception e) {
@@ -209,7 +209,7 @@ public class WenshuService {
     }
 
     public void deleteAll() throws IOException {
-        //wenshuDocRepository.deleteAll();
+        wenshuDocRepository.deleteAll();
         luceneIndex.deleteAll(indexName);
     }
 }

+ 3 - 3
search/search-service/src/test/java/ElasticTest.java

@@ -4,10 +4,10 @@ import ch.qos.logback.classic.LoggerContext;
 import cn.reghao.jutil.jdk.id.SnowFlake;
 import cn.reghao.tnb.content.api.constant.PostScope;
 import cn.reghao.tnb.search.app.SearchApplication;
-//import cn.reghao.tnb.search.app.es.*;
+import cn.reghao.tnb.search.app.es.*;
 import cn.reghao.tnb.search.app.model.po.VideoText;
-//import co.elastic.clients.elasticsearch._types.mapping.Property;
-//import co.elastic.clients.elasticsearch.indices.AnalyzeRequest;
+import co.elastic.clients.elasticsearch._types.mapping.Property;
+import co.elastic.clients.elasticsearch.indices.AnalyzeRequest;
 import lombok.extern.slf4j.Slf4j;
 import org.junit.jupiter.api.Test;
 import org.slf4j.LoggerFactory;

+ 3 - 3
search/search-service/src/test/java/NginxLogTest.java

@@ -1,9 +1,9 @@
 import cn.reghao.jutil.jdk.serializer.JsonConverter;
 import cn.reghao.jutil.jdk.id.SnowFlake;
 import cn.reghao.tnb.search.app.SearchApplication;
-//import cn.reghao.tnb.search.app.es.DocumentService;
-//import cn.reghao.tnb.search.app.es.QueryService;
-//import cn.reghao.tnb.search.app.es.SearchService;
+import cn.reghao.tnb.search.app.es.DocumentService;
+import cn.reghao.tnb.search.app.es.QueryService;
+import cn.reghao.tnb.search.app.es.SearchService;
 import cn.reghao.tnb.search.app.log.model.NginxLog;
 import lombok.extern.slf4j.Slf4j;
 import org.junit.jupiter.api.Test;

+ 9 - 2
zzz/run_thirdparty.sh

@@ -22,7 +22,14 @@ docker run -d --name=rabbitmq --network=host --restart=always \
     ${image}
 
 image='registry.cn-chengdu.aliyuncs.com/reghao/zookeeper:3.8.4'
-docker run -d --name=zookeeper --network=host --restart=always ${image}
+docker run -d --name=zk --network=host --restart=always ${image}
 
 image='registry.cn-chengdu.aliyuncs.com/reghao/nacos_nacos-server:v2.4.1'
-docker run -d --name=nacos --network=host --restart=always ${image}
+docker run -d --name=nacos --network=host --restart=always \
+  --env MODE=standalone \
+  ${image}
+
+image='registry.cn-chengdu.aliyuncs.com/reghao/elasticsearch:7.17.18'
+docker run -d --name=es --network=host \
+    -e "discovery.type=single-node" \
+    ${image}