Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,12 @@ private static String removeEndingAnchor(String normalized) {
*/
public static class PatternFragment {
public enum Type {
EXACT, // Exact literal match
PREFIX, // Starts with literal
SUFFIX, // Ends with literal
REGEX // Complex regex pattern
EXACT, // Exact literal match
PREFIX, // Starts with literal
PROPER_PREFIX, // Starts with literal but not the literal itself
SUFFIX, // Ends with literal
PROPER_SUFFIX, // Ends with literal but not the literal itself
REGEX // Complex regex pattern
}

private final Type type;
Expand Down Expand Up @@ -183,7 +185,10 @@ private static PatternFragment classifyPart(String part) {
// Suffix pattern: .*suffix
String suffix = trimmed.substring(2);
if (isLiteral(suffix)) {
return new PatternFragment(PatternFragment.Type.SUFFIX, suffix);
return new PatternFragment(
trimmed.startsWith(".*") ? PatternFragment.Type.SUFFIX : PatternFragment.Type.PROPER_SUFFIX,
suffix
);
}
// Complex suffix pattern - fallback to REGEX
return new PatternFragment(PatternFragment.Type.REGEX, part.trim());
Expand All @@ -193,7 +198,10 @@ private static PatternFragment classifyPart(String part) {
// Prefix pattern: prefix.*
String prefix = trimmed.substring(0, trimmed.length() - 2);
if (isLiteral(prefix)) {
return new PatternFragment(PatternFragment.Type.PREFIX, prefix);
return new PatternFragment(
trimmed.endsWith(".*") ? PatternFragment.Type.PREFIX : PatternFragment.Type.PROPER_PREFIX,
prefix
);
}
// Complex prefix pattern - fallback to REGEX
return new PatternFragment(PatternFragment.Type.REGEX, part.trim());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
import org.elasticsearch.xpack.esql.expression.function.scalar.string.StartsWith;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.regex.RLike;
import org.elasticsearch.xpack.esql.expression.predicate.Predicates;
import org.elasticsearch.xpack.esql.expression.predicate.logical.And;
import org.elasticsearch.xpack.esql.expression.predicate.nulls.IsNotNull;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.Equals;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.In;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.NotEquals;
import org.elasticsearch.xpack.esql.expression.promql.function.PromqlFunctionRegistry;
import org.elasticsearch.xpack.esql.optimizer.rules.logical.OptimizerRules;
import org.elasticsearch.xpack.esql.optimizer.rules.logical.TranslateTimeSeriesAggregate;
Expand Down Expand Up @@ -339,7 +341,9 @@ private static Expression translatePatternFragment(Source source, Expression fie
return switch (fragment.type()) {
case EXACT -> new Equals(source, field, value);
case PREFIX -> new StartsWith(source, field, value);
case PROPER_PREFIX -> new And(source, new NotEquals(source, field, value), new StartsWith(source, field, value));
case SUFFIX -> new EndsWith(source, field, value);
case PROPER_SUFFIX -> new And(source, new NotEquals(source, field, value), new EndsWith(source, field, value));
case REGEX -> new RLike(source, field, new RLikePattern(fragment.value()));
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@
import org.elasticsearch.xpack.esql.analysis.Analyzer;
import org.elasticsearch.xpack.esql.analysis.AnalyzerContext;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.expression.predicate.regex.RegexMatch;
import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.StartsWith;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.In;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.NotEquals;
import org.elasticsearch.xpack.esql.index.EsIndex;
import org.elasticsearch.xpack.esql.index.IndexResolution;
import org.elasticsearch.xpack.esql.optimizer.AbstractLogicalPlanOptimizerTests;
import org.elasticsearch.xpack.esql.plan.IndexPattern;
import org.elasticsearch.xpack.esql.plan.logical.Filter;
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
import org.junit.BeforeClass;

Expand All @@ -28,6 +33,8 @@
import static org.elasticsearch.xpack.esql.EsqlTestUtils.TEST_VERIFIER;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.emptyInferenceResolution;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.loadMapping;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;

//@TestLogging(value="org.elasticsearch.xpack.esql:TRACE", reason="debug tests")
//@Ignore("Proper assertions need to be added")
Expand Down Expand Up @@ -171,12 +178,15 @@ public void testLabelSelector() {
String testQuery = """
TS k8s
| promql time now (
max by (pod)(avg_over_time(network.bytes_in{pod=~"host-0|host-1|host-2"}[5m]))
)

max by (pod) (avg_over_time(network.bytes_in{pod=~"host-0|host-1|host-2"}[5m]))
)
""";

var plan = planPromql(testQuery);
var filters = plan.collect(Filter.class::isInstance);
assertThat(filters, hasSize(1));
var filter = (Filter) filters.getFirst();
assertThat(filter.condition().anyMatch(In.class::isInstance), equalTo(true));
System.out.println(plan);
}

Expand All @@ -188,14 +198,48 @@ public void testLabelSelectorPrefix() {
String testQuery = """
TS k8s
| promql time now (
avg by (pod)(avg_over_time(network.total_bytes_in{pod=~"host-.*"}[5m]))
avg by (pod) (avg_over_time(network.bytes_in{pod=~"host-.*"}[5m]))
)
""";

var plan = planPromql(testQuery);
var filters = plan.collect(Filter.class::isInstance);
assertThat(filters, hasSize(1));
var filter = (Filter) filters.getFirst();
assertThat(filter.condition().anyMatch(StartsWith.class::isInstance), equalTo(true));
assertThat(filter.condition().anyMatch(NotEquals.class::isInstance), equalTo(false));
System.out.println(plan);
}

public void testLabelSelectorProperPrefix() {
var plan = planPromql("""
TS k8s
| promql time now (
avg(avg_over_time(network.bytes_in{pod=~"host-.+"}[1h]))
)
""");

var filters = plan.collect(Filter.class::isInstance);
assertThat(filters, hasSize(1));
var filter = (Filter) filters.getFirst();
assertThat(filter.condition().anyMatch(StartsWith.class::isInstance), equalTo(true));
assertThat(filter.condition().anyMatch(NotEquals.class::isInstance), equalTo(true));
}

public void testLabelSelectorRegex() {
var plan = planPromql("""
TS k8s
| promql time now (
avg(avg_over_time(network.bytes_in{pod=~"[a-z]+"}[1h]))
)
""");

var filters = plan.collect(Filter.class::isInstance);
assertThat(filters, hasSize(1));
var filter = (Filter) filters.getFirst();
assertThat(filter.condition().anyMatch(RegexMatch.class::isInstance), equalTo(true));
}

public void testFsUsageTop5() {
// TS metrics-hostmetricsreceiver.otel-default | WHERE @timestamp >= \"{{from | minus .benchmark.duration}}\" AND @timestamp <=
// \"{{from}}\"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

import static org.elasticsearch.xpack.esql.optimizer.rules.logical.promql.AutomatonUtils.PatternFragment.Type.EXACT;
import static org.elasticsearch.xpack.esql.optimizer.rules.logical.promql.AutomatonUtils.PatternFragment.Type.PREFIX;
import static org.elasticsearch.xpack.esql.optimizer.rules.logical.promql.AutomatonUtils.PatternFragment.Type.PROPER_PREFIX;
import static org.elasticsearch.xpack.esql.optimizer.rules.logical.promql.AutomatonUtils.PatternFragment.Type.PROPER_SUFFIX;
import static org.elasticsearch.xpack.esql.optimizer.rules.logical.promql.AutomatonUtils.PatternFragment.Type.REGEX;
import static org.elasticsearch.xpack.esql.optimizer.rules.logical.promql.AutomatonUtils.PatternFragment.Type.SUFFIX;
import static org.elasticsearch.xpack.esql.optimizer.rules.logical.promql.AutomatonUtils.extractFragments;
Expand Down Expand Up @@ -63,6 +65,17 @@ public void testExtractFragments_MixedAlternation() {
assertFragments(fragments, expected);
}

public void testExtractFragments_ProperPrefixSuffixAlternation() {
List<PatternFragment> fragments = extractFragments("prod-.+|.+-dev");

assertThat(fragments, notNullValue());
assertThat(fragments, hasSize(2));

Object[][] expected = { { PROPER_PREFIX, "prod-" }, { PROPER_SUFFIX, "-dev" } };

assertFragments(fragments, expected);
}

public void testExtractFragments_HomogeneousExactAlternation() {
// All exact values
List<PatternFragment> fragments = extractFragments("api|web|service");
Expand Down