[TSVB] Lucene query on dashboard level is not respected for annotations request (#124802)

Closes: #124693

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Alexey Antonov 2022-02-08 17:05:16 +03:00 committed by GitHub
parent ca9c004f1a
commit 815685f264
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 796 additions and 19 deletions

View file

@ -0,0 +1,439 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { query } from './query';
import type {
AnnotationsRequestProcessorsFunction,
AnnotationsRequestProcessorsParams,
} from './types';
import { DefaultSearchCapabilities } from '../../../search_strategies/capabilities/default_search_capabilities';
describe('query', () => {
let req: AnnotationsRequestProcessorsParams['req'];
let panel: AnnotationsRequestProcessorsParams['panel'];
let annotation: AnnotationsRequestProcessorsParams['annotation'];
let esQueryConfig: AnnotationsRequestProcessorsParams['esQueryConfig'];
let annotationIndex: AnnotationsRequestProcessorsParams['annotationIndex'];
let capabilities: AnnotationsRequestProcessorsParams['capabilities'];
let uiSettings: AnnotationsRequestProcessorsParams['uiSettings'];
const next = jest.fn((x) => x) as unknown as ReturnType<
ReturnType<AnnotationsRequestProcessorsFunction>
>;
beforeEach(() => {
req = {
body: {
timerange: {
timezone: 'Europe/Minsk',
min: '2022-01-29T22:03:02.317Z',
max: '2022-02-07T09:00:00.000Z',
},
},
} as AnnotationsRequestProcessorsParams['req'];
panel = {} as AnnotationsRequestProcessorsParams['panel'];
annotation = {
time_field: 'fooField',
} as AnnotationsRequestProcessorsParams['annotation'];
annotationIndex = {
indexPattern: undefined,
indexPatternString: 'foo*',
};
capabilities = {
getValidTimeInterval: jest.fn((x) => x),
} as unknown as DefaultSearchCapabilities;
uiSettings = {
get: jest.fn().mockResolvedValue(100),
} as unknown as AnnotationsRequestProcessorsParams['uiSettings'];
});
test('should set "size" to 0', async () => {
const doc = await query({
req,
panel,
annotation,
esQueryConfig,
annotationIndex,
capabilities,
uiSettings,
} as AnnotationsRequestProcessorsParams)(next)({});
expect(doc.size).toBe(0);
});
test('should apply global query (Lucene)', async () => {
req.body.query = [
{
query: 'hour_of_day : 1',
language: 'lucene',
},
];
annotation.ignore_global_filters = 0;
const doc = await query({
req,
panel,
annotation,
esQueryConfig,
annotationIndex,
capabilities,
uiSettings,
} as AnnotationsRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [],
"must": Array [
Object {
"query_string": Object {
"query": "hour_of_day : 1",
},
},
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T08:00:00.000Z",
},
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
test('should apply global query (KQL)', async () => {
req.body.query = [
{
query: 'hour_of_day : 1',
language: 'kuery',
},
];
annotation.ignore_global_filters = 0;
const doc = await query({
req,
panel,
annotation,
esQueryConfig,
annotationIndex,
capabilities,
uiSettings,
} as AnnotationsRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [
Object {
"bool": Object {
"minimum_should_match": 1,
"should": Array [
Object {
"match": Object {
"hour_of_day": "1",
},
},
],
},
},
],
"must": Array [
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T08:00:00.000Z",
},
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
test('should apply global filters', async () => {
req.body.filters = [
{
meta: {
index: '90943e30-9a47-11e8-b64d-95841ca0b247',
alias: null,
negate: false,
disabled: false,
type: 'exists',
key: 'referer',
value: 'exists',
},
query: {
exists: {
field: 'referer',
},
},
},
];
annotation.ignore_global_filters = 0;
const doc = await query({
req,
panel,
annotation,
esQueryConfig,
annotationIndex,
capabilities,
uiSettings,
} as AnnotationsRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [
Object {
"exists": Object {
"field": "referer",
},
},
],
"must": Array [
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T08:00:00.000Z",
},
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
test('should add panel filters and merge it with global one', async () => {
req.body.query = [
{
query: 'hour_of_day : 1',
language: 'kuery',
},
];
panel.filter = {
query: 'agent : 2',
language: 'kuery',
};
annotation.ignore_global_filters = 0;
annotation.ignore_panel_filters = 0;
const doc = await query({
req,
panel,
annotation,
esQueryConfig,
annotationIndex,
capabilities,
uiSettings,
} as AnnotationsRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [
Object {
"bool": Object {
"minimum_should_match": 1,
"should": Array [
Object {
"match": Object {
"hour_of_day": "1",
},
},
],
},
},
],
"must": Array [
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T08:00:00.000Z",
},
},
},
Object {
"bool": Object {
"filter": Array [
Object {
"bool": Object {
"minimum_should_match": 1,
"should": Array [
Object {
"match": Object {
"agent": "2",
},
},
],
},
},
],
"must": Array [],
"must_not": Array [],
"should": Array [],
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
test('should ignore global and panel filters/queries ', async () => {
req.body.query = [
{
query: 'hour_of_day : 1',
language: 'kuery',
},
];
req.body.filters = [
{
meta: {
index: '90943e30-9a47-11e8-b64d-95841ca0b247',
alias: null,
negate: false,
disabled: false,
type: 'exists',
key: 'referer',
value: 'exists',
},
query: {
exists: {
field: 'referer',
},
},
},
];
panel.filter = {
query: 'agent : 2',
language: 'kuery',
};
annotation.ignore_global_filters = 1;
annotation.ignore_panel_filters = 1;
const doc = await query({
req,
panel,
annotation,
esQueryConfig,
annotationIndex,
capabilities,
uiSettings,
} as AnnotationsRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [],
"must": Array [
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T08:00:00.000Z",
},
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
test('should add annotation query ', async () => {
annotation.query_string = {
query: 'hour_of_day : 1',
language: 'kuery',
};
const doc = await query({
req,
panel,
annotation,
esQueryConfig,
annotationIndex,
capabilities,
uiSettings,
} as AnnotationsRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [],
"must": Array [
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T08:00:00.000Z",
},
},
},
Object {
"bool": Object {
"filter": Array [
Object {
"bool": Object {
"minimum_should_match": 1,
"should": Array [
Object {
"match": Object {
"hour_of_day": "1",
},
},
],
},
},
],
"must": Array [],
"must_not": Array [],
"should": Array [],
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
});

View file

@ -34,14 +34,12 @@ export const query: AnnotationsRequestProcessorsFunction = ({
const { bucketSize } = getBucketSize(req, 'auto', capabilities, barTargetUiSettings);
const { from, to } = getTimerange(req);
doc.size = 0;
const queries = !annotation.ignore_global_filters ? req.body.query : [];
const filters = !annotation.ignore_global_filters ? req.body.filters : [];
const esQuery = buildEsQuery(indexPattern, queries, filters, esQueryConfig);
doc.query = buildEsQuery(indexPattern, queries, filters, esQueryConfig);
const boolFilters: unknown[] = [
{
if (timeField) {
esQuery.bool.must.push({
range: {
[timeField]: {
gte: from.toISOString(),
@ -49,25 +47,28 @@ export const query: AnnotationsRequestProcessorsFunction = ({
format: 'strict_date_optional_time',
},
},
},
];
});
}
if (annotation.query_string) {
boolFilters.push(buildEsQuery(indexPattern, [annotation.query_string], [], esQueryConfig));
esQuery.bool.must.push(
buildEsQuery(indexPattern, [annotation.query_string], [], esQueryConfig)
);
}
if (!annotation.ignore_panel_filters && panel.filter) {
boolFilters.push(buildEsQuery(indexPattern, [panel.filter], [], esQueryConfig));
esQuery.bool.must.push(buildEsQuery(indexPattern, [panel.filter], [], esQueryConfig));
}
if (annotation.fields) {
const fields = annotation.fields.split(/[,\s]+/) || [];
fields.forEach((field) => {
boolFilters.push({ exists: { field } });
esQuery.bool.must.push({ exists: { field } });
});
}
overwrite(doc, 'query.bool.must', boolFilters);
overwrite(doc, 'size', 0);
overwrite(doc, 'query', esQuery);
return next(doc);
};

View file

@ -0,0 +1,340 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { query } from './query';
import type { TableRequestProcessorsFunction, TableRequestProcessorsParams } from './types';
describe('query', () => {
let req: TableRequestProcessorsParams['req'];
let panel: TableRequestProcessorsParams['panel'];
let seriesIndex: TableRequestProcessorsParams['seriesIndex'];
let buildSeriesMetaParams: TableRequestProcessorsParams['buildSeriesMetaParams'];
const next = jest.fn((x) => x) as unknown as ReturnType<
ReturnType<TableRequestProcessorsFunction>
>;
beforeEach(() => {
req = {
body: {
timerange: {
timezone: 'Europe/Minsk',
min: '2022-01-29T22:03:02.317Z',
max: '2022-02-07T09:00:00.000Z',
},
},
} as TableRequestProcessorsParams['req'];
panel = {} as TableRequestProcessorsParams['panel'];
seriesIndex = {
indexPattern: undefined,
indexPatternString: 'foo*',
};
buildSeriesMetaParams = jest.fn().mockResolvedValue({ timeField: 'fooField' });
});
test('should set "size" to 0', async () => {
const doc = await query({
req,
panel,
seriesIndex,
buildSeriesMetaParams,
} as TableRequestProcessorsParams)(next)({});
expect(doc.size).toBe(0);
});
test('should apply global query (Lucene)', async () => {
req.body.query = [
{
query: 'hour_of_day : 1',
language: 'lucene',
},
];
panel.ignore_global_filter = 0;
const doc = await query({
req,
panel,
seriesIndex,
buildSeriesMetaParams,
} as TableRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [],
"must": Array [
Object {
"query_string": Object {
"query": "hour_of_day : 1",
},
},
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T09:00:00.000Z",
},
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
test('should apply global query (KQL)', async () => {
req.body.query = [
{
query: 'hour_of_day : 1',
language: 'kuery',
},
];
panel.ignore_global_filter = 0;
const doc = await query({
req,
panel,
seriesIndex,
buildSeriesMetaParams,
} as TableRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [
Object {
"bool": Object {
"minimum_should_match": 1,
"should": Array [
Object {
"match": Object {
"hour_of_day": "1",
},
},
],
},
},
],
"must": Array [
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T09:00:00.000Z",
},
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
test('should apply global filters', async () => {
req.body.filters = [
{
meta: {
index: '90943e30-9a47-11e8-b64d-95841ca0b247',
alias: null,
negate: false,
disabled: false,
type: 'exists',
key: 'referer',
value: 'exists',
},
query: {
exists: {
field: 'referer',
},
},
},
];
panel.ignore_global_filter = 0;
const doc = await query({
req,
panel,
seriesIndex,
buildSeriesMetaParams,
} as TableRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [
Object {
"exists": Object {
"field": "referer",
},
},
],
"must": Array [
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T09:00:00.000Z",
},
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
test('should add panel filters and merge it with global one', async () => {
req.body.query = [
{
query: 'hour_of_day : 1',
language: 'kuery',
},
];
panel.filter = {
query: 'agent : 2',
language: 'kuery',
};
panel.ignore_global_filter = 0;
const doc = await query({
req,
panel,
seriesIndex,
buildSeriesMetaParams,
} as TableRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [
Object {
"bool": Object {
"minimum_should_match": 1,
"should": Array [
Object {
"match": Object {
"hour_of_day": "1",
},
},
],
},
},
],
"must": Array [
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T09:00:00.000Z",
},
},
},
Object {
"bool": Object {
"filter": Array [
Object {
"bool": Object {
"minimum_should_match": 1,
"should": Array [
Object {
"match": Object {
"agent": "2",
},
},
],
},
},
],
"must": Array [],
"must_not": Array [],
"should": Array [],
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
test('should ignore global filters/queries in case is panel.ignore_global_filter = 1 ', async () => {
req.body.query = [
{
query: 'hour_of_day : 1',
language: 'kuery',
},
];
req.body.filters = [
{
meta: {
index: '90943e30-9a47-11e8-b64d-95841ca0b247',
alias: null,
negate: false,
disabled: false,
type: 'exists',
key: 'referer',
value: 'exists',
},
query: {
exists: {
field: 'referer',
},
},
},
];
panel.ignore_global_filter = 1;
const doc = await query({
req,
panel,
seriesIndex,
buildSeriesMetaParams,
} as TableRequestProcessorsParams)(next)({});
expect(doc.query).toMatchInlineSnapshot(`
Object {
"bool": Object {
"filter": Array [],
"must": Array [
Object {
"range": Object {
"fooField": Object {
"format": "strict_date_optional_time",
"gte": "2022-01-29T22:03:02.317Z",
"lte": "2022-02-07T09:00:00.000Z",
},
},
},
],
"must_not": Array [],
"should": Array [],
},
}
`);
});
});

View file

@ -17,14 +17,10 @@ export const query: TableRequestProcessorsFunction =
const { timeField } = await buildSeriesMetaParams();
const { from, to } = getTimerange(req);
const indexPattern = seriesIndex.indexPattern || undefined;
doc.size = 0;
const queries = !panel.ignore_global_filter ? req.body.query : [];
const filters = !panel.ignore_global_filter ? req.body.filters : [];
doc.query = buildEsQuery(indexPattern, queries, filters, esQueryConfig);
const boolFilters: unknown[] = [];
const esQuery = buildEsQuery(indexPattern, queries, filters, esQueryConfig);
if (timeField) {
const timerange = {
@ -37,13 +33,14 @@ export const query: TableRequestProcessorsFunction =
},
};
boolFilters.push(timerange);
esQuery.bool.must.push(timerange);
}
if (panel.filter) {
boolFilters.push(buildEsQuery(indexPattern, [panel.filter], [], esQueryConfig));
esQuery.bool.must.push(buildEsQuery(indexPattern, [panel.filter], [], esQueryConfig));
}
overwrite(doc, 'query.bool.must', boolFilters);
overwrite(doc, 'size', 0);
overwrite(doc, 'query', esQuery);
return next(doc);
};