Enables optional use of the timezone set in Advanced Settings in queries (#34602)
* Adds time_zone to query * Adds dateFormatTZ to kuery query Removes comments * Adds defaults for the dateFormatTZ to the function signatures * Adds tests for date match in kuery Modifies test * Adds a test for get_es_query_config * Adds test for get timezone from settings utility method * Adds tests for modified range method Adds config param test to node_types/functions code clean up * resolves initial PR comments * Refactors build_es_query test * Refactors get_time_zone_from_settings test * Uses spys to test that the config is passed down to children in ast toElasticsearchQuery * removes default config nulls * Deletes sinon.spy tests in kuery * removes moment.setDefault from __tests__/get_timezone_from_settings.js
This commit is contained in:
parent
50fab1aea3
commit
95f93e5f81
|
@ -11,7 +11,8 @@
|
|||
"kbn:watch": "node scripts/build --source-maps --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": "npm:@elastic/lodash@3.10.1-kibana1"
|
||||
"lodash": "npm:@elastic/lodash@3.10.1-kibana1",
|
||||
"moment-timezone": "^0.5.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.2.3",
|
||||
|
|
|
@ -103,6 +103,36 @@ describe('build query', function () {
|
|||
expect(result).to.eql(expectedResult);
|
||||
});
|
||||
|
||||
it('should use the default time zone set in the Advanced Settings in queries and filters', function () {
|
||||
const queries = [
|
||||
{ query: '@timestamp:"2019-03-23T13:18:00"', language: 'kuery' },
|
||||
{ query: '@timestamp:"2019-03-23T13:18:00"', language: 'lucene' }
|
||||
];
|
||||
const filters = [
|
||||
{ match_all: {}, meta: { type: 'match_all' } }
|
||||
];
|
||||
const config = {
|
||||
allowLeadingWildcards: true,
|
||||
queryStringOptions: {},
|
||||
ignoreFilterIfFieldNotInIndex: false,
|
||||
dateFormatTZ: 'Africa/Johannesburg',
|
||||
};
|
||||
|
||||
const expectedResult = {
|
||||
bool: {
|
||||
must: [
|
||||
decorateQuery(luceneStringToDsl('@timestamp:"2019-03-23T13:18:00"'), config.queryStringOptions, config.dateFormatTZ),
|
||||
{ match_all: {} }
|
||||
],
|
||||
filter: [toElasticsearchQuery(fromKueryExpression('@timestamp:"2019-03-23T13:18:00"'), indexPattern, config)],
|
||||
should: [],
|
||||
must_not: [],
|
||||
}
|
||||
};
|
||||
const result = buildEsQuery(indexPattern, queries, filters, config);
|
||||
expect(result).to.eql(expectedResult);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -29,4 +29,9 @@ describe('Query decorator', function () {
|
|||
const decoratedQuery = decorateQuery({ query_string: { query: '*' } }, { analyze_wildcard: true });
|
||||
expect(decoratedQuery).to.eql({ query_string: { query: '*', analyze_wildcard: true } });
|
||||
});
|
||||
|
||||
it('should add a default of a time_zone parameter if one is provided', function () {
|
||||
const decoratedQuery = decorateQuery({ query_string: { query: '*' } }, { analyze_wildcard: true }, 'America/Phoenix');
|
||||
expect(decoratedQuery).to.eql({ query_string: { query: '*', analyze_wildcard: true, time_zone: 'America/Phoenix' } });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -60,6 +60,31 @@ describe('build query', function () {
|
|||
);
|
||||
});
|
||||
|
||||
|
||||
it('should accept a specific date format for a kuery query into an ES query in the bool\'s filter clause', function () {
|
||||
const queries = [{ query: '@timestamp:"2018-04-03T19:04:17"', language: 'kuery' }];
|
||||
|
||||
const expectedESQueries = queries.map(query => {
|
||||
return toElasticsearchQuery(fromKueryExpression(query.query), indexPattern, { dateFormatTZ: 'America/Phoenix' });
|
||||
});
|
||||
|
||||
const result = buildQueryFromKuery(indexPattern, queries, true, 'America/Phoenix');
|
||||
|
||||
expect(result.filter).to.eql(expectedESQueries);
|
||||
});
|
||||
|
||||
it('should gracefully handle date queries when no date format is provided', function () {
|
||||
const queries = [{ query: '@timestamp:"2018-04-03T19:04:17Z"', language: 'kuery' }];
|
||||
|
||||
const expectedESQueries = queries.map(query => {
|
||||
return toElasticsearchQuery(fromKueryExpression(query.query), indexPattern);
|
||||
});
|
||||
|
||||
const result = buildQueryFromKuery(indexPattern, queries, true);
|
||||
|
||||
expect(result.filter).to.eql(expectedESQueries);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -66,4 +66,22 @@ describe('build query', function () {
|
|||
|
||||
});
|
||||
|
||||
it('should accept a date format in the decorated queries and combine that into the bool\'s must clause', function () {
|
||||
const queries = [
|
||||
{ query: 'foo:bar', language: 'lucene' },
|
||||
{ query: 'bar:baz', language: 'lucene' },
|
||||
];
|
||||
const dateFormatTZ = 'America/Phoenix';
|
||||
|
||||
const expectedESQueries = queries.map(
|
||||
(query) => {
|
||||
return decorateQuery(luceneStringToDsl(query.query), {}, dateFormatTZ);
|
||||
}
|
||||
);
|
||||
|
||||
const result = buildQueryFromLucene(queries, {}, dateFormatTZ);
|
||||
|
||||
expect(result.must).to.eql(expectedESQueries);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { getEsQueryConfig } from '../get_es_query_config';
|
||||
|
||||
const config = {
|
||||
get(item) {
|
||||
return config[item];
|
||||
},
|
||||
'query:allowLeadingWildcards': {
|
||||
allowLeadingWildcards: true,
|
||||
},
|
||||
'query:queryString:options': {
|
||||
queryStringOptions: {},
|
||||
},
|
||||
'courier:ignoreFilterIfFieldNotInIndex': {
|
||||
ignoreFilterIfFieldNotInIndex: true,
|
||||
},
|
||||
'dateFormat:tz': {
|
||||
dateFormatTZ: 'Browser',
|
||||
},
|
||||
};
|
||||
|
||||
describe('getEsQueryConfig', function () {
|
||||
it('should return the parameters of an Elasticsearch query config requested', function () {
|
||||
const result = getEsQueryConfig(config);
|
||||
const expected = {
|
||||
allowLeadingWildcards: {
|
||||
allowLeadingWildcards: true,
|
||||
},
|
||||
dateFormatTZ: {
|
||||
dateFormatTZ: 'Browser',
|
||||
},
|
||||
ignoreFilterIfFieldNotInIndex: {
|
||||
ignoreFilterIfFieldNotInIndex: true,
|
||||
},
|
||||
queryStringOptions: {
|
||||
queryStringOptions: {},
|
||||
},
|
||||
};
|
||||
expect(result).to.eql(expected);
|
||||
expect(result).to.have.keys(
|
||||
'allowLeadingWildcards',
|
||||
'dateFormatTZ',
|
||||
'ignoreFilterIfFieldNotInIndex',
|
||||
'queryStringOptions'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -28,6 +28,7 @@ import { buildQueryFromLucene } from './from_lucene';
|
|||
* @param filters - a filter object or array of filter objects
|
||||
* @param config - an objects with query:allowLeadingWildcards and query:queryString:options UI
|
||||
* settings in form of { allowLeadingWildcards, queryStringOptions }
|
||||
* config contains dateformat:tz
|
||||
*/
|
||||
export function buildEsQuery(
|
||||
indexPattern,
|
||||
|
@ -37,15 +38,15 @@ export function buildEsQuery(
|
|||
allowLeadingWildcards: false,
|
||||
queryStringOptions: {},
|
||||
ignoreFilterIfFieldNotInIndex: false,
|
||||
dateFormatTZ: null,
|
||||
}) {
|
||||
queries = Array.isArray(queries) ? queries : [queries];
|
||||
filters = Array.isArray(filters) ? filters : [filters];
|
||||
|
||||
const validQueries = queries.filter((query) => has(query, 'query'));
|
||||
const queriesByLanguage = groupBy(validQueries, 'language');
|
||||
|
||||
const kueryQuery = buildQueryFromKuery(indexPattern, queriesByLanguage.kuery, config.allowLeadingWildcards);
|
||||
const luceneQuery = buildQueryFromLucene(queriesByLanguage.lucene, config.queryStringOptions);
|
||||
const kueryQuery = buildQueryFromKuery(indexPattern, queriesByLanguage.kuery, config.allowLeadingWildcards, config.dateFormatTZ);
|
||||
const luceneQuery = buildQueryFromLucene(queriesByLanguage.lucene, config.queryStringOptions, config.dateFormatTZ);
|
||||
const filterQuery = buildQueryFromFilters(filters, indexPattern, config.ignoreFilterIfFieldNotInIndex);
|
||||
|
||||
return {
|
||||
|
|
|
@ -18,16 +18,22 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { getTimeZoneFromSettings } from '../utils/get_time_zone_from_settings';
|
||||
|
||||
/**
|
||||
* Decorate queries with default parameters
|
||||
* @param query object
|
||||
* @param queryStringOptions query:queryString:options from UI settings
|
||||
* @param dateFormatTZ dateFormat:tz from UI settings
|
||||
* @returns {object}
|
||||
*/
|
||||
export function decorateQuery(query, queryStringOptions) {
|
||||
|
||||
export function decorateQuery(query, queryStringOptions, dateFormatTZ = null) {
|
||||
if (_.has(query, 'query_string.query')) {
|
||||
_.extend(query.query_string, queryStringOptions);
|
||||
if (dateFormatTZ) {
|
||||
_.defaults(query.query_string, { time_zone: getTimeZoneFromSettings(dateFormatTZ) });
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import { fromLegacyKueryExpression, fromKueryExpression, toElasticsearchQuery, nodeTypes } from '../kuery';
|
||||
|
||||
export function buildQueryFromKuery(indexPattern, queries = [], allowLeadingWildcards) {
|
||||
export function buildQueryFromKuery(indexPattern, queries = [], allowLeadingWildcards, dateFormatTZ = null) {
|
||||
const queryASTs = queries.map(query => {
|
||||
try {
|
||||
return fromKueryExpression(query.query, { allowLeadingWildcards });
|
||||
|
@ -32,12 +32,12 @@ export function buildQueryFromKuery(indexPattern, queries = [], allowLeadingWild
|
|||
throw Error('OutdatedKuerySyntaxError');
|
||||
}
|
||||
});
|
||||
return buildQuery(indexPattern, queryASTs);
|
||||
return buildQuery(indexPattern, queryASTs, { dateFormatTZ });
|
||||
}
|
||||
|
||||
function buildQuery(indexPattern, queryASTs) {
|
||||
function buildQuery(indexPattern, queryASTs, config = null) {
|
||||
const compoundQueryAST = nodeTypes.function.buildNode('and', queryASTs);
|
||||
const kueryQuery = toElasticsearchQuery(compoundQueryAST, indexPattern);
|
||||
const kueryQuery = toElasticsearchQuery(compoundQueryAST, indexPattern, config);
|
||||
return {
|
||||
must: [],
|
||||
filter: [],
|
||||
|
|
|
@ -21,10 +21,10 @@ import _ from 'lodash';
|
|||
import { decorateQuery } from './decorate_query';
|
||||
import { luceneStringToDsl } from './lucene_string_to_dsl';
|
||||
|
||||
export function buildQueryFromLucene(queries, queryStringOptions) {
|
||||
export function buildQueryFromLucene(queries, queryStringOptions, dateFormatTZ = null) {
|
||||
const combinedQueries = _.map(queries, (query) => {
|
||||
const queryDsl = luceneStringToDsl(query.query);
|
||||
return decorateQuery(queryDsl, queryStringOptions);
|
||||
return decorateQuery(queryDsl, queryStringOptions, dateFormatTZ);
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -21,5 +21,6 @@ export function getEsQueryConfig(config) {
|
|||
const allowLeadingWildcards = config.get('query:allowLeadingWildcards');
|
||||
const queryStringOptions = config.get('query:queryString:options');
|
||||
const ignoreFilterIfFieldNotInIndex = config.get('courier:ignoreFilterIfFieldNotInIndex');
|
||||
return { allowLeadingWildcards, queryStringOptions, ignoreFilterIfFieldNotInIndex };
|
||||
const dateFormatTZ = config.get('dateFormat:tz');
|
||||
return { allowLeadingWildcards, queryStringOptions, ignoreFilterIfFieldNotInIndex, dateFormatTZ };
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import indexPatternResponse from '../../../__fixtures__/index_pattern_response.j
|
|||
|
||||
// Helpful utility allowing us to test the PEG parser by simply checking for deep equality between
|
||||
// the nodes the parser generates and the nodes our constructor functions generate.
|
||||
|
||||
function fromLegacyKueryExpressionNoMeta(text) {
|
||||
return ast.fromLegacyKueryExpression(text, { includeMetadata: false });
|
||||
}
|
||||
|
@ -416,6 +417,14 @@ describe('kuery AST API', function () {
|
|||
expect(ast.toElasticsearchQuery(unknownTypeNode)).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should return the given node type\'s ES query representation including a time zone parameter when one is provided', function () {
|
||||
const config = { dateFormatTZ: 'America/Phoenix' };
|
||||
const node = nodeTypes.function.buildNode('is', '@timestamp', '"2018-04-03T19:04:17"');
|
||||
const expected = nodeTypes.function.toElasticsearchQuery(node, indexPattern, config);
|
||||
const result = ast.toElasticsearchQuery(node, indexPattern, config);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('doesKueryExpressionHaveLuceneSyntaxError', function () {
|
||||
|
|
|
@ -51,15 +51,19 @@ function fromExpression(expression, parseOptions = {}, parse = parseKuery) {
|
|||
|
||||
return parse(expression, parseOptions);
|
||||
}
|
||||
|
||||
// indexPattern isn't required, but if you pass one in, we can be more intelligent
|
||||
// about how we craft the queries (e.g. scripted fields)
|
||||
export function toElasticsearchQuery(node, indexPattern) {
|
||||
/**
|
||||
* @params {String} indexPattern
|
||||
* @params {Object} config - contains the dateFormatTZ
|
||||
*
|
||||
* IndexPattern isn't required, but if you pass one in, we can be more intelligent
|
||||
* about how we craft the queries (e.g. scripted fields)
|
||||
*/
|
||||
export function toElasticsearchQuery(node, indexPattern, config = {}) {
|
||||
if (!node || !node.type || !nodeTypes[node.type]) {
|
||||
return toElasticsearchQuery(nodeTypes.function.buildNode('and', []));
|
||||
}
|
||||
|
||||
return nodeTypes[node.type].toElasticsearchQuery(node, indexPattern);
|
||||
return nodeTypes[node.type].toElasticsearchQuery(node, indexPattern, config);
|
||||
}
|
||||
|
||||
export function doesKueryExpressionHaveLuceneSyntaxError(expression) {
|
||||
|
|
|
@ -23,7 +23,6 @@ import { nodeTypes } from '../../node_types';
|
|||
import * as ast from '../../ast';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
|
||||
|
||||
let indexPattern;
|
||||
|
||||
const childNode1 = nodeTypes.function.buildNode('is', 'machine.os', 'osx');
|
||||
|
@ -57,8 +56,6 @@ describe('kuery functions', function () {
|
|||
[childNode1, childNode2].map((childNode) => ast.toElasticsearchQuery(childNode, indexPattern))
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -199,6 +199,52 @@ describe('kuery functions', function () {
|
|||
expect(result.bool.should[0]).to.have.key('script');
|
||||
});
|
||||
|
||||
it('should support date fields without a dateFormat provided', function () {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: '2018-04-03T19:04:17',
|
||||
lte: '2018-04-03T19:04:17',
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', '@timestamp', '"2018-04-03T19:04:17"');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support date fields with a dateFormat provided', function () {
|
||||
const config = { dateFormatTZ: 'America/Phoenix' };
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: '2018-04-03T19:04:17',
|
||||
lte: '2018-04-03T19:04:17',
|
||||
time_zone: 'America/Phoenix',
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', '@timestamp', '"2018-04-03T19:04:17"');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern, config);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -23,7 +23,6 @@ import { nodeTypes } from '../../node_types';
|
|||
import * as ast from '../../ast';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
|
||||
|
||||
let indexPattern;
|
||||
|
||||
const childNode = nodeTypes.function.buildNode('is', 'extension', 'jpg');
|
||||
|
@ -32,7 +31,6 @@ describe('kuery functions', function () {
|
|||
|
||||
describe('not', function () {
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
});
|
||||
|
@ -56,6 +54,7 @@ describe('kuery functions', function () {
|
|||
expect(result.bool).to.only.have.keys('must_not');
|
||||
expect(result.bool.must_not).to.eql(ast.toElasticsearchQuery(childNode, indexPattern));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -23,7 +23,6 @@ import { nodeTypes } from '../../node_types';
|
|||
import * as ast from '../../ast';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
|
||||
|
||||
let indexPattern;
|
||||
|
||||
const childNode1 = nodeTypes.function.buildNode('is', 'machine.os', 'osx');
|
||||
|
@ -33,11 +32,11 @@ describe('kuery functions', function () {
|
|||
|
||||
describe('or', function () {
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
});
|
||||
|
||||
|
||||
describe('buildNodeParams', function () {
|
||||
|
||||
it('arguments should contain the unmodified child nodes', function () {
|
||||
|
|
|
@ -22,7 +22,6 @@ import * as range from '../range';
|
|||
import { nodeTypes } from '../../node_types';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
|
||||
|
||||
let indexPattern;
|
||||
|
||||
describe('kuery functions', function () {
|
||||
|
@ -136,6 +135,52 @@ describe('kuery functions', function () {
|
|||
expect(result.bool.should[0]).to.have.key('script');
|
||||
});
|
||||
|
||||
it('should support date fields without a dateFormat provided', function () {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gt: '2018-01-03T19:04:17',
|
||||
lt: '2018-04-03T19:04:17',
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('range', '@timestamp', { gt: '2018-01-03T19:04:17', lt: '2018-04-03T19:04:17' });
|
||||
const result = range.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support date fields with a dateFormat provided', function () {
|
||||
const config = { dateFormatTZ: 'America/Phoenix' };
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gt: '2018-01-03T19:04:17',
|
||||
lt: '2018-04-03T19:04:17',
|
||||
time_zone: 'America/Phoenix',
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('range', '@timestamp', { gt: '2018-01-03T19:04:17', lt: '2018-04-03T19:04:17' });
|
||||
const result = range.toElasticsearchQuery(node, indexPattern, config);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,13 +25,13 @@ export function buildNodeParams(children) {
|
|||
};
|
||||
}
|
||||
|
||||
export function toElasticsearchQuery(node, indexPattern) {
|
||||
export function toElasticsearchQuery(node, indexPattern, config) {
|
||||
const children = node.arguments || [];
|
||||
|
||||
return {
|
||||
bool: {
|
||||
filter: children.map((child) => {
|
||||
return ast.toElasticsearchQuery(child, indexPattern);
|
||||
return ast.toElasticsearchQuery(child, indexPattern, config);
|
||||
})
|
||||
}
|
||||
};
|
||||
|
|
|
@ -23,6 +23,7 @@ import * as literal from '../node_types/literal';
|
|||
import * as wildcard from '../node_types/wildcard';
|
||||
import { getPhraseScript } from '../../filters';
|
||||
import { getFields } from './utils/get_fields';
|
||||
import { getTimeZoneFromSettings } from '../../utils/get_time_zone_from_settings';
|
||||
|
||||
export function buildNodeParams(fieldName, value, isPhrase = false) {
|
||||
if (_.isUndefined(fieldName)) {
|
||||
|
@ -35,19 +36,16 @@ export function buildNodeParams(fieldName, value, isPhrase = false) {
|
|||
const fieldNode = typeof fieldName === 'string' ? ast.fromLiteralExpression(fieldName) : literal.buildNode(fieldName);
|
||||
const valueNode = typeof value === 'string' ? ast.fromLiteralExpression(value) : literal.buildNode(value);
|
||||
const isPhraseNode = literal.buildNode(isPhrase);
|
||||
|
||||
return {
|
||||
arguments: [fieldNode, valueNode, isPhraseNode],
|
||||
};
|
||||
}
|
||||
|
||||
export function toElasticsearchQuery(node, indexPattern) {
|
||||
export function toElasticsearchQuery(node, indexPattern = null, config = {}) {
|
||||
const { arguments: [ fieldNameArg, valueArg, isPhraseArg ] } = node;
|
||||
|
||||
const fieldName = ast.toElasticsearchQuery(fieldNameArg);
|
||||
const value = !_.isUndefined(valueArg) ? ast.toElasticsearchQuery(valueArg) : valueArg;
|
||||
const type = isPhraseArg.value ? 'phrase' : 'best_fields';
|
||||
|
||||
if (fieldNameArg.value === null) {
|
||||
if (valueArg.type === 'wildcard') {
|
||||
return {
|
||||
|
@ -67,7 +65,6 @@ export function toElasticsearchQuery(node, indexPattern) {
|
|||
}
|
||||
|
||||
const fields = indexPattern ? getFields(fieldNameArg, indexPattern) : [];
|
||||
|
||||
// If no fields are found in the index pattern we send through the given field name as-is. We do this to preserve
|
||||
// the behaviour of lucene on dashboards where there are panels based on different index patterns that have different
|
||||
// fields. If a user queries on a field that exists in one pattern but not the other, the index pattern without the
|
||||
|
@ -116,6 +113,22 @@ export function toElasticsearchQuery(node, indexPattern) {
|
|||
}
|
||||
}];
|
||||
}
|
||||
/*
|
||||
If we detect that it's a date field and the user wants an exact date, we need to convert the query to both >= and <= the value provided to force a range query. This is because match and match_phrase queries do not accept a timezone parameter.
|
||||
dateFormatTZ can have the value of 'Browser', in which case we guess the timezone using moment.tz.guess.
|
||||
*/
|
||||
else if (field.type === 'date') {
|
||||
const timeZoneParam = config.dateFormatTZ ? { time_zone: getTimeZoneFromSettings(config.dateFormatTZ) } : {};
|
||||
return [...accumulator, {
|
||||
range: {
|
||||
[field.name]: {
|
||||
gte: value,
|
||||
lte: value,
|
||||
...timeZoneParam,
|
||||
},
|
||||
}
|
||||
}];
|
||||
}
|
||||
else {
|
||||
const queryType = type === 'phrase' ? 'match_phrase' : 'match';
|
||||
return [...accumulator, {
|
||||
|
@ -134,3 +147,4 @@ export function toElasticsearchQuery(node, indexPattern) {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@ export function buildNodeParams(child) {
|
|||
};
|
||||
}
|
||||
|
||||
export function toElasticsearchQuery(node, indexPattern) {
|
||||
export function toElasticsearchQuery(node, indexPattern, config) {
|
||||
const [ argument ] = node.arguments;
|
||||
|
||||
return {
|
||||
bool: {
|
||||
must_not: ast.toElasticsearchQuery(argument, indexPattern)
|
||||
must_not: ast.toElasticsearchQuery(argument, indexPattern, config)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -25,13 +25,13 @@ export function buildNodeParams(children) {
|
|||
};
|
||||
}
|
||||
|
||||
export function toElasticsearchQuery(node, indexPattern) {
|
||||
export function toElasticsearchQuery(node, indexPattern, config) {
|
||||
const children = node.arguments || [];
|
||||
|
||||
return {
|
||||
bool: {
|
||||
should: children.map((child) => {
|
||||
return ast.toElasticsearchQuery(child, indexPattern);
|
||||
return ast.toElasticsearchQuery(child, indexPattern, config);
|
||||
}),
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
|
|
|
@ -22,6 +22,7 @@ import { nodeTypes } from '../node_types';
|
|||
import * as ast from '../ast';
|
||||
import { getRangeScript } from '../../filters';
|
||||
import { getFields } from './utils/get_fields';
|
||||
import { getTimeZoneFromSettings } from '../../utils/get_time_zone_from_settings';
|
||||
|
||||
export function buildNodeParams(fieldName, params) {
|
||||
params = _.pick(params, 'gt', 'lt', 'gte', 'lte', 'format');
|
||||
|
@ -35,7 +36,7 @@ export function buildNodeParams(fieldName, params) {
|
|||
};
|
||||
}
|
||||
|
||||
export function toElasticsearchQuery(node, indexPattern) {
|
||||
export function toElasticsearchQuery(node, indexPattern = null, config = {}) {
|
||||
const [ fieldNameArg, ...args ] = node.arguments;
|
||||
const fields = indexPattern ? getFields(fieldNameArg, indexPattern) : [];
|
||||
const namedArgs = extractArguments(args);
|
||||
|
@ -60,7 +61,17 @@ export function toElasticsearchQuery(node, indexPattern) {
|
|||
script: getRangeScript(field, queryParams),
|
||||
};
|
||||
}
|
||||
|
||||
else if (field.type === 'date') {
|
||||
const timeZoneParam = config.dateFormatTZ ? { time_zone: getTimeZoneFromSettings(config.dateFormatTZ) } : {};
|
||||
return {
|
||||
range: {
|
||||
[field.name]: {
|
||||
...queryParams,
|
||||
...timeZoneParam,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
range: {
|
||||
[field.name]: queryParams
|
||||
|
|
|
@ -31,7 +31,6 @@ describe('kuery node types', function () {
|
|||
|
||||
let indexPattern;
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
});
|
||||
|
|
|
@ -21,8 +21,8 @@ import _ from 'lodash';
|
|||
import { functions } from '../functions';
|
||||
|
||||
export function buildNode(functionName, ...functionArgs) {
|
||||
const kueryFunction = functions[functionName];
|
||||
|
||||
const kueryFunction = functions[functionName];
|
||||
if (_.isUndefined(kueryFunction)) {
|
||||
throw new Error(`Unknown function "${functionName}"`);
|
||||
}
|
||||
|
@ -47,8 +47,8 @@ export function buildNodeWithArgumentNodes(functionName, argumentNodes) {
|
|||
};
|
||||
}
|
||||
|
||||
export function toElasticsearchQuery(node, indexPattern) {
|
||||
export function toElasticsearchQuery(node, indexPattern, config = {}) {
|
||||
const kueryFunction = functions[node.function];
|
||||
return kueryFunction.toElasticsearchQuery(node, indexPattern);
|
||||
return kueryFunction.toElasticsearchQuery(node, indexPattern, config);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { getTimeZoneFromSettings } from '../get_time_zone_from_settings';
|
||||
|
||||
describe('get timezone from settings', function () {
|
||||
|
||||
it('should return the config timezone if the time zone is set', function () {
|
||||
const result = getTimeZoneFromSettings('America/Chicago');
|
||||
expect(result).to.eql('America/Chicago');
|
||||
});
|
||||
|
||||
it('should return the system timezone if the time zone is set to "Browser"', function () {
|
||||
const result = getTimeZoneFromSettings('Browser');
|
||||
expect(result).to.not.equal('Browser');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
const detectedTimezone = moment.tz.guess();
|
||||
|
||||
export function getTimeZoneFromSettings(dateFormatTZ) {
|
||||
if (dateFormatTZ === 'Browser') {
|
||||
return detectedTimezone;
|
||||
}
|
||||
return dateFormatTZ;
|
||||
}
|
20
packages/kbn-es-query/src/utils/index.js
Normal file
20
packages/kbn-es-query/src/utils/index.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export * from './get_time_zone_from_settings';
|
Loading…
Reference in a new issue