Skip to content

Commit a1b49f6

Browse files
[9.1] [Investigations][Bug] - Check for empty dataView (elastic#235144) (elastic#235215)
# Backport This will backport the following commits from `main` to `9.1`: - [[Investigations][Bug] - Check for empty dataView (elastic#235144)](elastic#235144) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Michael Olorunnisola","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-09-16T14:09:44Z","message":"[Investigations][Bug] - Check for empty dataView (elastic#235144)\n\n## Summary\n\nThis PR fixes an issue with the alert page filtering when the below\nconfig is enabled:\n\n<img width=\"627\" height=\"181\" alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/39fc9a61-d794-407d-bea9-16792c9a6535\"\n/>\n\nWhen enabled, the config looks to make sure that searches are only done\nagainst index patterns that are mapped to the given dataView. When\nintroducing the code to migrate to our new dataView picker\n[here](https://github.com/elastic/kibana/blob/9659a525327b2e46478f45d03ce39103848361cc/x-pack/solutions/security/plugins/security_solution/public/common/lib/kuery/index.ts#L231)\nin the following PR elastic#225726, a\ncheck was done to only apply the new DataView when it was provided. To\nfix a separate issue regarding flashing of the alerts page, this\nfollowing [initial\ndataView](https://github.com/elastic/kibana/blob/9659a525327b2e46478f45d03ce39103848361cc/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/hooks/use_data_view.ts#L45)\nwas introduced with this pr:\nhttps://github.com/elastic/pull/225675\n\nIn short, the dataView object was always defined, even if it was just an\ninitial dataView leading to the fields being queried against not being\nmapped.\n\nThe necessary checks are added in this PR\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios","sha":"128528cbfe123c5f0234824e5834755cab58b0c4","branchLabelMapping":{"^v9.2.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:fix","Team:Threat Hunting:Investigations","backport:version","v9.2.0","v9.1.4","v8.19.4","v8.19.5","v9.1.5"],"title":"[Investigations][Bug] - Check for empty dataView","number":235144,"url":"https://github.com/elastic/kibana/pull/235144","mergeCommit":{"message":"[Investigations][Bug] - Check for empty dataView (elastic#235144)\n\n## Summary\n\nThis PR fixes an issue with the alert page filtering when the below\nconfig is enabled:\n\n<img width=\"627\" height=\"181\" alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/39fc9a61-d794-407d-bea9-16792c9a6535\"\n/>\n\nWhen enabled, the config looks to make sure that searches are only done\nagainst index patterns that are mapped to the given dataView. When\nintroducing the code to migrate to our new dataView picker\n[here](https://github.com/elastic/kibana/blob/9659a525327b2e46478f45d03ce39103848361cc/x-pack/solutions/security/plugins/security_solution/public/common/lib/kuery/index.ts#L231)\nin the following PR elastic#225726, a\ncheck was done to only apply the new DataView when it was provided. To\nfix a separate issue regarding flashing of the alerts page, this\nfollowing [initial\ndataView](https://github.com/elastic/kibana/blob/9659a525327b2e46478f45d03ce39103848361cc/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/hooks/use_data_view.ts#L45)\nwas introduced with this pr:\nhttps://github.com/elastic/pull/225675\n\nIn short, the dataView object was always defined, even if it was just an\ninitial dataView leading to the fields being queried against not being\nmapped.\n\nThe necessary checks are added in this PR\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios","sha":"128528cbfe123c5f0234824e5834755cab58b0c4"}},"sourceBranch":"main","suggestedTargetBranches":["9.1","8.19"],"targetPullRequestStates":[{"branch":"main","label":"v9.2.0","branchLabelMappingKey":"^v9.2.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/235144","number":235144,"mergeCommit":{"message":"[Investigations][Bug] - Check for empty dataView (elastic#235144)\n\n## Summary\n\nThis PR fixes an issue with the alert page filtering when the below\nconfig is enabled:\n\n<img width=\"627\" height=\"181\" alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/39fc9a61-d794-407d-bea9-16792c9a6535\"\n/>\n\nWhen enabled, the config looks to make sure that searches are only done\nagainst index patterns that are mapped to the given dataView. When\nintroducing the code to migrate to our new dataView picker\n[here](https://github.com/elastic/kibana/blob/9659a525327b2e46478f45d03ce39103848361cc/x-pack/solutions/security/plugins/security_solution/public/common/lib/kuery/index.ts#L231)\nin the following PR elastic#225726, a\ncheck was done to only apply the new DataView when it was provided. To\nfix a separate issue regarding flashing of the alerts page, this\nfollowing [initial\ndataView](https://github.com/elastic/kibana/blob/9659a525327b2e46478f45d03ce39103848361cc/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/hooks/use_data_view.ts#L45)\nwas introduced with this pr:\nhttps://github.com/elastic/pull/225675\n\nIn short, the dataView object was always defined, even if it was just an\ninitial dataView leading to the fields being queried against not being\nmapped.\n\nThe necessary checks are added in this PR\n\n### Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers should verify this PR satisfies this list as well.\n\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios","sha":"128528cbfe123c5f0234824e5834755cab58b0c4"}},{"branch":"9.1","label":"v9.1.4","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.19","label":"v8.19.4","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Michael Olorunnisola <[email protected]>
1 parent 662ae32 commit a1b49f6

File tree

2 files changed

+172
-93
lines changed

2 files changed

+172
-93
lines changed

x-pack/solutions/security/plugins/security_solution/public/common/lib/kuery/index.test.ts

Lines changed: 169 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -58,115 +58,117 @@ describe('convertToBuildEsQuery', () => {
5858
dateFormatTZ: 'Browser',
5959
};
6060

61-
it('should, by default, build a query where the `nested` fields syntax includes the `"ignore_unmapped":true` option', () => {
62-
const [converted, _] = convertToBuildEsQuery({
63-
config,
64-
dataView: createStubDataView({ spec: {} }),
65-
queries: queryWithNestedFields,
66-
dataViewSpec: mockDataViewSpec,
67-
filters,
68-
});
69-
70-
expect(JSON.parse(converted ?? '')).to.eql({
71-
bool: {
72-
must: [],
73-
filter: [
74-
{
75-
bool: {
76-
filter: [
77-
{
78-
bool: {
79-
filter: [
80-
{
81-
// ✅ Nested fields are converted to use the `nested` query syntax
82-
nested: {
83-
path: 'threat.enrichments',
84-
query: {
85-
bool: {
86-
should: [
87-
{
88-
match: {
89-
'threat.enrichments.matched.atomic':
90-
'a4f87cbcd2a4241da77b6bf0c5d9e8553fec991f',
91-
},
61+
const expectedConverted = {
62+
bool: {
63+
must: [],
64+
filter: [
65+
{
66+
bool: {
67+
filter: [
68+
{
69+
bool: {
70+
filter: [
71+
{
72+
// ✅ Nested fields are converted to use the `nested` query syntax
73+
nested: {
74+
path: 'threat.enrichments',
75+
query: {
76+
bool: {
77+
should: [
78+
{
79+
match: {
80+
'threat.enrichments.matched.atomic':
81+
'a4f87cbcd2a4241da77b6bf0c5d9e8553fec991f',
9282
},
93-
],
94-
minimum_should_match: 1,
95-
},
83+
},
84+
],
85+
minimum_should_match: 1,
9686
},
97-
score_mode: 'none',
98-
// ✅ The `nested` query syntax includes the `ignore_unmapped` option
99-
ignore_unmapped: true,
10087
},
88+
score_mode: 'none',
89+
// ✅ The `nested` query syntax includes the `ignore_unmapped` option
90+
ignore_unmapped: true,
10191
},
102-
{
103-
nested: {
104-
path: 'threat.enrichments',
105-
query: {
106-
bool: {
107-
should: [
108-
{
109-
match: {
110-
'threat.enrichments.matched.type': 'indicator_match_rule',
111-
},
92+
},
93+
{
94+
nested: {
95+
path: 'threat.enrichments',
96+
query: {
97+
bool: {
98+
should: [
99+
{
100+
match: {
101+
'threat.enrichments.matched.type': 'indicator_match_rule',
112102
},
113-
],
114-
minimum_should_match: 1,
115-
},
103+
},
104+
],
105+
minimum_should_match: 1,
116106
},
117-
score_mode: 'none',
118-
ignore_unmapped: true,
119107
},
108+
score_mode: 'none',
109+
ignore_unmapped: true,
120110
},
121-
{
122-
nested: {
123-
path: 'threat.enrichments',
124-
query: {
125-
bool: {
126-
should: [
127-
{
128-
match: {
129-
'threat.enrichments.matched.field': 'file.hash.md5',
130-
},
111+
},
112+
{
113+
nested: {
114+
path: 'threat.enrichments',
115+
query: {
116+
bool: {
117+
should: [
118+
{
119+
match: {
120+
'threat.enrichments.matched.field': 'file.hash.md5',
131121
},
132-
],
133-
minimum_should_match: 1,
134-
},
122+
},
123+
],
124+
minimum_should_match: 1,
135125
},
136-
score_mode: 'none',
137-
ignore_unmapped: true,
138126
},
127+
score_mode: 'none',
128+
ignore_unmapped: true,
139129
},
140-
],
141-
},
130+
},
131+
],
142132
},
143-
{
144-
bool: {
145-
should: [
146-
{
147-
exists: {
148-
// ✅ Non-nested fields are NOT converted to the `nested` query syntax
149-
// ✅ Non-nested fields do NOT include the `ignore_unmapped` option
150-
field: '@timestamp',
151-
},
133+
},
134+
{
135+
bool: {
136+
should: [
137+
{
138+
exists: {
139+
// ✅ Non-nested fields are NOT converted to the `nested` query syntax
140+
// ✅ Non-nested fields do NOT include the `ignore_unmapped` option
141+
field: '@timestamp',
152142
},
153-
],
154-
minimum_should_match: 1,
155-
},
143+
},
144+
],
145+
minimum_should_match: 1,
156146
},
157-
],
158-
},
147+
},
148+
],
159149
},
160-
{
161-
exists: {
162-
field: '_id',
163-
},
150+
},
151+
{
152+
exists: {
153+
field: '_id',
164154
},
165-
],
166-
should: [],
167-
must_not: [],
168-
},
155+
},
156+
],
157+
should: [],
158+
must_not: [],
159+
},
160+
};
161+
162+
it('should, by default, build a query where the `nested` fields syntax includes the `"ignore_unmapped":true` option', () => {
163+
const [converted, _] = convertToBuildEsQuery({
164+
config,
165+
dataView: createStubDataView({ spec: {} }),
166+
queries: queryWithNestedFields,
167+
dataViewSpec: mockDataViewSpec,
168+
filters,
169169
});
170+
171+
expect(JSON.parse(converted ?? '')).to.eql(expectedConverted);
170172
});
171173

172174
it('should, when the default is overridden, build a query where `nested` fields include the `"ignore_unmapped":false` option', () => {
@@ -280,6 +282,82 @@ describe('convertToBuildEsQuery', () => {
280282
},
281283
});
282284
});
285+
286+
describe('When ignoreFilterIfFieldNotInIndex is true', () => {
287+
const updatedConfig = { ...config, ignoreFilterIfFieldNotInIndex: true };
288+
289+
it('should use dataViewSpec when an empty dataView is provided', () => {
290+
mockDataViewSpec.fields = {
291+
_id: {
292+
name: '_id',
293+
type: 'string',
294+
esTypes: ['keyword'],
295+
aggregatable: true,
296+
searchable: true,
297+
scripted: false,
298+
},
299+
};
300+
const emptyStubDataView = createStubDataView({ spec: { id: '', title: '' } });
301+
const [converted] = convertToBuildEsQuery({
302+
config: updatedConfig,
303+
dataView: emptyStubDataView, // <-- empty dataView
304+
queries: queryWithNestedFields,
305+
dataViewSpec: mockDataViewSpec, // <-- should be used instead of the empty dataView
306+
filters,
307+
});
308+
309+
expect(JSON.parse(converted ?? '')).to.eql(expectedConverted); // just verify that something was built
310+
});
311+
312+
it('should not use the field if the filter is not mapped in the', () => {
313+
const updatedConvertedWithoutIdQuery = structuredClone(expectedConverted);
314+
updatedConvertedWithoutIdQuery.bool.filter = [updatedConvertedWithoutIdQuery.bool.filter[0]]; // remove the search bar filter
315+
const dataViewWithoutIdMapped = createStubDataView({
316+
spec: {
317+
id: 'test-id',
318+
title: 'some-title',
319+
},
320+
});
321+
const [converted] = convertToBuildEsQuery({
322+
config: updatedConfig,
323+
dataView: dataViewWithoutIdMapped,
324+
queries: queryWithNestedFields,
325+
dataViewSpec: mockDataViewSpec,
326+
filters,
327+
});
328+
329+
expect(JSON.parse(converted ?? '')).to.eql(updatedConvertedWithoutIdQuery); // just verify that something was built
330+
});
331+
332+
it('should use the filters when the field is mapped in the dataView', () => {
333+
const dataViewWithIdMapped = createStubDataView({
334+
spec: {
335+
id: 'test-id',
336+
title: 'some-title',
337+
fields: {
338+
_id: {
339+
name: '_id',
340+
type: 'string',
341+
esTypes: ['keyword'],
342+
aggregatable: true,
343+
searchable: true,
344+
scripted: false,
345+
},
346+
},
347+
},
348+
});
349+
const [converted] = convertToBuildEsQuery({
350+
config: updatedConfig,
351+
dataView: dataViewWithIdMapped,
352+
queries: queryWithNestedFields,
353+
dataViewSpec: mockDataViewSpec,
354+
filters,
355+
});
356+
357+
// This should have the id with the
358+
expect(JSON.parse(converted ?? '')).to.eql(expectedConverted);
359+
});
360+
});
283361
});
284362

285363
describe('buildGlobalQuery', () => {

x-pack/solutions/security/plugins/security_solution/public/common/lib/kuery/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ export const dataViewSpecToViewBase = (dataViewSpec?: DataViewSpec): DataViewBas
210210

211211
export const convertToBuildEsQuery = ({
212212
config,
213-
dataView, // New dataview prepended with feature flag to enable easy cleanup
213+
dataView, // New dataview with newDataViewPickerEnabled
214214
dataViewSpec, // Account for the case where sourcerer is active, but this can just use dataView
215215
queries,
216216
filters,
@@ -225,10 +225,11 @@ export const convertToBuildEsQuery = ({
225225
filters: Filter[];
226226
}): [string, undefined] | [undefined, Error] => {
227227
try {
228+
const newDataViewExists = dataView?.id && dataView?.getIndexPattern();
228229
return [
229230
JSON.stringify(
230231
buildEsQuery(
231-
dataView ?? (dataViewSpecToViewBase(dataViewSpec) as DataView),
232+
newDataViewExists ? dataView : (dataViewSpecToViewBase(dataViewSpec) as DataView),
232233
queries,
233234
filters.filter((f) => f.meta.disabled === false),
234235
{

0 commit comments

Comments
 (0)