Skip to content

Commit 7b3aa2a

Browse files
ES|QL: support dot and parameters in FUSE GROUP BY (#135901)
1 parent 92f725a commit 7b3aa2a

File tree

7 files changed

+1437
-1328
lines changed

7 files changed

+1437
-1328
lines changed

docs/changelog/135901.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 135901
2+
summary: Add support for dot-separated attribute names (e.g. `foo.bar`) and for parameters (e.g. `??my_param`) in FUSE GROUP BY
3+
area: ES|QL
4+
type: bug
5+
issues: []

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,33 @@ _score:double | new_fork:keyword | _id:keyword
281281
0.01587 | fork1 | 26
282282
;
283283

284+
fuseWithRrfAndDiscriminatorColumnWithDots
285+
required_capability: fork_v9
286+
required_capability: fuse_v6
287+
required_capability: semantic_text_field_caps
288+
required_capability: metadata_score
289+
required_capability: dots_in_fuse
290+
291+
FROM books METADATA _id, _index, _score
292+
| FORK ( WHERE title:"Tolkien" | SORT _score, _id DESC | LIMIT 3)
293+
( WHERE author:"Tolkien" | SORT _score, _id DESC | LIMIT 3)
294+
| EVAL new.fork = _fork
295+
| DROP _fork
296+
| FUSE rrf GROUP BY new.fork
297+
| SORT _score DESC, _id, _index
298+
| EVAL new.fork = mv_sort(new.fork)
299+
| EVAL _score = round(_score, 5)
300+
| KEEP _score, new.fork, _id
301+
;
302+
303+
_score:double | new.fork:keyword | _id:keyword
304+
0.03279 | [fork1, fork2] | 4
305+
0.01613 | fork1 | 56
306+
0.01613 | fork2 | 60
307+
0.01587 | fork2 | 1
308+
0.01587 | fork1 | 26
309+
;
310+
284311
fuseWithRrfAndKeyColumns
285312
required_capability: fork_v9
286313
required_capability: fuse_v6

x-pack/plugin/esql/src/main/antlr/lexer/Fuse.g4

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ KEY : 'key';
2020

2121
FUSE_WITH: WITH -> type(WITH), popMode, pushMode(EXPRESSION_MODE);
2222
FUSE_COMMA: COMMA -> type(COMMA);
23+
FUSE_DOT: DOT -> type(DOT);
24+
FUSE_PARAM : PARAM -> type(PARAM);
25+
FUSE_NAMED_OR_POSITIONAL_PARAM : NAMED_OR_POSITIONAL_PARAM -> type(NAMED_OR_POSITIONAL_PARAM);
26+
FUSE_DOUBLE_PARAMS : DOUBLE_PARAMS -> type(DOUBLE_PARAMS);
27+
FUSE_NAMED_OR_POSITIONAL_DOUBLE_PARAMS : NAMED_OR_POSITIONAL_DOUBLE_PARAMS -> type(NAMED_OR_POSITIONAL_DOUBLE_PARAMS);
2328
FUSE_BY: BY -> type(BY);
2429
FUSE_QUOTED_IDENTIFIER: QUOTED_IDENTIFIER -> type(QUOTED_IDENTIFIER);
2530
FUSE_UNQUOTED_IDENTIFIER: UNQUOTED_IDENTIFIER -> type(UNQUOTED_IDENTIFIER);

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1593,7 +1593,12 @@ public enum Cap {
15931593
* Fix management of plans with no columns
15941594
* https://github.com/elastic/elasticsearch/issues/120272
15951595
*/
1596-
FIX_NO_COLUMNS;
1596+
FIX_NO_COLUMNS,
1597+
1598+
/**
1599+
* Support for dots in FUSE attributes
1600+
*/
1601+
DOTS_IN_FUSE;
15971602

15981603
private final boolean enabled;
15991604

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.interp

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.java

Lines changed: 1348 additions & 1326 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4307,6 +4307,46 @@ public void testValidFuse() {
43074307
assertThat(fuse.fuseType(), equalTo(Fuse.FuseType.RRF));
43084308
options = fuse.options();
43094309
assertThat(options.get("rank_constant"), equalTo(Literal.integer(null, 15)));
4310+
4311+
plan = statement("""
4312+
FROM foo* METADATA _id, _index, _score
4313+
| EVAL a.b = my_group
4314+
| FORK ( WHERE a:"baz" )
4315+
( WHERE b:"bar" )
4316+
| FUSE GROUP BY a.b KEY BY my_key1,my_key2 SCORE BY my_score WITH {"rank_constant": 15 }
4317+
""");
4318+
4319+
fuse = as(plan, Fuse.class);
4320+
assertThat(fuse.keys().size(), equalTo(2));
4321+
assertThat(fuse.keys().get(0), instanceOf(UnresolvedAttribute.class));
4322+
assertThat(fuse.keys().get(0).name(), equalTo("my_key1"));
4323+
assertThat(fuse.keys().get(1), instanceOf(UnresolvedAttribute.class));
4324+
assertThat(fuse.keys().get(1).name(), equalTo("my_key2"));
4325+
assertThat(fuse.discriminator().name(), equalTo("a.b"));
4326+
assertThat(fuse.score().name(), equalTo("my_score"));
4327+
assertThat(fuse.fuseType(), equalTo(Fuse.FuseType.RRF));
4328+
options = fuse.options();
4329+
assertThat(options.get("rank_constant"), equalTo(Literal.integer(null, 15)));
4330+
4331+
plan = statement("""
4332+
FROM foo* METADATA _id, _index, _score
4333+
| EVAL ??p = my_group
4334+
| FORK ( WHERE a:"baz" )
4335+
( WHERE b:"bar" )
4336+
| FUSE GROUP BY ??p KEY BY my_key1,my_key2 SCORE BY my_score WITH {"rank_constant": 15 }
4337+
""", new QueryParams(List.of(paramAsConstant("p", "a.b"))));
4338+
4339+
fuse = as(plan, Fuse.class);
4340+
assertThat(fuse.keys().size(), equalTo(2));
4341+
assertThat(fuse.keys().get(0), instanceOf(UnresolvedAttribute.class));
4342+
assertThat(fuse.keys().get(0).name(), equalTo("my_key1"));
4343+
assertThat(fuse.keys().get(1), instanceOf(UnresolvedAttribute.class));
4344+
assertThat(fuse.keys().get(1).name(), equalTo("my_key2"));
4345+
assertThat(fuse.discriminator().name(), equalTo("a.b"));
4346+
assertThat(fuse.score().name(), equalTo("my_score"));
4347+
assertThat(fuse.fuseType(), equalTo(Fuse.FuseType.RRF));
4348+
options = fuse.options();
4349+
assertThat(options.get("rank_constant"), equalTo(Literal.integer(null, 15)));
43104350
}
43114351

43124352
public void testInvalidFuse() {

0 commit comments

Comments
 (0)