Move @kbn/es-query into data plugin - filters folder (#49843)

Dismissing reviews from ml and canvas as this is only an import change. 


* Move @kbn/es-query into data plugin - filters folder

* fix PR comments
This commit is contained in:
Alexey Antonov 2019-11-08 06:02:49 +03:00 committed by Liza Katz
parent 4baf5d9751
commit c3951e9374
197 changed files with 1965 additions and 1589 deletions

View file

@ -18,7 +18,7 @@
*/
import _ from 'lodash';
import { getConvertedValueForField } from '../filters';
import { getConvertedValueForField } from '../utils/filters';
export function migrateFilter(filter, indexPattern) {
if (filter.match) {

View file

@ -1,92 +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 { buildInlineScriptForPhraseFilter, buildPhraseFilter } from '../phrase';
import expect from '@kbn/expect';
import _ from 'lodash';
import indexPattern from '../../__fixtures__/index_pattern_response.json';
import filterSkeleton from '../../__fixtures__/filter_skeleton';
let expected;
describe('Filter Manager', function () {
describe('Phrase filter builder', function () {
beforeEach(() => {
expected = _.cloneDeep(filterSkeleton);
});
it('should be a function', function () {
expect(buildPhraseFilter).to.be.a(Function);
});
it('should return a match query filter when passed a standard field', function () {
const field = getField(indexPattern, 'bytes');
expected.query = {
match_phrase: {
bytes: 5
}
};
expect(buildPhraseFilter(field, 5, indexPattern)).to.eql(expected);
});
it('should return a script filter when passed a scripted field', function () {
const field = getField(indexPattern, 'script number');
expected.meta.field = 'script number';
_.set(expected, 'script.script', {
source: '(' + field.script + ') == value',
lang: 'expression',
params: {
value: 5,
}
});
expect(buildPhraseFilter(field, 5, indexPattern)).to.eql(expected);
});
});
describe('buildInlineScriptForPhraseFilter', function () {
it('should wrap painless scripts in a lambda', function () {
const field = {
lang: 'painless',
script: 'return foo;',
};
const expected = `boolean compare(Supplier s, def v) {return s.get() == v;}` +
`compare(() -> { return foo; }, params.value);`;
expect(buildInlineScriptForPhraseFilter(field)).to.be(expected);
});
it('should create a simple comparison for other langs', function () {
const field = {
lang: 'expression',
script: 'doc[bytes].value',
};
const expected = `(doc[bytes].value) == value`;
expect(buildInlineScriptForPhraseFilter(field)).to.be(expected);
});
});
});
function getField(indexPattern, name) {
return indexPattern.fields.find(field => field.name === name);
}

View file

@ -1,46 +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 { buildQueryFilter } from '../query';
import { cloneDeep } from 'lodash';
import expect from '@kbn/expect';
import indexPattern from '../../__fixtures__/index_pattern_response.json';
import filterSkeleton from '../../__fixtures__/filter_skeleton';
let expected;
describe('Filter Manager', function () {
describe('Phrase filter builder', function () {
beforeEach(() => {
expected = cloneDeep(filterSkeleton);
});
it('should be a function', function () {
expect(buildQueryFilter).to.be.a(Function);
});
it('should return a query filter when passed a standard field', function () {
expected.query = {
foo: 'bar'
};
expect(buildQueryFilter({ foo: 'bar' }, indexPattern.id)).to.eql(expected);
});
});
});

View file

@ -1,156 +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 { buildRangeFilter } from '../range';
import expect from '@kbn/expect';
import _ from 'lodash';
import indexPattern from '../../__fixtures__/index_pattern_response.json';
import filterSkeleton from '../../__fixtures__/filter_skeleton';
let expected;
describe('Filter Manager', function () {
describe('Range filter builder', function () {
beforeEach(() => {
expected = _.cloneDeep(filterSkeleton);
});
it('should be a function', function () {
expect(buildRangeFilter).to.be.a(Function);
});
it('should return a range filter when passed a standard field', function () {
const field = getField(indexPattern, 'bytes');
expected.range = {
bytes: {
gte: 1,
lte: 3
}
};
expect(buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern)).to.eql(expected);
});
it('should return a script filter when passed a scripted field', function () {
const field = getField(indexPattern, 'script number');
expected.meta.field = 'script number';
_.set(expected, 'script.script', {
lang: 'expression',
source: '(' + field.script + ')>=gte && (' + field.script + ')<=lte',
params: {
value: '>=1 <=3',
gte: 1,
lte: 3
}
});
expect(buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern)).to.eql(expected);
});
it('should wrap painless scripts in comparator lambdas', function () {
const field = getField(indexPattern, 'script date');
const expected = `boolean gte(Supplier s, def v) {return !s.get().toInstant().isBefore(Instant.parse(v))} ` +
`boolean lte(Supplier s, def v) {return !s.get().toInstant().isAfter(Instant.parse(v))}` +
`gte(() -> { ${field.script} }, params.gte) && ` +
`lte(() -> { ${field.script} }, params.lte)`;
const inlineScript = buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern).script.script.source;
expect(inlineScript).to.be(expected);
});
it('should throw an error when gte and gt, or lte and lt are both passed', function () {
const field = getField(indexPattern, 'script number');
expect(function () {
buildRangeFilter(field, { gte: 1, gt: 3 }, indexPattern);
}).to.throwError();
expect(function () {
buildRangeFilter(field, { lte: 1, lt: 3 }, indexPattern);
}).to.throwError();
});
it('to use the right operator for each of gte, gt, lt and lte', function () {
const field = getField(indexPattern, 'script number');
_.each({ gte: '>=', gt: '>', lte: '<=', lt: '<' }, function (operator, key) {
const params = {};
params[key] = 5;
const filter = buildRangeFilter(field, params, indexPattern);
expect(filter.script.script.source).to.be(
'(' + field.script + ')' + operator + key);
expect(filter.script.script.params[key]).to.be(5);
expect(filter.script.script.params.value).to.be(operator + 5);
});
});
describe('when given params where one side is infinite', function () {
const field = getField(indexPattern, 'script number');
let filter;
beforeEach(function () {
filter = buildRangeFilter(field, { gte: 0, lt: Infinity }, indexPattern);
});
describe('returned filter', function () {
it('is a script filter', function () {
expect(filter).to.have.property('script');
});
it('contain a param for the finite side', function () {
expect(filter.script.script.params).to.have.property('gte', 0);
});
it('does not contain a param for the infinite side', function () {
expect(filter.script.script.params).not.to.have.property('lt');
});
it('does not contain a script condition for the infinite side', function () {
const field = getField(indexPattern, 'script number');
const script = field.script;
expect(filter.script.script.source).to.equal(`(${script})>=gte`);
});
});
});
describe('when given params where both sides are infinite', function () {
const field = getField(indexPattern, 'script number');
let filter;
beforeEach(function () {
filter = buildRangeFilter(
field, { gte: -Infinity, lt: Infinity }, indexPattern);
});
describe('returned filter', function () {
it('is a match_all filter', function () {
expect(filter).not.to.have.property('script');
expect(filter).to.have.property('match_all');
});
it('does not contain params', function () {
expect(filter).not.to.have.property('params');
});
it('meta field is set to field name', function () {
expect(filter.meta.field).to.equal('script number');
});
});
});
});
});
function getField(indexPattern, name) {
return indexPattern.fields.find(field => field.name === name);
}

View file

@ -1,30 +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.
*/
// Creates a filter where the given field exists
export function buildExistsFilter(field, indexPattern) {
return {
meta: {
index: indexPattern.id
},
exists: {
field: field.name
}
};
}

View file

@ -1,51 +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 { CustomFilter, ExistsFilter, PhraseFilter, PhrasesFilter, RangeFilter } from './lib';
import { RangeFilterParams } from './lib/range_filter';
export * from './lib';
// We can't import the real types from the data plugin, so need to either duplicate
// them here or figure out another solution, perhaps housing them in this package
type Field = any;
type IndexPattern = any;
export function buildExistsFilter(field: Field, indexPattern: IndexPattern): ExistsFilter;
export function buildPhraseFilter(
field: Field,
value: string,
indexPattern: IndexPattern
): PhraseFilter;
export function buildPhrasesFilter(
field: Field,
values: string[],
indexPattern: IndexPattern
): PhrasesFilter;
export function buildQueryFilter(query: any, index: string, alias?: string): CustomFilter;
export function buildRangeFilter(
field: Field,
params: RangeFilterParams,
indexPattern: IndexPattern,
formattedValue?: string
): RangeFilter;

View file

@ -1,95 +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.
*/
// 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 {
PhraseFilter,
isPhraseFilter,
isScriptedPhraseFilter,
getPhraseFilterField,
getPhraseFilterValue,
} from './phrase_filter';
import { PhrasesFilter, isPhrasesFilter } from './phrases_filter';
import { QueryStringFilter, isQueryStringFilter } from './query_string_filter';
import {
RangeFilter,
isRangeFilter,
isScriptedRangeFilter,
RangeFilterParams,
} from './range_filter';
import { MatchAllFilter, isMatchAllFilter } from './match_all_filter';
import { MissingFilter, isMissingFilter } from './missing_filter';
export {
CustomFilter,
ExistsFilter,
isExistsFilter,
GeoBoundingBoxFilter,
isGeoBoundingBoxFilter,
GeoPolygonFilter,
isGeoPolygonFilter,
PhraseFilter,
isPhraseFilter,
isScriptedPhraseFilter,
getPhraseFilterField,
getPhraseFilterValue,
PhrasesFilter,
isPhrasesFilter,
QueryStringFilter,
isQueryStringFilter,
RangeFilter,
isRangeFilter,
isScriptedRangeFilter,
RangeFilterParams,
MatchAllFilter,
isMatchAllFilter,
MissingFilter,
isMissingFilter,
};
// Any filter associated with a field (used in the filter bar/editor)
export type FieldFilter =
| ExistsFilter
| GeoBoundingBoxFilter
| GeoPolygonFilter
| PhraseFilter
| PhrasesFilter
| RangeFilter
| MatchAllFilter
| MissingFilter;
export enum FILTERS {
CUSTOM = 'custom',
PHRASES = 'phrases',
PHRASE = 'phrase',
EXISTS = 'exists',
MATCH_ALL = 'match_all',
MISSING = 'missing',
QUERY_STRING = 'query_string',
RANGE = 'range',
GEO_BOUNDING_BOX = 'geo_bounding_box',
GEO_POLYGON = 'geo_polygon',
}

View file

@ -1,65 +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 { get, isPlainObject } from 'lodash';
import { Filter, FilterMeta } from './meta_filter';
export type PhraseFilterMeta = FilterMeta & {
params: {
query: string; // The unformatted value
};
script?: {
script: {
params: any;
};
};
field?: any;
};
export type PhraseFilter = Filter & {
meta: PhraseFilterMeta;
};
type PhraseFilterValue = string | number | boolean;
export const isPhraseFilter = (filter: any): filter is PhraseFilter => {
const isMatchPhraseQuery = filter && filter.query && filter.query.match_phrase;
const isDeprecatedMatchPhraseQuery =
filter &&
filter.query &&
filter.query.match &&
Object.values(filter.query.match).find((params: any) => params.type === 'phrase');
return !!(isMatchPhraseQuery || isDeprecatedMatchPhraseQuery);
};
export const isScriptedPhraseFilter = (filter: any): filter is PhraseFilter =>
Boolean(get(filter, 'script.script.params.value'));
export const getPhraseFilterField = (filter: PhraseFilter) => {
const queryConfig = filter.query.match_phrase || filter.query.match;
return Object.keys(queryConfig)[0];
};
export const getPhraseFilterValue = (filter: PhraseFilter): PhraseFilterValue => {
const queryConfig = filter.query.match_phrase || filter.query.match;
const queryValue = Object.values(queryConfig)[0] as any;
return isPlainObject(queryValue) ? queryValue.query : queryValue;
};

View file

@ -1,58 +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 { get, keys } from 'lodash';
import { Filter, FilterMeta } from './meta_filter';
export interface RangeFilterParams {
from?: number | string;
to?: number | string;
gt?: number | string;
lt?: number | string;
gte?: number | string;
lte?: number | string;
format?: string;
}
export type RangeFilterMeta = FilterMeta & {
params: RangeFilterParams;
field?: any;
};
export type RangeFilter = Filter & {
meta: RangeFilterMeta;
script?: {
script: {
params: any;
};
};
range: { [key: string]: RangeFilterParams };
};
const hasRangeKeys = (params: RangeFilterParams) =>
Boolean(
keys(params).find((key: string) => ['gte', 'gt', 'lte', 'lt', 'from', 'to'].includes(key))
);
export const isRangeFilter = (filter: any): filter is RangeFilter => filter && filter.range;
export const isScriptedRangeFilter = (filter: any): filter is RangeFilter => {
const params: RangeFilterParams = get(filter, 'script.script.params', {});
return hasRangeKeys(params);
};

View file

@ -1,85 +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.
*/
// Creates an filter where the given field matches the given value
export function buildPhraseFilter(field, value, indexPattern) {
const filter = { meta: { index: indexPattern.id } };
const convertedValue = getConvertedValueForField(field, value);
if (field.scripted) {
filter.script = getPhraseScript(field, value);
filter.meta.field = field.name;
} else {
filter.query = { match_phrase: {} };
filter.query.match_phrase[field.name] = convertedValue;
}
return filter;
}
export function getPhraseScript(field, value) {
const convertedValue = getConvertedValueForField(field, value);
const script = buildInlineScriptForPhraseFilter(field);
return {
script: {
source: script,
lang: field.lang,
params: {
value: convertedValue
}
}
};
}
// See https://github.com/elastic/elasticsearch/issues/20941 and https://github.com/elastic/kibana/issues/8677
// and https://github.com/elastic/elasticsearch/pull/22201
// for the reason behind this change. Aggs now return boolean buckets with a key of 1 or 0.
export function getConvertedValueForField(field, value) {
if (typeof value !== 'boolean' && field.type === 'boolean') {
if ([1, 'true'].includes(value)) {
return true;
}
else if ([0, 'false'].includes(value)) {
return false;
}
else {
throw new Error(`${value} is not a valid boolean value for boolean field ${field.name}`);
}
}
return value;
}
/**
* Takes a scripted field and returns an inline script appropriate for use in a script query.
* Handles lucene expression and Painless scripts. Other langs aren't guaranteed to generate valid
* scripts.
*
* @param {object} scriptedField A Field object representing a scripted field
* @returns {string} The inline script string
*/
export function buildInlineScriptForPhraseFilter(scriptedField) {
// We must wrap painless scripts in a lambda in case they're more than a simple expression
if (scriptedField.lang === 'painless') {
return `boolean compare(Supplier s, def v) {return s.get() == v;}` +
`compare(() -> { ${scriptedField.script} }, params.value);`;
}
else {
return `(${scriptedField.script}) == value`;
}
}

View file

@ -19,4 +19,3 @@
export * from './es_query';
export * from './kuery';
export * from './filters';

View file

@ -18,5 +18,4 @@
*/
export * from './kuery';
export * from './filters';
export * from './es_query';

View file

@ -21,7 +21,7 @@ import _ from 'lodash';
import * as ast from '../ast';
import * as literal from '../node_types/literal';
import * as wildcard from '../node_types/wildcard';
import { getPhraseScript } from '../../filters';
import { getPhraseScript } from '../../utils/filters';
import { getFields } from './utils/get_fields';
import { getTimeZoneFromSettings } from '../../utils/get_time_zone_from_settings';
import { getFullFieldNameNode } from './utils/get_full_field_name_node';

View file

@ -20,7 +20,7 @@
import _ from 'lodash';
import { nodeTypes } from '../node_types';
import * as ast from '../ast';
import { getRangeScript } from '../../filters';
import { getRangeScript } from '../../utils/filters';
import { getFields } from './utils/get_fields';
import { getTimeZoneFromSettings } from '../../utils/get_time_zone_from_settings';
import { getFullFieldNameNode } from './utils/get_full_field_name_node';

View file

@ -0,0 +1,133 @@
/*
* 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 { pick, get, reduce, map } from 'lodash';
/** @deprecated
* @see src/plugins/data/public/es_query/filters/phrase_filter.ts
* Code was already moved into src/plugins/data/public.
* This method will be removed after moving 'es_query' into new platform
* */
export const getConvertedValueForField = (field, value) => {
if (typeof value !== 'boolean' && field.type === 'boolean') {
if ([1, 'true'].includes(value)) {
return true;
} else if ([0, 'false'].includes(value)) {
return false;
} else {
throw new Error(`${value} is not a valid boolean value for boolean field ${field.name}`);
}
}
return value;
};
/** @deprecated
* @see src/plugins/data/public/es_query/filters/phrase_filter.ts
* Code was already moved into src/plugins/data/public.
* This method will be removed after moving 'es_query' into new platform
* */
export const buildInlineScriptForPhraseFilter = (scriptedField) => {
// We must wrap painless scripts in a lambda in case they're more than a simple expression
if (scriptedField.lang === 'painless') {
return (
`boolean compare(Supplier s, def v) {return s.get() == v;}` +
`compare(() -> { ${scriptedField.script} }, params.value);`
);
} else {
return `(${scriptedField.script}) == value`;
}
};
/** @deprecated
* @see src/plugins/data/public/es_query/filters/phrase_filter.ts
* Code was already moved into src/plugins/data/public.
* This method will be removed after moving 'es_query' into new platform
* */
export function getPhraseScript(field, value) {
const convertedValue = getConvertedValueForField(field, value);
const script = buildInlineScriptForPhraseFilter(field);
return {
script: {
source: script,
lang: field.lang,
params: {
value: convertedValue,
},
},
};
}
/** @deprecated
* @see src/plugins/data/public/es_query/filters/range_filter.ts
* Code was already moved into src/plugins/data/public.
* This method will be removed after moving 'kuery' into new platform
* */
export function getRangeScript(field, params) {
const operators = {
gt: '>',
gte: '>=',
lte: '<=',
lt: '<',
};
const comparators = {
gt: 'boolean gt(Supplier s, def v) {return s.get() > v}',
gte: 'boolean gte(Supplier s, def v) {return s.get() >= v}',
lte: 'boolean lte(Supplier s, def v) {return s.get() <= v}',
lt: 'boolean lt(Supplier s, def v) {return s.get() < v}',
};
const dateComparators = {
gt: 'boolean gt(Supplier s, def v) {return s.get().toInstant().isAfter(Instant.parse(v))}',
gte: 'boolean gte(Supplier s, def v) {return !s.get().toInstant().isBefore(Instant.parse(v))}',
lte: 'boolean lte(Supplier s, def v) {return !s.get().toInstant().isAfter(Instant.parse(v))}',
lt: 'boolean lt(Supplier s, def v) {return s.get().toInstant().isBefore(Instant.parse(v))}',
};
const knownParams = pick(params, (val, key) => {
return key in operators;
});
let script = map(knownParams, (val, key) => {
return '(' + field.script + ')' + get(operators, key) + key;
}).join(' && ');
// We must wrap painless scripts in a lambda in case they're more than a simple expression
if (field.lang === 'painless') {
const comp = field.type === 'date' ? dateComparators : comparators;
const currentComparators = reduce(
knownParams,
(acc, val, key) => acc.concat(get(comp, key)),
[]
).join(' ');
const comparisons = map(knownParams, (val, key) => {
return `${key}(() -> { ${field.script} }, params.${key})`;
}).join(' && ');
script = `${currentComparators}${comparisons}`;
}
return {
script: {
source: script,
params: knownParams,
lang: field.lang,
},
};
}

View file

@ -18,21 +18,20 @@
*/
import { i18n } from '@kbn/i18n';
import { Filter } from '@kbn/es-query';
import { CoreStart } from 'src/core/public';
import {
IAction,
createAction,
IncompatibleActionError,
} from '../../../../../../plugins/ui_actions/public';
import { FilterManager } from '../../../../../../plugins/data/public';
import { FilterManager, esFilters } from '../../../../../../plugins/data/public';
import { TimefilterContract, changeTimeFilter, extractTimeFilter } from '../../timefilter';
import { applyFiltersPopover } from '../apply_filters/apply_filters_popover';
import { IndexPatternsStart } from '../../index_patterns';
export const GLOBAL_APPLY_FILTER_ACTION = 'GLOBAL_APPLY_FILTER_ACTION';
interface ActionContext {
filters: Filter[];
filters: esFilters.Filter[];
timeFieldName?: string;
}
@ -64,7 +63,7 @@ export function createFilterAction(
throw new IncompatibleActionError();
}
let selectedFilters: Filter[] = filters;
let selectedFilters: esFilters.Filter[] = filters;
if (selectedFilters.length > 1) {
const indexPatterns = await Promise.all(
@ -73,7 +72,7 @@ export function createFilterAction(
})
);
const filterSelectionPromise: Promise<Filter[]> = new Promise(resolve => {
const filterSelectionPromise: Promise<esFilters.Filter[]> = new Promise(resolve => {
const overlay = overlays.openModal(
applyFiltersPopover(
filters,
@ -82,7 +81,7 @@ export function createFilterAction(
overlay.close();
resolve([]);
},
(filterSelection: Filter[]) => {
(filterSelection: esFilters.Filter[]) => {
overlay.close();
resolve(filterSelection);
}

View file

@ -28,19 +28,18 @@ import {
EuiModalHeaderTitle,
EuiSwitch,
} from '@elastic/eui';
import { Filter } from '@kbn/es-query';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component } from 'react';
import { IndexPattern } from '../../index_patterns';
import { getFilterDisplayText } from '../filter_bar/filter_editor/lib/get_filter_display_text';
import { mapAndFlattenFilters } from '../../../../../../plugins/data/public';
import { mapAndFlattenFilters, esFilters } from '../../../../../../plugins/data/public';
import { getDisplayValueFromFilter } from '../filter_bar/filter_editor/lib/get_display_value';
interface Props {
filters: Filter[];
filters: esFilters.Filter[];
indexPatterns: IndexPattern[];
onCancel: () => void;
onSubmit: (filters: Filter[]) => void;
onSubmit: (filters: esFilters.Filter[]) => void;
}
interface State {
@ -58,7 +57,7 @@ export class ApplyFiltersPopoverContent extends Component<Props, State> {
isFilterSelected: props.filters.map(() => true),
};
}
private getLabel(filter: Filter) {
private getLabel(filter: esFilters.Filter) {
const filterDisplayValue = getDisplayValueFromFilter(filter, this.props.indexPatterns);
return getFilterDisplayText(filter, filterDisplayValue);
}

View file

@ -18,15 +18,15 @@
*/
import { EuiModal, EuiOverlayMask } from '@elastic/eui';
import { Filter } from '@kbn/es-query';
import React, { Component } from 'react';
import { ApplyFiltersPopoverContent } from './apply_filter_popover_content';
import { IndexPattern } from '../../index_patterns/index_patterns';
import { esFilters } from '../../../../../../plugins/data/public';
interface Props {
filters: Filter[];
filters: esFilters.Filter[];
onCancel: () => void;
onSubmit: (filters: Filter[]) => void;
onSubmit: (filters: esFilters.Filter[]) => void;
indexPatterns: IndexPattern[];
}
@ -56,9 +56,9 @@ export class ApplyFiltersPopover extends Component<Props, State> {
}
type cancelFunction = () => void;
type submitFunction = (filters: Filter[]) => void;
type submitFunction = (filters: esFilters.Filter[]) => void;
export const applyFiltersPopover = (
filters: Filter[],
filters: esFilters.Filter[],
indexPatterns: IndexPattern[],
onCancel: cancelFunction,
onSubmit: submitFunction

View file

@ -18,16 +18,6 @@
*/
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPopover } from '@elastic/eui';
import {
buildEmptyFilter,
disableFilter,
enableFilter,
Filter,
pinFilter,
toggleFilterDisabled,
toggleFilterNegated,
unpinFilter,
} from '@kbn/es-query';
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
import classNames from 'classnames';
import React, { useState } from 'react';
@ -38,10 +28,11 @@ import { FilterEditor } from './filter_editor';
import { FilterItem } from './filter_item';
import { FilterOptions } from './filter_options';
import { useKibana, KibanaContextProvider } from '../../../../../../plugins/kibana_react/public';
import { esFilters } from '../../../../../../plugins/data/public';
interface Props {
filters: Filter[];
onFiltersUpdated?: (filters: Filter[]) => void;
filters: esFilters.Filter[];
onFiltersUpdated?: (filters: esFilters.Filter[]) => void;
className: string;
indexPatterns: IndexPattern[];
intl: InjectedIntl;
@ -87,7 +78,7 @@ function FilterBarUI(props: Props) {
return content;
}
function onFiltersUpdated(filters: Filter[]) {
function onFiltersUpdated(filters: esFilters.Filter[]) {
if (props.onFiltersUpdated) {
props.onFiltersUpdated(filters);
}
@ -112,7 +103,7 @@ function FilterBarUI(props: Props) {
const isPinned = uiSettings!.get('filters:pinnedByDefault');
const [indexPattern] = props.indexPatterns;
const index = indexPattern && indexPattern.id;
const newFilter = buildEmptyFilter(isPinned, index);
const newFilter = esFilters.buildEmptyFilter(isPinned, index);
const button = (
<EuiButtonEmpty
@ -157,7 +148,7 @@ function FilterBarUI(props: Props) {
);
}
function onAdd(filter: Filter) {
function onAdd(filter: esFilters.Filter) {
setIsAddFilterPopoverOpen(false);
const filters = [...props.filters, filter];
onFiltersUpdated(filters);
@ -169,39 +160,39 @@ function FilterBarUI(props: Props) {
onFiltersUpdated(filters);
}
function onUpdate(i: number, filter: Filter) {
function onUpdate(i: number, filter: esFilters.Filter) {
const filters = [...props.filters];
filters[i] = filter;
onFiltersUpdated(filters);
}
function onEnableAll() {
const filters = props.filters.map(enableFilter);
const filters = props.filters.map(esFilters.enableFilter);
onFiltersUpdated(filters);
}
function onDisableAll() {
const filters = props.filters.map(disableFilter);
const filters = props.filters.map(esFilters.disableFilter);
onFiltersUpdated(filters);
}
function onPinAll() {
const filters = props.filters.map(pinFilter);
const filters = props.filters.map(esFilters.pinFilter);
onFiltersUpdated(filters);
}
function onUnpinAll() {
const filters = props.filters.map(unpinFilter);
const filters = props.filters.map(esFilters.unpinFilter);
onFiltersUpdated(filters);
}
function onToggleAllNegated() {
const filters = props.filters.map(toggleFilterNegated);
const filters = props.filters.map(esFilters.toggleFilterNegated);
onFiltersUpdated(filters);
}
function onToggleAllDisabled() {
const filters = props.filters.map(toggleFilterDisabled);
const filters = props.filters.map(esFilters.toggleFilterDisabled);
onFiltersUpdated(filters);
}

View file

@ -31,7 +31,6 @@ import {
EuiSpacer,
EuiSwitch,
} from '@elastic/eui';
import { FieldFilter, Filter } from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
import { get } from 'lodash';
@ -54,11 +53,12 @@ import { Operator } from './lib/filter_operators';
import { PhraseValueInput } from './phrase_value_input';
import { PhrasesValuesInput } from './phrases_values_input';
import { RangeValueInput } from './range_value_input';
import { esFilters } from '../../../../../../../plugins/data/public';
interface Props {
filter: Filter;
filter: esFilters.Filter;
indexPatterns: IndexPattern[];
onSubmit: (filter: Filter) => void;
onSubmit: (filter: esFilters.Filter) => void;
onCancel: () => void;
intl: InjectedIntl;
}
@ -379,7 +379,9 @@ class FilterEditorUI extends Component<Props, State> {
private getFieldFromFilter() {
const indexPattern = this.getIndexPatternFromFilter();
return indexPattern && getFieldFromFilter(this.props.filter as FieldFilter, indexPattern);
return (
indexPattern && getFieldFromFilter(this.props.filter as esFilters.FieldFilter, indexPattern)
);
}
private getSelectedOperator() {

View file

@ -17,7 +17,6 @@
* under the License.
*/
import { FilterStateStore, toggleFilterNegated } from '@kbn/es-query';
import { mockFields, mockIndexPattern } from '../../../../index_patterns';
import { IndexPattern, Field } from '../../../../index';
import {
@ -42,6 +41,7 @@ import { existsFilter } from './fixtures/exists_filter';
import { phraseFilter } from './fixtures/phrase_filter';
import { phrasesFilter } from './fixtures/phrases_filter';
import { rangeFilter } from './fixtures/range_filter';
import { esFilters } from '../../../../../../../../plugins/data/public';
jest.mock('ui/new_platform');
@ -81,7 +81,7 @@ describe('Filter editor utils', () => {
});
it('should return "is not" for phrase filter', () => {
const negatedPhraseFilter = toggleFilterNegated(phraseFilter);
const negatedPhraseFilter = esFilters.toggleFilterNegated(phraseFilter);
const operator = getOperatorFromFilter(negatedPhraseFilter);
expect(operator).not.toBeUndefined();
expect(operator && operator.type).toBe('phrase');
@ -96,7 +96,7 @@ describe('Filter editor utils', () => {
});
it('should return "is not one of" for negated phrases filter', () => {
const negatedPhrasesFilter = toggleFilterNegated(phrasesFilter);
const negatedPhrasesFilter = esFilters.toggleFilterNegated(phrasesFilter);
const operator = getOperatorFromFilter(negatedPhrasesFilter);
expect(operator).not.toBeUndefined();
expect(operator && operator.type).toBe('phrases');
@ -111,7 +111,7 @@ describe('Filter editor utils', () => {
});
it('should return "is not between" for negated range filter', () => {
const negatedRangeFilter = toggleFilterNegated(rangeFilter);
const negatedRangeFilter = esFilters.toggleFilterNegated(rangeFilter);
const operator = getOperatorFromFilter(negatedRangeFilter);
expect(operator).not.toBeUndefined();
expect(operator && operator.type).toBe('range');
@ -126,7 +126,7 @@ describe('Filter editor utils', () => {
});
it('should return "does not exists" for negated exists filter', () => {
const negatedExistsFilter = toggleFilterNegated(existsFilter);
const negatedExistsFilter = esFilters.toggleFilterNegated(existsFilter);
const operator = getOperatorFromFilter(negatedExistsFilter);
expect(operator).not.toBeUndefined();
expect(operator && operator.type).toBe('exists');
@ -246,7 +246,7 @@ describe('Filter editor utils', () => {
it('should build phrase filters', () => {
const params = 'foo';
const alias = 'bar';
const state = FilterStateStore.APP_STATE;
const state = esFilters.FilterStateStore.APP_STATE;
const filter = buildFilter(
mockedIndexPattern,
mockedFields[0],
@ -268,7 +268,7 @@ describe('Filter editor utils', () => {
it('should build phrases filters', () => {
const params = ['foo', 'bar'];
const alias = 'bar';
const state = FilterStateStore.APP_STATE;
const state = esFilters.FilterStateStore.APP_STATE;
const filter = buildFilter(
mockedIndexPattern,
mockedFields[0],
@ -290,7 +290,7 @@ describe('Filter editor utils', () => {
it('should build range filters', () => {
const params = { from: 'foo', to: 'qux' };
const alias = 'bar';
const state = FilterStateStore.APP_STATE;
const state = esFilters.FilterStateStore.APP_STATE;
const filter = buildFilter(
mockedIndexPattern,
mockedFields[0],
@ -311,7 +311,7 @@ describe('Filter editor utils', () => {
it('should build exists filters', () => {
const params = undefined;
const alias = 'bar';
const state = FilterStateStore.APP_STATE;
const state = esFilters.FilterStateStore.APP_STATE;
const filter = buildFilter(
mockedIndexPattern,
mockedFields[0],
@ -332,7 +332,7 @@ describe('Filter editor utils', () => {
it('should include disabled state', () => {
const params = undefined;
const alias = 'bar';
const state = FilterStateStore.APP_STATE;
const state = esFilters.FilterStateStore.APP_STATE;
const filter = buildFilter(
mockedIndexPattern,
mockedFields[0],
@ -348,7 +348,7 @@ describe('Filter editor utils', () => {
it('should negate based on operator', () => {
const params = undefined;
const alias = 'bar';
const state = FilterStateStore.APP_STATE;
const state = esFilters.FilterStateStore.APP_STATE;
const filter = buildFilter(
mockedIndexPattern,
mockedFields[0],

View file

@ -18,42 +18,30 @@
*/
import dateMath from '@elastic/datemath';
import {
buildExistsFilter,
buildPhraseFilter,
buildPhrasesFilter,
buildRangeFilter,
FieldFilter,
Filter,
FilterMeta,
FilterStateStore,
PhraseFilter,
PhrasesFilter,
RangeFilter,
} from '@kbn/es-query';
import { omit } from 'lodash';
import { Ipv4Address } from '../../../../../../../../plugins/kibana_utils/public';
import { Field, IndexPattern, isFilterable } from '../../../../index_patterns';
import { FILTER_OPERATORS, Operator } from './filter_operators';
import { esFilters } from '../../../../../../../../plugins/data/public';
export function getIndexPatternFromFilter(
filter: Filter,
filter: esFilters.Filter,
indexPatterns: IndexPattern[]
): IndexPattern | undefined {
return indexPatterns.find(indexPattern => indexPattern.id === filter.meta.index);
}
export function getFieldFromFilter(filter: FieldFilter, indexPattern: IndexPattern) {
export function getFieldFromFilter(filter: esFilters.FieldFilter, indexPattern: IndexPattern) {
return indexPattern.fields.find(field => field.name === filter.meta.key);
}
export function getOperatorFromFilter(filter: Filter) {
export function getOperatorFromFilter(filter: esFilters.Filter) {
return FILTER_OPERATORS.find(operator => {
return filter.meta.type === operator.type && filter.meta.negate === operator.negate;
});
}
export function getQueryDslFromFilter(filter: Filter) {
export function getQueryDslFromFilter(filter: esFilters.Filter) {
return omit(filter, ['$state', 'meta']);
}
@ -67,16 +55,16 @@ export function getOperatorOptions(field: Field) {
});
}
export function getFilterParams(filter: Filter) {
export function getFilterParams(filter: esFilters.Filter) {
switch (filter.meta.type) {
case 'phrase':
return (filter as PhraseFilter).meta.params.query;
return (filter as esFilters.PhraseFilter).meta.params.query;
case 'phrases':
return (filter as PhrasesFilter).meta.params;
return (filter as esFilters.PhrasesFilter).meta.params;
case 'range':
return {
from: (filter as RangeFilter).meta.params.gte,
to: (filter as RangeFilter).meta.params.lt,
from: (filter as esFilters.RangeFilter).meta.params.gte,
to: (filter as esFilters.RangeFilter).meta.params.lt,
};
}
}
@ -133,8 +121,8 @@ export function buildFilter(
disabled: boolean,
params: any,
alias: string | null,
store: FilterStateStore
): Filter {
store: esFilters.FilterStateStore
): esFilters.Filter {
const filter = buildBaseFilter(indexPattern, field, operator, params);
filter.meta.alias = alias;
filter.meta.negate = operator.negate;
@ -148,17 +136,17 @@ function buildBaseFilter(
field: Field,
operator: Operator,
params: any
): Filter {
): esFilters.Filter {
switch (operator.type) {
case 'phrase':
return buildPhraseFilter(field, params, indexPattern);
return esFilters.buildPhraseFilter(field, params, indexPattern);
case 'phrases':
return buildPhrasesFilter(field, params, indexPattern);
return esFilters.buildPhrasesFilter(field, params, indexPattern);
case 'range':
const newParams = { gte: params.from, lt: params.to };
return buildRangeFilter(field, newParams, indexPattern);
return esFilters.buildRangeFilter(field, newParams, indexPattern);
case 'exists':
return buildExistsFilter(field, indexPattern);
return esFilters.buildExistsFilter(field, indexPattern);
default:
throw new Error(`Unknown operator type: ${operator.type}`);
}
@ -170,10 +158,10 @@ export function buildCustomFilter(
disabled: boolean,
negate: boolean,
alias: string | null,
store: FilterStateStore
): Filter {
const meta: FilterMeta = { index, type: 'custom', disabled, negate, alias };
const filter: Filter = { ...queryDsl, meta };
store: esFilters.FilterStateStore
): esFilters.Filter {
const meta: esFilters.FilterMeta = { index, type: 'custom', disabled, negate, alias };
const filter: esFilters.Filter = { ...queryDsl, meta };
filter.$state = { store };
return filter;
}

View file

@ -17,9 +17,9 @@
* under the License.
*/
import { ExistsFilter, FilterStateStore } from '@kbn/es-query';
import { esFilters } from '../../../../../../../../../plugins/data/public';
export const existsFilter: ExistsFilter = {
export const existsFilter: esFilters.ExistsFilter = {
meta: {
index: 'logstash-*',
negate: false,
@ -29,6 +29,6 @@ export const existsFilter: ExistsFilter = {
alias: null,
},
$state: {
store: FilterStateStore.APP_STATE,
store: esFilters.FilterStateStore.APP_STATE,
},
};

View file

@ -17,9 +17,9 @@
* under the License.
*/
import { FilterStateStore, PhraseFilter } from '@kbn/es-query';
import { esFilters } from '../../../../../../../../../plugins/data/public';
export const phraseFilter: PhraseFilter = {
export const phraseFilter: esFilters.PhraseFilter = {
meta: {
negate: false,
index: 'logstash-*',
@ -33,6 +33,6 @@ export const phraseFilter: PhraseFilter = {
},
},
$state: {
store: FilterStateStore.APP_STATE,
store: esFilters.FilterStateStore.APP_STATE,
},
};

View file

@ -17,9 +17,9 @@
* under the License.
*/
import { FilterStateStore, PhrasesFilter } from '@kbn/es-query';
import { esFilters } from '../../../../../../../../../plugins/data/public';
export const phrasesFilter: PhrasesFilter = {
export const phrasesFilter: esFilters.PhrasesFilter = {
meta: {
index: 'logstash-*',
type: 'phrases',
@ -31,6 +31,6 @@ export const phrasesFilter: PhrasesFilter = {
alias: null,
},
$state: {
store: FilterStateStore.APP_STATE,
store: esFilters.FilterStateStore.APP_STATE,
},
};

View file

@ -17,9 +17,9 @@
* under the License.
*/
import { FilterStateStore, RangeFilter } from '@kbn/es-query';
import { esFilters } from '../../../../../../../../../plugins/data/public';
export const rangeFilter: RangeFilter = {
export const rangeFilter: esFilters.RangeFilter = {
meta: {
index: 'logstash-*',
negate: false,
@ -34,7 +34,7 @@ export const rangeFilter: RangeFilter = {
},
},
$state: {
store: FilterStateStore.APP_STATE,
store: esFilters.FilterStateStore.APP_STATE,
},
range: {},
};

View file

@ -18,7 +18,7 @@
*/
import { get } from 'lodash';
import { Filter } from '@kbn/es-query';
import { esFilters } from '../../../../../../../../plugins/data/public';
import { IndexPattern } from '../../../../index_patterns/index_patterns';
import { Field } from '../../../../index_patterns/fields';
import { getIndexPatternFromFilter } from './filter_editor_utils';
@ -33,7 +33,10 @@ function getValueFormatter(indexPattern?: IndexPattern, key?: string) {
return format;
}
export function getDisplayValueFromFilter(filter: Filter, indexPatterns: IndexPattern[]): string {
export function getDisplayValueFromFilter(
filter: esFilters.Filter,
indexPatterns: IndexPattern[]
): string {
const indexPattern = getIndexPatternFromFilter(filter, indexPatterns);
if (typeof filter.meta.value === 'function') {

View file

@ -19,11 +19,11 @@
import React, { Fragment } from 'react';
import { EuiTextColor } from '@elastic/eui';
import { Filter } from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import { existsOperator, isOneOfOperator } from './filter_operators';
import { esFilters } from '../../../../../../../../plugins/data/public';
export function getFilterDisplayText(filter: Filter, filterDisplayName: string) {
export function getFilterDisplayText(filter: esFilters.Filter, filterDisplayName: string) {
const prefixText = filter.meta.negate
? ` ${i18n.translate('data.filter.filterBar.negatedFilterPrefix', {
defaultMessage: 'NOT ',

View file

@ -18,13 +18,6 @@
*/
import { EuiContextMenu, EuiPopover } from '@elastic/eui';
import {
Filter,
isFilterPinned,
toggleFilterDisabled,
toggleFilterNegated,
toggleFilterPinned,
} from '@kbn/es-query';
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
import classNames from 'classnames';
import React, { Component } from 'react';
@ -33,13 +26,14 @@ import { IndexPattern } from '../../index_patterns';
import { FilterEditor } from './filter_editor';
import { FilterView } from './filter_view';
import { getDisplayValueFromFilter } from './filter_editor/lib/get_display_value';
import { esFilters } from '../../../../../../plugins/data/public';
interface Props {
id: string;
filter: Filter;
filter: esFilters.Filter;
indexPatterns: IndexPattern[];
className?: string;
onUpdate: (filter: Filter) => void;
onUpdate: (filter: esFilters.Filter) => void;
onRemove: () => void;
intl: InjectedIntl;
uiSettings: UiSettingsClientContract;
@ -62,7 +56,7 @@ class FilterItemUI extends Component<Props, State> {
'globalFilterItem',
{
'globalFilterItem-isDisabled': disabled,
'globalFilterItem-isPinned': isFilterPinned(filter),
'globalFilterItem-isPinned': esFilters.isFilterPinned(filter),
'globalFilterItem-isExcluded': negate,
},
this.props.className
@ -91,7 +85,7 @@ class FilterItemUI extends Component<Props, State> {
id: 0,
items: [
{
name: isFilterPinned(filter)
name: esFilters.isFilterPinned(filter)
? this.props.intl.formatMessage({
id: 'data.filter.filterBar.unpinFilterButtonLabel',
defaultMessage: 'Unpin',
@ -209,23 +203,23 @@ class FilterItemUI extends Component<Props, State> {
});
};
private onSubmit = (filter: Filter) => {
private onSubmit = (filter: esFilters.Filter) => {
this.closePopover();
this.props.onUpdate(filter);
};
private onTogglePinned = () => {
const filter = toggleFilterPinned(this.props.filter);
const filter = esFilters.toggleFilterPinned(this.props.filter);
this.props.onUpdate(filter);
};
private onToggleNegated = () => {
const filter = toggleFilterNegated(this.props.filter);
const filter = esFilters.toggleFilterNegated(this.props.filter);
this.props.onUpdate(filter);
};
private onToggleDisabled = () => {
const filter = toggleFilterDisabled(this.props.filter);
const filter = esFilters.toggleFilterDisabled(this.props.filter);
this.props.onUpdate(filter);
};
}

View file

@ -18,13 +18,13 @@
*/
import { EuiBadge, useInnerText } from '@elastic/eui';
import { Filter, isFilterPinned } from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import React, { SFC } from 'react';
import { getFilterDisplayText } from '../filter_editor/lib/get_filter_display_text';
import { esFilters } from '../../../../../../../plugins/data/public';
interface Props {
filter: Filter;
filter: esFilters.Filter;
displayName: string;
[propName: string]: any;
}
@ -44,7 +44,7 @@ export const FilterView: SFC<Props> = ({
values: { innerText },
});
if (isFilterPinned(filter)) {
if (esFilters.isFilterPinned(filter)) {
title = `${i18n.translate('data.filter.filterBar.pinnedFilterPrefix', {
defaultMessage: 'Pinned',
})} ${title}`;

View file

@ -19,12 +19,11 @@
import sinon from 'sinon';
import { FilterStateStore } from '@kbn/es-query';
import { FilterStateManager } from './filter_state_manager';
import { StubState } from './test_helpers/stub_state';
import { getFilter } from './test_helpers/get_stub_filter';
import { FilterManager } from '../../../../../../plugins/data/public';
import { FilterManager, esFilters } from '../../../../../../plugins/data/public';
import { coreMock } from '../../../../../../core/public/mocks';
const setupMock = coreMock.createSetup();
@ -59,7 +58,7 @@ describe('filter_state_manager', () => {
});
test('should NOT watch state until both app and global state are defined', done => {
const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34);
const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34);
globalStateStub.filters.push(f1);
setTimeout(() => {
@ -72,8 +71,8 @@ describe('filter_state_manager', () => {
appStateStub.save = sinon.stub();
globalStateStub.save = sinon.stub();
const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34);
const f2 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34);
const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34);
const f2 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34);
filterManager.setFilters([f1, f2]);
@ -109,7 +108,7 @@ describe('filter_state_manager', () => {
done();
});
const f1 = getFilter(FilterStateStore.GLOBAL_STATE, true, true, 'age', 34);
const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, true, true, 'age', 34);
globalStateStub.filters.push(f1);
});
@ -122,7 +121,7 @@ describe('filter_state_manager', () => {
done();
});
const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34);
const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34);
appStateStub.filters.push(f1);
});
@ -130,8 +129,8 @@ describe('filter_state_manager', () => {
appStateStub.save = sinon.stub();
globalStateStub.save = sinon.stub();
const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34);
const f2 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34);
const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34);
const f2 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34);
filterManager.setFilters([f1, f2]);
@ -143,8 +142,8 @@ describe('filter_state_manager', () => {
appStateStub.save = sinon.stub();
globalStateStub.save = sinon.stub();
const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34);
const f2 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34);
const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34);
const f2 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34);
filterManager.addFilters([f1, f2]);
@ -160,7 +159,7 @@ describe('filter_state_manager', () => {
** And triggers *another* filter manager update.
*/
test('should NOT re-trigger filter manager', done => {
const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34);
const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34);
filterManager.setFilters([f1]);
const setFiltersSpy = sinon.spy(filterManager, 'setFilters');

View file

@ -17,11 +17,9 @@
* under the License.
*/
import { FilterStateStore } from '@kbn/es-query';
import _ from 'lodash';
import { State } from 'ui/state_management/state';
import { FilterManager } from '../../../../../../plugins/data/public';
import { FilterManager, esFilters } from '../../../../../../plugins/data/public';
type GetAppStateFunc = () => State | undefined | null;
@ -73,8 +71,8 @@ export class FilterStateManager {
const newGlobalFilters = _.cloneDeep(globalFilters);
const newAppFilters = _.cloneDeep(appFilters);
FilterManager.setFiltersStore(newAppFilters, FilterStateStore.APP_STATE);
FilterManager.setFiltersStore(newGlobalFilters, FilterStateStore.GLOBAL_STATE);
FilterManager.setFiltersStore(newAppFilters, esFilters.FilterStateStore.APP_STATE);
FilterManager.setFiltersStore(newGlobalFilters, esFilters.FilterStateStore.GLOBAL_STATE);
this.filterManager.setFilters(newGlobalFilters.concat(newAppFilters));
}, 10);

View file

@ -17,15 +17,15 @@
* under the License.
*/
import { Filter, FilterStateStore } from '@kbn/es-query';
import { esFilters } from '../../../../../../../plugins/data/public';
export function getFilter(
store: FilterStateStore,
store: esFilters.FilterStateStore,
disabled: boolean,
negated: boolean,
queryKey: string,
queryValue: any
): Filter {
): esFilters.Filter {
return {
$state: {
store,

View file

@ -19,11 +19,11 @@
import sinon from 'sinon';
import { Filter } from '@kbn/es-query';
import { State } from 'ui/state_management/state';
import { esFilters } from '../../../../../../../plugins/data/public';
export class StubState implements State {
filters: Filter[];
filters: esFilters.Filter[];
save: sinon.SinonSpy<any[], any>;
constructor() {

View file

@ -19,7 +19,6 @@
import React, { useState, useEffect } from 'react';
import { Subscription } from 'rxjs';
import { Filter } from '@kbn/es-query';
import { CoreStart } from 'src/core/public';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
@ -27,6 +26,7 @@ import { KibanaContextProvider } from '../../../../../../../../src/plugins/kiban
import { TimefilterSetup } from '../../../timefilter';
import { SearchBar } from '../../../';
import { SearchBarOwnProps } from '.';
import { esFilters } from '../../../../../../../plugins/data/public';
interface StatefulSearchBarDeps {
core: CoreStart;
@ -40,7 +40,7 @@ export type StatetfulSearchBarProps = SearchBarOwnProps & {
};
const defaultFiltersUpdated = (data: DataPublicPluginStart) => {
return (filters: Filter[]) => {
return (filters: esFilters.Filter[]) => {
data.query.filterManager.setFilters(filters);
};
};

View file

@ -18,7 +18,6 @@
*/
import { compact } from 'lodash';
import { Filter } from '@kbn/es-query';
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
import classNames from 'classnames';
import React, { Component } from 'react';
@ -39,14 +38,15 @@ import {
KibanaReactContextValue,
} from '../../../../../../../plugins/kibana_react/public';
import { IDataPluginServices } from '../../../types';
import { esFilters } from '../../../../../../../plugins/data/public';
interface SearchBarInjectedDeps {
kibana: KibanaReactContextValue<IDataPluginServices>;
intl: InjectedIntl;
timeHistory: TimeHistoryContract;
// Filter bar
onFiltersUpdated?: (filters: Filter[]) => void;
filters?: Filter[];
onFiltersUpdated?: (filters: esFilters.Filter[]) => void;
filters?: esFilters.Filter[];
// Date picker
dateRangeFrom?: string;
dateRangeTo?: string;

View file

@ -17,9 +17,9 @@
* under the License.
*/
import { Filter } from '@kbn/es-query';
import { RefreshInterval, TimeRange } from 'src/plugins/data/public';
import { Query } from '../../query/query_bar';
import { esFilters } from '../../../../../../plugins/data/public';
export * from './components';
@ -36,6 +36,6 @@ export interface SavedQueryAttributes {
title: string;
description: string;
query: Query;
filters?: Filter[];
filters?: esFilters.Filter[];
timefilter?: SavedQueryTimeFilter;
}

View file

@ -19,7 +19,7 @@
import { SavedQueryAttributes } from '../index';
import { createSavedQueryService } from './saved_query_service';
import { FilterStateStore } from '@kbn/es-query';
import { esFilters } from '../../../../../../../plugins/data/public';
const savedQueryAttributes: SavedQueryAttributes = {
title: 'foo',
@ -43,7 +43,7 @@ const savedQueryAttributesWithFilters: SavedQueryAttributes = {
filters: [
{
query: { match_all: {} },
$state: { store: FilterStateStore.APP_STATE },
$state: { store: esFilters.FilterStateStore.APP_STATE },
meta: {
disabled: false,
negate: false,

View file

@ -16,10 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
import { RangeFilter } from '@kbn/es-query';
import { changeTimeFilter } from './change_time_filter';
import { TimeRange } from 'src/plugins/data/public';
import { timefilterServiceMock } from '../timefilter_service.mock';
import { esFilters } from '../../../../../../plugins/data/public';
const timefilterMock = timefilterServiceMock.createSetupContract();
const timefilter = timefilterMock.timefilter;
@ -42,7 +42,7 @@ describe('changeTimeFilter()', () => {
test('should change the timefilter to match the range gt/lt', () => {
const filter: any = { range: { '@timestamp': { gt, lt } } };
changeTimeFilter(timefilter, filter as RangeFilter);
changeTimeFilter(timefilter, filter as esFilters.RangeFilter);
const { to, from } = timefilter.getTime();
@ -52,7 +52,7 @@ describe('changeTimeFilter()', () => {
test('should change the timefilter to match the range gte/lte', () => {
const filter: any = { range: { '@timestamp': { gte: gt, lte: lt } } };
changeTimeFilter(timefilter, filter as RangeFilter);
changeTimeFilter(timefilter, filter as esFilters.RangeFilter);
const { to, from } = timefilter.getTime();

View file

@ -19,10 +19,10 @@
import moment from 'moment';
import { keys } from 'lodash';
import { RangeFilter } from '@kbn/es-query';
import { TimefilterContract } from '../timefilter';
import { esFilters } from '../../../../../../plugins/data/public';
export function convertRangeFilterToTimeRange(filter: RangeFilter) {
export function convertRangeFilterToTimeRange(filter: esFilters.RangeFilter) {
const key = keys(filter.range)[0];
const values = filter.range[key];
@ -32,6 +32,6 @@ export function convertRangeFilterToTimeRange(filter: RangeFilter) {
};
}
export function changeTimeFilter(timeFilter: TimefilterContract, filter: RangeFilter) {
export function changeTimeFilter(timeFilter: TimefilterContract, filter: esFilters.RangeFilter) {
timeFilter.setTime(convertRangeFilterToTimeRange(filter));
}

View file

@ -17,15 +17,23 @@
* under the License.
*/
import { Filter, buildRangeFilter, buildQueryFilter, buildPhraseFilter } from '@kbn/es-query';
import { extractTimeFilter } from './extract_time_filter';
import { esFilters } from '../../../../../../plugins/data/public';
describe('filter manager utilities', () => {
describe('extractTimeFilter()', () => {
test('should detect timeFilter', async () => {
const filters: Filter[] = [
buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'logstash-*'),
buildRangeFilter({ name: 'time' }, { gt: 1388559600000, lt: 1388646000000 }, 'logstash-*'),
const filters: esFilters.Filter[] = [
esFilters.buildQueryFilter(
{ _type: { match: { query: 'apache', type: 'phrase' } } },
'logstash-*',
''
),
esFilters.buildRangeFilter(
{ name: 'time' },
{ gt: 1388559600000, lt: 1388646000000 },
'logstash-*'
),
];
const result = await extractTimeFilter('time', filters);
@ -34,9 +42,13 @@ describe('filter manager utilities', () => {
});
test("should not return timeFilter when name doesn't match", async () => {
const filters: Filter[] = [
buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'logstash-*'),
buildRangeFilter({ name: '@timestamp' }, { from: 1, to: 2 }, 'logstash-*'),
const filters: esFilters.Filter[] = [
esFilters.buildQueryFilter(
{ _type: { match: { query: 'apache', type: 'phrase' } } },
'logstash-*',
''
),
esFilters.buildRangeFilter({ name: '@timestamp' }, { from: 1, to: 2 }, 'logstash-*', ''),
];
const result = await extractTimeFilter('time', filters);
@ -45,9 +57,13 @@ describe('filter manager utilities', () => {
});
test('should not return a non range filter, even when names match', async () => {
const filters: Filter[] = [
buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'logstash-*'),
buildPhraseFilter({ name: 'time' }, 'banana', 'logstash-*'),
const filters: esFilters.Filter[] = [
esFilters.buildQueryFilter(
{ _type: { match: { query: 'apache', type: 'phrase' } } },
'logstash-*',
''
),
esFilters.buildPhraseFilter({ name: 'time' }, 'banana', 'logstash-*'),
];
const result = await extractTimeFilter('time', filters);

View file

@ -18,13 +18,13 @@
*/
import { keys, partition } from 'lodash';
import { Filter, isRangeFilter, RangeFilter } from '@kbn/es-query';
import { esFilters } from '../../../../../../plugins/data/public';
export function extractTimeFilter(timeFieldName: string, filters: Filter[]) {
const [timeRangeFilter, restOfFilters] = partition(filters, (obj: Filter) => {
export function extractTimeFilter(timeFieldName: string, filters: esFilters.Filter[]) {
const [timeRangeFilter, restOfFilters] = partition(filters, (obj: esFilters.Filter) => {
let key;
if (isRangeFilter(obj)) {
if (esFilters.isRangeFilter(obj)) {
key = keys(obj.range)[0];
}
@ -33,6 +33,6 @@ export function extractTimeFilter(timeFieldName: string, filters: Filter[]) {
return {
restOfFilters,
timeRangeFilter: timeRangeFilter[0] as RangeFilter | undefined,
timeRangeFilter: timeRangeFilter[0] as esFilters.RangeFilter | undefined,
};
}

View file

@ -17,10 +17,9 @@
* under the License.
*/
import { Filter } from '@kbn/es-query';
import { TimeRange } from '../../../../../../plugins/data/public';
import { Adapters } from '../../../../../../plugins/inspector/public';
import { Query } from '../../../../../../plugins/data/public';
export { TimeRange, Adapters, Filter, Query };
export { TimeRange, Adapters, Query };
export * from '../../../../../../plugins/expressions/public';

View file

@ -20,12 +20,8 @@
import _ from 'lodash';
import { FilterManager } from './filter_manager.js';
import {
buildPhraseFilter,
buildPhrasesFilter,
getPhraseFilterField,
getPhraseFilterValue,
isPhraseFilter,
} from '@kbn/es-query';
esFilters,
} from '../../../../../../plugins/data/public';
export class PhraseFilterManager extends FilterManager {
constructor(controlId, fieldName, indexPattern, queryFilter) {
@ -43,12 +39,12 @@ export class PhraseFilterManager extends FilterManager {
createFilter(phrases) {
let newFilter;
if (phrases.length === 1) {
newFilter = buildPhraseFilter(
newFilter = esFilters.buildPhraseFilter(
this.indexPattern.fields.getByName(this.fieldName),
phrases[0],
this.indexPattern);
} else {
newFilter = buildPhrasesFilter(
newFilter = esFilters.buildPhrasesFilter(
this.indexPattern.fields.getByName(this.fieldName),
phrases,
this.indexPattern);
@ -107,12 +103,12 @@ export class PhraseFilterManager extends FilterManager {
}
// single phrase filter
if (isPhraseFilter(kbnFilter)) {
if (getPhraseFilterField(kbnFilter) !== this.fieldName) {
if (esFilters.isPhraseFilter(kbnFilter)) {
if (esFilters.getPhraseFilterField(kbnFilter) !== this.fieldName) {
return;
}
return getPhraseFilterValue(kbnFilter);
return esFilters.getPhraseFilterValue(kbnFilter);
}
// single phrase filter from bool filter

View file

@ -19,7 +19,7 @@
import _ from 'lodash';
import { FilterManager } from './filter_manager.js';
import { buildRangeFilter } from '@kbn/es-query';
import { esFilters } from '../../../../../../plugins/data/public';
// Convert slider value into ES range filter
function toRange(sliderValue) {
@ -55,7 +55,7 @@ export class RangeFilterManager extends FilterManager {
* @return {object} range filter
*/
createFilter(value) {
const newFilter = buildRangeFilter(
const newFilter = esFilters.buildRangeFilter(
this.indexPattern.fields.getByName(this.fieldName),
toRange(value),
this.indexPattern);

View file

@ -35,7 +35,6 @@ import {
} from 'ui/state_management/app_state';
import { KbnUrl } from 'ui/url/kbn_url';
import { Filter } from '@kbn/es-query';
import { TimeRange } from 'src/plugins/data/public';
import { IndexPattern } from 'ui/index_patterns';
import { IPrivate } from 'ui/private';
@ -46,6 +45,7 @@ import { Subscription } from 'rxjs';
import { ViewMode } from '../../../embeddable_api/public/np_ready/public';
import { SavedObjectDashboard } from './saved_dashboard/saved_dashboard';
import { DashboardAppState, SavedDashboardPanel, ConfirmModalFn } from './types';
import { esFilters } from '../../../../../../src/plugins/data/public';
import { DashboardAppController } from './dashboard_app_controller';
@ -55,7 +55,7 @@ export interface DashboardAppScope extends ng.IScope {
screenTitle: string;
model: {
query: Query;
filters: Filter[];
filters: esFilters.Filter[];
timeRestore: boolean;
title: string;
description: string;
@ -81,9 +81,9 @@ export interface DashboardAppScope extends ng.IScope {
isPaused: boolean;
refreshInterval: any;
}) => void;
onFiltersUpdated: (filters: Filter[]) => void;
onFiltersUpdated: (filters: esFilters.Filter[]) => void;
onCancelApplyFilters: () => void;
onApplyFilters: (filters: Filter[]) => void;
onApplyFilters: (filters: esFilters.Filter[]) => void;
onQuerySaved: (savedQuery: SavedQuery) => void;
onSavedQueryUpdated: (savedQuery: SavedQuery) => void;
onClearSavedQuery: () => void;

View file

@ -48,7 +48,6 @@ import {
} from 'ui/state_management/app_state';
import { KbnUrl } from 'ui/url/kbn_url';
import { Filter } from '@kbn/es-query';
import { IndexPattern } from 'ui/index_patterns';
import { IPrivate } from 'ui/private';
import { Query, SavedQuery } from 'src/legacy/core_plugins/data/public';
@ -59,6 +58,7 @@ import { npStart } from 'ui/new_platform';
import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder';
import { extractTimeFilter, changeTimeFilter } from '../../../data/public';
import { start as data } from '../../../data/public/legacy';
import { esFilters } from '../../../../../plugins/data/public';
import {
DashboardContainer,
@ -514,7 +514,7 @@ export class DashboardAppController {
}
);
$scope.$watch('appState.$newFilters', (filters: Filter[] = []) => {
$scope.$watch('appState.$newFilters', (filters: esFilters.Filter[] = []) => {
if (filters.length === 1) {
$scope.onApplyFilters(filters);
}

View file

@ -20,7 +20,6 @@
import { i18n } from '@kbn/i18n';
import _ from 'lodash';
import { Filter } from '@kbn/es-query';
import { stateMonitorFactory, StateMonitor } from 'ui/state_management/state_monitor_factory';
import { Timefilter } from 'ui/timefilter';
import { AppStateClass as TAppStateClass } from 'ui/state_management/app_state';
@ -29,6 +28,7 @@ import { Moment } from 'moment';
import { DashboardContainer } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public';
import { ViewMode } from '../../../../../../src/plugins/embeddable/public';
import { esFilters } from '../../../../../../src/plugins/data/public';
import { Query } from '../../../data/public';
import { getAppStateDefaults, migrateAppState } from './lib';
@ -50,7 +50,7 @@ export class DashboardStateManager {
public lastSavedDashboardFilters: {
timeTo?: string | Moment;
timeFrom?: string | Moment;
filterBars: Filter[];
filterBars: esFilters.Filter[];
query: Query;
};
private stateDefaults: DashboardAppStateDefaults;
@ -303,7 +303,7 @@ export class DashboardStateManager {
return this.savedDashboard.timeRestore;
}
public getLastSavedFilterBars(): Filter[] {
public getLastSavedFilterBars(): esFilters.Filter[] {
return this.lastSavedDashboardFilters.filterBars;
}
@ -461,7 +461,7 @@ export class DashboardStateManager {
* Applies the current filter state to the dashboard.
* @param filter An array of filter bar filters.
*/
public applyFilters(query: Query, filters: Filter[]) {
public applyFilters(query: Query, filters: esFilters.Filter[]) {
this.appState.query = query;
this.savedDashboard.searchSource.setField('query', query);
this.savedDashboard.searchSource.setField('filter', filters);

View file

@ -19,7 +19,7 @@
import _ from 'lodash';
import moment, { Moment } from 'moment';
import { Filter } from '@kbn/es-query';
import { esFilters } from '../../../../../../plugins/data/public';
/**
* @typedef {Object} QueryFilter
@ -65,9 +65,9 @@ export class FilterUtils {
* @param filters {Array.<Object>}
* @returns {Array.<Object>}
*/
public static cleanFiltersForComparison(filters: Filter[]) {
public static cleanFiltersForComparison(filters: esFilters.Filter[]) {
return _.map(filters, filter => {
const f: Partial<Filter> = _.omit(filter, ['$$hashKey', '$state']);
const f: Partial<esFilters.Filter> = _.omit(filter, ['$$hashKey', '$state']);
if (f.meta) {
// f.meta.value is the value displayed in the filter bar.
// It may also be loaded differently and shouldn't be used in this comparison.

View file

@ -18,12 +18,12 @@
*/
import { moveFiltersToQuery, Pre600FilterQuery } from './move_filters_to_query';
import { Filter, FilterStateStore } from '@kbn/es-query';
import { esFilters } from '../../../../../../plugins/data/public';
const filter: Filter = {
const filter: esFilters.Filter = {
meta: { disabled: false, negate: false, alias: '' },
query: {},
$state: { store: FilterStateStore.APP_STATE },
$state: { store: esFilters.FilterStateStore.APP_STATE },
};
const queryFilter: Pre600FilterQuery = {
@ -38,7 +38,7 @@ test('Migrates an old filter query into the query field', () => {
expect(newSearchSource).toEqual({
filter: [
{
$state: { store: FilterStateStore.APP_STATE },
$state: { store: esFilters.FilterStateStore.APP_STATE },
meta: {
alias: '',
disabled: false,

View file

@ -18,7 +18,7 @@
*/
import { Query } from 'src/legacy/core_plugins/data/public';
import { Filter } from '@kbn/es-query';
import { esFilters } from '../../../../../../plugins/data/public';
export interface Pre600FilterQuery {
// pre 6.0.0 global query:queryString:options were stored per dashboard and would
@ -30,18 +30,18 @@ export interface Pre600FilterQuery {
export interface SearchSourcePre600 {
// I encountered at least one export from 7.0.0-alpha that was missing the filter property in here.
// The maps data in esarchives actually has it, but I don't know how/when they created it.
filter?: Array<Filter | Pre600FilterQuery>;
filter?: Array<esFilters.Filter | Pre600FilterQuery>;
}
export interface SearchSource730 {
filter: Filter[];
filter: esFilters.Filter[];
query: Query;
highlightAll?: boolean;
version?: boolean;
}
function isQueryFilter(filter: Filter | { query: unknown }): filter is Pre600FilterQuery {
return filter.query && !(filter as Filter).meta;
function isQueryFilter(filter: esFilters.Filter | { query: unknown }): filter is Pre600FilterQuery {
return filter.query && !(filter as esFilters.Filter).meta;
}
export function moveFiltersToQuery(

View file

@ -19,10 +19,9 @@
import { SearchSource } from 'ui/courier';
import { SavedObject } from 'ui/saved_objects/saved_object';
import moment from 'moment';
import { RefreshInterval } from 'src/plugins/data/public';
import { Query } from 'src/legacy/core_plugins/data/public';
import { Filter } from '@kbn/es-query';
import { esFilters } from '../../../../../../plugins/data/public';
export interface SavedObjectDashboard extends SavedObject {
id?: string;
@ -41,5 +40,5 @@ export interface SavedObjectDashboard extends SavedObject {
destroy: () => void;
refreshInterval?: RefreshInterval;
getQuery(): Query;
getFilters(): Filter[];
getFilters(): esFilters.Filter[];
}

View file

@ -18,7 +18,6 @@
*/
import { AppState } from 'ui/state_management/app_state';
import { Filter } from '@kbn/es-query';
import { Query } from 'src/legacy/core_plugins/data/public';
import { AppState as TAppState } from 'ui/state_management/app_state';
import { ViewMode } from 'src/plugins/embeddable/public';
@ -30,6 +29,7 @@ import {
RawSavedDashboardPanel640To720,
RawSavedDashboardPanel730ToLatest,
} from './migrations/types';
import { esFilters } from '../../../../../plugins/data/public';
export type NavAction = (anchorElement?: any) => void;
@ -110,7 +110,7 @@ export interface DashboardAppStateParameters {
useMargins: boolean;
};
query: Query | string;
filters: Filter[];
filters: esFilters.Filter[];
viewMode: ViewMode;
savedQuery?: string;
}

View file

@ -17,7 +17,6 @@
* under the License.
*/
import { Filter } from '@kbn/es-query';
import { IndexPatterns, IndexPattern, getServices } from '../../../kibana_services';
import { reverseSortDir, SortDirection } from './utils/sorting';
import { extractNanos, convertIsoToMillis } from './utils/date_conversion';
@ -25,6 +24,7 @@ import { fetchHitsInInterval } from './utils/fetch_hits_in_interval';
import { generateIntervals } from './utils/generate_intervals';
import { getEsQuerySearchAfter } from './utils/get_es_query_search_after';
import { getEsQuerySort } from './utils/get_es_query_sort';
import { esFilters } from '../../../../../../../../plugins/data/public';
export type SurrDocType = 'successors' | 'predecessors';
export interface EsHitRecord {
@ -67,7 +67,7 @@ function fetchContextProvider(indexPatterns: IndexPatterns) {
tieBreakerField: string,
sortDir: SortDirection,
size: number,
filters: Filter[]
filters: esFilters.Filter[]
) {
if (typeof anchor !== 'object' || anchor === null) {
return [];
@ -112,7 +112,7 @@ function fetchContextProvider(indexPatterns: IndexPatterns) {
return documents;
}
async function createSearchSource(indexPattern: IndexPattern, filters: Filter[]) {
async function createSearchSource(indexPattern: IndexPattern, filters: esFilters.Filter[]) {
return new SearchSource()
.setParent(false)
.setField('index', indexPattern)

View file

@ -26,10 +26,10 @@ import { noWhiteSpace } from '../../../../../common/utils/no_white_space';
import openRowHtml from './table_row/open.html';
import detailsHtml from './table_row/details.html';
import { getServices } from '../../../kibana_services';
import { disableFilter } from '@kbn/es-query';
import { dispatchRenderComplete } from '../../../../../../../../plugins/kibana_utils/public';
import cellTemplateHtml from '../components/table_row/cell.html';
import truncateByHeightTemplateHtml from '../components/table_row/truncate_by_height.html';
import { esFilters } from '../../../../../../../../plugins/data/public';
const module = getServices().uiModules.get('app/discover');
@ -117,7 +117,7 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl
const hash = $httpParamSerializer({
_a: rison.encode({
columns: $scope.columns,
filters: ($scope.filters || []).map(disableFilter),
filters: ($scope.filters || []).map(esFilters.disableFilter),
}),
});
return `${path}?${hash}`;

View file

@ -19,12 +19,12 @@
import _ from 'lodash';
import * as Rx from 'rxjs';
import { Subscription } from 'rxjs';
import { Filter, FilterStateStore } from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public';
import { TimeRange, onlyDisabledFiltersChanged } from '../../../../../../plugins/data/public';
import { setup as data } from '../../../../data/public/legacy';
import { Query, getTime } from '../../../../data/public';
import { esFilters } from '../../../../../../plugins/data/public';
import {
APPLY_FILTER_TRIGGER,
Container,
@ -75,7 +75,7 @@ export interface FilterManager {
values: string | string[],
operation: string,
index: number
) => Filter[];
) => esFilters.Filter[];
}
interface SearchEmbeddableConfig {
@ -105,7 +105,7 @@ export class SearchEmbeddable extends Embeddable<SearchInput, SearchOutput>
private abortController?: AbortController;
private prevTimeRange?: TimeRange;
private prevFilters?: Filter[];
private prevFilters?: esFilters.Filter[];
private prevQuery?: Query;
constructor(
@ -248,7 +248,7 @@ export class SearchEmbeddable extends Embeddable<SearchInput, SearchOutput>
let filters = this.filterGen.generate(field, value, operator, indexPattern.id);
filters = filters.map(filter => ({
...filter,
$state: { store: FilterStateStore.APP_STATE },
$state: { store: esFilters.FilterStateStore.APP_STATE },
}));
await this.executeTriggerActions(APPLY_FILTER_TRIGGER, {

View file

@ -19,16 +19,16 @@
import { TimeRange } from 'src/plugins/data/public';
import { Query } from 'src/legacy/core_plugins/data/public';
import { Filter } from '@kbn/es-query';
import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from 'src/plugins/embeddable/public';
import { StaticIndexPattern } from '../kibana_services';
import { SavedSearch } from '../types';
import { SortOrder } from '../angular/doc_table/components/table_header/helpers';
import { esFilters } from '../../../../../../plugins/data/public';
export interface SearchInput extends EmbeddableInput {
timeRange: TimeRange;
query?: Query;
filters?: Filter[];
filters?: esFilters.Filter[];
hidePanelTitles?: boolean;
columns?: string[];
sort?: SortOrder[];

View file

@ -21,8 +21,11 @@ import _ from 'lodash';
import { EmbeddedVisualizeHandler } from 'ui/visualize/loader/embedded_visualize_handler';
import { Subscription } from 'rxjs';
import * as Rx from 'rxjs';
import { Filter } from '@kbn/es-query';
import { TimeRange, onlyDisabledFiltersChanged } from '../../../../../../plugins/data/public';
import {
TimeRange,
onlyDisabledFiltersChanged,
esFilters,
} from '../../../../../../plugins/data/public';
import { Query } from '../../../../data/public';
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
@ -55,7 +58,7 @@ export interface VisualizeEmbeddableConfiguration {
export interface VisualizeInput extends EmbeddableInput {
timeRange?: TimeRange;
query?: Query;
filters?: Filter[];
filters?: esFilters.Filter[];
vis?: {
colors?: { [key: string]: string };
};
@ -79,7 +82,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
private timeRange?: TimeRange;
private query?: Query;
private title?: string;
private filters?: Filter[];
private filters?: esFilters.Filter[];
private visCustomizations: VisualizeInput['vis'];
private subscription: Subscription;
public readonly type = VISUALIZE_EMBEDDABLE_TYPE;

View file

@ -18,12 +18,12 @@
*/
// @ts-ignore
import { buildEsQuery, getEsQueryConfig, Filter } from '@kbn/es-query';
import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query';
// @ts-ignore
import { timezoneProvider } from 'ui/vis/lib/timezone';
import { KIBANA_CONTEXT_NAME } from 'src/plugins/expressions/public';
import { Query } from 'src/legacy/core_plugins/data/public';
import { TimeRange } from 'src/plugins/data/public';
import { TimeRange, esFilters } from 'src/plugins/data/public';
import { VisParams } from 'ui/vis';
import { i18n } from '@kbn/i18n';
import { TimelionVisualizationDependencies } from '../plugin';
@ -60,7 +60,7 @@ export function getTimelionRequestHandler(dependencies: TimelionVisualizationDep
visParams,
}: {
timeRange: TimeRange;
filters: Filter[];
filters: esFilters.Filter[];
query: Query;
visParams: VisParams;
forceFetch?: boolean;

View file

@ -16,13 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Filter } from '@kbn/es-query';
import { timefilter } from 'ui/timefilter';
import { TimeRange } from 'src/plugins/data/public';
import { Query } from 'src/legacy/core_plugins/data/public';
// @ts-ignore
import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query';
import { esFilters } from '../../../../plugins/data/public';
// @ts-ignore
import { VegaParser } from './data_model/vega_parser';
// @ts-ignore
@ -35,7 +36,7 @@ import { VisParams } from './vega_fn';
interface VegaRequestHandlerParams {
query: Query;
filters: Filter;
filters: esFilters.Filter;
timeRange: TimeRange;
visParams: VisParams;
}

View file

@ -27,7 +27,7 @@ import { Utils } from '../data_model/utils';
import { VISUALIZATION_COLORS } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { TooltipHandler } from './vega_tooltip';
import { buildQueryFilter } from '@kbn/es-query';
import { esFilters } from '../../../../../plugins/data/public';
import { getEnableExternalUrls } from '../helpers/vega_config_provider';
@ -263,7 +263,7 @@ export class VegaBaseView {
*/
async addFilterHandler(query, index) {
const indexId = await this._findIndex(index);
const filter = buildQueryFilter(query, indexId);
const filter = esFilters.buildQueryFilter(query, indexId);
this._queryfilter.addFilters(filter);
}
@ -274,7 +274,7 @@ export class VegaBaseView {
async removeFilterHandler(query, index) {
const $injector = await chrome.dangerouslyGetActiveInjector();
const indexId = await this._findIndex(index);
const filter = buildQueryFilter(query, indexId);
const filter = esFilters.buildQueryFilter(query, indexId);
// This is a workaround for the https://github.com/elastic/kibana/issues/18863
// Once fixed, replace with a direct call (no await is needed because its not async)

View file

@ -18,8 +18,9 @@
*/
import _ from 'lodash';
import { buildExistsFilter, buildPhrasesFilter, buildQueryFromFilters } from '@kbn/es-query';
import { buildQueryFromFilters } from '@kbn/es-query';
import { AggGroupNames } from '../../vis/editors/default/agg_groups';
import { esFilters } from '../../../../../plugins/data/public';
/**
* walks the aggregation DSL and returns DSL starting at aggregation with id of startFromAggId
@ -180,7 +181,7 @@ export const buildOtherBucketAgg = (aggConfigs, aggWithOtherBucket, response) =>
agg.buckets.some(bucket => bucket.key === '__missing__')
) {
filters.push(
buildExistsFilter(
esFilters.buildExistsFilter(
aggWithOtherBucket.params.field,
aggWithOtherBucket.params.field.indexPattern
)
@ -232,7 +233,7 @@ export const mergeOtherBucketAggResponse = (
);
const requestFilterTerms = getOtherAggTerms(requestAgg, key, otherAgg);
const phraseFilter = buildPhrasesFilter(
const phraseFilter = esFilters.buildPhrasesFilter(
otherAgg.params.field,
requestFilterTerms,
otherAgg.params.field.indexPattern
@ -243,7 +244,7 @@ export const mergeOtherBucketAggResponse = (
if (aggResultBuckets.some(bucket => bucket.key === '__missing__')) {
bucket.filters.push(
buildExistsFilter(otherAgg.params.field, otherAgg.params.field.indexPattern)
esFilters.buildExistsFilter(otherAgg.params.field, otherAgg.params.field.indexPattern)
);
}
aggResultBuckets.push(bucket);

View file

@ -18,19 +18,19 @@
*/
import moment from 'moment';
import { RangeFilter } from '@kbn/es-query';
import { createFilterDateHistogram } from './date_histogram';
import { intervalOptions } from '../_interval_options';
import { AggConfigs } from '../../agg_configs';
import { IBucketDateHistogramAggConfig } from '../date_histogram';
import { BUCKET_TYPES } from '../bucket_agg_types';
import { esFilters } from '../../../../../../plugins/data/public';
jest.mock('ui/new_platform');
describe('AggConfig Filters', () => {
describe('date_histogram', () => {
let agg: IBucketDateHistogramAggConfig;
let filter: RangeFilter;
let filter: esFilters.RangeFilter;
let bucketStart: any;
let field: any;

View file

@ -18,8 +18,8 @@
*/
import moment from 'moment';
import { buildRangeFilter } from '@kbn/es-query';
import { IBucketDateHistogramAggConfig } from '../date_histogram';
import { esFilters } from '../../../../../../plugins/data/public';
export const createFilterDateHistogram = (
agg: IBucketDateHistogramAggConfig,
@ -28,7 +28,7 @@ export const createFilterDateHistogram = (
const start = moment(key);
const interval = agg.buckets.getInterval();
return buildRangeFilter(
return esFilters.buildRangeFilter(
agg.params.field,
{
gte: start.toISOString(),

View file

@ -17,16 +17,16 @@
* under the License.
*/
import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query';
import moment from 'moment';
import { IBucketAggConfig } from '../_bucket_agg_type';
import { DateRangeKey } from '../date_range';
import { esFilters } from '../../../../../../plugins/data/public';
export const createFilterDateRange = (agg: IBucketAggConfig, { from, to }: DateRangeKey) => {
const filter: RangeFilterParams = {};
const filter: esFilters.RangeFilterParams = {};
if (from) filter.gte = moment(from).toISOString();
if (to) filter.lt = moment(to).toISOString();
if (to && from) filter.format = 'strict_date_optional_time';
return buildRangeFilter(agg.params.field, filter, agg.getIndexPattern());
return esFilters.buildRangeFilter(agg.params.field, filter, agg.getIndexPattern());
};

View file

@ -18,8 +18,8 @@
*/
import { get } from 'lodash';
import { buildQueryFilter } from '@kbn/es-query';
import { IBucketAggConfig } from '../_bucket_agg_type';
import { esFilters } from '../../../../../../plugins/data/public';
export const createFilterFilters = (aggConfig: IBucketAggConfig, key: string) => {
// have the aggConfig write agg dsl params
@ -28,6 +28,6 @@ export const createFilterFilters = (aggConfig: IBucketAggConfig, key: string) =>
const indexPattern = aggConfig.getIndexPattern();
if (filter && indexPattern && indexPattern.id) {
return buildQueryFilter(filter.query, indexPattern.id, key);
return esFilters.buildQueryFilter(filter.query, indexPattern.id, key);
}
};

View file

@ -17,14 +17,14 @@
* under the License.
*/
import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query';
import { IBucketAggConfig } from '../_bucket_agg_type';
import { esFilters } from '../../../../../../plugins/data/public';
export const createFilterHistogram = (aggConfig: IBucketAggConfig, key: string) => {
const value = parseInt(key, 10);
const params: RangeFilterParams = { gte: value, lt: value + aggConfig.params.interval };
const params: esFilters.RangeFilterParams = { gte: value, lt: value + aggConfig.params.interval };
return buildRangeFilter(
return esFilters.buildRangeFilter(
aggConfig.params.field,
params,
aggConfig.getIndexPattern(),

View file

@ -17,13 +17,13 @@
* under the License.
*/
import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query';
import { CidrMask } from '../../../utils/cidr_mask';
import { IBucketAggConfig } from '../_bucket_agg_type';
import { IpRangeKey } from '../ip_range';
import { esFilters } from '../../../../../../plugins/data/public';
export const createFilterIpRange = (aggConfig: IBucketAggConfig, key: IpRangeKey) => {
let range: RangeFilterParams;
let range: esFilters.RangeFilterParams;
if (key.type === 'mask') {
range = new CidrMask(key.mask).getRange();
@ -34,7 +34,7 @@ export const createFilterIpRange = (aggConfig: IBucketAggConfig, key: IpRangeKey
};
}
return buildRangeFilter(
return esFilters.buildRangeFilter(
aggConfig.params.field,
{ gte: range.from, lte: range.to },
aggConfig.getIndexPattern()

View file

@ -17,11 +17,11 @@
* under the License.
*/
import { buildRangeFilter } from '@kbn/es-query';
import { IBucketAggConfig } from '../_bucket_agg_type';
import { esFilters } from '../../../../../../plugins/data/public';
export const createFilterRange = (aggConfig: IBucketAggConfig, params: any) => {
return buildRangeFilter(
return esFilters.buildRangeFilter(
aggConfig.params.field,
params,
aggConfig.getIndexPattern(),

View file

@ -16,10 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
import { ExistsFilter, Filter } from '@kbn/es-query';
import { createFilterTerms } from './terms';
import { AggConfigs } from '../../agg_configs';
import { BUCKET_TYPES } from '../bucket_agg_types';
import { esFilters } from '../../../../../../plugins/data/public';
jest.mock('ui/new_platform');
@ -48,7 +49,7 @@ describe('AggConfig Filters', () => {
{ type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } },
]);
const filter = createFilterTerms(aggConfigs.aggs[0], 'apache', {}) as Filter;
const filter = createFilterTerms(aggConfigs.aggs[0], 'apache', {}) as esFilters.Filter;
expect(filter).toHaveProperty('query');
expect(filter.query).toHaveProperty('match_phrase');
@ -63,14 +64,14 @@ describe('AggConfig Filters', () => {
{ type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } },
]);
const filterFalse = createFilterTerms(aggConfigs.aggs[0], '', {}) as Filter;
const filterFalse = createFilterTerms(aggConfigs.aggs[0], '', {}) as esFilters.Filter;
expect(filterFalse).toHaveProperty('query');
expect(filterFalse.query).toHaveProperty('match_phrase');
expect(filterFalse.query.match_phrase).toHaveProperty('field');
expect(filterFalse.query.match_phrase.field).toBeFalsy();
const filterTrue = createFilterTerms(aggConfigs.aggs[0], '1', {}) as Filter;
const filterTrue = createFilterTerms(aggConfigs.aggs[0], '1', {}) as esFilters.Filter;
expect(filterTrue).toHaveProperty('query');
expect(filterTrue.query).toHaveProperty('match_phrase');
@ -82,7 +83,11 @@ describe('AggConfig Filters', () => {
const aggConfigs = getAggConfigs([
{ type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } },
]);
const filter = createFilterTerms(aggConfigs.aggs[0], '__missing__', {}) as ExistsFilter;
const filter = createFilterTerms(
aggConfigs.aggs[0],
'__missing__',
{}
) as esFilters.ExistsFilter;
expect(filter).toHaveProperty('exists');
expect(filter.exists).toHaveProperty('field', 'field');
@ -98,7 +103,7 @@ describe('AggConfig Filters', () => {
const [filter] = createFilterTerms(aggConfigs.aggs[0], '__other__', {
terms: ['apache'],
}) as Filter[];
}) as esFilters.Filter[];
expect(filter).toHaveProperty('query');
expect(filter.query).toHaveProperty('bool');

View file

@ -17,8 +17,8 @@
* under the License.
*/
import { Filter, buildPhraseFilter, buildPhrasesFilter, buildExistsFilter } from '@kbn/es-query';
import { IBucketAggConfig } from '../_bucket_agg_type';
import { esFilters } from '../../../../../../plugins/data/public';
export const createFilterTerms = (aggConfig: IBucketAggConfig, key: string, params: any) => {
const field = aggConfig.params.field;
@ -27,20 +27,20 @@ export const createFilterTerms = (aggConfig: IBucketAggConfig, key: string, para
if (key === '__other__') {
const terms = params.terms;
const phraseFilter = buildPhrasesFilter(field, terms, indexPattern);
const phraseFilter = esFilters.buildPhrasesFilter(field, terms, indexPattern);
phraseFilter.meta.negate = true;
const filters: Filter[] = [phraseFilter];
const filters: esFilters.Filter[] = [phraseFilter];
if (terms.some((term: string) => term === '__missing__')) {
filters.push(buildExistsFilter(field, indexPattern));
filters.push(esFilters.buildExistsFilter(field, indexPattern));
}
return filters;
} else if (key === '__missing__') {
const existsFilter = buildExistsFilter(field, indexPattern);
const existsFilter = esFilters.buildExistsFilter(field, indexPattern);
existsFilter.meta.negate = true;
return existsFilter;
}
return buildPhraseFilter(field, key, indexPattern);
return esFilters.buildPhraseFilter(field, key, indexPattern);
};

View file

@ -32,7 +32,6 @@ import { ScaleMetricsParamEditor } from '../../vis/editors/default/controls/scal
import { dateHistogramInterval } from '../../../../core_plugins/data/public';
import { writeParams } from '../agg_params';
import { AggConfigs } from '../agg_configs';
import { AggConfig } from '../agg_config';
import { isMetricAggType } from '../metrics/metric_agg_type';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
@ -189,7 +188,7 @@ export const dateHistogramBucketAgg = new BucketAggType<IBucketDateHistogramAggC
const scaleMetrics = scaleMetricValues && interval.scaled && interval.scale < 1;
if (scaleMetrics && aggs) {
const metrics = aggs.aggs.filter(a => isMetricAggType(a.type));
const all = _.every(metrics, (a: AggConfig) => {
const all = _.every(metrics, (a: IBucketAggConfig) => {
const { type } = a;
if (isMetricAggType(type)) {

View file

@ -21,9 +21,8 @@ import moment from 'moment-timezone';
import { i18n } from '@kbn/i18n';
import { npStart } from 'ui/new_platform';
import { BUCKET_TYPES } from './bucket_agg_types';
import { BucketAggType } from './_bucket_agg_type';
import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type';
import { createFilterDateRange } from './create_filter/date_range';
import { AggConfig } from '../agg_config';
import { FieldFormat } from '../../../../../plugins/data/common/field_formats';
import { DateRangesParamEditor } from '../../vis/editors/default/controls/date_ranges';
@ -64,7 +63,7 @@ export const dateRangeBucketAgg = new BucketAggType({
name: 'field',
type: 'field',
filterFieldTypes: KBN_FIELD_TYPES.DATE,
default(agg: AggConfig) {
default(agg: IBucketAggConfig) {
return agg.getIndexPattern().timeFieldName;
},
},
@ -83,7 +82,7 @@ export const dateRangeBucketAgg = new BucketAggType({
default: undefined,
// Implimentation method is the same as that of date_histogram
serialize: () => undefined,
write: (agg: AggConfig, output: Record<string, any>) => {
write: (agg: IBucketAggConfig, output: Record<string, any>) => {
const field = agg.getParam('field');
let tz = agg.getParam('time_zone');

View file

@ -26,7 +26,6 @@ import { IsFilteredByCollarParamEditor } from '../../vis/editors/default/control
import { PrecisionParamEditor } from '../../vis/editors/default/controls/precision';
import { geohashColumns } from '../../utils/decode_geo_hash';
import { AggGroupNames } from '../../vis/editors/default/agg_groups';
import { AggConfig } from '../agg_config';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
// @ts-ignore
@ -143,7 +142,7 @@ export const geoHashBucketAgg = new BucketAggType<IBucketGeoHashGridAggConfig>({
},
],
getRequestAggs(agg) {
const aggs: AggConfig[] = [];
const aggs: IBucketAggConfig[] = [];
const params = agg.params;
if (params.isFilteredByCollar && agg.getField()) {

View file

@ -28,7 +28,6 @@ import { NumberIntervalParamEditor } from '../../vis/editors/default/controls/nu
import { MinDocCountParamEditor } from '../../vis/editors/default/controls/min_doc_count';
import { HasExtendedBoundsParamEditor } from '../../vis/editors/default/controls/has_extended_bounds';
import { ExtendedBoundsParamEditor } from '../../vis/editors/default/controls/extended_bounds';
import { AggConfig } from '../agg_config';
import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common';
import { BUCKET_TYPES } from './bucket_agg_types';
@ -177,7 +176,7 @@ export const histogramBucketAgg = new BucketAggType<IBucketHistogramAggConfig>({
name: 'min_doc_count',
default: false,
editorComponent: MinDocCountParamEditor,
write(aggConfig: AggConfig, output: Record<string, any>) {
write(aggConfig: IBucketAggConfig, output: Record<string, any>) {
if (aggConfig.params.min_doc_count) {
output.params.min_doc_count = 0;
} else {
@ -198,14 +197,14 @@ export const histogramBucketAgg = new BucketAggType<IBucketHistogramAggConfig>({
max: '',
},
editorComponent: ExtendedBoundsParamEditor,
write(aggConfig: AggConfig, output: Record<string, any>) {
write(aggConfig: IBucketAggConfig, output: Record<string, any>) {
const { min, max } = aggConfig.params.extended_bounds;
if (aggConfig.params.has_extended_bounds && (min || min === 0) && (max || max === 0)) {
output.params.extended_bounds = { min, max };
}
},
shouldShow: (aggConfig: AggConfig) => aggConfig.params.has_extended_bounds,
shouldShow: (aggConfig: IBucketAggConfig) => aggConfig.params.has_extended_bounds,
},
],
});

View file

@ -18,7 +18,6 @@
*/
import { isString, isObject } from 'lodash';
import { AggConfig } from 'ui/agg_types';
import { IBucketAggConfig, BucketAggType, BucketAggParam } from './_bucket_agg_type';
export const isType = (type: string) => {
@ -32,12 +31,16 @@ export const isType = (type: string) => {
export const isStringType = isType('string');
export const migrateIncludeExcludeFormat = {
serialize(this: BucketAggParam, value: any, agg: AggConfig) {
serialize(this: BucketAggParam, value: any, agg: IBucketAggConfig) {
if (this.shouldShow && !this.shouldShow(agg)) return;
if (!value || isString(value)) return value;
else return value.pattern;
},
write(this: BucketAggType<IBucketAggConfig>, aggConfig: AggConfig, output: Record<string, any>) {
write(
this: BucketAggType<IBucketAggConfig>,
aggConfig: IBucketAggConfig,
output: Record<string, any>
) {
const value = aggConfig.getParam(this.name);
if (isObject(value)) {

View file

@ -71,13 +71,13 @@
import _ from 'lodash';
import angular from 'angular';
import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query';
import { getEsQueryConfig, buildEsQuery } from '@kbn/es-query';
import { normalizeSortRequest } from './_normalize_sort_request';
import { fetchSoon } from '../fetch';
import { fieldWildcardFilter } from '../../field_wildcard';
import { getHighlightRequest } from '../../../../../plugins/data/common/field_formats';
import { getHighlightRequest } from '../../../../../plugins/data/public';
import { npSetup } from 'ui/new_platform';
import chrome from '../../chrome';
import { RequestFailure } from '../fetch/errors';

View file

@ -24,8 +24,8 @@ import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { getFilterGenerator } from '..';
import { FilterBarQueryFilterProvider } from '../../filter_manager/query_filter';
import { uniqFilters } from '../../../../../plugins/data/public';
import { getPhraseScript } from '@kbn/es-query';
import { uniqFilters, esFilters } from '../../../../../plugins/data/public';
let queryFilter;
let filterGen;
let appState;
@ -137,14 +137,14 @@ describe('Filter Manager', function () {
filterGen.add(scriptedField, 1, '+', 'myIndex');
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: false, field: 'scriptedField' },
script: getPhraseScript(scriptedField, 1)
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: getPhraseScript(scriptedField, 1)
script: esFilters.getPhraseScript(scriptedField, 1)
}], 5);
expect(appState.filters).to.have.length(3);
});

View file

@ -18,7 +18,7 @@
*/
import _ from 'lodash';
import { getPhraseFilterField, getPhraseFilterValue, getPhraseScript, isPhraseFilter } from '@kbn/es-query';
import { esFilters } from '../../../../plugins/data/public';
// Adds a filter to a passed state
export function getFilterGenerator(queryFilter) {
@ -42,8 +42,8 @@ export function getFilterGenerator(queryFilter) {
return filter.exists.field === value;
}
if (isPhraseFilter(filter)) {
return getPhraseFilterField(filter) === fieldName && getPhraseFilterValue(filter) === value;
if (esFilters.isPhraseFilter(filter)) {
return esFilters.getPhraseFilterField(filter) === fieldName && esFilters.getPhraseFilterValue(filter) === value;
}
if (filter.script) {
@ -73,7 +73,7 @@ export function getFilterGenerator(queryFilter) {
if (field.scripted) {
filter = {
meta: { negate, index, field: fieldName },
script: getPhraseScript(field, value)
script: esFilters.getPhraseScript(field, value)
};
} else {
filter = { meta: { negate, index }, query: { match_phrase: {} } };

View file

@ -19,7 +19,7 @@
import _ from 'lodash';
import moment from 'moment';
import { buildRangeFilter } from '@kbn/es-query';
import { esFilters } from '../../../../../plugins/data/public';
export function onBrushEvent(event) {
const isNumber = event.data.ordered;
@ -56,7 +56,7 @@ export function onBrushEvent(event) {
};
}
const newFilter = buildRangeFilter(
const newFilter = esFilters.buildRangeFilter(
field,
range,
indexPattern,

View file

@ -20,8 +20,8 @@
import _ from 'lodash';
import { pushFilterBarFilters } from '../push_filters';
import { onBrushEvent } from './brush_event';
import { uniqFilters } from '../../../../../plugins/data/public';
import { toggleFilterNegated } from '@kbn/es-query';
import { uniqFilters, esFilters } from '../../../../../plugins/data/public';
/**
* For terms aggregations on `__other__` buckets, this assembles a list of applicable filter
* terms based on a specific cell in the tabified data.
@ -94,7 +94,7 @@ const createFiltersFromEvent = (event) => {
if (filter) {
filter.forEach(f => {
if (event.negate) {
f = toggleFilterNegated(f);
f = esFilters.toggleFilterNegated(f);
}
filters.push(f);
});

View file

@ -22,7 +22,6 @@ import { debounce, forEach, get, isEqual } from 'lodash';
import * as Rx from 'rxjs';
import { share } from 'rxjs/operators';
import { i18n } from '@kbn/i18n';
import { Filter } from '@kbn/es-query';
import { toastNotifications } from 'ui/notify';
// @ts-ignore untyped dependency
import { AggConfigs } from 'ui/agg_types/agg_configs';
@ -44,6 +43,7 @@ import { VisFiltersProvider } from '../../vis/vis_filters';
import { PipelineDataLoader } from './pipeline_data_loader';
import { visualizationLoader } from './visualization_loader';
import { Query } from '../../../../core_plugins/data/public';
import { esFilters } from '../../../../../plugins/data/public';
import { DataAdapter, RequestAdapter } from '../../inspector/adapters';
@ -67,7 +67,7 @@ export interface RequestHandlerParams {
aggs: AggConfigs;
timeRange?: TimeRange;
query?: Query;
filters?: Filter[];
filters?: esFilters.Filter[];
forceFetch: boolean;
queryFilter: QueryFilter;
uiState?: PersistedState;

View file

@ -17,7 +17,6 @@
* under the License.
*/
import { Filter } from '@kbn/es-query';
import { TimeRange } from 'src/plugins/data/public';
import { Query } from 'src/legacy/core_plugins/data/public';
import { SavedObject } from 'ui/saved_objects/saved_object';
@ -26,6 +25,7 @@ import { SearchSource } from '../../courier';
import { PersistedState } from '../../persisted_state';
import { AppState } from '../../state_management/app_state';
import { Vis } from '../../vis';
import { esFilters } from '../../../../../plugins/data/public';
export interface VisSavedObject extends SavedObject {
vis: Vis;
@ -68,7 +68,7 @@ export interface VisualizeLoaderParams {
/**
* Specifies the filters that should be applied to that visualization.
*/
filters?: Filter[];
filters?: esFilters.Filter[];
/**
* The query that should apply to that visualization.
*/

View file

@ -22,13 +22,13 @@ import { get } from 'lodash';
import { toastNotifications } from 'ui/notify';
import { AggConfig } from 'ui/vis';
import { Filter } from '@kbn/es-query';
import { Query } from 'src/legacy/core_plugins/data/public';
import { timefilter } from 'ui/timefilter';
import { Vis } from '../../../vis';
import { esFilters } from '../../../../../../plugins/data/public';
interface QueryGeohashBoundsParams {
filters?: Filter[];
filters?: esFilters.Filter[];
query?: Query;
}
@ -76,7 +76,7 @@ export async function queryGeohashBounds(vis: Vis, params: QueryGeohashBoundsPar
const useTimeFilter = !!indexPattern.timeFieldName;
if (useTimeFilter) {
const filter = timefilter.createFilter(indexPattern);
if (filter) activeFilters.push((filter as any) as Filter);
if (filter) activeFilters.push((filter as any) as esFilters.Filter);
}
return activeFilters;
});

View file

@ -20,7 +20,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { Filter } from '@kbn/es-query';
import { RefreshInterval, TimeRange, Query } from '../../../data/public';
import { CoreStart } from '../../../../core/public';
import { IUiActionsStart } from '../ui_actions_plugin';
@ -38,6 +37,7 @@ import { createPanelState } from './panel';
import { DashboardPanelState } from './types';
import { DashboardViewport } from './viewport/dashboard_viewport';
import { Start as InspectorStartContract } from '../../../inspector/public';
import { esFilters } from '../../../../plugins/data/public';
import {
KibanaContextProvider,
KibanaReactContext,
@ -46,7 +46,7 @@ import {
export interface DashboardContainerInput extends ContainerInput {
viewMode: ViewMode;
filters: Filter[];
filters: esFilters.Filter[];
query: Query;
timeRange: TimeRange;
refreshConfig?: RefreshInterval;
@ -65,7 +65,7 @@ interface IndexSignature {
}
export interface InheritedChildInput extends IndexSignature {
filters: Filter[];
filters: esFilters.Filter[];
query: Query;
timeRange: TimeRange;
refreshConfig?: RefreshInterval;

View file

@ -0,0 +1,320 @@
/*
* 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 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' } },
},
];
export const getField = (name: string) => fields.find(field => field.name === name);

View file

@ -18,6 +18,7 @@
*/
import { Filter, FilterMeta } from './meta_filter';
import { IndexPattern, Field } from './types';
export type ExistsFilterMeta = FilterMeta;
@ -31,3 +32,14 @@ export type ExistsFilter = Filter & {
};
export const isExistsFilter = (filter: any): filter is ExistsFilter => filter && filter.exists;
export const buildExistsFilter = (field: Field, indexPattern: IndexPattern) => {
return {
meta: {
index: indexPattern.id,
},
exists: {
field: field.name,
},
} as ExistsFilter;
};

View file

@ -17,16 +17,16 @@
* under the License.
*/
import { Filter, FilterMeta } from './meta_filter';
export * from './custom_filter';
export * from './exists_filter';
export * from './geo_bounding_box_filter';
export * from './geo_polygon_filter';
export * from './match_all_filter';
export * from './meta_filter';
export * from './missing_filter';
export * from './phrase_filter';
export * from './phrases_filter';
export * from './query_string_filter';
export * from './range_filter';
export type PhrasesFilterMeta = FilterMeta & {
params: string[]; // The unformatted values
field?: string;
};
export type PhrasesFilter = Filter & {
meta: PhrasesFilterMeta;
};
export const isPhrasesFilter = (filter: any): filter is PhrasesFilter =>
filter && filter.meta.type === 'phrases';
export * from './types';

View file

@ -55,7 +55,7 @@ export interface LatLon {
lon: number;
}
export function buildEmptyFilter(isPinned: boolean, index?: string): Filter {
export const buildEmptyFilter = (isPinned: boolean, index?: string): Filter => {
const meta: FilterMeta = {
disabled: false,
negate: false,
@ -65,43 +65,43 @@ export function buildEmptyFilter(isPinned: boolean, index?: string): Filter {
const $state: FilterState = {
store: isPinned ? FilterStateStore.GLOBAL_STATE : FilterStateStore.APP_STATE,
};
return { meta, $state };
}
};
export function isFilterPinned(filter: Filter) {
export const isFilterPinned = (filter: Filter) => {
return filter.$state && filter.$state.store === FilterStateStore.GLOBAL_STATE;
}
};
export function toggleFilterDisabled(filter: Filter) {
export const toggleFilterDisabled = (filter: Filter) => {
const disabled = !filter.meta.disabled;
const meta = { ...filter.meta, disabled };
return { ...filter, meta };
}
export function toggleFilterNegated(filter: Filter) {
return { ...filter, meta };
};
export const toggleFilterNegated = (filter: Filter) => {
const negate = !filter.meta.negate;
const meta = { ...filter.meta, negate };
return { ...filter, meta };
}
export function toggleFilterPinned(filter: Filter) {
return { ...filter, meta };
};
export const toggleFilterPinned = (filter: Filter) => {
const store = isFilterPinned(filter) ? FilterStateStore.APP_STATE : FilterStateStore.GLOBAL_STATE;
const $state = { ...filter.$state, store };
return { ...filter, $state };
}
};
export function enableFilter(filter: Filter) {
return !filter.meta.disabled ? filter : toggleFilterDisabled(filter);
}
export const enableFilter = (filter: Filter) =>
!filter.meta.disabled ? filter : toggleFilterDisabled(filter);
export function disableFilter(filter: Filter) {
return filter.meta.disabled ? filter : toggleFilterDisabled(filter);
}
export const disableFilter = (filter: Filter) =>
filter.meta.disabled ? filter : toggleFilterDisabled(filter);
export function pinFilter(filter: Filter) {
return isFilterPinned(filter) ? filter : toggleFilterPinned(filter);
}
export const pinFilter = (filter: Filter) =>
isFilterPinned(filter) ? filter : toggleFilterPinned(filter);
export function unpinFilter(filter: Filter) {
return !isFilterPinned(filter) ? filter : toggleFilterPinned(filter);
}
export const unpinFilter = (filter: Filter) =>
!isFilterPinned(filter) ? filter : toggleFilterPinned(filter);

View file

@ -0,0 +1,97 @@
/*
* 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 { buildInlineScriptForPhraseFilter, buildPhraseFilter } from './phrase_filter';
import { IndexPattern } from './types';
import { getField } from '../__tests__/fields_mock';
describe('Phrase filter builder', () => {
let indexPattern: IndexPattern;
beforeEach(() => {
indexPattern = {
id: 'id',
};
});
it('should be a function', () => {
expect(typeof buildPhraseFilter).toBe('function');
});
it('should return a match query filter when passed a standard field', () => {
const field = getField('bytes');
expect(buildPhraseFilter(field, 5, indexPattern)).toEqual({
meta: {
index: 'id',
},
query: {
match_phrase: {
bytes: 5,
},
},
});
});
it('should return a script filter when passed a scripted field', () => {
const field = getField('script number');
expect(buildPhraseFilter(field, 5, indexPattern)).toEqual({
meta: {
index: 'id',
field: 'script number',
},
script: {
script: {
lang: 'expression',
params: {
value: 5,
},
source: '(1234) == value',
},
},
});
});
});
describe('buildInlineScriptForPhraseFilter', () => {
it('should wrap painless scripts in a lambda', () => {
const field = {
lang: 'painless',
script: 'return foo;',
};
const expected =
`boolean compare(Supplier s, def v) {return s.get() == v;}` +
`compare(() -> { return foo; }, params.value);`;
expect(buildInlineScriptForPhraseFilter(field)).toBe(expected);
});
it('should create a simple comparison for other langs', () => {
const field = {
lang: 'expression',
script: 'doc[bytes].value',
};
const expected = `(doc[bytes].value) == value`;
expect(buildInlineScriptForPhraseFilter(field)).toBe(expected);
});
});

View file

@ -0,0 +1,144 @@
/*
* 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 { get, isPlainObject } from 'lodash';
import { Filter, FilterMeta } from './meta_filter';
import { IndexPattern, Field } from './types';
export type PhraseFilterMeta = FilterMeta & {
params?: {
query: string; // The unformatted value
};
script?: {
script: {
source?: any;
lang?: string;
params: any;
};
};
field?: any;
index?: any;
};
export type PhraseFilter = Filter & {
meta: PhraseFilterMeta;
};
type PhraseFilterValue = string | number | boolean;
export const isPhraseFilter = (filter: any): filter is PhraseFilter => {
const isMatchPhraseQuery = filter && filter.query && filter.query.match_phrase;
const isDeprecatedMatchPhraseQuery =
filter &&
filter.query &&
filter.query.match &&
Object.values(filter.query.match).find((params: any) => params.type === 'phrase');
return !!(isMatchPhraseQuery || isDeprecatedMatchPhraseQuery);
};
export const isScriptedPhraseFilter = (filter: any): filter is PhraseFilter =>
Boolean(get(filter, 'script.script.params.value'));
export const getPhraseFilterField = (filter: PhraseFilter) => {
const queryConfig = filter.query.match_phrase || filter.query.match;
return Object.keys(queryConfig)[0];
};
export const getPhraseFilterValue = (filter: PhraseFilter): PhraseFilterValue => {
const queryConfig = filter.query.match_phrase || filter.query.match;
const queryValue = Object.values(queryConfig)[0] as any;
return isPlainObject(queryValue) ? queryValue.query : queryValue;
};
export const buildPhraseFilter = (
field: Field,
value: any,
indexPattern: IndexPattern
): PhraseFilter => {
const convertedValue = getConvertedValueForField(field, value);
if (field.scripted) {
return {
meta: { index: indexPattern.id, field: field.name } as PhraseFilterMeta,
script: getPhraseScript(field, value),
} as PhraseFilter;
} else {
return {
meta: { index: indexPattern.id },
query: {
match_phrase: {
[field.name]: convertedValue,
},
},
} as PhraseFilter;
}
};
export const getPhraseScript = (field: Field, value: string) => {
const convertedValue = getConvertedValueForField(field, value);
const script = buildInlineScriptForPhraseFilter(field);
return {
script: {
source: script,
lang: field.lang,
params: {
value: convertedValue,
},
},
};
};
// See https://github.com/elastic/elasticsearch/issues/20941 and https://github.com/elastic/kibana/issues/8677
// and https://github.com/elastic/elasticsearch/pull/22201
// for the reason behind this change. Aggs now return boolean buckets with a key of 1 or 0.
export const getConvertedValueForField = (field: Field, value: any) => {
if (typeof value !== 'boolean' && field.type === 'boolean') {
if ([1, 'true'].includes(value)) {
return true;
} else if ([0, 'false'].includes(value)) {
return false;
} else {
throw new Error(`${value} is not a valid boolean value for boolean field ${field.name}`);
}
}
return value;
};
/**
* Takes a scripted field and returns an inline script appropriate for use in a script query.
* Handles lucene expression and Painless scripts. Other langs aren't guaranteed to generate valid
* scripts.
*
* @param {object} scriptedField A Field object representing a scripted field
* @returns {string} The inline script string
*/
export const buildInlineScriptForPhraseFilter = (scriptedField: any) => {
// We must wrap painless scripts in a lambda in case they're more than a simple expression
if (scriptedField.lang === 'painless') {
return (
`boolean compare(Supplier s, def v) {return s.get() == v;}` +
`compare(() -> { ${scriptedField.script} }, params.value);`
);
} else {
return `(${scriptedField.script}) == value`;
}
};

View file

@ -17,47 +17,54 @@
* under the License.
*/
import { getPhraseScript } from './phrase';
import { Filter, FilterMeta } from './meta_filter';
import { Field, IndexPattern } from './types';
import { getPhraseScript } from './phrase_filter';
export type PhrasesFilterMeta = FilterMeta & {
params: string[]; // The unformatted values
field?: string;
};
export type PhrasesFilter = Filter & {
meta: PhrasesFilterMeta;
};
export const isPhrasesFilter = (filter: any): filter is PhrasesFilter =>
filter && filter.meta.type === 'phrases';
// Creates a filter where the given field matches one or more of the given values
// params should be an array of values
export function buildPhrasesFilter(field, params, indexPattern) {
export const buildPhrasesFilter = (field: Field, params: any, indexPattern: IndexPattern) => {
const index = indexPattern.id;
const type = 'phrases';
const key = field.name;
const value = params
.map(value => format(field, value))
.join(', ');
const filter = {
meta: { index, type, key, value, params }
};
const format = (f: Field, value: any) =>
f && f.format && f.format.convert ? f.format.convert(value) : value;
const value = params.map((v: any) => format(field, v)).join(', ');
let should;
if (field.scripted) {
should = params.map((value) => ({
script: getPhraseScript(field, value)
should = params.map((v: any) => ({
script: getPhraseScript(field, v),
}));
} else {
should = params.map((value) => ({
should = params.map((v: any) => ({
match_phrase: {
[field.name]: value
}
[field.name]: v,
},
}));
}
filter.query = {
bool: {
should,
minimum_should_match: 1
}
};
return filter;
}
function format(field, value) {
return field && field.format && field.format.convert
? field.format.convert(value)
: value;
}
return {
meta: { index, type, key, value, params },
query: {
bool: {
should,
minimum_should_match: 1,
},
},
} as PhrasesFilter;
};

View file

@ -17,18 +17,31 @@
* under the License.
*/
// Creates a filter corresponding to a raw Elasticsearch query DSL object
export function buildQueryFilter(query, index, alias) {
const filter = {
query: query,
meta: {
index,
}
};
import { buildQueryFilter } from './query_string_filter';
import { IndexPattern } from './types';
if (alias) {
filter.meta.alias = alias;
}
describe('Phrase filter builder', () => {
let indexPattern: IndexPattern;
return filter;
}
beforeEach(() => {
indexPattern = {
id: 'id',
};
});
it('should be a function', () => {
expect(typeof buildQueryFilter).toBe('function');
});
it('should return a query filter when passed a standard field', () => {
expect(buildQueryFilter({ foo: 'bar' }, indexPattern.id, '')).toEqual({
meta: {
alias: '',
index: 'id',
},
query: {
foo: 'bar',
},
});
});
});

View file

@ -18,6 +18,7 @@
*/
import { Filter, FilterMeta } from './meta_filter';
import { IndexPattern } from './types';
export type QueryStringFilterMeta = FilterMeta;
@ -32,3 +33,17 @@ export type QueryStringFilter = Filter & {
export const isQueryStringFilter = (filter: any): filter is QueryStringFilter =>
filter && filter.query && filter.query.query_string;
// Creates a filter corresponding to a raw Elasticsearch query DSL object
export const buildQueryFilter = (
query: QueryStringFilter['query'],
index: IndexPattern,
alias: string
) =>
({
query,
meta: {
index,
alias,
},
} as QueryStringFilter);

View file

@ -0,0 +1,174 @@
/*
* 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 { each } from 'lodash';
import { buildRangeFilter, RangeFilter } from './range_filter';
import { IndexPattern, Field } from './types';
import { getField } from '../__tests__/fields_mock';
describe('Range filter builder', () => {
let indexPattern: IndexPattern;
beforeEach(() => {
indexPattern = {
id: 'id',
};
});
it('should be a function', () => {
expect(typeof buildRangeFilter).toBe('function');
});
it('should return a range filter when passed a standard field', () => {
const field = getField('bytes');
expect(buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern)).toEqual({
meta: {
index: 'id',
params: {},
},
range: {
bytes: {
gte: 1,
lte: 3,
},
},
});
});
it('should return a script filter when passed a scripted field', () => {
const field = getField('script number');
expect(buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern)).toEqual({
meta: {
field: 'script number',
index: 'id',
params: {},
},
script: {
script: {
lang: 'expression',
source: '(' + field!.script + ')>=gte && (' + field!.script + ')<=lte',
params: {
value: '>=1 <=3',
gte: 1,
lte: 3,
},
},
},
});
});
it('should wrap painless scripts in comparator lambdas', () => {
const field = getField('script date');
const expected =
`boolean gte(Supplier s, def v) {return !s.get().toInstant().isBefore(Instant.parse(v))} ` +
`boolean lte(Supplier s, def v) {return !s.get().toInstant().isAfter(Instant.parse(v))}` +
`gte(() -> { ${field!.script} }, params.gte) && ` +
`lte(() -> { ${field!.script} }, params.lte)`;
const rangeFilter = buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern);
expect(rangeFilter.script!.script.source).toBe(expected);
});
it('should throw an error when gte and gt, or lte and lt are both passed', () => {
const field = getField('script number');
expect(() => {
buildRangeFilter(field, { gte: 1, gt: 3 }, indexPattern);
}).toThrowError();
expect(() => {
buildRangeFilter(field, { lte: 1, lt: 3 }, indexPattern);
}).toThrowError();
});
it('to use the right operator for each of gte, gt, lt and lte', () => {
const field = getField('script number');
each({ gte: '>=', gt: '>', lte: '<=', lt: '<' }, (operator: string, key: any) => {
const params = {
[key]: 5,
};
const filter = buildRangeFilter(field, params, indexPattern);
const script = filter.script!.script;
expect(script.source).toBe('(' + field!.script + ')' + operator + key);
expect(script.params[key]).toBe(5);
expect(script.params.value).toBe(operator + 5);
});
});
describe('when given params where one side is infinite', () => {
let field: Field;
let filter: RangeFilter;
beforeEach(() => {
field = getField('script number');
filter = buildRangeFilter(field, { gte: 0, lt: Infinity }, indexPattern);
});
describe('returned filter', () => {
it('is a script filter', () => {
expect(filter).toHaveProperty('script');
});
it('contain a param for the finite side', () => {
expect(filter.script!.script.params).toHaveProperty('gte', 0);
});
it('does not contain a param for the infinite side', () => {
expect(filter.script!.script.params).not.toHaveProperty('lt');
});
it('does not contain a script condition for the infinite side', () => {
const script = field!.script;
expect(filter.script!.script.source).toEqual(`(${script})>=gte`);
});
});
});
describe('when given params where both sides are infinite', () => {
let field: Field;
let filter: RangeFilter;
beforeEach(() => {
field = getField('script number');
filter = buildRangeFilter(field, { gte: -Infinity, lt: Infinity }, indexPattern);
});
describe('returned filter', () => {
it('is a match_all filter', () => {
expect(filter).not.toHaveProperty('script');
expect(filter).toHaveProperty('match_all');
});
it('does not contain params', () => {
expect(filter).not.toHaveProperty('params');
});
it('meta field is set to field name', () => {
expect(filter.meta.field).toEqual('script number');
});
});
});
});

Some files were not shown because too many files have changed in this diff Show more