Moved filter generator ⇒ NP (#50118)

* Moved filter generator to NP

* Deleted unused test

* Fixed browser test + fix to discover actions

* Added jsdcos
This commit is contained in:
Liza Katz 2019-11-14 16:26:58 +02:00 committed by GitHub
parent 4f888196b7
commit 6219cfdc16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 269 additions and 407 deletions

View file

@ -26,9 +26,6 @@ import { IInjector } from 'ui/chrome';
// @ts-ignore
import * as filterActions from 'plugins/kibana/discover/doc_table/actions/filter';
// @ts-ignore
import { getFilterGenerator } from 'ui/filter_manager';
import {
AppStateClass as TAppStateClass,
AppState as TAppState,

View file

@ -26,7 +26,8 @@ export function createIndexPatternsStub() {
get: sinon.spy(indexPatternId =>
Promise.resolve({
id: indexPatternId,
isTimeNanosBased: () => false
isTimeNanosBased: () => false,
popularizeField: () => {},
})
),
};

View file

@ -19,32 +19,33 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import sinon from 'sinon';
import { getServices } from '../../../../kibana_services';
import { createStateStub } from './_utils';
import { QueryParameterActionsProvider } from '../actions';
import { createIndexPatternsStub } from '../../api/__tests__/_stubs';
import { npStart } from 'ui/new_platform';
describe('context app', function () {
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.module(function createServiceStubs($provide) {
$provide.value('indexPatterns', createIndexPatternsStub());
}));
describe('action addFilter', function () {
let filterManagerStub;
let addFilter;
beforeEach(ngMock.inject(function createPrivateStubs(Private) {
filterManagerStub = createQueryFilterStub();
Private.stub(getServices().FilterBarQueryFilterProvider, filterManagerStub);
Private.stub(getServices().FilterBarQueryFilterProvider);
addFilter = Private(QueryParameterActionsProvider).addFilter;
}));
it('should pass the given arguments to the filterManager', function () {
const state = createStateStub();
const filterManagerAddStub = npStart.plugins.data.query.filterManager.addFilters;
addFilter(state)('FIELD_NAME', 'FIELD_VALUE', 'FILTER_OPERATION');
const filterManagerAddStub = filterManagerStub.addFilters;
//get the generated filter
const generatedFilter = filterManagerAddStub.firstCall.args[0][0];
const queryKeys = Object.keys(generatedFilter.query.match_phrase);
@ -55,20 +56,12 @@ describe('context app', function () {
it('should pass the index pattern id to the filterManager', function () {
const state = createStateStub();
const filterManagerAddStub = npStart.plugins.data.query.filterManager.addFilters;
addFilter(state)('FIELD_NAME', 'FIELD_VALUE', 'FILTER_OPERATION');
const filterManagerAddStub = filterManagerStub.addFilters;
const generatedFilter = filterManagerAddStub.firstCall.args[0][0];
expect(filterManagerAddStub.calledOnce).to.be(true);
expect(generatedFilter.meta.index).to.eql('INDEX_PATTERN_ID');
});
});
});
function createQueryFilterStub() {
return {
addFilters: sinon.stub(),
getAppFilters: sinon.stub(),
};
}

View file

@ -18,7 +18,8 @@
*/
import _ from 'lodash';
import { getServices, getFilterGenerator } from '../../../kibana_services';
import { generateFilters } from '../../../../../../../../plugins/data/public';
import { npStart } from 'ui/new_platform';
import {
MAX_CONTEXT_SIZE,
@ -27,9 +28,8 @@ import {
} from './constants';
export function QueryParameterActionsProvider(indexPatterns, Private) {
const queryFilter = Private(getServices().FilterBarQueryFilterProvider);
const filterGen = getFilterGenerator(queryFilter);
export function QueryParameterActionsProvider(indexPatterns) {
const { filterManager } = npStart.plugins.data.query;
const setPredecessorCount = (state) => (predecessorCount) => (
state.queryParameters.predecessorCount = clamp(
@ -55,13 +55,13 @@ export function QueryParameterActionsProvider(indexPatterns, Private) {
);
const updateFilters = () => filters => {
queryFilter.setFilters(filters);
filterManager.setFilters(filters);
};
const addFilter = (state) => async (field, values, operation) => {
const indexPatternId = state.queryParameters.indexPatternId;
const newFilters = filterGen.generate(field, values, operation, indexPatternId);
queryFilter.addFilters(newFilters);
const newFilters = generateFilters(filterManager, field, values, operation, indexPatternId);
filterManager.addFilters(newFilters);
const indexPattern = await indexPatterns.get(indexPatternId);
indexPattern.popularizeField(field.name, 1);
};

View file

@ -31,7 +31,6 @@ import './doc_table';
import { getSort } from './doc_table/lib/get_sort';
import { getSortForSearchSource } from './doc_table/lib/get_sort_for_search_source';
import * as columnActions from './doc_table/actions/columns';
import * as filterActions from './doc_table/actions/filter';
import indexTemplate from './discover.html';
import { showOpenSearchPanel } from '../top_nav/show_open_search_panel';
@ -41,7 +40,6 @@ import { getPainlessError } from './get_painless_error';
import {
angular,
buildVislibDimensions,
getFilterGenerator,
getRequestInspectorStats,
getResponseInspectorStats,
getServices,
@ -76,7 +74,7 @@ const {
import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../breadcrumbs';
import { extractTimeFilter, changeTimeFilter } from '../../../../data/public';
import { start as data } from '../../../../data/public/legacy';
import { generateFilters } from '../../../../../../plugins/data/public';
const { savedQueryService } = data.search.services;
@ -195,7 +193,6 @@ function discoverController(
const shareContextMenuExtensions = Private(ShareContextMenuExtensionsRegistryProvider);
const queryFilter = Private(FilterBarQueryFilterProvider);
const filterGen = getFilterGenerator(queryFilter);
const inspectorAdapters = {
requests: new RequestAdapter()
@ -900,7 +897,8 @@ function discoverController(
// TODO: On array fields, negating does not negate the combination, rather all terms
$scope.filterQuery = function (field, values, operation) {
$scope.indexPattern.popularizeField(field, 1);
filterActions.addFilter(field, values, operation, $scope.indexPattern.id, $scope.state, filterGen);
const newFilters = generateFilters(queryFilter, field, values, operation, $scope.indexPattern.id);
return queryFilter.addFilters(newFilters);
};
$scope.addColumn = function addColumn(columnName) {

View file

@ -1,66 +0,0 @@
/*
* 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 { addFilter } from '../../actions/filter';
import StubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
import NoDigestPromises from 'test_utils/no_digest_promises';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import sinon from 'sinon';
function getFilterGeneratorStub() {
return {
add: sinon.stub()
};
}
describe('doc table filter actions', function () {
NoDigestPromises.activateForSuite();
let filterGen;
let indexPattern;
beforeEach(ngMock.module(
'kibana',
function ($provide) {
$provide.service('indexPatterns', require('fixtures/mock_index_patterns'));
}
));
beforeEach(ngMock.inject(function (Private) {
indexPattern = Private(StubbedLogstashIndexPatternProvider);
filterGen = getFilterGeneratorStub();
}));
describe('add', function () {
it('should defer to the FilterManager when dealing with a lucene query', function () {
const state = {
query: { query: 'foo', language: 'lucene' }
};
const args = ['foo', ['bar'], '+', indexPattern, ];
addFilter('foo', ['bar'], '+', indexPattern, state, filterGen);
expect(filterGen.add.calledOnce).to.be(true);
expect(filterGen.add.calledWith(...args)).to.be(true);
});
});
});

View file

@ -1,26 +0,0 @@
/*
* 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 function addFilter(field, values = [], operation, index, state, filterGen) {
if (!Array.isArray(values)) {
values = [values];
}
filterGen.add(field, values, operation, index);
}

View file

@ -25,7 +25,9 @@ import { npStart } from 'ui/new_platform';
import {
esFilters,
TimeRange,
FilterManager,
onlyDisabledFiltersChanged,
generateFilters,
getTime,
Query,
} from '../../../../../../plugins/data/public';
@ -43,7 +45,6 @@ import { getSortForSearchSource } from '../angular/doc_table/lib/get_sort_for_se
import {
Adapters,
angular,
getFilterGenerator,
getRequestInspectorStats,
getResponseInspectorStats,
getServices,
@ -72,18 +73,6 @@ interface SearchScope extends ng.IScope {
isLoading?: boolean;
}
export interface FilterManager {
generate: (
field: {
name: string;
scripted: boolean;
},
values: string | string[],
operation: string,
index: number
) => esFilters.Filter[];
}
interface SearchEmbeddableConfig {
$rootScope: ng.IRootScopeService;
$compile: ng.ICompileService;
@ -107,7 +96,7 @@ export class SearchEmbeddable extends Embeddable<SearchInput, SearchOutput>
private autoRefreshFetchSubscription?: Subscription;
private subscription?: Subscription;
public readonly type = SEARCH_EMBEDDABLE_TYPE;
private filterGen: FilterManager;
private filterManager: FilterManager;
private abortController?: AbortController;
private prevTimeRange?: TimeRange;
@ -134,7 +123,7 @@ export class SearchEmbeddable extends Embeddable<SearchInput, SearchOutput>
parent
);
this.filterGen = getFilterGenerator(queryFilter);
this.filterManager = queryFilter as FilterManager;
this.savedSearch = savedSearch;
this.$rootScope = $rootScope;
this.$compile = $compile;
@ -251,7 +240,7 @@ export class SearchEmbeddable extends Embeddable<SearchInput, SearchOutput>
};
searchScope.filter = async (field, value, operator) => {
let filters = this.filterGen.generate(field, value, operator, indexPattern.id);
let filters = generateFilters(this.filterManager, field, value, operator, indexPattern.id);
filters = filters.map(filter => ({
...filter,
$state: { store: esFilters.FilterStateStore.APP_STATE },

View file

@ -84,8 +84,6 @@ export { angular };
export { buildVislibDimensions } from 'ui/visualize/loader/pipeline_helpers/build_pipeline';
// @ts-ignore
export { callAfterBindingsWorkaround } from 'ui/compat';
// @ts-ignore
export { getFilterGenerator } from 'ui/filter_manager';
export {
getRequestInspectorStats,
getResponseInspectorStats,

View file

@ -1,166 +0,0 @@
/*
* 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 _ from 'lodash';
import sinon from 'sinon';
import MockState from 'fixtures/mock_state';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { getFilterGenerator } from '..';
import { FilterBarQueryFilterProvider } from '../../filter_manager/query_filter';
import { uniqFilters, esFilters } from '../../../../../plugins/data/public';
let queryFilter;
let filterGen;
let appState;
function checkAddFilters(length, comps, idx) {
idx = idx || 0;
const filters = queryFilter.addFilters.getCall(idx).args[0];
expect(filters.length).to.be(length);
if (!Array.isArray(comps)) return;
comps.forEach(function (comp, i) {
expect(filters[i]).to.eql(comp);
});
}
describe('Filter Manager', function () {
beforeEach(ngMock.module(
'kibana',
'kibana/global_state',
function ($provide) {
$provide.service('indexPatterns', require('fixtures/mock_index_patterns'));
appState = new MockState({ filters: [] });
$provide.service('getAppState', function () {
return function () { return appState; };
});
}
));
beforeEach(ngMock.inject(function (_$rootScope_, Private) {
// mock required queryFilter methods, used in the manager
queryFilter = Private(FilterBarQueryFilterProvider);
filterGen = getFilterGenerator(queryFilter);
sinon.stub(queryFilter, 'getAppFilters').callsFake(() => appState.filters);
sinon.stub(queryFilter, 'addFilters').callsFake((filters) => {
if (!Array.isArray(filters)) filters = [filters];
appState.filters = uniqFilters(appState.filters.concat(filters));
});
}));
it('should have an `add` function', function () {
expect(filterGen.add).to.be.a(Function);
});
it('should add a filter', function () {
filterGen.add('myField', 1, '+', 'myIndex');
expect(queryFilter.addFilters.callCount).to.be(1);
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: false },
query: { match_phrase: { myField: 1 } }
}]);
});
it('should add multiple filters if passed an array of values', function () {
filterGen.add('myField', [1, 2, 3], '+', 'myIndex');
expect(queryFilter.addFilters.callCount).to.be(1);
checkAddFilters(3, [{
meta: { index: 'myIndex', negate: false },
query: { match_phrase: { myField: 1 } }
}, {
meta: { index: 'myIndex', negate: false },
query: { match_phrase: { myField: 2 } }
}, {
meta: { index: 'myIndex', negate: false },
query: { match_phrase: { myField: 3 } }
}]);
});
it('should add an exists filter if _exists_ is used as the field', function () {
filterGen.add('_exists_', 'myField', '+', 'myIndex');
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: false },
exists: { field: 'myField' }
}]);
});
it('should negate existing filter instead of added a conflicting filter', function () {
filterGen.add('myField', 1, '+', 'myIndex');
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: false },
query: { match_phrase: { myField: 1 } }
}], 0);
expect(appState.filters).to.have.length(1);
// NOTE: negating exists filters also forces disabled to false
filterGen.add('myField', 1, '-', 'myIndex');
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: true, disabled: false },
query: { match_phrase: { myField: 1 } }
}], 1);
expect(appState.filters).to.have.length(1);
filterGen.add('_exists_', 'myField', '+', 'myIndex');
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: false },
exists: { field: 'myField' }
}], 2);
expect(appState.filters).to.have.length(2);
filterGen.add('_exists_', 'myField', '-', 'myIndex');
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: true, disabled: false },
exists: { field: 'myField' }
}], 3);
expect(appState.filters).to.have.length(2);
const scriptedField = { name: 'scriptedField', scripted: true, script: 1, lang: 'painless' };
filterGen.add(scriptedField, 1, '+', 'myIndex');
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: false, field: 'scriptedField' },
script: esFilters.getPhraseScript(scriptedField, 1)
}], 4);
expect(appState.filters).to.have.length(3);
filterGen.add(scriptedField, 1, '-', 'myIndex');
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: true, disabled: false, field: 'scriptedField' },
script: esFilters.getPhraseScript(scriptedField, 1)
}], 5);
expect(appState.filters).to.have.length(3);
});
it('should enable matching filters being changed', function () {
_.each([true, false], function (negate) {
appState.filters = [{
query: { match_phrase: { myField: 1 } },
meta: { disabled: true, negate: negate }
}];
expect(appState.filters.length).to.be(1);
expect(appState.filters[0].meta.disabled).to.be(true);
filterGen.add('myField', 1, '+', 'myIndex');
expect(appState.filters.length).to.be(1);
expect(appState.filters[0].meta.disabled).to.be(false);
});
});
});

View file

@ -1,98 +0,0 @@
/*
* 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 _ from 'lodash';
import { esFilters } from '../../../../plugins/data/public';
// Adds a filter to a passed state
export function getFilterGenerator(queryFilter) {
const filterGen = {};
filterGen.generate = (field, values, operation, index) => {
values = Array.isArray(values) ? values : [values];
const fieldName = _.isObject(field) ? field.name : field;
const filters = _.flatten([queryFilter.getAppFilters()]);
const newFilters = [];
const negate = (operation === '-');
// TODO: On array fields, negating does not negate the combination, rather all terms
_.each(values, function (value) {
let filter;
const existing = _.find(filters, function (filter) {
if (!filter) return;
if (fieldName === '_exists_' && filter.exists) {
return filter.exists.field === value;
}
if (esFilters.isPhraseFilter(filter)) {
return esFilters.getPhraseFilterField(filter) === fieldName && esFilters.getPhraseFilterValue(filter) === value;
}
if (filter.script) {
return filter.meta.field === fieldName && filter.script.script.params.value === value;
}
});
if (existing) {
existing.meta.disabled = false;
if (existing.meta.negate !== negate) {
existing.meta.negate = !existing.meta.negate;
}
newFilters.push(existing);
return;
}
switch (fieldName) {
case '_exists_':
filter = {
meta: { negate, index },
exists: {
field: value
}
};
break;
default:
if (field.scripted) {
filter = {
meta: { negate, index, field: fieldName },
script: esFilters.getPhraseScript(field, value)
};
} else {
filter = { meta: { negate, index }, query: { match_phrase: {} } };
filter.query.match_phrase[fieldName] = value;
}
break;
}
newFilters.push(filter);
});
return newFilters;
};
filterGen.add = function (field, values, operation, index) {
const newFilters = this.generate(field, values, operation, index);
return queryFilter.addFilters(newFilters);
};
return filterGen;
}

View file

@ -17,4 +17,3 @@
* under the License.
*/
export { getFilterGenerator } from './filter_generator';

View file

@ -22,3 +22,4 @@ export { FilterManager } from './filter_manager';
export { uniqFilters } from './lib/uniq_filters';
export { mapAndFlattenFilters } from './lib/map_and_flatten_filters';
export { onlyDisabledFiltersChanged } from './lib/only_disabled';
export { generateFilters } from './lib/generate_filters';

View file

@ -0,0 +1,130 @@
/*
* 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 { generateFilters } from './generate_filters';
import { FilterManager } from '../filter_manager';
import { esFilters } from '../../..';
const INDEX_NAME = 'my-index';
const EXISTS_FIELD_NAME = '_exists_';
const FIELD = {
name: 'my-field',
};
const PHRASE_VALUE = 'my-value';
describe('Generate filters', () => {
let mockFilterManager: FilterManager;
let filtersArray: esFilters.Filter[];
beforeEach(() => {
filtersArray = [];
mockFilterManager = {
getAppFilters: () => {
return filtersArray;
},
} as FilterManager;
});
it('should create exists filter', () => {
const filters = generateFilters(
mockFilterManager,
EXISTS_FIELD_NAME,
FIELD.name,
'',
INDEX_NAME
);
expect(filters).toHaveLength(1);
expect(filters[0].meta.index === INDEX_NAME);
expect(filters[0].meta.negate).toBeFalsy();
expect(esFilters.isExistsFilter(filters[0])).toBeTruthy();
});
it('should create negated exists filter', () => {
const filters = generateFilters(
mockFilterManager,
EXISTS_FIELD_NAME,
FIELD.name,
'-',
INDEX_NAME
);
expect(filters).toHaveLength(1);
expect(filters[0].meta.index === INDEX_NAME);
expect(filters[0].meta.negate).toBeTruthy();
expect(esFilters.isExistsFilter(filters[0])).toBeTruthy();
});
it('should update and re-enable EXISTING exists filter', () => {
const filter = esFilters.buildExistsFilter(FIELD, { id: INDEX_NAME });
filter.meta.disabled = true;
filtersArray.push(filter);
const filters = generateFilters(mockFilterManager, '_exists_', FIELD.name, '-', INDEX_NAME);
expect(filters).toHaveLength(1);
expect(filters[0].meta.index === INDEX_NAME);
expect(filters[0].meta.negate).toBeTruthy();
expect(filters[0].meta.disabled).toBeFalsy();
expect(esFilters.isExistsFilter(filters[0])).toBeTruthy();
});
it('should create phrase filter', () => {
const filters = generateFilters(mockFilterManager, FIELD, PHRASE_VALUE, '', INDEX_NAME);
expect(filters).toHaveLength(1);
expect(filters[0].meta.index === INDEX_NAME);
expect(filters[0].meta.negate).toBeFalsy();
expect(esFilters.isPhraseFilter(filters[0])).toBeTruthy();
expect((filters[0] as esFilters.PhraseFilter).query.match_phrase).toEqual({
[FIELD.name]: PHRASE_VALUE,
});
});
it('should create negated phrase filter', () => {
const filters = generateFilters(mockFilterManager, FIELD, PHRASE_VALUE, '-', INDEX_NAME);
expect(filters).toHaveLength(1);
expect(filters[0].meta.index === INDEX_NAME);
expect(filters[0].meta.negate).toBeTruthy();
expect(esFilters.isPhraseFilter(filters[0])).toBeTruthy();
expect((filters[0] as esFilters.PhraseFilter).query.match_phrase).toEqual({
[FIELD.name]: PHRASE_VALUE,
});
});
it('should create multiple phrase filters', () => {
const ANOTHER_PHRASE = 'another-value';
const filters = generateFilters(
mockFilterManager,
FIELD,
[PHRASE_VALUE, ANOTHER_PHRASE],
'',
INDEX_NAME
);
expect(filters).toHaveLength(2);
expect(filters[0].meta.index === INDEX_NAME);
expect(filters[0].meta.negate).toBeFalsy();
expect(filters[1].meta.index === INDEX_NAME);
expect(filters[1].meta.negate).toBeFalsy();
expect(esFilters.isPhraseFilter(filters[0])).toBeTruthy();
expect(esFilters.isPhraseFilter(filters[1])).toBeTruthy();
expect((filters[0] as esFilters.PhraseFilter).query.match_phrase).toEqual({
[FIELD.name]: PHRASE_VALUE,
});
expect((filters[1] as esFilters.PhraseFilter).query.match_phrase).toEqual({
[FIELD.name]: ANOTHER_PHRASE,
});
});
});

View file

@ -0,0 +1,112 @@
/*
* 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 _ from 'lodash';
import { FilterManager, esFilters, Field } from '../../..';
function getExistingFilter(
appFilters: esFilters.Filter[],
fieldName: string,
value: any
): esFilters.Filter | undefined {
// TODO: On array fields, negating does not negate the combination, rather all terms
return _.find(appFilters, function(filter) {
if (!filter) return;
if (fieldName === '_exists_' && esFilters.isExistsFilter(filter)) {
return filter.exists!.field === value;
}
if (esFilters.isPhraseFilter(filter)) {
return (
esFilters.getPhraseFilterField(filter) === fieldName &&
esFilters.getPhraseFilterValue(filter) === value
);
}
if (esFilters.isScriptedPhraseFilter(filter)) {
return filter.meta.field === fieldName && filter.meta.script!.script.params.value === value;
}
});
}
function updateExistingFilter(existingFilter: esFilters.Filter, negate: boolean) {
existingFilter.meta.disabled = false;
if (existingFilter.meta.negate !== negate) {
existingFilter.meta.negate = !existingFilter.meta.negate;
}
}
/**
* Generate filter objects, as a result of triggering a filter action on a
* specific index pattern field.
*
* @param {FilterManager} filterManager - The active filter manager to lookup for existing filters
* @param {Field | string} field - The field for which filters should be generated
* @param {any} values - One or more values to filter for.
* @param {string} operation - "-" to create a negated filter
* @param {string} index - Index string to generate filters for
*
* @returns {object} An array of filters to be added back to filterManager
*/
export function generateFilters(
filterManager: FilterManager,
field: Field | string,
values: any,
operation: string,
index: string
): esFilters.Filter[] {
values = Array.isArray(values) ? values : [values];
const fieldObj = _.isObject(field)
? field
: {
name: field,
};
const fieldName = fieldObj.name;
const newFilters: esFilters.Filter[] = [];
const appFilters = filterManager.getAppFilters();
const negate = operation === '-';
let filter;
_.each(values, function(value) {
const existing = getExistingFilter(appFilters, fieldName, value);
if (existing) {
updateExistingFilter(existing, negate);
filter = existing;
} else {
const tmpIndexPattern = { id: index };
switch (fieldName) {
case '_exists_':
filter = esFilters.buildExistsFilter(fieldObj, tmpIndexPattern);
break;
default:
filter = esFilters.buildPhraseFilter(fieldObj, value, tmpIndexPattern);
break;
}
filter.meta.negate = negate;
}
newFilters.push(filter);
});
return newFilters;
}