Skip to content

Commit e81cd4d

Browse files
committed
Merge remote-tracking branch 'upstream/main' into exclude_vector_source
2 parents 762e7be + ec82abd commit e81cd4d

File tree

83 files changed

+3741
-2712
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+3741
-2712
lines changed

docs/changelog/132167.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 132167
2+
summary: Deal with internally created IN in a different way for EQL
3+
area: EQL
4+
type: bug
5+
issues:
6+
- 118621

docs/changelog/132320.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 132320
2+
summary: "Aggs: Add validation to Bucket script pipeline agg"
3+
area: Aggregations
4+
type: bug
5+
issues:
6+
- 132272

modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/bucket_script.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,31 @@ top level fails:
340340
buckets_path:
341341
b: b
342342
script: params.b + 12
343+
344+
---
345+
invalid parent aggregation:
346+
- requires:
347+
capabilities:
348+
- method: POST
349+
path: /_search
350+
capabilities: [ bucket_script_parent_multi_bucket_error ]
351+
test_runner_features: [capabilities]
352+
reason: "changed error 500 to 400"
353+
- do:
354+
catch: /Expected a multi bucket aggregation but got \[InternalFilter\] for aggregation \[d\]/
355+
search:
356+
body:
357+
aggs:
358+
a:
359+
filter:
360+
term:
361+
a: 1
362+
aggs:
363+
b:
364+
sum:
365+
field: b
366+
d:
367+
bucket_script:
368+
buckets_path:
369+
b: b
370+
script: params.b + 12

server/src/main/java/org/elasticsearch/rest/action/search/SearchCapabilities.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ private SearchCapabilities() {}
5555
private static final String FIELD_EXISTS_QUERY_FOR_TEXT_FIELDS_NO_INDEX_OR_DV = "field_exists_query_for_text_fields_no_index_or_dv";
5656
private static final String UPDATE_FIELD_TO_BBQ_DISK = "update_field_to_bbq_disk";
5757
private static final String KNN_FILTER_ON_NESTED_FIELDS_CAPABILITY = "knn_filter_on_nested_fields";
58+
private static final String BUCKET_SCRIPT_PARENT_MULTI_BUCKET_ERROR = "bucket_script_parent_multi_bucket_error";
5859
private static final String EXCLUDE_SOURCE_VECTORS_SETTING = "exclude_source_vectors_setting";
5960

6061
public static final Set<String> CAPABILITIES;
@@ -81,8 +82,9 @@ private SearchCapabilities() {}
8182
capabilities.add(DENSE_VECTOR_UPDATABLE_BBQ);
8283
capabilities.add(FIELD_EXISTS_QUERY_FOR_TEXT_FIELDS_NO_INDEX_OR_DV);
8384
capabilities.add(UPDATE_FIELD_TO_BBQ_DISK);
84-
capabilities.add(EXCLUDE_SOURCE_VECTORS_SETTING);
8585
capabilities.add(KNN_FILTER_ON_NESTED_FIELDS_CAPABILITY);
86+
capabilities.add(BUCKET_SCRIPT_PARENT_MULTI_BUCKET_ERROR);
87+
capabilities.add(EXCLUDE_SOURCE_VECTORS_SETTING);
8688
CAPABILITIES = Set.copyOf(capabilities);
8789
}
8890
}

server/src/main/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptPipelineAggregator.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.ArrayList;
2222
import java.util.HashMap;
2323
import java.util.List;
24+
import java.util.Locale;
2425
import java.util.Map;
2526

2627
import static org.elasticsearch.search.aggregations.pipeline.BucketHelpers.resolveBucketValue;
@@ -47,10 +48,24 @@ public class BucketScriptPipelineAggregator extends PipelineAggregator {
4748
}
4849

4950
@Override
51+
@SuppressWarnings({ "rawtypes", "unchecked" })
5052
public InternalAggregation reduce(InternalAggregation aggregation, AggregationReduceContext reduceContext) {
51-
@SuppressWarnings({ "rawtypes", "unchecked" })
52-
InternalMultiBucketAggregation<InternalMultiBucketAggregation, InternalMultiBucketAggregation.InternalBucket> originalAgg =
53-
(InternalMultiBucketAggregation<InternalMultiBucketAggregation, InternalMultiBucketAggregation.InternalBucket>) aggregation;
53+
54+
InternalMultiBucketAggregation<InternalMultiBucketAggregation, InternalMultiBucketAggregation.InternalBucket> originalAgg;
55+
56+
if (aggregation instanceof InternalMultiBucketAggregation multiBucketAggregation) {
57+
originalAgg = multiBucketAggregation;
58+
} else {
59+
throw new IllegalArgumentException(
60+
String.format(
61+
Locale.ROOT,
62+
"Expected a multi bucket aggregation but got [%s] for aggregation [%s]",
63+
aggregation.getClass().getSimpleName(),
64+
name()
65+
)
66+
);
67+
}
68+
5469
List<? extends InternalMultiBucketAggregation.InternalBucket> buckets = originalAgg.getBuckets();
5570

5671
BucketAggregationScript.Factory factory = reduceContext.scriptService().compile(script, BucketAggregationScript.CONTEXT);

server/src/test/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptAggregatorTests.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
import org.elasticsearch.script.ScriptModule;
3232
import org.elasticsearch.script.ScriptService;
3333
import org.elasticsearch.script.ScriptType;
34+
import org.elasticsearch.search.aggregations.AggregationBuilder;
3435
import org.elasticsearch.search.aggregations.AggregatorTestCase;
36+
import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
3537
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregationBuilder;
3638
import org.elasticsearch.search.aggregations.bucket.filter.InternalFilters;
3739
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
@@ -102,8 +104,42 @@ public void testScript() throws IOException {
102104
);
103105
}
104106

107+
public void testNonMultiBucketParent() {
108+
MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType("number_field", NumberFieldMapper.NumberType.INTEGER);
109+
MappedFieldType fieldType1 = new KeywordFieldMapper.KeywordFieldType("the_field");
110+
111+
FilterAggregationBuilder filter = new FilterAggregationBuilder("placeholder", new MatchAllQueryBuilder()).subAggregation(
112+
new TermsAggregationBuilder("the_terms").userValueTypeHint(ValueType.STRING)
113+
.field("the_field")
114+
.subAggregation(new AvgAggregationBuilder("the_avg").field("number_field"))
115+
)
116+
.subAggregation(
117+
new BucketScriptPipelineAggregationBuilder(
118+
"bucket_script",
119+
Collections.singletonMap("the_avg", "the_terms['test1']>the_avg.value"),
120+
new Script(ScriptType.INLINE, MockScriptEngine.NAME, SCRIPT_NAME, Collections.emptyMap())
121+
)
122+
);
123+
124+
assertThrows(
125+
"Expected a multi bucket aggregation but got [InternalFilter] for aggregation [bucket_script]",
126+
IllegalArgumentException.class,
127+
() -> testCase(filter, new MatchAllDocsQuery(), iw -> {
128+
Document doc = new Document();
129+
doc.add(new SortedSetDocValuesField("the_field", new BytesRef("test1")));
130+
doc.add(new SortedNumericDocValuesField("number_field", 19));
131+
iw.addDocument(doc);
132+
133+
doc = new Document();
134+
doc.add(new SortedSetDocValuesField("the_field", new BytesRef("test2")));
135+
doc.add(new SortedNumericDocValuesField("number_field", 55));
136+
iw.addDocument(doc);
137+
}, f -> fail("This shouldn't be called"), fieldType, fieldType1)
138+
);
139+
}
140+
105141
private void testCase(
106-
FiltersAggregationBuilder aggregationBuilder,
142+
AggregationBuilder aggregationBuilder,
107143
Query query,
108144
CheckedConsumer<RandomIndexWriter, IOException> buildIndex,
109145
Consumer<InternalFilters> verify,

x-pack/plugin/eql/qa/common/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,10 @@ dependencies {
88
// TOML parser for EqlActionIT tests
99
api 'io.ous:jtoml:2.0.0'
1010
}
11+
12+
tasks.register("loadTestData", JavaExec) {
13+
group = "Execution"
14+
description = "Loads EQL Spec Tests data on a running stand-alone instance"
15+
classpath = sourceSets.main.runtimeClasspath
16+
mainClass = "org.elasticsearch.test.eql.DataLoader"
17+
}

x-pack/plugin/eql/qa/common/src/main/java/org/elasticsearch/test/eql/DataLoader.java

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,47 +76,69 @@ private static Map<String, String[]> getReplacementPatterns() {
7676
public static void main(String[] args) throws IOException {
7777
main = true;
7878
try (RestClient client = RestClient.builder(new HttpHost("localhost", 9200)).build()) {
79-
loadDatasetIntoEs(client, DataLoader::createParser);
79+
loadDatasetIntoEsWithIndexCreator(client, DataLoader::createParser, (restClient, indexName, indexMapping) -> {
80+
// don't use ESRestTestCase methods here or, if you do, test running the main method before making the change
81+
StringBuilder jsonBody = new StringBuilder("{");
82+
jsonBody.append("\"settings\":{\"number_of_shards\":1},");
83+
jsonBody.append("\"mappings\":");
84+
jsonBody.append(indexMapping);
85+
jsonBody.append("}");
86+
87+
Request request = new Request("PUT", "/" + indexName);
88+
request.setJsonEntity(jsonBody.toString());
89+
restClient.performRequest(request);
90+
});
8091
}
8192
}
8293

8394
public static void loadDatasetIntoEs(RestClient client, CheckedBiFunction<XContent, InputStream, XContentParser, IOException> p)
8495
throws IOException {
96+
loadDatasetIntoEsWithIndexCreator(client, p, (restClient, indexName, indexMapping) -> {
97+
ESRestTestCase.createIndex(restClient, indexName, Settings.builder().put("number_of_shards", 1).build(), indexMapping, null);
98+
});
99+
}
100+
101+
private static void loadDatasetIntoEsWithIndexCreator(
102+
RestClient client,
103+
CheckedBiFunction<XContent, InputStream, XContentParser, IOException> p,
104+
IndexCreator indexCreator
105+
) throws IOException {
85106

86107
//
87108
// Main Index
88109
//
89-
load(client, TEST_INDEX, null, DataLoader::timestampToUnixMillis, p);
110+
load(client, TEST_INDEX, null, DataLoader::timestampToUnixMillis, p, indexCreator);
90111
//
91112
// Aux Index
92113
//
93-
load(client, TEST_EXTRA_INDEX, null, null, p);
114+
load(client, TEST_EXTRA_INDEX, null, null, p, indexCreator);
94115
//
95116
// Date_Nanos index
96117
//
97118
// The data for this index is loaded from the same endgame-140.data sample, only having the mapping for @timestamp changed: the
98119
// chosen Windows filetime timestamps (2017+) can coincidentally also be readily used as nano-resolution unix timestamps (1973+).
99120
// There are mixed values with and without nanos precision so that the filtering is properly tested for both cases.
100-
load(client, TEST_NANOS_INDEX, TEST_INDEX, DataLoader::timestampToUnixNanos, p);
101-
load(client, TEST_SAMPLE, null, null, p);
121+
load(client, TEST_NANOS_INDEX, TEST_INDEX, DataLoader::timestampToUnixNanos, p, indexCreator);
122+
load(client, TEST_SAMPLE, null, null, p, indexCreator);
102123
//
103124
// missing_events index
104125
//
105-
load(client, TEST_MISSING_EVENTS_INDEX, null, null, p);
106-
load(client, TEST_SAMPLE_MULTI, null, null, p);
126+
load(client, TEST_MISSING_EVENTS_INDEX, null, null, p, indexCreator);
127+
load(client, TEST_SAMPLE_MULTI, null, null, p, indexCreator);
107128
//
108129
// index with a runtime field ("broken", type long) that causes shard failures.
109130
// the rest of the mapping is the same as TEST_INDEX
110131
//
111-
load(client, TEST_SHARD_FAILURES_INDEX, null, DataLoader::timestampToUnixMillis, p);
132+
load(client, TEST_SHARD_FAILURES_INDEX, null, DataLoader::timestampToUnixMillis, p, indexCreator);
112133
}
113134

114135
private static void load(
115136
RestClient client,
116137
String indexNames,
117138
String dataName,
118139
Consumer<Map<String, Object>> datasetTransform,
119-
CheckedBiFunction<XContent, InputStream, XContentParser, IOException> p
140+
CheckedBiFunction<XContent, InputStream, XContentParser, IOException> p,
141+
IndexCreator indexCreator
120142
) throws IOException {
121143
String[] splitNames = indexNames.split(",");
122144
for (String indexName : splitNames) {
@@ -130,15 +152,11 @@ private static void load(
130152
if (data == null) {
131153
throw new IllegalArgumentException("Cannot find resource " + name);
132154
}
133-
createTestIndex(client, indexName, readMapping(mapping));
155+
indexCreator.createIndex(client, indexName, readMapping(mapping));
134156
loadData(client, indexName, datasetTransform, data, p);
135157
}
136158
}
137159

138-
private static void createTestIndex(RestClient client, String indexName, String mapping) throws IOException {
139-
ESRestTestCase.createIndex(client, indexName, Settings.builder().put("number_of_shards", 1).build(), mapping, null);
140-
}
141-
142160
/**
143161
* Reads the mapping file, ignoring comments and replacing placeholders for random types.
144162
*/
@@ -236,4 +254,8 @@ private static XContentParser createParser(XContent xContent, InputStream data)
236254
NamedXContentRegistry contentRegistry = new NamedXContentRegistry(ClusterModule.getNamedXWriteables());
237255
return xContent.createParser(contentRegistry, LoggingDeprecationHandler.INSTANCE, data);
238256
}
257+
258+
private interface IndexCreator {
259+
void createIndex(RestClient client, String indexName, String mapping) throws IOException;
260+
}
239261
}

x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/optimizer/Optimizer.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.BinaryComparisonSimplification;
4545
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.BooleanFunctionEqualsElimination;
4646
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.BooleanSimplification;
47-
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.CombineDisjunctionsToIn;
4847
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.ConstantFolding;
4948
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.LiteralsOnTheRight;
5049
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.OptimizerRule;
@@ -252,6 +251,14 @@ protected Expression maybeSimplifyNegatable(Expression e) {
252251

253252
}
254253

254+
static class CombineDisjunctionsToIn extends org.elasticsearch.xpack.ql.optimizer.OptimizerRules.CombineDisjunctionsToIn {
255+
256+
@Override
257+
protected boolean shouldValidateIn() {
258+
return true;
259+
}
260+
}
261+
255262
static class PruneFilters extends org.elasticsearch.xpack.ql.optimizer.OptimizerRules.PruneFilters {
256263

257264
@Override

x-pack/plugin/eql/src/test/resources/querytranslator_tests.txt

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,90 @@ process where process_name in ("python.exe", "SMSS.exe", "explorer.exe")
123123
"terms":{"process_name":["python.exe","SMSS.exe","explorer.exe"],
124124
;
125125

126+
mutipleOrEquals_As_InTranslation1
127+
process where process_name == "python.exe" or process_name == "SMSS.exe" or process_name == "explorer.exe"
128+
;
129+
"terms":{"process_name":["python.exe","SMSS.exe","explorer.exe"],
130+
;
131+
132+
multipleOrAndEquals_As_InTranslation
133+
process where process_name == "python.exe" and process_name == "SMSS.exe" or process_name == "explorer.exe" or process_name == "test.exe"
134+
;
135+
{"bool":{"should":[{"bool":{"must":[{"term":{"process_name":{"value":"python.exe"}}},{"term":{"process_name":{"value":"SMSS.exe"}}}],"boost":1.0}},{"terms":{"process_name":["explorer.exe","test.exe"],"boost":1.0}}],"boost":1.0}}
136+
;
137+
138+
mutipleOrEquals_As_InTranslation2
139+
process where source_address == "123.12.1.1" or (opcode == 123 or opcode == 127)
140+
;
141+
{"bool":{"should":[{"term":{"source_address":{"value":"123.12.1.1"}}},{"terms":{"opcode":[123,127],"boost":1.0}}],"boost":1.0}}
142+
;
143+
144+
mutipleOrEquals_As_InTranslation3
145+
process where (source_address == "123.12.1.1" or source_address == "127.0.0.1") and (opcode == 123 or opcode == 127)
146+
;
147+
{"bool":{"should":[{"term":{"source_address":{"value":"123.12.1.1"}}},{"term":{"source_address":{"value":"127.0.0.1"}}}],"boost":1.0}},{"terms":{"opcode":[123,127],"boost":1.0}}
148+
;
149+
150+
mutipleOrEquals_As_InTranslation4
151+
process where (source_address == "123.12.1.1" or source_address == "127.0.0.1") and (opcode == 123 or opcode == 127)
152+
;
153+
"must":[{"bool":{"should":[{"term":{"source_address":{"value":"123.12.1.1"}}},{"term":{"source_address":{"value":"127.0.0.1"}}}],"boost":1.0}},{"terms":{"opcode":[123,127],"boost":1.0}},{"term":{"event.category":{"value":"process"}}}]
154+
;
155+
156+
multipleOrIncompatibleTypes1
157+
process where process_name == "python.exe" or process_name == 2 or process_name == "3"
158+
;
159+
{"bool":{"should":[{"term":{"process_name":{"value":"python.exe"}}},{"term":{"process_name":{"value":2}}},{"term":{"process_name":{"value":"3"}}}],"boost":1.0}}
160+
;
161+
162+
multipleOrIncompatibleTypes2
163+
process where process_name == "1" or process_name == 2 or process_name == "3"
164+
;
165+
{"bool":{"should":[{"term":{"process_name":{"value":"1"}}},{"term":{"process_name":{"value":2}}},{"term":{"process_name":{"value":"3"}}}],"boost":1.0}}
166+
;
167+
168+
multipleOrIncompatibleTypes3
169+
process where process_name == 1.2 or process_name == 2 or process_name == "3"
170+
;
171+
{"bool":{"should":[{"term":{"process_name":{"value":1.2}}},{"term":{"process_name":{"value":2}}},{"term":{"process_name":{"value":"3"}}}],"boost":1.0}}
172+
;
173+
174+
// this query as an equivalent with
175+
// process where process_name in (1.2, 2, 3)
176+
// will result in a user error: 1st argument of [process_name in (1.2, 2, 3)] must be [keyword], found value [1.2] type [double]
177+
multipleOrIncompatibleTypes4
178+
process where process_name == 1.2 or process_name == 2 or process_name == 3
179+
;
180+
{"bool":{"should":[{"term":{"process_name":{"value":1.2}}},{"term":{"process_name":{"value":2}}},{"term":{"process_name":{"value":3}}}],"boost":1.0}}
181+
;
182+
183+
// this query as an equivalent with
184+
// process where source_address in ("123.12.1.1", "123.12.1.2")
185+
// will result in a user error: 1st argument of [source_address in ("123.12.1.1", "123.12.1.2")] must be [ip], found value ["123.12.1.1"] type [keyword]
186+
multipleOrIncompatibleTypes5
187+
process where source_address == "123.12.1.1" or source_address == "123.12.1.2"
188+
;
189+
{"bool":{"should":[{"term":{"source_address":{"value":"123.12.1.1"}}},{"term":{"source_address":{"value":"123.12.1.2"}}}],"boost":1.0}}
190+
;
191+
192+
multipleOrIncompatibleTypes6
193+
process where source_address == "123.12.1.1" or source_address == concat("123.12.","1.2")
194+
;
195+
{"bool":{"should":[{"term":{"source_address":{"value":"123.12.1.1"}}},{"term":{"source_address":{"value":"123.12.1.2"}}}],"boost":1.0}}
196+
;
197+
198+
multipleOrIncompatibleTypes7
199+
process where source_address == "123.12.1.1" and (source_address == "123.12.1.2" or source_address >= "127.0.0.1")
200+
;
201+
"must":[{"term":{"source_address":{"value":"123.12.1.1"}}},{"bool":{"should":[{"term":{"source_address":{"value":"123.12.1.2"}}},{"range":{"source_address":{"gte":"127.0.0.1","boost":1.0}}}],"boost":1.0}},{"term":{"event.category":{"value":"process"}}}]
202+
;
203+
204+
multipleOrIncompatibleTypes8
205+
process where source_address == "123.12.1.1" and (source_address == "123.12.1.2" or source_address == "127.0.0.1")
206+
;
207+
"must":[{"term":{"source_address":{"value":"123.12.1.1"}}},{"bool":{"should":[{"term":{"source_address":{"value":"123.12.1.2"}}},{"term":{"source_address":{"value":"127.0.0.1"}}}],"boost":1.0}},{"term":{"event.category":{"value":"process"}}}]
208+
;
209+
126210
inFilterWithScripting
127211
process where substring(command_line, 5) in ("test*","best")
128212
;

0 commit comments

Comments
 (0)