|
|
@@ -0,0 +1,394 @@
|
|
|
+package cn.reghao.tnb.search.app.log;
|
|
|
+
|
|
|
+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 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 NginxLogSearchService {
|
|
|
+ private final ElasticsearchClient esClient;
|
|
|
+ private final String indexName = "NginxLog";
|
|
|
+
|
|
|
+ public NginxLogSearchService(ElasticService elasticService) throws Exception {
|
|
|
+ this.esClient = elasticService.getElasticsearchClient();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void searchOne(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(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() throws IOException {
|
|
|
+ String fieldName3 = "status";
|
|
|
+ String fieldValue3 = "200";
|
|
|
+ SearchRequest searchRequest = SearchRequest.of(s -> s
|
|
|
+ .index(indexName)
|
|
|
+ .query(q -> q.bool(b -> b
|
|
|
+ .must(m -> m.term(t -> t.field(fieldName3).value(FieldValue.of(fieldValue3))))
|
|
|
+ //.must(m -> m.term(t -> t.field("name").value(FieldValue.of("test"))))
|
|
|
+ ))
|
|
|
+ );
|
|
|
+
|
|
|
+ SearchResponse<NginxLog> searchResponse = esClient.search(searchRequest, NginxLog.class);
|
|
|
+ List<NginxLog> list = searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ public void searchByField(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 search1() throws IOException {
|
|
|
+ String fieldName = "url";
|
|
|
+ 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))*/
|
|
|
+ .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();
|
|
|
+ SearchRequest searchRequest = new SearchRequest.Builder()
|
|
|
+ .index(indexName)
|
|
|
+ .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() 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(indexName)
|
|
|
+ //.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() 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(indexName)
|
|
|
+ .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());
|
|
|
+ }
|
|
|
+}
|