Skip to content

Commit 54f2668

Browse files
authored
ESQL: Keep DROP attributes when resolving field names (#127009)
1 parent 9bcdbc0 commit 54f2668

File tree

7 files changed

+103
-10
lines changed

7 files changed

+103
-10
lines changed

docs/changelog/127009.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 127009
2+
summary: "ESQL: Keep `DROP` attributes when resolving field names"
3+
area: ES|QL
4+
type: bug
5+
issues:
6+
- 126418

x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/generative/GenerativeRestTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ public abstract class GenerativeRestTest extends ESRestTestCase {
5151
"Unknown column \\[<all-fields-projected>\\]", // https://github.com/elastic/elasticsearch/issues/121741,
5252
"Plan \\[ProjectExec\\[\\[<no-fields>.* optimized incorrectly due to missing references", // https://github.com/elastic/elasticsearch/issues/125866
5353
"optimized incorrectly due to missing references", // https://github.com/elastic/elasticsearch/issues/116781
54-
"No matches found for pattern", // https://github.com/elastic/elasticsearch/issues/126418
5554
"Unknown column", // https://github.com/elastic/elasticsearch/issues/127467
5655
"only supports KEYWORD or TEXT values", // https://github.com/elastic/elasticsearch/issues/127468
5756
"The incoming YAML document exceeds the limit:" // still to investigate, but it seems to be specific to the test framework

x-pack/plugin/esql/qa/testFixtures/src/main/resources/drop.csv-spec

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,19 @@ Milky Way
172172
Milky Way
173173
Milky Way
174174
;
175+
176+
dropAgainWithWildcardAfterEval
177+
required_capability: drop_again_with_wildcard_after_eval
178+
from languages
179+
| eval language_code = 12, x = 13
180+
| drop language_code
181+
| drop language*
182+
| keep x
183+
| limit 3
184+
;
185+
186+
x:integer
187+
13
188+
13
189+
13
190+
;

x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join.csv-spec

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,3 +1650,26 @@ event_duration:long
16501650
2764889
16511651
3450233
16521652
;
1653+
1654+
dropAgainWithWildcardAfterEval2
1655+
required_capability: join_lookup_v12
1656+
required_capability: drop_again_with_wildcard_after_eval
1657+
from addresses,cartesian_multipolygons,hosts
1658+
| rename city.name as message
1659+
| lookup join message_types_lookup on message
1660+
| eval card = -6013949614291505456, hOntTwnVC = null, PQAF = null, DXkxCFXyw = null, number = -7336429038807752405
1661+
| eval dewAwHC = -1186293612, message = null
1662+
| sort number ASC, street ASC, ip0 DESC, name ASC NULLS FIRST, host ASC
1663+
| drop number, host_group, *umber, `city.country.continent.name`, dewAwHC, `zip_code`, `message`, city.country.continent.planet.name, `name`, `ip1`, message, zip_code
1664+
| drop description, *e, id
1665+
| keep `hOntTwnVC`, city.country.continent.planet.galaxy, street
1666+
| limit 5
1667+
;
1668+
1669+
hOntTwnVC:null | city.country.continent.planet.galaxy:keyword | street:keyword
1670+
null | Milky Way | Kearny St
1671+
null | Milky Way | Keizersgracht
1672+
null | Milky Way | Marunouchi
1673+
null | null | null
1674+
null | null | null
1675+
;

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,12 @@ public enum Cap {
10361036
*/
10371037
FIX_JOIN_MASKING_EVAL,
10381038

1039+
/**
1040+
* Support for keeping `DROP` attributes when resolving field names.
1041+
* see <a href="https://github.com/elastic/elasticsearch/issues/126418"> ES|QL: no matches for pattern #126418 </a>
1042+
*/
1043+
DROP_AGAIN_WITH_WILDCARD_AFTER_EVAL,
1044+
10391045
/**
10401046
* Support last_over_time aggregation that gets evaluated per time-series
10411047
*/

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -590,9 +590,15 @@ static PreAnalysisResult fieldNames(LogicalPlan parsed, Set<String> enrichPolicy
590590
}
591591

592592
var referencesBuilder = AttributeSet.builder();
593-
// "keep" attributes are special whenever a wildcard is used in their name
593+
// "keep" and "drop" attributes are special whenever a wildcard is used in their name, as the wildcard can shadow some
594+
// attributes ("lookup join" generated columns among others) and steps like removal of Aliases should ignore the fields
595+
// to remove if their name matches one of these wildcards.
596+
//
594597
// ie "from test | eval lang = languages + 1 | keep *l" should consider both "languages" and "*l" as valid fields to ask for
595-
var keepCommandRefsBuilder = AttributeSet.builder();
598+
// "from test | eval first_name = 1 | drop first_name | drop *name should also consider "*name" as valid field to ask for
599+
//
600+
// NOTE: the grammar allows wildcards to be used in other commands as well, but these are forbidden in the LogicalPlanBuilder
601+
var shadowingRefsBuilder = AttributeSet.builder();
596602
var keepJoinRefsBuilder = AttributeSet.builder();
597603
Set<String> wildcardJoinIndices = new java.util.HashSet<>();
598604

@@ -617,12 +623,12 @@ static PreAnalysisResult fieldNames(LogicalPlan parsed, Set<String> enrichPolicy
617623
if (join.config().type() instanceof JoinTypes.UsingJoinType usingJoinType) {
618624
keepJoinRefsBuilder.addAll(usingJoinType.columns());
619625
}
620-
if (keepCommandRefsBuilder.isEmpty()) {
626+
if (shadowingRefsBuilder.isEmpty()) {
621627
// No KEEP commands after the JOIN, so we need to mark this index for "*" field resolution
622628
wildcardJoinIndices.add(((UnresolvedRelation) join.right()).indexPattern().indexPattern());
623629
} else {
624630
// Keep commands can reference the join columns with names that shadow aliases, so we block their removal
625-
keepJoinRefsBuilder.addAll(keepCommandRefsBuilder);
631+
keepJoinRefsBuilder.addAll(shadowingRefsBuilder);
626632
}
627633
} else {
628634
referencesBuilder.addAll(p.references());
@@ -634,12 +640,10 @@ static PreAnalysisResult fieldNames(LogicalPlan parsed, Set<String> enrichPolicy
634640
p.forEachExpression(UnresolvedNamePattern.class, up -> {
635641
var ua = new UnresolvedAttribute(up.source(), up.name());
636642
referencesBuilder.add(ua);
637-
if (p instanceof Keep) {
638-
keepCommandRefsBuilder.add(ua);
639-
}
643+
shadowingRefsBuilder.add(ua);
640644
});
641645
if (p instanceof Keep) {
642-
keepCommandRefsBuilder.addAll(p.references());
646+
shadowingRefsBuilder.addAll(p.references());
643647
}
644648
}
645649

@@ -669,7 +673,7 @@ static PreAnalysisResult fieldNames(LogicalPlan parsed, Set<String> enrichPolicy
669673
if (fieldNames.contains(alias.name())) {
670674
return;
671675
}
672-
referencesBuilder.removeIf(attr -> matchByName(attr, alias.name(), keepCommandRefsBuilder.contains(attr)));
676+
referencesBuilder.removeIf(attr -> matchByName(attr, alias.name(), shadowingRefsBuilder.contains(attr)));
673677
});
674678
}
675679
});

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/IndexResolverFieldNamesTests.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,6 +1710,45 @@ public void testEnrichAndJoinMaskingEvalWh() {
17101710
| keep emp_no, language_name""", Set.of("emp_no", "language_name", "languages", "language_name.*", "languages.*", "emp_no.*"));
17111711
}
17121712

1713+
public void testDropAgainWithWildcardAfterEval() {
1714+
assertFieldNames("""
1715+
from employees
1716+
| eval full_name = 12
1717+
| drop full_name
1718+
| drop *name
1719+
| keep emp_no
1720+
""", Set.of("emp_no", "emp_no.*", "*name", "*name.*"));
1721+
}
1722+
1723+
public void testDropWildcardedFields_AfterRename() {
1724+
assertFieldNames(
1725+
"""
1726+
from employees
1727+
| rename first_name AS first_names, last_name AS last_names
1728+
| eval first_names = 1
1729+
| drop first_names
1730+
| drop *_names
1731+
| keep gender""",
1732+
Set.of("first_name", "first_name.*", "last_name", "last_name.*", "*_names", "*_names.*", "gender", "gender.*")
1733+
);
1734+
}
1735+
1736+
public void testDropWildcardFields_WithLookupJoin() {
1737+
assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled());
1738+
assertFieldNames(
1739+
"""
1740+
FROM sample_data
1741+
| EVAL client_ip = client_ip::keyword
1742+
| LOOKUP JOIN clientips_lookup ON client_ip
1743+
| LOOKUP JOIN message_types_lookup ON message
1744+
| KEEP @timestamp, message, *e*
1745+
| SORT @timestamp
1746+
| DROP *e""",
1747+
Set.of("client_ip", "client_ip.*", "message", "message.*", "@timestamp", "@timestamp.*", "*e*", "*e", "*e.*"),
1748+
Set.of()
1749+
);
1750+
}
1751+
17131752
private Set<String> fieldNames(String query, Set<String> enrichPolicyMatchFields) {
17141753
var preAnalysisResult = new EsqlSession.PreAnalysisResult(null);
17151754
return EsqlSession.fieldNames(parser.createStatement(query), enrichPolicyMatchFields, preAnalysisResult).fieldNames();

0 commit comments

Comments
 (0)