From 6f751b8a3dbdba56ed1fc45b56a41b690cd978d0 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 28 Oct 2019 16:27:58 -0400 Subject: [PATCH 01/13] Add automatic support for nested fields in existing filter types --- .../kbn-es-query/src/es_query/clean_filter.js | 29 ++++++++++++ .../kbn-es-query/src/es_query/from_filters.js | 13 ++--- .../src/es_query/handle_nested_filter.js | 42 +++++++++++++++++ .../src/es_query/migrate_filter.js | 3 +- .../src/filters/lib/exists_filter.ts | 4 ++ .../filters/lib/geo_bounding_box_filter.ts | 7 +++ .../src/filters/lib/geo_polygon_filter.ts | 6 +++ .../kbn-es-query/src/filters/lib/index.ts | 47 +++++++++++++++++-- .../src/filters/lib/missing_filter.ts | 4 ++ .../src/filters/lib/phrases_filter.ts | 8 +++- .../src/filters/lib/range_filter.ts | 4 ++ 11 files changed, 151 insertions(+), 16 deletions(-) create mode 100644 packages/kbn-es-query/src/es_query/clean_filter.js create mode 100644 packages/kbn-es-query/src/es_query/handle_nested_filter.js diff --git a/packages/kbn-es-query/src/es_query/clean_filter.js b/packages/kbn-es-query/src/es_query/clean_filter.js new file mode 100644 index 0000000000000..6cabddf99d425 --- /dev/null +++ b/packages/kbn-es-query/src/es_query/clean_filter.js @@ -0,0 +1,29 @@ +/* + * 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 { omit } from 'lodash'; + +/** + * Clean out any invalid attributes from the filters + * @param {object} filter The filter to clean + * @returns {object} + */ +export const cleanFilter = function (filter) { + return omit(filter, ['meta', '$state']); +}; diff --git a/packages/kbn-es-query/src/es_query/from_filters.js b/packages/kbn-es-query/src/es_query/from_filters.js index 10f9cf82fc972..9ef2276fe754a 100644 --- a/packages/kbn-es-query/src/es_query/from_filters.js +++ b/packages/kbn-es-query/src/es_query/from_filters.js @@ -20,6 +20,8 @@ import _ from 'lodash'; import { migrateFilter } from './migrate_filter'; import { filterMatchesIndex } from './filter_matches_index'; +import { handleNestedFilter } from './handle_nested_filter'; +import { cleanFilter } from './clean_filter'; /** * Create a filter that can be reversed for filters with negate set @@ -50,15 +52,6 @@ const translateToQuery = function (filter) { return filter; }; -/** - * Clean out any invalid attributes from the filters - * @param {object} filter The filter to clean - * @returns {object} - */ -const cleanFilter = function (filter) { - return _.omit(filter, ['meta', '$state']); -}; - export function buildQueryFromFilters(filters = [], indexPattern, ignoreFilterIfFieldNotInIndex) { filters = filters.filter(filter => filter && !_.get(filter, ['meta', 'disabled'])); return { @@ -66,6 +59,7 @@ export function buildQueryFromFilters(filters = [], indexPattern, ignoreFilterIf filter: filters .filter(filterNegate(false)) .filter(filter => !ignoreFilterIfFieldNotInIndex || filterMatchesIndex(filter, indexPattern)) + .map((filter) => handleNestedFilter(filter, indexPattern)) .map(translateToQuery) .map(cleanFilter) .map(filter => { @@ -75,6 +69,7 @@ export function buildQueryFromFilters(filters = [], indexPattern, ignoreFilterIf must_not: filters .filter(filterNegate(true)) .filter(filter => !ignoreFilterIfFieldNotInIndex || filterMatchesIndex(filter, indexPattern)) + .map((filter) => handleNestedFilter(filter, indexPattern)) .map(translateToQuery) .map(cleanFilter) .map(filter => { diff --git a/packages/kbn-es-query/src/es_query/handle_nested_filter.js b/packages/kbn-es-query/src/es_query/handle_nested_filter.js new file mode 100644 index 0000000000000..1f01138f32e3c --- /dev/null +++ b/packages/kbn-es-query/src/es_query/handle_nested_filter.js @@ -0,0 +1,42 @@ +/* + * 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 { getFilterField } from '../filters/lib'; +import { cleanFilter } from './clean_filter'; + +export const handleNestedFilter = (filter, indexPattern) => { + const fieldName = getFilterField(filter); + if (!fieldName) { + return filter; + } + + const field = indexPattern.fields.find(field => field.name === fieldName); + if (!field.subType || !field.subType.nested || !field.subType.nested.path) { + return filter; + } + + const query = cleanFilter(filter); + + return { + nested: { + path: field.subType.nested.path, + query: query.query || query, + }, + }; +}; diff --git a/packages/kbn-es-query/src/es_query/migrate_filter.js b/packages/kbn-es-query/src/es_query/migrate_filter.js index d5f52648b027e..ba8b859a91d07 100644 --- a/packages/kbn-es-query/src/es_query/migrate_filter.js +++ b/packages/kbn-es-query/src/es_query/migrate_filter.js @@ -45,5 +45,6 @@ export function migrateFilter(filter, indexPattern) { } function isMatchPhraseFilter(filter, fieldName) { - return _.get(filter, ['match', fieldName, 'type']) === 'phrase'; + const type = _.get(filter, ['match', fieldName, 'type']) || _.get(filter, ['nested', 'query', 'match', fieldName, 'type']); + return type === 'phrase'; } diff --git a/packages/kbn-es-query/src/filters/lib/exists_filter.ts b/packages/kbn-es-query/src/filters/lib/exists_filter.ts index 5843c25c43cff..f500b6c0fd70b 100644 --- a/packages/kbn-es-query/src/filters/lib/exists_filter.ts +++ b/packages/kbn-es-query/src/filters/lib/exists_filter.ts @@ -31,3 +31,7 @@ export type ExistsFilter = Filter & { }; export const isExistsFilter = (filter: any): filter is ExistsFilter => filter && filter.exists; + +export const getExistsFilterField = (filter: ExistsFilter) => { + return filter.exists && filter.exists.field; +}; diff --git a/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.ts b/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.ts index f4673af96b2cd..619903954ff55 100644 --- a/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.ts +++ b/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.ts @@ -33,3 +33,10 @@ export type GeoBoundingBoxFilter = Filter & { export const isGeoBoundingBoxFilter = (filter: any): filter is GeoBoundingBoxFilter => filter && filter.geo_bounding_box; + +export const getGeoBoundingBoxFilterField = (filter: GeoBoundingBoxFilter) => { + return ( + filter.geo_bounding_box && + Object.keys(filter.geo_bounding_box).find(key => key !== 'ignore_unmapped') + ); +}; diff --git a/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.ts b/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.ts index 4cf82a92d2cef..03367feb83ee4 100644 --- a/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.ts +++ b/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.ts @@ -32,3 +32,9 @@ export type GeoPolygonFilter = Filter & { export const isGeoPolygonFilter = (filter: any): filter is GeoPolygonFilter => filter && filter.geo_polygon; + +export const getGeoPolygonFilterField = (filter: GeoPolygonFilter) => { + return ( + filter.geo_polygon && Object.keys(filter.geo_polygon).find(key => key !== 'ignore_unmapped') + ); +}; diff --git a/packages/kbn-es-query/src/filters/lib/index.ts b/packages/kbn-es-query/src/filters/lib/index.ts index ea02398710341..4d6ab4817b320 100644 --- a/packages/kbn-es-query/src/filters/lib/index.ts +++ b/packages/kbn-es-query/src/filters/lib/index.ts @@ -17,14 +17,24 @@ * under the License. */ +import { Filter } from './meta_filter'; + // The interface the other filters extend export * from './meta_filter'; // The actual filter types import { CustomFilter } from './custom_filter'; -import { ExistsFilter, isExistsFilter } from './exists_filter'; -import { GeoBoundingBoxFilter, isGeoBoundingBoxFilter } from './geo_bounding_box_filter'; -import { GeoPolygonFilter, isGeoPolygonFilter } from './geo_polygon_filter'; +import { ExistsFilter, getExistsFilterField, isExistsFilter } from './exists_filter'; +import { + GeoBoundingBoxFilter, + getGeoBoundingBoxFilterField, + isGeoBoundingBoxFilter, +} from './geo_bounding_box_filter'; +import { + GeoPolygonFilter, + getGeoPolygonFilterField, + isGeoPolygonFilter, +} from './geo_polygon_filter'; import { PhraseFilter, isPhraseFilter, @@ -32,16 +42,17 @@ import { getPhraseFilterField, getPhraseFilterValue, } from './phrase_filter'; -import { PhrasesFilter, isPhrasesFilter } from './phrases_filter'; +import { PhrasesFilter, isPhrasesFilter, getPhrasesFilterField } from './phrases_filter'; import { QueryStringFilter, isQueryStringFilter } from './query_string_filter'; import { RangeFilter, isRangeFilter, isScriptedRangeFilter, RangeFilterParams, + getRangeFilterField, } from './range_filter'; import { MatchAllFilter, isMatchAllFilter } from './match_all_filter'; -import { MissingFilter, isMissingFilter } from './missing_filter'; +import { MissingFilter, isMissingFilter, getMissingFilterField } from './missing_filter'; export { CustomFilter, @@ -93,3 +104,29 @@ export enum FILTERS { GEO_BOUNDING_BOX = 'geo_bounding_box', GEO_POLYGON = 'geo_polygon', } + +export const getFilterField = (filter: Filter) => { + if (isExistsFilter(filter)) { + return getExistsFilterField(filter); + } + if (isGeoBoundingBoxFilter(filter)) { + return getGeoBoundingBoxFilterField(filter); + } + if (isGeoPolygonFilter(filter)) { + return getGeoPolygonFilterField(filter); + } + if (isPhraseFilter(filter)) { + return getPhraseFilterField(filter); + } + if (isPhrasesFilter(filter)) { + return getPhrasesFilterField(filter); + } + if (isRangeFilter(filter)) { + return getRangeFilterField(filter); + } + if (isMissingFilter(filter)) { + return getMissingFilterField(filter); + } + + return; +}; diff --git a/packages/kbn-es-query/src/filters/lib/missing_filter.ts b/packages/kbn-es-query/src/filters/lib/missing_filter.ts index 5411187cbcfd7..c8e1194a8f3cc 100644 --- a/packages/kbn-es-query/src/filters/lib/missing_filter.ts +++ b/packages/kbn-es-query/src/filters/lib/missing_filter.ts @@ -27,3 +27,7 @@ export type MissingFilter = Filter & { }; export const isMissingFilter = (filter: any): filter is MissingFilter => filter && filter.missing; + +export const getMissingFilterField = (filter: MissingFilter) => { + return filter.missing && filter.missing.field; +}; diff --git a/packages/kbn-es-query/src/filters/lib/phrases_filter.ts b/packages/kbn-es-query/src/filters/lib/phrases_filter.ts index 213afb409a0a6..16bee0e5801c8 100644 --- a/packages/kbn-es-query/src/filters/lib/phrases_filter.ts +++ b/packages/kbn-es-query/src/filters/lib/phrases_filter.ts @@ -29,4 +29,10 @@ export type PhrasesFilter = Filter & { }; export const isPhrasesFilter = (filter: any): filter is PhrasesFilter => - filter && filter.meta.type === 'phrases'; + filter && filter.meta && filter.meta.type === 'phrases'; + +export const getPhrasesFilterField = (filter: PhrasesFilter) => { + // Phrases is a newer filter type that has always been created via a constructor that ensures + // `meta.key` is set to the field name + return filter.meta.key; +}; diff --git a/packages/kbn-es-query/src/filters/lib/range_filter.ts b/packages/kbn-es-query/src/filters/lib/range_filter.ts index fc8d05d575d59..a4f46e8465604 100644 --- a/packages/kbn-es-query/src/filters/lib/range_filter.ts +++ b/packages/kbn-es-query/src/filters/lib/range_filter.ts @@ -56,3 +56,7 @@ export const isScriptedRangeFilter = (filter: any): filter is RangeFilter => { return hasRangeKeys(params); }; + +export const getRangeFilterField = (filter: RangeFilter) => { + return filter.range && Object.keys(filter.range)[0]; +}; From 3ab9ac50022fc5dd2adae2e7442f7ef1bc9daae2 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 29 Oct 2019 01:25:18 -0400 Subject: [PATCH 02/13] Index pattern could be undefined --- packages/kbn-es-query/src/es_query/handle_nested_filter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/kbn-es-query/src/es_query/handle_nested_filter.js b/packages/kbn-es-query/src/es_query/handle_nested_filter.js index 1f01138f32e3c..15bc5b8676b7e 100644 --- a/packages/kbn-es-query/src/es_query/handle_nested_filter.js +++ b/packages/kbn-es-query/src/es_query/handle_nested_filter.js @@ -21,6 +21,8 @@ import { getFilterField } from '../filters/lib'; import { cleanFilter } from './clean_filter'; export const handleNestedFilter = (filter, indexPattern) => { + if (!indexPattern) return filter; + const fieldName = getFilterField(filter); if (!fieldName) { return filter; From 1ad9a5b2b5cc13c1f65c92828d8205ad414a7f44 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 29 Oct 2019 13:39:39 -0400 Subject: [PATCH 03/13] add test for handleNestedFilter function --- .../__tests__/handle_nested_filter.js | 84 +++++++++++++++++++ .../src/es_query/handle_nested_filter.js | 2 +- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js diff --git a/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js b/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js new file mode 100644 index 0000000000000..9c2e1d3979044 --- /dev/null +++ b/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js @@ -0,0 +1,84 @@ +/* + * 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 { handleNestedFilter } from '../handle_nested_filter'; +import indexPattern from '../../__fixtures__/index_pattern_response.json'; +import { buildPhraseFilter, buildQueryFilter } from '../../filters'; +import expect from '@kbn/expect'; + +describe('handleNestedFilter', function () { + + it('should return the filter\'s query wrapped in nested query if the target field is nested', () => { + const field = getField('nestedField.child'); + const filter = buildPhraseFilter(field, 'foo', indexPattern); + const result = handleNestedFilter(filter, indexPattern); + console.log(JSON.stringify(result, null, 2)); + expect(result).to.eql({ + nested: { + path: 'nestedField', + query: { + match_phrase: { + 'nestedField.child': 'foo' + } + } + } + } + ); + }); + + it('should return filter untouched if it does not target a nested field', () => { + const field = getField('extension'); + const filter = buildPhraseFilter(field, 'jpg', indexPattern); + const result = handleNestedFilter(filter, indexPattern); + expect(result).to.be(filter); + }); + + it('should return filter untouched if it does not target a field from the given index pattern', () => { + const field = { ...getField('extension'), name: 'notarealfield' }; + const filter = buildPhraseFilter(field, 'jpg', indexPattern); + const result = handleNestedFilter(filter, indexPattern); + expect(result).to.be(filter); + }); + + it('should return filter untouched if no index pattern is provided', () => { + const field = getField('extension'); + const filter = buildPhraseFilter(field, 'jpg', indexPattern); + const result = handleNestedFilter(filter); + expect(result).to.be(filter); + }); + + it('should return the filter untouched if a target field cannot be determined', () => { + // for example, we don't support query_string queries + const filter = buildQueryFilter({ + query: { + query_string: { + query: 'response:200' + } + } + }, indexPattern.id); + const result = handleNestedFilter(filter); + expect(result).to.be(filter); + }); +}); + + +function getField(name) { + return indexPattern.fields.find(field => field.name === name); +} diff --git a/packages/kbn-es-query/src/es_query/handle_nested_filter.js b/packages/kbn-es-query/src/es_query/handle_nested_filter.js index 15bc5b8676b7e..27c06d9becb2a 100644 --- a/packages/kbn-es-query/src/es_query/handle_nested_filter.js +++ b/packages/kbn-es-query/src/es_query/handle_nested_filter.js @@ -29,7 +29,7 @@ export const handleNestedFilter = (filter, indexPattern) => { } const field = indexPattern.fields.find(field => field.name === fieldName); - if (!field.subType || !field.subType.nested || !field.subType.nested.path) { + if (!field || !field.subType || !field.subType.nested || !field.subType.nested.path) { return filter; } From 79ff6c2d4df2a53b8758a9a53e8dbba7433a863f Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 29 Oct 2019 15:10:12 -0400 Subject: [PATCH 04/13] remove console.log --- .../kbn-es-query/src/es_query/__tests__/handle_nested_filter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js b/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js index 9c2e1d3979044..5656980e2d605 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js +++ b/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js @@ -29,7 +29,6 @@ describe('handleNestedFilter', function () { const field = getField('nestedField.child'); const filter = buildPhraseFilter(field, 'foo', indexPattern); const result = handleNestedFilter(filter, indexPattern); - console.log(JSON.stringify(result, null, 2)); expect(result).to.eql({ nested: { path: 'nestedField', From c4dea5cfdc5f5cd1a79485f3d4170e13d2c01d3b Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 29 Oct 2019 16:15:00 -0400 Subject: [PATCH 05/13] add tests for all "getFilterField" functions --- .../__fixtures__/index_pattern_response.json | 303 ---------------- .../__fixtures__/index_pattern_response.ts | 322 ++++++++++++++++++ .../src/es_query/__tests__/build_es_query.js | 2 +- .../src/es_query/__tests__/from_kuery.js | 2 +- .../__tests__/handle_nested_filter.js | 2 +- .../src/filters/__tests__/phrase.js | 2 +- .../src/filters/__tests__/query.js | 2 +- .../src/filters/__tests__/range.js | 2 +- .../src/filters/lib/exists_filter.test.ts | 33 ++ .../lib/geo_bounding_box_filter.test.ts | 46 +++ .../filters/lib/geo_polygon_filter.test.ts | 44 +++ .../src/filters/lib/get_filter_field.test.ts | 46 +++ .../src/filters/lib/get_filter_field.ts | 53 +++ .../kbn-es-query/src/filters/lib/index.ts | 49 +-- .../src/filters/lib/meta_filter.ts | 2 +- .../src/filters/lib/missing_filter.test.ts | 38 +++ .../src/filters/lib/phrase_filter.test.ts | 33 ++ .../src/filters/lib/phrases_filter.test.ts | 33 ++ .../src/filters/lib/range_filter.test.ts | 33 ++ .../src/kuery/ast/__tests__/ast.js | 2 +- .../src/kuery/functions/__tests__/and.js | 2 +- .../src/kuery/functions/__tests__/exists.js | 2 +- .../functions/__tests__/geo_bounding_box.js | 2 +- .../kuery/functions/__tests__/geo_polygon.js | 2 +- .../src/kuery/functions/__tests__/is.js | 2 +- .../src/kuery/functions/__tests__/nested.js | 2 +- .../src/kuery/functions/__tests__/not.js | 2 +- .../src/kuery/functions/__tests__/or.js | 2 +- .../src/kuery/functions/__tests__/range.js | 2 +- .../functions/__tests__/utils/get_fields.js | 2 +- .../utils/get_full_field_name_node.js | 2 +- .../kuery/node_types/__tests__/function.js | 2 +- 32 files changed, 708 insertions(+), 365 deletions(-) delete mode 100644 packages/kbn-es-query/src/__fixtures__/index_pattern_response.json create mode 100644 packages/kbn-es-query/src/__fixtures__/index_pattern_response.ts create mode 100644 packages/kbn-es-query/src/filters/lib/exists_filter.test.ts create mode 100644 packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.test.ts create mode 100644 packages/kbn-es-query/src/filters/lib/geo_polygon_filter.test.ts create mode 100644 packages/kbn-es-query/src/filters/lib/get_filter_field.test.ts create mode 100644 packages/kbn-es-query/src/filters/lib/get_filter_field.ts create mode 100644 packages/kbn-es-query/src/filters/lib/missing_filter.test.ts create mode 100644 packages/kbn-es-query/src/filters/lib/phrase_filter.test.ts create mode 100644 packages/kbn-es-query/src/filters/lib/phrases_filter.test.ts create mode 100644 packages/kbn-es-query/src/filters/lib/range_filter.test.ts diff --git a/packages/kbn-es-query/src/__fixtures__/index_pattern_response.json b/packages/kbn-es-query/src/__fixtures__/index_pattern_response.json deleted file mode 100644 index 588e6ada69cfe..0000000000000 --- a/packages/kbn-es-query/src/__fixtures__/index_pattern_response.json +++ /dev/null @@ -1,303 +0,0 @@ -{ - "id": "logstash-*", - "title": "logstash-*", - "fields": [ - { - "name": "bytes", - "type": "number", - "esTypes": ["long"], - "count": 10, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "ssl", - "type": "boolean", - "esTypes": ["boolean"], - "count": 20, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "@timestamp", - "type": "date", - "esTypes": ["date"], - "count": 30, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "time", - "type": "date", - "esTypes": ["date"], - "count": 30, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "@tags", - "type": "string", - "esTypes": ["keyword"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "utc_time", - "type": "date", - "esTypes": ["date"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "phpmemory", - "type": "number", - "esTypes": ["integer"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "ip", - "type": "ip", - "esTypes": ["ip"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "request_body", - "type": "attachment", - "esTypes": ["attachment"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "point", - "type": "geo_point", - "esTypes": ["geo_point"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "area", - "type": "geo_shape", - "esTypes": ["geo_shape"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "hashed", - "type": "murmur3", - "esTypes": ["murmur3"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": false, - "readFromDocValues": false - }, - { - "name": "geo.coordinates", - "type": "geo_point", - "esTypes": ["geo_point"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "extension", - "type": "string", - "esTypes": ["keyword"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "machine.os", - "type": "string", - "esTypes": ["text"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": false - }, - { - "name": "machine.os.raw", - "type": "string", - "esTypes": ["keyword"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true, - "subType": { "multi": { "parent": "machine.os" } } - }, - { - "name": "geo.src", - "type": "string", - "esTypes": ["keyword"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "_id", - "type": "string", - "esTypes": ["_id"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": false - }, - { - "name": "_type", - "type": "string", - "esTypes": ["_type"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": false - }, - { - "name": "_source", - "type": "_source", - "esTypes": ["_source"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": false - }, - { - "name": "non-filterable", - "type": "string", - "esTypes": ["text"], - "count": 0, - "scripted": false, - "searchable": false, - "aggregatable": true, - "readFromDocValues": false - }, - { - "name": "non-sortable", - "type": "string", - "esTypes": ["text"], - "count": 0, - "scripted": false, - "searchable": false, - "aggregatable": false, - "readFromDocValues": false - }, - { - "name": "custom_user_field", - "type": "conflict", - "esTypes": ["long", "text"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": true, - "readFromDocValues": true - }, - { - "name": "script string", - "type": "string", - "count": 0, - "scripted": true, - "script": "'i am a string'", - "lang": "expression", - "searchable": true, - "aggregatable": true, - "readFromDocValues": false - }, - { - "name": "script number", - "type": "number", - "count": 0, - "scripted": true, - "script": "1234", - "lang": "expression", - "searchable": true, - "aggregatable": true, - "readFromDocValues": false - }, - { - "name": "script date", - "type": "date", - "count": 0, - "scripted": true, - "script": "1234", - "lang": "painless", - "searchable": true, - "aggregatable": true, - "readFromDocValues": false - }, - { - "name": "script murmur3", - "type": "murmur3", - "count": 0, - "scripted": true, - "script": "1234", - "lang": "expression", - "searchable": true, - "aggregatable": true, - "readFromDocValues": false - }, - { - "name": "nestedField.child", - "type": "string", - "esTypes": ["text"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": false, - "readFromDocValues": false, - "subType": { "nested": { "path": "nestedField" } } - }, - { - "name": "nestedField.nestedChild.doublyNestedChild", - "type": "string", - "esTypes": ["text"], - "count": 0, - "scripted": false, - "searchable": true, - "aggregatable": false, - "readFromDocValues": false, - "subType": { "nested": { "path": "nestedField.nestedChild" } } - } - ] -} diff --git a/packages/kbn-es-query/src/__fixtures__/index_pattern_response.ts b/packages/kbn-es-query/src/__fixtures__/index_pattern_response.ts new file mode 100644 index 0000000000000..1784a2650a95a --- /dev/null +++ b/packages/kbn-es-query/src/__fixtures__/index_pattern_response.ts @@ -0,0 +1,322 @@ +/* + * 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 const indexPatternResponse = { + id: 'logstash-*', + title: 'logstash-*', + fields: [ + { + name: 'bytes', + type: 'number', + esTypes: ['long'], + count: 10, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'ssl', + type: 'boolean', + esTypes: ['boolean'], + count: 20, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: '@timestamp', + type: 'date', + esTypes: ['date'], + count: 30, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'time', + type: 'date', + esTypes: ['date'], + count: 30, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: '@tags', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'utc_time', + type: 'date', + esTypes: ['date'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'phpmemory', + type: 'number', + esTypes: ['integer'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'ip', + type: 'ip', + esTypes: ['ip'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'request_body', + type: 'attachment', + esTypes: ['attachment'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'point', + type: 'geo_point', + esTypes: ['geo_point'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'area', + type: 'geo_shape', + esTypes: ['geo_shape'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'hashed', + type: 'murmur3', + esTypes: ['murmur3'], + count: 0, + scripted: false, + searchable: true, + aggregatable: false, + readFromDocValues: false, + }, + { + name: 'geo.coordinates', + type: 'geo_point', + esTypes: ['geo_point'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'extension', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'machine.os', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'machine.os.raw', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + subType: { multi: { parent: 'machine.os' } }, + }, + { + name: 'geo.src', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: '_id', + type: 'string', + esTypes: ['_id'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: '_type', + type: 'string', + esTypes: ['_type'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: '_source', + type: '_source', + esTypes: ['_source'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'non-filterable', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: false, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'non-sortable', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: false, + aggregatable: false, + readFromDocValues: false, + }, + { + name: 'custom_user_field', + type: 'conflict', + esTypes: ['long', 'text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'script string', + type: 'string', + count: 0, + scripted: true, + script: "'i am a string'", + lang: 'expression', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'script number', + type: 'number', + count: 0, + scripted: true, + script: '1234', + lang: 'expression', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'script date', + type: 'date', + count: 0, + scripted: true, + script: '1234', + lang: 'painless', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'script murmur3', + type: 'murmur3', + count: 0, + scripted: true, + script: '1234', + lang: 'expression', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'nestedField.child', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: false, + readFromDocValues: false, + subType: { nested: { path: 'nestedField' } }, + }, + { + name: 'nestedField.nestedChild.doublyNestedChild', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: false, + readFromDocValues: false, + subType: { nested: { path: 'nestedField.nestedChild' } }, + }, + ], +}; diff --git a/packages/kbn-es-query/src/es_query/__tests__/build_es_query.js b/packages/kbn-es-query/src/es_query/__tests__/build_es_query.js index fde3d063caaa6..407995cb1dae0 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/build_es_query.js +++ b/packages/kbn-es-query/src/es_query/__tests__/build_es_query.js @@ -19,7 +19,7 @@ import expect from '@kbn/expect'; import { buildEsQuery } from '../build_es_query'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; import { fromKueryExpression, toElasticsearchQuery } from '../../kuery'; import { luceneStringToDsl } from '../lucene_string_to_dsl'; import { decorateQuery } from '../decorate_query'; diff --git a/packages/kbn-es-query/src/es_query/__tests__/from_kuery.js b/packages/kbn-es-query/src/es_query/__tests__/from_kuery.js index 18738b05ea69d..cbd74a8737298 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/from_kuery.js +++ b/packages/kbn-es-query/src/es_query/__tests__/from_kuery.js @@ -18,7 +18,7 @@ */ import { buildQueryFromKuery } from '../from_kuery'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; import expect from '@kbn/expect'; import { fromKueryExpression, toElasticsearchQuery } from '../../kuery'; diff --git a/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js b/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js index 5656980e2d605..d3ef8f3374466 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js +++ b/packages/kbn-es-query/src/es_query/__tests__/handle_nested_filter.js @@ -19,7 +19,7 @@ import { handleNestedFilter } from '../handle_nested_filter'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; import { buildPhraseFilter, buildQueryFilter } from '../../filters'; import expect from '@kbn/expect'; diff --git a/packages/kbn-es-query/src/filters/__tests__/phrase.js b/packages/kbn-es-query/src/filters/__tests__/phrase.js index dbd794a018d9e..3597ce4ed3fcd 100644 --- a/packages/kbn-es-query/src/filters/__tests__/phrase.js +++ b/packages/kbn-es-query/src/filters/__tests__/phrase.js @@ -21,7 +21,7 @@ import { buildInlineScriptForPhraseFilter, buildPhraseFilter } from '../phrase'; import expect from '@kbn/expect'; import _ from 'lodash'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; import filterSkeleton from '../../__fixtures__/filter_skeleton'; let expected; diff --git a/packages/kbn-es-query/src/filters/__tests__/query.js b/packages/kbn-es-query/src/filters/__tests__/query.js index 8b774f05c29d3..d5b76e97fdf07 100644 --- a/packages/kbn-es-query/src/filters/__tests__/query.js +++ b/packages/kbn-es-query/src/filters/__tests__/query.js @@ -20,7 +20,7 @@ import { buildQueryFilter } from '../query'; import { cloneDeep } from 'lodash'; import expect from '@kbn/expect'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; import filterSkeleton from '../../__fixtures__/filter_skeleton'; let expected; diff --git a/packages/kbn-es-query/src/filters/__tests__/range.js b/packages/kbn-es-query/src/filters/__tests__/range.js index 9b23fc23908d4..7e0d2549f1141 100644 --- a/packages/kbn-es-query/src/filters/__tests__/range.js +++ b/packages/kbn-es-query/src/filters/__tests__/range.js @@ -20,7 +20,7 @@ import { buildRangeFilter } from '../range'; import expect from '@kbn/expect'; import _ from 'lodash'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; import filterSkeleton from '../../__fixtures__/filter_skeleton'; let expected; diff --git a/packages/kbn-es-query/src/filters/lib/exists_filter.test.ts b/packages/kbn-es-query/src/filters/lib/exists_filter.test.ts new file mode 100644 index 0000000000000..cda6d95178234 --- /dev/null +++ b/packages/kbn-es-query/src/filters/lib/exists_filter.test.ts @@ -0,0 +1,33 @@ +/* + * 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 { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; +import { buildExistsFilter } from '../index'; +import { getExistsFilterField } from './exists_filter'; + +describe('exists filter', function() { + describe('getExistsFilterField', function() { + it('should return the name of the field an exists query is targeting', () => { + const field = indexPattern.fields.find(patternField => patternField.name === 'extension'); + const filter = buildExistsFilter(field, indexPattern); + const result = getExistsFilterField(filter); + expect(result).toBe('extension'); + }); + }); +}); diff --git a/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.test.ts b/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.test.ts new file mode 100644 index 0000000000000..979da6d1c4d76 --- /dev/null +++ b/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.test.ts @@ -0,0 +1,46 @@ +/* + * 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 { getGeoBoundingBoxFilterField } from './geo_bounding_box_filter'; + +describe('geo_bounding_box filter', function() { + describe('getGeoBoundingBoxFilterField', function() { + it('should return the name of the field a geo_bounding_box query is targeting', () => { + const filter = { + geo_bounding_box: { + geoPointField: { + bottom_right: { lat: 1, lon: 1 }, + top_left: { lat: 1, lon: 1 }, + }, + ignore_unmapped: true, + }, + meta: { + disabled: false, + negate: false, + params: { + bottom_right: { lat: 1, lon: 1 }, + top_left: { lat: 1, lon: 1 }, + }, + }, + }; + const result = getGeoBoundingBoxFilterField(filter); + expect(result).toBe('geoPointField'); + }); + }); +}); diff --git a/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.test.ts b/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.test.ts new file mode 100644 index 0000000000000..5f81188e930f2 --- /dev/null +++ b/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.test.ts @@ -0,0 +1,44 @@ +/* + * 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 { getGeoPolygonFilterField } from './geo_polygon_filter'; + +describe('geo_polygon filter', function() { + describe('getGeoPolygonFilterField', function() { + it('should return the name of the field a geo_polygon query is targeting', () => { + const filter = { + geo_polygon: { + geoPointField: { + points: [{ lat: 1, lon: 1 }], + }, + ignore_unmapped: true, + }, + meta: { + disabled: false, + negate: false, + params: { + points: [{ lat: 1, lon: 1 }], + }, + }, + }; + const result = getGeoPolygonFilterField(filter); + expect(result).toBe('geoPointField'); + }); + }); +}); diff --git a/packages/kbn-es-query/src/filters/lib/get_filter_field.test.ts b/packages/kbn-es-query/src/filters/lib/get_filter_field.test.ts new file mode 100644 index 0000000000000..b927f27034958 --- /dev/null +++ b/packages/kbn-es-query/src/filters/lib/get_filter_field.test.ts @@ -0,0 +1,46 @@ +/* + * 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 { buildPhraseFilter, buildQueryFilter } from '../index'; +import { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; +import { getFilterField } from '../lib'; + +describe('getFilterField', function() { + it('should return the field name from known filter types that target a specific field', () => { + const field = indexPattern.fields.find(patternField => patternField.name === 'extension'); + const filter = buildPhraseFilter(field, 'jpg', indexPattern); + const result = getFilterField(filter); + expect(result).toBe('extension'); + }); + + it('should return undefined for filters that do not target a specific field', () => { + const filter = buildQueryFilter( + { + query: { + query_string: { + query: 'response:200 and extension:jpg', + }, + }, + }, + indexPattern.id + ); + const result = getFilterField(filter); + expect(result).toBe(undefined); + }); +}); diff --git a/packages/kbn-es-query/src/filters/lib/get_filter_field.ts b/packages/kbn-es-query/src/filters/lib/get_filter_field.ts new file mode 100644 index 0000000000000..dfb575157d362 --- /dev/null +++ b/packages/kbn-es-query/src/filters/lib/get_filter_field.ts @@ -0,0 +1,53 @@ +/* + * 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 { Filter } from './meta_filter'; +import { getExistsFilterField, isExistsFilter } from './exists_filter'; +import { getGeoBoundingBoxFilterField, isGeoBoundingBoxFilter } from './geo_bounding_box_filter'; +import { getGeoPolygonFilterField, isGeoPolygonFilter } from './geo_polygon_filter'; +import { getPhraseFilterField, isPhraseFilter } from './phrase_filter'; +import { getPhrasesFilterField, isPhrasesFilter } from './phrases_filter'; +import { getRangeFilterField, isRangeFilter } from './range_filter'; +import { getMissingFilterField, isMissingFilter } from './missing_filter'; + +export const getFilterField = (filter: Filter) => { + if (isExistsFilter(filter)) { + return getExistsFilterField(filter); + } + if (isGeoBoundingBoxFilter(filter)) { + return getGeoBoundingBoxFilterField(filter); + } + if (isGeoPolygonFilter(filter)) { + return getGeoPolygonFilterField(filter); + } + if (isPhraseFilter(filter)) { + return getPhraseFilterField(filter); + } + if (isPhrasesFilter(filter)) { + return getPhrasesFilterField(filter); + } + if (isRangeFilter(filter)) { + return getRangeFilterField(filter); + } + if (isMissingFilter(filter)) { + return getMissingFilterField(filter); + } + + return; +}; diff --git a/packages/kbn-es-query/src/filters/lib/index.ts b/packages/kbn-es-query/src/filters/lib/index.ts index 4d6ab4817b320..1f107b9bfae32 100644 --- a/packages/kbn-es-query/src/filters/lib/index.ts +++ b/packages/kbn-es-query/src/filters/lib/index.ts @@ -17,24 +17,16 @@ * under the License. */ -import { Filter } from './meta_filter'; - // The interface the other filters extend export * from './meta_filter'; +export * from './get_filter_field'; + // The actual filter types import { CustomFilter } from './custom_filter'; -import { ExistsFilter, getExistsFilterField, isExistsFilter } from './exists_filter'; -import { - GeoBoundingBoxFilter, - getGeoBoundingBoxFilterField, - isGeoBoundingBoxFilter, -} from './geo_bounding_box_filter'; -import { - GeoPolygonFilter, - getGeoPolygonFilterField, - isGeoPolygonFilter, -} from './geo_polygon_filter'; +import { ExistsFilter, isExistsFilter } from './exists_filter'; +import { GeoBoundingBoxFilter, isGeoBoundingBoxFilter } from './geo_bounding_box_filter'; +import { GeoPolygonFilter, isGeoPolygonFilter } from './geo_polygon_filter'; import { PhraseFilter, isPhraseFilter, @@ -42,17 +34,16 @@ import { getPhraseFilterField, getPhraseFilterValue, } from './phrase_filter'; -import { PhrasesFilter, isPhrasesFilter, getPhrasesFilterField } from './phrases_filter'; +import { PhrasesFilter, isPhrasesFilter } from './phrases_filter'; import { QueryStringFilter, isQueryStringFilter } from './query_string_filter'; import { RangeFilter, isRangeFilter, isScriptedRangeFilter, RangeFilterParams, - getRangeFilterField, } from './range_filter'; import { MatchAllFilter, isMatchAllFilter } from './match_all_filter'; -import { MissingFilter, isMissingFilter, getMissingFilterField } from './missing_filter'; +import { MissingFilter, isMissingFilter } from './missing_filter'; export { CustomFilter, @@ -104,29 +95,3 @@ export enum FILTERS { GEO_BOUNDING_BOX = 'geo_bounding_box', GEO_POLYGON = 'geo_polygon', } - -export const getFilterField = (filter: Filter) => { - if (isExistsFilter(filter)) { - return getExistsFilterField(filter); - } - if (isGeoBoundingBoxFilter(filter)) { - return getGeoBoundingBoxFilterField(filter); - } - if (isGeoPolygonFilter(filter)) { - return getGeoPolygonFilterField(filter); - } - if (isPhraseFilter(filter)) { - return getPhraseFilterField(filter); - } - if (isPhrasesFilter(filter)) { - return getPhrasesFilterField(filter); - } - if (isRangeFilter(filter)) { - return getRangeFilterField(filter); - } - if (isMissingFilter(filter)) { - return getMissingFilterField(filter); - } - - return; -}; diff --git a/packages/kbn-es-query/src/filters/lib/meta_filter.ts b/packages/kbn-es-query/src/filters/lib/meta_filter.ts index 8f6aef782cea2..c696a3d08578c 100644 --- a/packages/kbn-es-query/src/filters/lib/meta_filter.ts +++ b/packages/kbn-es-query/src/filters/lib/meta_filter.ts @@ -38,7 +38,7 @@ export interface FilterMeta { type?: string; disabled: boolean; negate: boolean; - alias: string | null; + alias?: string | null; key?: string; value?: string | ((formatter?: FilterValueFormatter) => string); params?: any; diff --git a/packages/kbn-es-query/src/filters/lib/missing_filter.test.ts b/packages/kbn-es-query/src/filters/lib/missing_filter.test.ts new file mode 100644 index 0000000000000..633863e3949dc --- /dev/null +++ b/packages/kbn-es-query/src/filters/lib/missing_filter.test.ts @@ -0,0 +1,38 @@ +/* + * 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 { getMissingFilterField } from './missing_filter'; + +describe('missing filter', function() { + describe('getMissingFilterField', function() { + it('should return the name of the field an missing query is targeting', () => { + const filter = { + missing: { + field: 'extension', + }, + meta: { + disabled: false, + negate: false, + }, + }; + const result = getMissingFilterField(filter); + expect(result).toBe('extension'); + }); + }); +}); diff --git a/packages/kbn-es-query/src/filters/lib/phrase_filter.test.ts b/packages/kbn-es-query/src/filters/lib/phrase_filter.test.ts new file mode 100644 index 0000000000000..9518db1cc2dfc --- /dev/null +++ b/packages/kbn-es-query/src/filters/lib/phrase_filter.test.ts @@ -0,0 +1,33 @@ +/* + * 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 { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; +import { buildPhraseFilter } from '../index'; +import { getPhraseFilterField } from './phrase_filter'; + +describe('Phrase filter', function() { + describe('getPhraseFilterField', function() { + it('should return the name of the field a phrase query is targeting', () => { + const field = indexPattern.fields.find(patternField => patternField.name === 'extension'); + const filter = buildPhraseFilter(field, 'jpg', indexPattern); + const result = getPhraseFilterField(filter); + expect(result).toBe('extension'); + }); + }); +}); diff --git a/packages/kbn-es-query/src/filters/lib/phrases_filter.test.ts b/packages/kbn-es-query/src/filters/lib/phrases_filter.test.ts new file mode 100644 index 0000000000000..92c65d363e9a1 --- /dev/null +++ b/packages/kbn-es-query/src/filters/lib/phrases_filter.test.ts @@ -0,0 +1,33 @@ +/* + * 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 { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; +import { buildPhrasesFilter } from '../index'; +import { getPhrasesFilterField } from './phrases_filter'; + +describe('phrases filter', function() { + describe('getPhrasesFilterField', function() { + it('should return the name of the field a phrases query is targeting', () => { + const field = indexPattern.fields.find(patternField => patternField.name === 'extension'); + const filter = buildPhrasesFilter(field, ['jpg', 'png'], indexPattern); + const result = getPhrasesFilterField(filter); + expect(result).toBe('extension'); + }); + }); +}); diff --git a/packages/kbn-es-query/src/filters/lib/range_filter.test.ts b/packages/kbn-es-query/src/filters/lib/range_filter.test.ts new file mode 100644 index 0000000000000..fae9539fe5f04 --- /dev/null +++ b/packages/kbn-es-query/src/filters/lib/range_filter.test.ts @@ -0,0 +1,33 @@ +/* + * 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 { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; +import { buildRangeFilter } from '../index'; +import { getRangeFilterField } from './range_filter'; + +describe('Range filter', function() { + describe('getRangeFilterField', function() { + it('should return the name of the field a range query is targeting', () => { + const field = indexPattern.fields.find(patternField => patternField.name === 'bytes'); + const filter = buildRangeFilter(field, {}, indexPattern); + const result = getRangeFilterField(filter); + expect(result).toBe('bytes'); + }); + }); +}); diff --git a/packages/kbn-es-query/src/kuery/ast/__tests__/ast.js b/packages/kbn-es-query/src/kuery/ast/__tests__/ast.js index 3cbe1203bc533..0d7b4313b861e 100644 --- a/packages/kbn-es-query/src/kuery/ast/__tests__/ast.js +++ b/packages/kbn-es-query/src/kuery/ast/__tests__/ast.js @@ -20,7 +20,7 @@ import * as ast from '../ast'; import expect from '@kbn/expect'; import { nodeTypes } from '../../node_types/index'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; let indexPattern; diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/and.js b/packages/kbn-es-query/src/kuery/functions/__tests__/and.js index 07289a878e8c1..8d1d24061daa5 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/and.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/and.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; import * as and from '../and'; import { nodeTypes } from '../../node_types'; import * as ast from '../../ast'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; let indexPattern; diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/exists.js b/packages/kbn-es-query/src/kuery/functions/__tests__/exists.js index ee4cfab94e614..d2a87e95bac23 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/exists.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/exists.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; import * as exists from '../exists'; import { nodeTypes } from '../../node_types'; import _ from 'lodash'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; let indexPattern; diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/geo_bounding_box.js b/packages/kbn-es-query/src/kuery/functions/__tests__/geo_bounding_box.js index 7afa0fcce1bfe..a0c85b2499aa9 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/geo_bounding_box.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/geo_bounding_box.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import * as geoBoundingBox from '../geo_bounding_box'; import { nodeTypes } from '../../node_types'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; let indexPattern; const params = { diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/geo_polygon.js b/packages/kbn-es-query/src/kuery/functions/__tests__/geo_polygon.js index c1f2fae0bb3e1..34bbeb8776f29 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/geo_polygon.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/geo_polygon.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import * as geoPolygon from '../geo_polygon'; import { nodeTypes } from '../../node_types'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; let indexPattern; diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/is.js b/packages/kbn-es-query/src/kuery/functions/__tests__/is.js index b2f3d7ec16a65..2d8dcb986f84e 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/is.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/is.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import * as is from '../is'; import { nodeTypes } from '../../node_types'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; let indexPattern; diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/nested.js b/packages/kbn-es-query/src/kuery/functions/__tests__/nested.js index 5ba73e485ddf1..f15a46d6f9a2a 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/nested.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/nested.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; import * as nested from '../nested'; import { nodeTypes } from '../../node_types'; import * as ast from '../../ast'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; let indexPattern; diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/not.js b/packages/kbn-es-query/src/kuery/functions/__tests__/not.js index 7a2d7fa39c152..5a2391d94956e 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/not.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/not.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; import * as not from '../not'; import { nodeTypes } from '../../node_types'; import * as ast from '../../ast'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; let indexPattern; diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/or.js b/packages/kbn-es-query/src/kuery/functions/__tests__/or.js index f24f24b98e7fb..defaa79f32669 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/or.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/or.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; import * as or from '../or'; import { nodeTypes } from '../../node_types'; import * as ast from '../../ast'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; let indexPattern; diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/range.js b/packages/kbn-es-query/src/kuery/functions/__tests__/range.js index 2361e8bb66769..bad67e2dd0345 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/range.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/range.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import * as range from '../range'; import { nodeTypes } from '../../node_types'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; let indexPattern; diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/utils/get_fields.js b/packages/kbn-es-query/src/kuery/functions/__tests__/utils/get_fields.js index 7718479130a8a..0a99f0285656b 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/utils/get_fields.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/utils/get_fields.js @@ -19,7 +19,7 @@ import { getFields } from '../../utils/get_fields'; import expect from '@kbn/expect'; -import indexPatternResponse from '../../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../../__fixtures__/index_pattern_response'; import { nodeTypes } from '../../..'; diff --git a/packages/kbn-es-query/src/kuery/functions/__tests__/utils/get_full_field_name_node.js b/packages/kbn-es-query/src/kuery/functions/__tests__/utils/get_full_field_name_node.js index dae15979a161c..0bae44bbe2950 100644 --- a/packages/kbn-es-query/src/kuery/functions/__tests__/utils/get_full_field_name_node.js +++ b/packages/kbn-es-query/src/kuery/functions/__tests__/utils/get_full_field_name_node.js @@ -19,7 +19,7 @@ import expect from '@kbn/expect'; import { nodeTypes } from '../../../node_types'; -import indexPatternResponse from '../../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../../__fixtures__/index_pattern_response'; import { getFullFieldNameNode } from '../../utils/get_full_field_name_node'; let indexPattern; diff --git a/packages/kbn-es-query/src/kuery/node_types/__tests__/function.js b/packages/kbn-es-query/src/kuery/node_types/__tests__/function.js index de00c083fc830..b4c1dc11c466c 100644 --- a/packages/kbn-es-query/src/kuery/node_types/__tests__/function.js +++ b/packages/kbn-es-query/src/kuery/node_types/__tests__/function.js @@ -21,7 +21,7 @@ import * as functionType from '../function'; import _ from 'lodash'; import expect from '@kbn/expect'; import * as isFunction from '../../functions/is'; -import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json'; +import { indexPatternResponse } from '../../../__fixtures__/index_pattern_response'; import { nodeTypes } from '../../node_types'; From 12a7c572afe8dcbc6995f252ce82290eede4ed47 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 29 Oct 2019 16:47:21 -0400 Subject: [PATCH 06/13] update migrateFilters to work on full filter objects so that it doesn't have to worry about queries that have been wrapped with `nested` --- .../src/es_query/__tests__/_migrate_filter.js | 24 ++++++++++++------- .../kbn-es-query/src/es_query/from_filters.js | 16 ++++++------- .../src/es_query/migrate_filter.js | 18 +++++++------- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js b/packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js index d9f559987f58b..f1e131513f64e 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js +++ b/packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js @@ -24,20 +24,26 @@ import { migrateFilter } from '../migrate_filter'; describe('migrateFilter', function () { const oldMatchPhraseFilter = { - match: { - fieldFoo: { - query: 'foobar', - type: 'phrase' + query: { + match: { + fieldFoo: { + query: 'foobar', + type: 'phrase' + } } - } + }, + meta: {} }; const newMatchPhraseFilter = { - match_phrase: { - fieldFoo: { - query: 'foobar' + query: { + match_phrase: { + fieldFoo: { + query: 'foobar' + } } - } + }, + meta: {} }; // https://github.com/elastic/elasticsearch/pull/17508 diff --git a/packages/kbn-es-query/src/es_query/from_filters.js b/packages/kbn-es-query/src/es_query/from_filters.js index 9ef2276fe754a..06ed48e924611 100644 --- a/packages/kbn-es-query/src/es_query/from_filters.js +++ b/packages/kbn-es-query/src/es_query/from_filters.js @@ -59,21 +59,21 @@ export function buildQueryFromFilters(filters = [], indexPattern, ignoreFilterIf filter: filters .filter(filterNegate(false)) .filter(filter => !ignoreFilterIfFieldNotInIndex || filterMatchesIndex(filter, indexPattern)) - .map((filter) => handleNestedFilter(filter, indexPattern)) - .map(translateToQuery) - .map(cleanFilter) .map(filter => { return migrateFilter(filter, indexPattern); - }), + }) + .map((filter) => handleNestedFilter(filter, indexPattern)) + .map(translateToQuery) + .map(cleanFilter), should: [], must_not: filters .filter(filterNegate(true)) .filter(filter => !ignoreFilterIfFieldNotInIndex || filterMatchesIndex(filter, indexPattern)) - .map((filter) => handleNestedFilter(filter, indexPattern)) - .map(translateToQuery) - .map(cleanFilter) .map(filter => { return migrateFilter(filter, indexPattern); - }), + }) + .map((filter) => handleNestedFilter(filter, indexPattern)) + .map(translateToQuery) + .map(cleanFilter), }; } diff --git a/packages/kbn-es-query/src/es_query/migrate_filter.js b/packages/kbn-es-query/src/es_query/migrate_filter.js index ba8b859a91d07..bd48178de9e49 100644 --- a/packages/kbn-es-query/src/es_query/migrate_filter.js +++ b/packages/kbn-es-query/src/es_query/migrate_filter.js @@ -21,12 +21,12 @@ import _ from 'lodash'; import { getConvertedValueForField } from '../filters'; export function migrateFilter(filter, indexPattern) { - if (filter.match) { - const fieldName = Object.keys(filter.match)[0]; + if (filter && filter.query && filter.query.match) { + const fieldName = Object.keys(filter.query.match)[0]; if (isMatchPhraseFilter(filter, fieldName)) { - const params = _.get(filter, ['match', fieldName]); + const params = _.get(filter, ['query', 'match', fieldName]); if (indexPattern) { const field = indexPattern.fields.find(f => f.name === fieldName); if (field) { @@ -34,9 +34,12 @@ export function migrateFilter(filter, indexPattern) { } } return { - match_phrase: { - [fieldName]: _.omit(params, 'type'), - }, + ...filter, + query: { + match_phrase: { + [fieldName]: _.omit(params, 'type'), + }, + } }; } } @@ -45,6 +48,5 @@ export function migrateFilter(filter, indexPattern) { } function isMatchPhraseFilter(filter, fieldName) { - const type = _.get(filter, ['match', fieldName, 'type']) || _.get(filter, ['nested', 'query', 'match', fieldName, 'type']); - return type === 'phrase'; + return _.get(filter, ['query', 'match', fieldName, 'type']) === 'phrase'; } From 485426235a54475cc334190c8867f75828c83fa7 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 29 Oct 2019 16:56:33 -0400 Subject: [PATCH 07/13] add test to ensure fromFilters auto wraps filters on nested fields --- .../src/es_query/__tests__/from_filters.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/kbn-es-query/src/es_query/__tests__/from_filters.js b/packages/kbn-es-query/src/es_query/__tests__/from_filters.js index 676992e4dddc8..c68e63894d138 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/from_filters.js +++ b/packages/kbn-es-query/src/es_query/__tests__/from_filters.js @@ -19,6 +19,7 @@ import expect from '@kbn/expect'; import { buildQueryFromFilters } from '../from_filters'; +import { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; describe('build query', function () { describe('buildQueryFromFilters', function () { @@ -147,5 +148,30 @@ describe('build query', function () { expect(result.filter).to.eql(expectedESQueries); }); + + it('should wrap filters targeting nested fields in a nested query', () => { + const filters = [ + { + exists: { field: 'nestedField.child' }, + meta: { type: 'exists' }, + }, + ]; + + const expectedESQueries = [ + { + nested: { + path: 'nestedField', + query: { + exists: { + field: 'nestedField.child' + } + } + } + }, + ]; + + const result = buildQueryFromFilters(filters, indexPattern); + expect(result.filter).to.eql(expectedESQueries); + }); }); }); From 29f943d373847f4de04c1bd6063118f363aef17a Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 29 Oct 2019 17:22:59 -0400 Subject: [PATCH 08/13] Add smoke test for nested filter and move filter editor tests into their own suite for easier running and debugging --- test/functional/apps/discover/_discover.js | 15 ---- .../apps/discover/_filter_editor.js | 75 +++++++++++++++++++ test/functional/apps/discover/index.js | 1 + 3 files changed, 76 insertions(+), 15 deletions(-) create mode 100644 test/functional/apps/discover/_filter_editor.js diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js index 9d3f95e28942a..e89eb72d614bb 100644 --- a/test/functional/apps/discover/_discover.js +++ b/test/functional/apps/discover/_discover.js @@ -25,7 +25,6 @@ export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const browser = getService('browser'); const kibanaServer = getService('kibanaServer'); - const filterBar = getService('filterBar'); const queryBar = getService('queryBar'); const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); const defaultSettings = { @@ -175,20 +174,6 @@ export default function ({ getService, getPageObjects }) { }); - describe('filter editor', function () { - it('should add a phrases filter', async function () { - await filterBar.addFilter('extension.raw', 'is one of', 'jpg'); - expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); - }); - - it('should show the phrases if you re-open a phrases filter', async function () { - await filterBar.clickEditFilter('extension.raw', 'jpg'); - const phrases = await filterBar.getFilterEditorSelectedPhrases(); - expect(phrases.length).to.be(1); - expect(phrases[0]).to.be('jpg'); - }); - }); - describe('data-shared-item', function () { it('should have correct data-shared-item title and description', async () => { const expected = { diff --git a/test/functional/apps/discover/_filter_editor.js b/test/functional/apps/discover/_filter_editor.js new file mode 100644 index 0000000000000..5a7ca75f6c5c0 --- /dev/null +++ b/test/functional/apps/discover/_filter_editor.js @@ -0,0 +1,75 @@ +/* + * 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'; + +export default function ({ getService, getPageObjects }) { + const log = getService('log'); + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const filterBar = getService('filterBar'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); + const defaultSettings = { + defaultIndex: 'logstash-*', + }; + + describe('discover filter editor', function describeIndexTests() { + const fromTime = '2015-09-19 06:31:44.000'; + const toTime = '2015-09-23 18:31:44.000'; + + before(async function () { + log.debug('load kibana index with default index pattern'); + await esArchiver.loadIfNeeded('discover'); + + // and load a set of makelogs data + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace(defaultSettings); + log.debug('discover filter editor'); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + }); + + describe('filter editor', function () { + it('should add a phrases filter', async function () { + await filterBar.addFilter('extension.raw', 'is one of', 'jpg'); + expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); + }); + + it('should show the phrases if you re-open a phrases filter', async function () { + await filterBar.clickEditFilter('extension.raw', 'jpg'); + const phrases = await filterBar.getFilterEditorSelectedPhrases(); + expect(phrases.length).to.be(1); + expect(phrases[0]).to.be('jpg'); + await filterBar.ensureFieldEditorModalIsClosed(); + }); + + it('should support filtering on nested fields', async () => { + await filterBar.addFilter('nestedField.child', 'is', 'nestedValue'); + expect(await filterBar.hasFilter('nestedField.child', 'nestedValue')).to.be(true); + await retry.try(async function () { + expect(await PageObjects.discover.getHitCount()).to.be( + '1' + ); + }); + }); + }); + }); +} diff --git a/test/functional/apps/discover/index.js b/test/functional/apps/discover/index.js index 902490bebd1ac..28df897b67c09 100644 --- a/test/functional/apps/discover/index.js +++ b/test/functional/apps/discover/index.js @@ -34,6 +34,7 @@ export default function ({ getService, loadTestFile }) { loadTestFile(require.resolve('./_saved_queries')); loadTestFile(require.resolve('./_discover')); + loadTestFile(require.resolve('./_filter_editor')); loadTestFile(require.resolve('./_errors')); loadTestFile(require.resolve('./_field_data')); loadTestFile(require.resolve('./_shared_links')); From 6fe0fbe08e1a348a8968bb3058bc73121d89139d Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 29 Oct 2019 22:43:30 -0400 Subject: [PATCH 09/13] fix bad type change --- .../src/filters/lib/geo_bounding_box_filter.test.ts | 1 + .../kbn-es-query/src/filters/lib/geo_polygon_filter.test.ts | 1 + packages/kbn-es-query/src/filters/lib/meta_filter.ts | 2 +- packages/kbn-es-query/src/filters/lib/missing_filter.test.ts | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.test.ts b/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.test.ts index 979da6d1c4d76..63c3a59044c1f 100644 --- a/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.test.ts +++ b/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.test.ts @@ -33,6 +33,7 @@ describe('geo_bounding_box filter', function() { meta: { disabled: false, negate: false, + alias: null, params: { bottom_right: { lat: 1, lon: 1 }, top_left: { lat: 1, lon: 1 }, diff --git a/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.test.ts b/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.test.ts index 5f81188e930f2..ba8e43b0cea85 100644 --- a/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.test.ts +++ b/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.test.ts @@ -32,6 +32,7 @@ describe('geo_polygon filter', function() { meta: { disabled: false, negate: false, + alias: null, params: { points: [{ lat: 1, lon: 1 }], }, diff --git a/packages/kbn-es-query/src/filters/lib/meta_filter.ts b/packages/kbn-es-query/src/filters/lib/meta_filter.ts index c696a3d08578c..8f6aef782cea2 100644 --- a/packages/kbn-es-query/src/filters/lib/meta_filter.ts +++ b/packages/kbn-es-query/src/filters/lib/meta_filter.ts @@ -38,7 +38,7 @@ export interface FilterMeta { type?: string; disabled: boolean; negate: boolean; - alias?: string | null; + alias: string | null; key?: string; value?: string | ((formatter?: FilterValueFormatter) => string); params?: any; diff --git a/packages/kbn-es-query/src/filters/lib/missing_filter.test.ts b/packages/kbn-es-query/src/filters/lib/missing_filter.test.ts index 633863e3949dc..240d8fb26f3e0 100644 --- a/packages/kbn-es-query/src/filters/lib/missing_filter.test.ts +++ b/packages/kbn-es-query/src/filters/lib/missing_filter.test.ts @@ -29,6 +29,7 @@ describe('missing filter', function() { meta: { disabled: false, negate: false, + alias: null, }, }; const result = getMissingFilterField(filter); From abad2010550d0d28cab7f45ca3abf6c08ab421d9 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 29 Oct 2019 22:54:00 -0400 Subject: [PATCH 10/13] dedupe filterToQuery logic --- .../kbn-es-query/src/es_query/from_filters.js | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/packages/kbn-es-query/src/es_query/from_filters.js b/packages/kbn-es-query/src/es_query/from_filters.js index 06ed48e924611..ed660cc57f172 100644 --- a/packages/kbn-es-query/src/es_query/from_filters.js +++ b/packages/kbn-es-query/src/es_query/from_filters.js @@ -54,26 +54,23 @@ const translateToQuery = function (filter) { export function buildQueryFromFilters(filters = [], indexPattern, ignoreFilterIfFieldNotInIndex) { filters = filters.filter(filter => filter && !_.get(filter, ['meta', 'disabled'])); - return { - must: [], - filter: filters - .filter(filterNegate(false)) + + const filtersToESQueries = (negate) => { + return filters + .filter(filterNegate(negate)) .filter(filter => !ignoreFilterIfFieldNotInIndex || filterMatchesIndex(filter, indexPattern)) .map(filter => { return migrateFilter(filter, indexPattern); }) - .map((filter) => handleNestedFilter(filter, indexPattern)) + .map(filter => handleNestedFilter(filter, indexPattern)) .map(translateToQuery) - .map(cleanFilter), + .map(cleanFilter); + }; + + return { + must: [], + filter: filtersToESQueries(false), should: [], - must_not: filters - .filter(filterNegate(true)) - .filter(filter => !ignoreFilterIfFieldNotInIndex || filterMatchesIndex(filter, indexPattern)) - .map(filter => { - return migrateFilter(filter, indexPattern); - }) - .map((filter) => handleNestedFilter(filter, indexPattern)) - .map(translateToQuery) - .map(cleanFilter), + must_not: filtersToESQueries(true), }; } From ad6b76239820d0b649ea40895ae61632d1fac721 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 30 Oct 2019 16:21:49 -0400 Subject: [PATCH 11/13] fix helper that wasn't doing what it said it did --- test/functional/services/filter_bar.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/functional/services/filter_bar.ts b/test/functional/services/filter_bar.ts index adf0f2266ba17..9d494b1e6d950 100644 --- a/test/functional/services/filter_bar.ts +++ b/test/functional/services/filter_bar.ts @@ -166,6 +166,7 @@ export function FilterBarProvider({ getService, getPageObjects }: FtrProviderCon if (cancelSaveFilterModalButtonExists) { await testSubjects.click('cancelSaveFilter'); } + await testSubjects.waitForDeleted('cancelSaveFilter'); } /** From af4e53e1f8187b24444bfbcd5530597d428bb421 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 5 Dec 2019 16:19:26 -0500 Subject: [PATCH 12/13] Convert test from pre-merge to jest --- ...filter.js => handle_nested_filter.test.ts} | 76 ++++++++++--------- 1 file changed, 42 insertions(+), 34 deletions(-) rename src/plugins/data/common/es_query/es_query/{__tests__/handle_nested_filter.js => handle_nested_filter.test.ts} (57%) diff --git a/src/plugins/data/common/es_query/es_query/__tests__/handle_nested_filter.js b/src/plugins/data/common/es_query/es_query/handle_nested_filter.test.ts similarity index 57% rename from src/plugins/data/common/es_query/es_query/__tests__/handle_nested_filter.js rename to src/plugins/data/common/es_query/es_query/handle_nested_filter.test.ts index d3ef8f3374466..594b2641c39be 100644 --- a/src/plugins/data/common/es_query/es_query/__tests__/handle_nested_filter.js +++ b/src/plugins/data/common/es_query/es_query/handle_nested_filter.test.ts @@ -17,67 +17,75 @@ * under the License. */ +import { handleNestedFilter } from './handle_nested_filter'; +import { fields } from '../../index_patterns/mocks'; +import { buildPhraseFilter, buildQueryFilter } from '../filters'; +import { IFieldType, IIndexPattern } from '../../index_patterns'; -import { handleNestedFilter } from '../handle_nested_filter'; -import { indexPatternResponse as indexPattern } from '../../__fixtures__/index_pattern_response'; -import { buildPhraseFilter, buildQueryFilter } from '../../filters'; -import expect from '@kbn/expect'; +describe('handleNestedFilter', function() { + const indexPattern: IIndexPattern = ({ + id: 'logstash-*', + fields, + } as unknown) as IIndexPattern; -describe('handleNestedFilter', function () { - - it('should return the filter\'s query wrapped in nested query if the target field is nested', () => { + it("should return the filter's query wrapped in nested query if the target field is nested", () => { const field = getField('nestedField.child'); - const filter = buildPhraseFilter(field, 'foo', indexPattern); + const filter = buildPhraseFilter(field!, 'foo', indexPattern); const result = handleNestedFilter(filter, indexPattern); - expect(result).to.eql({ + expect(result).toEqual({ + meta: { + index: 'logstash-*', + }, nested: { path: 'nestedField', query: { match_phrase: { - 'nestedField.child': 'foo' - } - } - } - } - ); + 'nestedField.child': 'foo', + }, + }, + }, + }); }); it('should return filter untouched if it does not target a nested field', () => { const field = getField('extension'); - const filter = buildPhraseFilter(field, 'jpg', indexPattern); + const filter = buildPhraseFilter(field!, 'jpg', indexPattern); const result = handleNestedFilter(filter, indexPattern); - expect(result).to.be(filter); + expect(result).toBe(filter); }); it('should return filter untouched if it does not target a field from the given index pattern', () => { const field = { ...getField('extension'), name: 'notarealfield' }; - const filter = buildPhraseFilter(field, 'jpg', indexPattern); + const filter = buildPhraseFilter(field as IFieldType, 'jpg', indexPattern); const result = handleNestedFilter(filter, indexPattern); - expect(result).to.be(filter); + expect(result).toBe(filter); }); it('should return filter untouched if no index pattern is provided', () => { const field = getField('extension'); - const filter = buildPhraseFilter(field, 'jpg', indexPattern); + const filter = buildPhraseFilter(field!, 'jpg', indexPattern); const result = handleNestedFilter(filter); - expect(result).to.be(filter); + expect(result).toBe(filter); }); it('should return the filter untouched if a target field cannot be determined', () => { // for example, we don't support query_string queries - const filter = buildQueryFilter({ - query: { - query_string: { - query: 'response:200' - } - } - }, indexPattern.id); + const filter = buildQueryFilter( + { + query: { + query_string: { + query: 'response:200', + }, + }, + }, + 'logstash-*', + 'foo' + ); const result = handleNestedFilter(filter); - expect(result).to.be(filter); + expect(result).toBe(filter); }); -}); - -function getField(name) { - return indexPattern.fields.find(field => field.name === name); -} + function getField(name: string) { + return indexPattern.fields.find(field => field.name === name); + } +}); From 6d3efd36886e415dddfde1d1b071db0f2ce4b9cd Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 5 Dec 2019 16:30:58 -0500 Subject: [PATCH 13/13] Use new time range style --- test/functional/apps/discover/_filter_editor.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/functional/apps/discover/_filter_editor.js b/test/functional/apps/discover/_filter_editor.js index 5a7ca75f6c5c0..cab16252add4a 100644 --- a/test/functional/apps/discover/_filter_editor.js +++ b/test/functional/apps/discover/_filter_editor.js @@ -32,8 +32,6 @@ export default function ({ getService, getPageObjects }) { }; describe('discover filter editor', function describeIndexTests() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; before(async function () { log.debug('load kibana index with default index pattern'); @@ -44,7 +42,7 @@ export default function ({ getService, getPageObjects }) { await kibanaServer.uiSettings.replace(defaultSettings); log.debug('discover filter editor'); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); describe('filter editor', function () {