Skip to content

Commit c38e9f3

Browse files
committed
Allow parameter for ES|QL SAMPLE
1 parent 6998c96 commit c38e9f3

File tree

9 files changed

+61
-34
lines changed

9 files changed

+61
-34
lines changed

x-pack/plugin/esql/src/main/antlr/EsqlBaseParser.g4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,5 +313,5 @@ completionCommand
313313
;
314314

315315
sampleCommand
316-
: DEV_SAMPLE probability=decimalValue
316+
: DEV_SAMPLE probability=constant
317317
;

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,7 @@ public enum Cap {
11681168
LUCENE_QUERY_EVALUATOR_QUERY_REWRITE,
11691169

11701170
/**
1171-
* Support parameters for LiMIT command.
1171+
* Support parameters for LIMIT command.
11721172
*/
11731173
PARAMETER_FOR_LIMIT,
11741174

@@ -1195,7 +1195,12 @@ public enum Cap {
11951195
/**
11961196
* Support knn function
11971197
*/
1198-
KNN_FUNCTION(Build.current().isSnapshot());
1198+
KNN_FUNCTION(Build.current().isSnapshot()),
1199+
1200+
/**
1201+
* Support parameters for SAMPLE command.
1202+
*/
1203+
PARAMETER_FOR_SAMPLE(Build.current().isSnapshot());
11991204

12001205
private final boolean enabled;
12011206

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

Lines changed: 1 addition & 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/EsqlBaseParser.java

Lines changed: 5 additions & 5 deletions
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/LogicalPlanBuilder.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import java.util.function.Function;
8989

9090
import static java.util.Collections.emptyList;
91+
import static org.elasticsearch.xpack.esql.common.Failure.fail;
9192
import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
9293
import static org.elasticsearch.xpack.esql.core.util.StringUtils.WILDCARD;
9394
import static org.elasticsearch.xpack.esql.expression.NamedExpressions.mergeOutputExpressions;
@@ -783,7 +784,15 @@ public Literal inferenceId(EsqlBaseParser.IdentifierOrParameterContext ctx) {
783784
}
784785

785786
public PlanFactory visitSampleCommand(EsqlBaseParser.SampleCommandContext ctx) {
786-
var probability = visitDecimalValue(ctx.probability);
787-
return plan -> new Sample(source(ctx), probability, plan);
787+
Source source = source(ctx);
788+
Object val = expression(ctx.probability).fold(FoldContext.small() /* TODO remove me */);
789+
if (val instanceof Double probability && probability > 0.0 && probability < 1.0) {
790+
return input -> new Sample(source, new Literal(source, probability, DataType.DOUBLE), input);
791+
} else {
792+
throw new ParsingException(
793+
source(ctx),
794+
"invalid value for SAMPLE probability [" + val + "], expecting a number between 0 and 1, exclusive"
795+
);
796+
}
788797
}
789798
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3458,21 +3458,7 @@ public void testRrfError() {
34583458
"""));
34593459
assertThat(e.getMessage(), containsString("Unknown column [_id]"));
34603460
}
3461-
3462-
public void testRandomSampleProbability() {
3463-
assumeTrue("requires SAMPLE capability", EsqlCapabilities.Cap.SAMPLE_V3.isEnabled());
3464-
3465-
var e = expectThrows(VerificationException.class, () -> analyze("FROM test | SAMPLE 1."));
3466-
assertThat(e.getMessage(), containsString("RandomSampling probability must be strictly between 0.0 and 1.0, was [1.0]"));
3467-
3468-
e = expectThrows(VerificationException.class, () -> analyze("FROM test | SAMPLE .0"));
3469-
assertThat(e.getMessage(), containsString("RandomSampling probability must be strictly between 0.0 and 1.0, was [0.0]"));
3470-
3471-
double p = randomValueOtherThanTest(d -> 0 < d && d < 1, () -> randomDoubleBetween(0, Double.MAX_VALUE, false));
3472-
e = expectThrows(VerificationException.class, () -> analyze("FROM test | SAMPLE " + p));
3473-
assertThat(e.getMessage(), containsString("RandomSampling probability must be strictly between 0.0 and 1.0, was [" + p + "]"));
3474-
}
3475-
3461+
34763462
// TODO There's too much boilerplate involved here! We need a better way of creating FieldCapabilitiesResponses from a mapping or index.
34773463
private static FieldCapabilitiesIndexResponse fieldCapabilitiesIndexResponse(
34783464
String indexName,

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/ParsingTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,25 @@ public void testInvalidLimit() {
162162
assertEquals("1:13: Invalid value for LIMIT [-1], expecting a non negative integer", error("row a = 1 | limit -1"));
163163
}
164164

165+
public void testInvalidSample() {
166+
assertEquals(
167+
"1:13: invalid value for SAMPLE probability [foo], expecting a number between 0 and 1, exclusive",
168+
error("row a = 1 | sample \"foo\"")
169+
);
170+
assertEquals(
171+
"1:13: invalid value for SAMPLE probability [-1.0], expecting a number between 0 and 1, exclusive",
172+
error("row a = 1 | sample -1.0")
173+
);
174+
assertEquals(
175+
"1:13: invalid value for SAMPLE probability [0], expecting a number between 0 and 1, exclusive",
176+
error("row a = 1 | sample 0")
177+
);
178+
assertEquals(
179+
"1:13: invalid value for SAMPLE probability [1], expecting a number between 0 and 1, exclusive",
180+
error("row a = 1 | sample 1")
181+
);
182+
}
183+
165184
private String error(String query) {
166185
ParsingException e = expectThrows(ParsingException.class, () -> defaultAnalyzer.analyze(parser.createStatement(query)));
167186
String message = e.getMessage();

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
import static org.hamcrest.Matchers.hasSize;
105105
import static org.hamcrest.Matchers.instanceOf;
106106
import static org.hamcrest.Matchers.is;
107+
import static org.hamcrest.Matchers.startsWith;
107108

108109
//@TestLogging(value = "org.elasticsearch.xpack.esql:TRACE", reason = "debug")
109110
public class StatementParserTests extends AbstractStatementParserTests {
@@ -3485,8 +3486,15 @@ public void testSample() {
34853486
assumeTrue("SAMPLE requires corresponding capability", EsqlCapabilities.Cap.SAMPLE_V3.isEnabled());
34863487
expectError("FROM test | SAMPLE .1 2", "line 1:23: extraneous input '2' expecting <EOF>");
34873488
expectError("FROM test | SAMPLE .1 \"2\"", "line 1:23: extraneous input '\"2\"' expecting <EOF>");
3488-
expectError("FROM test | SAMPLE 1", "line 1:20: mismatched input '1' expecting {DECIMAL_LITERAL, '+', '-'}");
3489-
expectError("FROM test | SAMPLE", "line 1:19: mismatched input '<EOF>' expecting {DECIMAL_LITERAL, '+', '-'}");
3489+
expectError(
3490+
"FROM test | SAMPLE 1",
3491+
"line 1:13: invalid value for SAMPLE probability [1], expecting a number between 0 and 1, exclusive"
3492+
);
3493+
expectThrows(
3494+
ParsingException.class,
3495+
startsWith("line 1:19: mismatched input '<EOF>' expecting {"),
3496+
() -> statement("FROM test | SAMPLE")
3497+
);
34903498
}
34913499

34923500
static Alias alias(String name, Expression value) {

x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/10_basic.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -455,14 +455,14 @@ FROM EVAL SORT LIMIT with documents_found:
455455
- method: POST
456456
path: /_query
457457
parameters: [ ]
458-
capabilities: [ parameter_for_limit ]
458+
capabilities: [ parameter_for_limit, parameter_for_sample ]
459459
reason: "named or positional parameters"
460460

461461
- do:
462462
esql.query:
463463
body:
464-
query: 'from test | eval x = ?, y = ?, z = ?, t = ?, u = ?, v = ? | keep x, y, z, t, u, v | limit ?'
465-
params: [{"n1" : "1"}, {"n2" : 2.0}, {"n3" : null}, {"n4" : true}, {"n5" : 123}, {"n6": 1674835275193}, {"l": 3}]
464+
query: 'from test | sample ? | eval x = ?, y = ?, z = ?, t = ?, u = ?, v = ? | keep x, y, z, t, u, v | limit ?'
465+
params: [{"s": 0.99999999999}, {"n1" : "1"}, {"n2" : 2.0}, {"n3" : null}, {"n4" : true}, {"n5" : 123}, {"n6": 1674835275193}, {"l": 3}]
466466

467467
- length: {columns: 6}
468468
- match: {columns.0.name: "x"}
@@ -519,14 +519,14 @@ FROM EVAL SORT LIMIT with documents_found:
519519
- method: POST
520520
path: /_query
521521
parameters: [ ]
522-
capabilities: [ parameter_for_limit ]
522+
capabilities: [ parameter_for_limit, parameter_for_sample ]
523523
reason: "named or positional parameters for field names"
524524

525525
- do:
526526
esql.query:
527527
body:
528-
query: 'from test | stats x = count(?f1), y = sum(?f2) by ?f3 | sort ?f3 | keep ?f3, x, y | limit ?l'
529-
params: [{"f1" : {"identifier" : "time"}}, {"f2" : { "identifier" : "count" }}, {"f3" : { "identifier" : "color"}}, {"l": 3}]
528+
query: 'from test | sample ?s | stats x = count(?f1), y = sum(?f2) by ?f3 | sort ?f3 | keep ?f3, x, y | limit ?l'
529+
params: [{"s": 0.99999999999}, {"f1" : {"identifier" : "time"}}, {"f2" : { "identifier" : "count" }}, {"f3" : { "identifier" : "color"}}, {"l": 3}]
530530

531531
- length: {columns: 3}
532532
- match: {columns.0.name: "color"}

0 commit comments

Comments
 (0)