This is an automated email from the ASF dual-hosted git repository.

zqr10159 pushed a commit to branch 2.0.0
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/2.0.0 by this push:
     new ada22b22c9 Support trace resource filter IN clauses
ada22b22c9 is described below

commit ada22b22c9143d9af85cce26a5db7fcfd192f8af
Author: Logic <[email protected]>
AuthorDate: Tue Jun 9 20:41:47 2026 +0800

    Support trace resource filter IN clauses
---
 .../service/impl/EntityTraceQueryServiceImpl.java  | 118 ++++++++++++++++++++-
 .../impl/EntityTraceQueryServiceImplTest.java      |   5 +-
 2 files changed, 120 insertions(+), 3 deletions(-)

diff --git 
a/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/traces/service/impl/EntityTraceQueryServiceImpl.java
 
b/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/traces/service/impl/EntityTraceQueryServiceImpl.java
index 716fbfab39..3f4bbf36bc 100644
--- 
a/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/traces/service/impl/EntityTraceQueryServiceImpl.java
+++ 
b/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/traces/service/impl/EntityTraceQueryServiceImpl.java
@@ -1059,11 +1059,14 @@ public class EntityTraceQueryServiceImpl implements 
EntityTraceQueryService {
             return Collections.emptyMap();
         }
         Map<String, Set<String>> filters = new LinkedHashMap<>();
-        for (String clause : 
resourceFilter.split("(?i)\\s+and\\s+|\\s*,\\s*")) {
+        for (String clause : splitResourceFilterClauses(resourceFilter)) {
             String trimmedClause = trimText(clause);
             if (!StringUtils.hasText(trimmedClause)) {
                 continue;
             }
+            if (appendResourceFilterInValues(filters, trimmedClause)) {
+                continue;
+            }
             int separatorIndex = resourceFilterSeparatorIndex(trimmedClause);
             if (separatorIndex <= 0 || separatorIndex >= 
trimmedClause.length() - 1) {
                 continue;
@@ -1078,6 +1081,119 @@ public class EntityTraceQueryServiceImpl implements 
EntityTraceQueryService {
         return filters;
     }
 
+    private boolean appendResourceFilterInValues(Map<String, Set<String>> 
filters, String clause) {
+        int inIndex = resourceFilterInOperatorIndex(clause);
+        if (inIndex <= 0) {
+            return false;
+        }
+        String key = trimText(clause.substring(0, inIndex));
+        String valueList = trimText(clause.substring(inIndex + 4));
+        if (!isSafeResourceFilterKey(key) || !StringUtils.hasText(valueList)
+                || !valueList.startsWith("(") || !valueList.endsWith(")")) {
+            return false;
+        }
+        for (String value : 
splitResourceFilterListValues(valueList.substring(1, valueList.length() - 1))) {
+            String normalizedValue = 
stripResourceFilterQuotes(trimText(value));
+            if (StringUtils.hasText(normalizedValue)) {
+                filters.computeIfAbsent(key, ignored -> new 
LinkedHashSet<>()).add(normalizedValue);
+            }
+        }
+        return filters.containsKey(key);
+    }
+
+    private int resourceFilterInOperatorIndex(String clause) {
+        String normalized = clause == null ? null : 
clause.toLowerCase(Locale.ROOT);
+        if (!StringUtils.hasText(normalized)) {
+            return -1;
+        }
+        int index = normalized.indexOf(" in ");
+        return index < 0 ? -1 : index;
+    }
+
+    private List<String> splitResourceFilterClauses(String resourceFilter) {
+        List<String> clauses = new ArrayList<>();
+        StringBuilder current = new StringBuilder();
+        int depth = 0;
+        char quote = 0;
+        for (int index = 0; index < resourceFilter.length(); index++) {
+            char character = resourceFilter.charAt(index);
+            if (quote != 0) {
+                current.append(character);
+                if (character == quote) {
+                    quote = 0;
+                }
+                continue;
+            }
+            if (character == '\'' || character == '"') {
+                quote = character;
+                current.append(character);
+                continue;
+            }
+            if (character == '(') {
+                depth++;
+                current.append(character);
+                continue;
+            }
+            if (character == ')') {
+                depth = Math.max(0, depth - 1);
+                current.append(character);
+                continue;
+            }
+            if (depth == 0 && character == ',') {
+                addResourceFilterClause(clauses, current);
+                continue;
+            }
+            if (depth == 0 && isResourceFilterAndDelimiter(resourceFilter, 
index)) {
+                addResourceFilterClause(clauses, current);
+                index += 4;
+                continue;
+            }
+            current.append(character);
+        }
+        addResourceFilterClause(clauses, current);
+        return clauses;
+    }
+
+    private List<String> splitResourceFilterListValues(String values) {
+        List<String> result = new ArrayList<>();
+        StringBuilder current = new StringBuilder();
+        char quote = 0;
+        for (int index = 0; index < values.length(); index++) {
+            char character = values.charAt(index);
+            if (quote != 0) {
+                current.append(character);
+                if (character == quote) {
+                    quote = 0;
+                }
+                continue;
+            }
+            if (character == '\'' || character == '"') {
+                quote = character;
+                current.append(character);
+                continue;
+            }
+            if (character == ',') {
+                addResourceFilterClause(result, current);
+                continue;
+            }
+            current.append(character);
+        }
+        addResourceFilterClause(result, current);
+        return result;
+    }
+
+    private void addResourceFilterClause(List<String> clauses, StringBuilder 
current) {
+        String clause = trimText(current.toString());
+        if (StringUtils.hasText(clause)) {
+            clauses.add(clause);
+        }
+        current.setLength(0);
+    }
+
+    private boolean isResourceFilterAndDelimiter(String value, int index) {
+        return index + 5 <= value.length() && value.regionMatches(true, index, 
" and ", 0, 5);
+    }
+
     private int resourceFilterSeparatorIndex(String clause) {
         int equalsIndex = clause.indexOf('=');
         int colonIndex = clause.indexOf(':');
diff --git 
a/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/traces/service/impl/EntityTraceQueryServiceImplTest.java
 
b/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/traces/service/impl/EntityTraceQueryServiceImplTest.java
index be2cec643e..3e9c143ac4 100644
--- 
a/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/traces/service/impl/EntityTraceQueryServiceImplTest.java
+++ 
b/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/traces/service/impl/EntityTraceQueryServiceImplTest.java
@@ -200,7 +200,7 @@ class EntityTraceQueryServiceImplTest {
                 "checkout",
                 "commerce",
                 "prod",
-                "host.name=checkout-1",
+                "host.name IN (\"checkout-1\", 'checkout-2') and 
k8s.namespace.name:commerce",
                 "GET /checkout",
                 100L,
                 500L,
@@ -239,7 +239,8 @@ class EntityTraceQueryServiceImplTest {
                 eq("latency-p95-desc"),
                 eq(5L),
                 eq(7));
-        assertEquals(Set.of("checkout-1"), 
filterCaptor.getValue().get("host.name"));
+        assertEquals(Set.of("checkout-1", "checkout-2"), 
filterCaptor.getValue().get("host.name"));
+        assertEquals(Set.of("commerce"), 
filterCaptor.getValue().get("k8s.namespace.name"));
     }
 
     @Test


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to