From 4377f227954567540de48e46dab681dd0e9b04a5 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Wed, 7 Mar 2018 17:29:34 -0800 Subject: [PATCH] Revert "Clean up EuiSearchBar docs and reorganize code. (#454)" (#470) * Revert "Clean up EuiSearchBar docs and reorganize code. (#454)" This partially reverts commit 3a910d34666f31b89c4d9ecf432d7aa7bb893fe0. * Change random.oneOf() to accept an array. * Export Query and Ast services. Import components into examples via the root components module. * Fix typos in docs. * Refactor search bar docs for clearer data flow and render methods. * Import Enzyme utilities from enzyme root module in tests. * Use Component instead of React.Component. * Rename astToES to astToEs. * Fix line-break in CHANGELOG. --- src-docs/src/views/file_picker/file_picker.js | 4 +- src-docs/src/views/search_bar/search_bar.js | 55 +++---- .../implicit_record_action.js | 4 +- .../tables/in_memory/in_memory_search.js | 4 +- src-docs/src/views/tabs/tabs.js | 4 +- .../basic_table/collapsed_item_actions.js | 4 +- .../basic_table/custom_item_action.js | 4 +- .../basic_table/default_item_action.js | 4 +- src/components/basic_table/in_memory_table.js | 9 +- .../basic_table/loading_table_body.js | 4 +- .../filter_group/filter_select_item.js | 4 +- .../form/field_search/field_search.js | 4 +- src/components/index.js | 2 +- .../__snapshots__/search_bar.test.js.snap | 2 +- .../__snapshots__/search_filters.test.js.snap | 4 +- .../filters/field_value_selection_filter.js | 6 +- .../field_value_selection_filter.test.js | 4 +- .../filters/field_value_toggle_filter.js | 6 +- .../filters/field_value_toggle_filter.test.js | 4 +- .../field_value_toggle_group_filter.js | 6 +- .../field_value_toggle_group_filter.test.js | 4 +- .../search_bar/filters/is_filter.js | 6 +- .../search_bar/filters/is_filter.test.js | 4 +- src/components/search_bar/index.js | 1 + .../__snapshots__/ast_to_es.test.js.snap | 0 .../search_bar}/query/ast.js | 31 ++-- .../search_bar}/query/ast_to_es.js | 10 +- .../search_bar/query/ast_to_es.test.js | 69 +++++++++ .../search_bar}/query/default_syntax.js | 30 ++-- .../search_bar}/query/default_syntax.test.js | 146 +++++++++--------- .../search_bar}/query/execute_ast.js | 12 +- .../search_bar}/query/execute_ast.test.js | 100 ++++++------ .../search_bar}/query/index.js | 2 +- .../search_bar}/query/must.js | 2 +- .../search_bar}/query/must_not.js | 2 +- .../search_bar}/query/query.js | 18 ++- src/components/search_bar/search_bar.js | 5 +- src/components/search_bar/search_box.js | 3 +- src/components/search_bar/search_filters.js | 3 +- .../search_bar/search_filters.test.js | 2 +- .../collapsed_record_actions.js | 4 +- .../table_of_records/custom_record_action.js | 4 +- .../table_of_records/default_record_action.js | 4 +- .../table_of_records/table_of_records.js | 4 +- src/components/tooltip/tooltip_trigger.js | 4 +- src/services/index.js | 6 +- src/services/query/ast_to_es.test.js | 67 -------- 47 files changed, 345 insertions(+), 336 deletions(-) rename src/{services => components/search_bar}/query/__snapshots__/ast_to_es.test.js.snap (100%) rename src/{services => components/search_bar}/query/ast.js (93%) rename src/{services => components/search_bar}/query/ast_to_es.js (94%) create mode 100644 src/components/search_bar/query/ast_to_es.test.js rename src/{services => components/search_bar}/query/default_syntax.js (75%) rename src/{services => components/search_bar}/query/default_syntax.test.js (73%) rename src/{services => components/search_bar}/query/execute_ast.js (92%) rename src/{services => components/search_bar}/query/execute_ast.test.js (73%) rename src/{services => components/search_bar}/query/index.js (53%) rename src/{services => components/search_bar}/query/must.js (95%) rename src/{services => components/search_bar}/query/must_not.js (95%) rename src/{services => components/search_bar}/query/query.js (93%) delete mode 100644 src/services/query/ast_to_es.test.js diff --git a/src-docs/src/views/file_picker/file_picker.js b/src-docs/src/views/file_picker/file_picker.js index aae4b14486a..dcdcc5d52e5 100644 --- a/src-docs/src/views/file_picker/file_picker.js +++ b/src-docs/src/views/file_picker/file_picker.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import { EuiFilePicker, @@ -7,7 +7,7 @@ import { EuiText, } from '../../../../src/components'; -export class FilePicker extends React.Component { +export class FilePicker extends Component { constructor(props) { super(props); diff --git a/src-docs/src/views/search_bar/search_bar.js b/src-docs/src/views/search_bar/search_bar.js index 4f7a7f83ebf..3b17700590a 100644 --- a/src-docs/src/views/search_bar/search_bar.js +++ b/src-docs/src/views/search_bar/search_bar.js @@ -1,4 +1,5 @@ import React, { Component, Fragment } from 'react'; +import { times } from 'lodash'; import { EuiHealth, @@ -19,8 +20,6 @@ import { Random, } from '../../../../src/services'; -import { times } from 'lodash'; - const random = new Random(); const tags = [{ @@ -215,10 +214,34 @@ export class SearchBar extends Component { const esQuery = Query.toESQuery(query); + const content = this.renderError() || ( + + + +

Elasticsearch query

+
+ + + + + {esQuery ? JSON.stringify(esQuery, null, 2) : ''} + +
+ + + +

JS execution

+
+ + + + {this.renderTable()} +
+
+ ); + return ( - {this.renderError()} - {this.renderSearch()} @@ -235,29 +258,7 @@ export class SearchBar extends Component { - - - -

Elasticsearch query

-
- - - - - {esQuery ? JSON.stringify(esQuery, null, 2) : ''} - -
- - - -

JS execution

-
- - - - {this.renderTable()} -
-
+ {content}
); } diff --git a/src-docs/src/views/table_of_records/implicit_record_action.js b/src-docs/src/views/table_of_records/implicit_record_action.js index 77d7056a29a..bc4d0fe1202 100644 --- a/src-docs/src/views/table_of_records/implicit_record_action.js +++ b/src-docs/src/views/table_of_records/implicit_record_action.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import { times } from 'lodash'; import { @@ -43,7 +43,7 @@ function loadPage(pageIndex, pageSize, sort) { }; } -export default class PeopleTable extends React.Component { +export default class PeopleTable extends Component { constructor(props) { super(props); diff --git a/src-docs/src/views/tables/in_memory/in_memory_search.js b/src-docs/src/views/tables/in_memory/in_memory_search.js index 585c29ab31c..ebabdadd16e 100644 --- a/src-docs/src/views/tables/in_memory/in_memory_search.js +++ b/src-docs/src/views/tables/in_memory/in_memory_search.js @@ -1,4 +1,4 @@ -import React, { Fragment } from 'react'; +import React, { Component, Fragment } from 'react'; import { formatDate } from '../../../../../src/services/format'; import { createDataStore } from '../data_store'; import { @@ -35,7 +35,7 @@ Example country object: const store = createDataStore(); -export class Table extends React.Component { +export class Table extends Component { constructor(props) { super(props); diff --git a/src-docs/src/views/tabs/tabs.js b/src-docs/src/views/tabs/tabs.js index b975867c1b7..eb65c8c2380 100644 --- a/src-docs/src/views/tabs/tabs.js +++ b/src-docs/src/views/tabs/tabs.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import { EuiTabs, @@ -6,7 +6,7 @@ import { EuiSpacer, } from '../../../../src/components'; -class EuiTabsExample extends React.Component { +class EuiTabsExample extends Component { constructor(props) { super(props); diff --git a/src/components/basic_table/collapsed_item_actions.js b/src/components/basic_table/collapsed_item_actions.js index 10bf25eb73b..3603ebfd532 100644 --- a/src/components/basic_table/collapsed_item_actions.js +++ b/src/components/basic_table/collapsed_item_actions.js @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { Component } from 'react'; import { EuiContextMenuItem, EuiContextMenuPanel } from '../context_menu'; import { EuiPopover } from '../popover'; import { EuiButtonIcon } from '../button'; -export class CollapsedItemActions extends React.Component { +export class CollapsedItemActions extends Component { constructor(props) { super(props); diff --git a/src/components/basic_table/custom_item_action.js b/src/components/basic_table/custom_item_action.js index 6b3d3456e7a..aabecece419 100644 --- a/src/components/basic_table/custom_item_action.js +++ b/src/components/basic_table/custom_item_action.js @@ -1,6 +1,6 @@ -import React, { cloneElement } from 'react'; +import React, { Component, cloneElement } from 'react'; -export class CustomItemAction extends React.Component { +export class CustomItemAction extends Component { constructor(props) { super(props); diff --git a/src/components/basic_table/default_item_action.js b/src/components/basic_table/default_item_action.js index cdd9a4fbeed..f4bb460ec63 100644 --- a/src/components/basic_table/default_item_action.js +++ b/src/components/basic_table/default_item_action.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import { isString } from '../../services/predicate'; import { EuiButton, EuiButtonIcon } from '../button'; @@ -6,7 +6,7 @@ const defaults = { color: 'primary' }; -export class DefaultItemAction extends React.Component { +export class DefaultItemAction extends Component { constructor(props) { super(props); diff --git a/src/components/basic_table/in_memory_table.js b/src/components/basic_table/in_memory_table.js index d6f48095370..108b129c920 100644 --- a/src/components/basic_table/in_memory_table.js +++ b/src/components/basic_table/in_memory_table.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiBasicTable, @@ -8,14 +8,13 @@ import { import { defaults as paginationBarDefaults } from './pagination_bar'; -import { Query } from '../../services/query'; import { isBoolean, isString } from '../../services/predicate'; import { Comparators } from '../../services/sort'; import { + Query, QueryType, SearchFiltersFiltersType, - SearchBoxConfigPropTypes, - EuiSearchBar, + SearchBoxConfigPropTypes, EuiSearchBar } from '../search_bar'; import { EuiSpacer } from '../spacer/spacer'; @@ -60,7 +59,7 @@ const initialCriteria = (props) => { }; }; -export class EuiInMemoryTable extends React.Component { +export class EuiInMemoryTable extends Component { static propTypes = InMemoryTablePropTypes; static defaultProps = { diff --git a/src/components/basic_table/loading_table_body.js b/src/components/basic_table/loading_table_body.js index 47b44f276f0..d667329c8e1 100644 --- a/src/components/basic_table/loading_table_body.js +++ b/src/components/basic_table/loading_table_body.js @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { Component } from 'react'; import { EuiTableBody } from '../table/table_body'; -export class LoadingTableBody extends React.Component { +export class LoadingTableBody extends Component { constructor(props) { super(props); diff --git a/src/components/filter_group/filter_select_item.js b/src/components/filter_group/filter_select_item.js index 46788598f81..04761c62785 100644 --- a/src/components/filter_group/filter_select_item.js +++ b/src/components/filter_group/filter_select_item.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; @@ -23,7 +23,7 @@ const resolveIconAndColor = (checked) => { { icon: 'cross', color: 'text' }; }; -export class EuiFilterSelectItem extends React.Component { +export class EuiFilterSelectItem extends Component { constructor(props) { super(props); diff --git a/src/components/form/field_search/field_search.js b/src/components/form/field_search/field_search.js index e017daf1f5f..59c8bd3f15f 100644 --- a/src/components/form/field_search/field_search.js +++ b/src/components/form/field_search/field_search.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { Browser } from '../../../services/browser'; @@ -36,7 +36,7 @@ const defaultProps = { incremental: false }; -export class EuiFieldSearch extends React.Component { +export class EuiFieldSearch extends Component { static propTypes = propTypes; static defaultProps = defaultProps; diff --git a/src/components/index.js b/src/components/index.js index e072c7e0bce..b99f97ab8a3 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -209,7 +209,7 @@ export { } from './progress'; export { - EuiSearchBar, + EuiSearchBar } from './search_bar'; export { diff --git a/src/components/search_bar/__snapshots__/search_bar.test.js.snap b/src/components/search_bar/__snapshots__/search_bar.test.js.snap index 19825888375..99c45e8b419 100644 --- a/src/components/search_bar/__snapshots__/search_bar.test.js.snap +++ b/src/components/search_bar/__snapshots__/search_bar.test.js.snap @@ -92,7 +92,7 @@ exports[`SearchBar render - provided query, filters 1`] = ` onChange={[Function]} query={ Query { - "ast": _Ast { + "ast": _AST { "_clauses": Array [ Object { "match": "must", diff --git a/src/components/search_bar/__snapshots__/search_filters.test.js.snap b/src/components/search_bar/__snapshots__/search_filters.test.js.snap index 8c072425f5d..269c295f2e7 100644 --- a/src/components/search_bar/__snapshots__/search_filters.test.js.snap +++ b/src/components/search_bar/__snapshots__/search_filters.test.js.snap @@ -24,7 +24,7 @@ exports[`EuiSearchFilters render - with filters 1`] = ` onChange={[Function]} query={ Query { - "ast": _Ast { + "ast": _AST { "_clauses": Array [], "_indexedClauses": Object { "field": Object {}, @@ -62,7 +62,7 @@ exports[`EuiSearchFilters render - with filters 1`] = ` onChange={[Function]} query={ Query { - "ast": _Ast { + "ast": _AST { "_clauses": Array [], "_indexedClauses": Object { "field": Object {}, diff --git a/src/components/search_bar/filters/field_value_selection_filter.js b/src/components/search_bar/filters/field_value_selection_filter.js index ded96fe7bae..d57e651548d 100644 --- a/src/components/search_bar/filters/field_value_selection_filter.js +++ b/src/components/search_bar/filters/field_value_selection_filter.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { isArray, isNil } from '../../../services/predicate'; import { keyCodes } from '../../../services'; @@ -10,7 +10,7 @@ import { EuiFilterSelectItem, EuiFilterButton } from '../../filter_group'; import { EuiLoadingChart } from '../../loading/loading_chart'; import { EuiSpacer } from '../../spacer/spacer'; import { EuiIcon } from '../../icon/icon'; -import { Query } from '../../../services/query'; +import { Query } from '../query'; const FieldValueOptionType = PropTypes.shape({ value: PropTypes.any.isRequired, @@ -52,7 +52,7 @@ const defaults = { } }; -export class FieldValueSelectionFilter extends React.Component { +export class FieldValueSelectionFilter extends Component { static propTypes = FieldValueSelectionFilterPropTypes; diff --git a/src/components/search_bar/filters/field_value_selection_filter.test.js b/src/components/search_bar/filters/field_value_selection_filter.test.js index fd659e635ef..f9e2a3f6bf4 100644 --- a/src/components/search_bar/filters/field_value_selection_filter.test.js +++ b/src/components/search_bar/filters/field_value_selection_filter.test.js @@ -1,8 +1,8 @@ import React from 'react'; -import { shallow } from 'enzyme'; import { requiredProps } from '../../../test'; +import { shallow } from 'enzyme'; import { FieldValueSelectionFilter } from './field_value_selection_filter'; -import { Query } from '../../../services/query'; +import { Query } from '../query'; describe('FieldValueSelectionFilter', () => { diff --git a/src/components/search_bar/filters/field_value_toggle_filter.js b/src/components/search_bar/filters/field_value_toggle_filter.js index 7c9b1fd7cb7..90a2964725a 100644 --- a/src/components/search_bar/filters/field_value_toggle_filter.js +++ b/src/components/search_bar/filters/field_value_toggle_filter.js @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiFilterButton } from '../../filter_group'; import { isNil } from '../../../services/predicate'; import { EuiPropTypes } from '../../../utils/prop_types'; -import { Query } from '../../../services/query'; +import { Query } from '../query'; export const FieldValueToggleFilterConfigType = PropTypes.shape({ type: EuiPropTypes.is('field_value_toggle').isRequired, @@ -21,7 +21,7 @@ const FieldValueToggleFilterPropTypes = { onChange: PropTypes.func.isRequired, // (value: boolean) => void }; -export class FieldValueToggleFilter extends React.Component { +export class FieldValueToggleFilter extends Component { static propTypes = FieldValueToggleFilterPropTypes; diff --git a/src/components/search_bar/filters/field_value_toggle_filter.test.js b/src/components/search_bar/filters/field_value_toggle_filter.test.js index ad90e02dde4..18099bc9353 100644 --- a/src/components/search_bar/filters/field_value_toggle_filter.test.js +++ b/src/components/search_bar/filters/field_value_toggle_filter.test.js @@ -1,7 +1,7 @@ import React from 'react'; -import { shallow } from 'enzyme'; import { requiredProps } from '../../../test'; -import { Query } from '../../../services/query'; +import { shallow } from 'enzyme'; +import { Query } from '../query'; import { FieldValueToggleFilter } from './field_value_toggle_filter'; describe('FieldValueToggleFilter', () => { diff --git a/src/components/search_bar/filters/field_value_toggle_group_filter.js b/src/components/search_bar/filters/field_value_toggle_group_filter.js index 350670ca28b..0c0189bfc2d 100644 --- a/src/components/search_bar/filters/field_value_toggle_group_filter.js +++ b/src/components/search_bar/filters/field_value_toggle_group_filter.js @@ -1,8 +1,8 @@ -import React from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiFilterButton } from '../../filter_group'; import { EuiPropTypes } from '../../../utils/prop_types'; -import { Query } from '../../../services/query'; +import { Query } from '../query'; export const FieldValueToggleGroupFilterItemType = PropTypes.shape({ value: PropTypes.string.isRequired, @@ -24,7 +24,7 @@ const FieldValueToggleGroupFilterPropTypes = { onChange: PropTypes.func.isRequired, // (value: boolean) => void }; -export class FieldValueToggleGroupFilter extends React.Component { +export class FieldValueToggleGroupFilter extends Component { static propTypes = FieldValueToggleGroupFilterPropTypes; diff --git a/src/components/search_bar/filters/field_value_toggle_group_filter.test.js b/src/components/search_bar/filters/field_value_toggle_group_filter.test.js index f7fb294403a..11ab23605ee 100644 --- a/src/components/search_bar/filters/field_value_toggle_group_filter.test.js +++ b/src/components/search_bar/filters/field_value_toggle_group_filter.test.js @@ -1,7 +1,7 @@ import React from 'react'; -import { shallow } from 'enzyme'; import { requiredProps } from '../../../test'; -import { Query } from '../../../services/query'; +import { shallow } from 'enzyme'; +import { Query } from '../query'; import { FieldValueToggleGroupFilter } from './field_value_toggle_group_filter'; describe('TermToggleGroupFilter', () => { diff --git a/src/components/search_bar/filters/is_filter.js b/src/components/search_bar/filters/is_filter.js index ce1e54ea97c..5c6ff4e0587 100644 --- a/src/components/search_bar/filters/is_filter.js +++ b/src/components/search_bar/filters/is_filter.js @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiFilterButton } from '../../filter_group'; import { isNil } from '../../../services/predicate'; import { EuiPropTypes } from '../../../utils/prop_types'; -import { Query } from '../../../services/query'; +import { Query } from '../query'; export const IsFilterConfigType = PropTypes.shape({ type: EuiPropTypes.is('is').isRequired, @@ -20,7 +20,7 @@ const IsFilterPropTypes = { onChange: PropTypes.func.isRequired, // (value: boolean) => void }; -export class IsFilter extends React.Component { +export class IsFilter extends Component { static propTypes = IsFilterPropTypes; diff --git a/src/components/search_bar/filters/is_filter.test.js b/src/components/search_bar/filters/is_filter.test.js index 891bc0e2265..30126dbb3c1 100644 --- a/src/components/search_bar/filters/is_filter.test.js +++ b/src/components/search_bar/filters/is_filter.test.js @@ -1,8 +1,8 @@ import React from 'react'; -import { shallow } from 'enzyme'; import { requiredProps } from '../../../test'; -import { Query } from '../../../services/query'; +import { shallow } from 'enzyme'; import { IsFilter } from './is_filter'; +import { Query } from '../query'; describe('IsFilter', () => { diff --git a/src/components/search_bar/index.js b/src/components/search_bar/index.js index a203e6fc36f..89174986313 100644 --- a/src/components/search_bar/index.js +++ b/src/components/search_bar/index.js @@ -1,4 +1,5 @@ export { EuiSearchBar } from './search_bar'; +export { Query } from './query'; export { QueryType } from './search_bar'; export { SearchBoxConfigPropTypes } from './search_box'; export { SearchFiltersFiltersType } from './search_filters'; diff --git a/src/services/query/__snapshots__/ast_to_es.test.js.snap b/src/components/search_bar/query/__snapshots__/ast_to_es.test.js.snap similarity index 100% rename from src/services/query/__snapshots__/ast_to_es.test.js.snap rename to src/components/search_bar/query/__snapshots__/ast_to_es.test.js.snap diff --git a/src/services/query/ast.js b/src/components/search_bar/query/ast.js similarity index 93% rename from src/services/query/ast.js rename to src/components/search_bar/query/ast.js index 15bc356a04f..89fbe1d5248 100644 --- a/src/services/query/ast.js +++ b/src/components/search_bar/query/ast.js @@ -1,4 +1,4 @@ -import { isArray, isNil } from '../predicate'; +import { isArray, isNil } from '../../../services/predicate'; export const Match = Object.freeze({ MUST: 'must', @@ -68,9 +68,10 @@ const Is = Object.freeze({ * * This AST is immutable - every "mutating" operation returns a newly mutated AST. */ -export class _Ast { +export class _AST { + static create(clauses) { - return new _Ast(clauses); + return new _AST(clauses); } constructor(clauses = []) { @@ -141,7 +142,7 @@ export class _Ast { const existingClause = this.getOrFieldClause(field); if (!existingClause) { const newClause = must ? Field.must(field, [ value ]) : Field.mustNot(field, [ value ]); - return new _Ast([ ...this._clauses, newClause ]); + return new _AST([ ...this._clauses, newClause ]); } const clauses = this._clauses.map(clause => { if (clause === existingClause) { @@ -149,13 +150,13 @@ export class _Ast { } return clause; }); - return new _Ast(clauses); + return new _AST(clauses); } removeOrFieldValue(field, value) { const existingClause = this.getOrFieldClause(field, value); if (!existingClause) { - return new _Ast([ ...this._clauses ]); + return new _AST([ ...this._clauses ]); } const clauses = this._clauses.reduce((clauses, clause) => { if (clause !== existingClause) { @@ -169,14 +170,14 @@ export class _Ast { clauses.push({ ...clause, value: filteredValue }); return clauses; }, []); - return new _Ast(clauses); + return new _AST(clauses); } removeOrFieldClauses(field) { const clauses = this._clauses.filter(clause => { return !Field.isInstance(clause) || clause.field !== field || !isArray(clause.value); }); - return new _Ast(clauses); + return new _AST(clauses); } hasSimpleFieldClause(field, value = undefined) { @@ -199,17 +200,17 @@ export class _Ast { removeSimpleFieldValue(field, value) { const existingClause = this.getSimpleFieldClause(field, value); if (!existingClause) { - return new _Ast([ ...this._clauses ]); + return new _AST([ ...this._clauses ]); } const clauses = this._clauses.filter(clause => clause !== existingClause); - return new _Ast(clauses); + return new _AST(clauses); } removeSimpleFieldClauses(field) { const clauses = this._clauses.filter(clause => { return !Field.isInstance(clause) || clause.field !== field || isArray(clause.value); }); - return new _Ast(clauses); + return new _AST(clauses); } getIsClauses() { @@ -221,7 +222,7 @@ export class _Ast { } removeIsClause(flag) { - return new _Ast(this._clauses.filter(clause => !Is.isInstance(clause) || clause.flag !== flag)); + return new _AST(this._clauses.filter(clause => !Is.isInstance(clause) || clause.flag !== flag)); } /** @@ -279,14 +280,14 @@ export class _Ast { if (!added) { newClauses.push(newClause); } - return new _Ast(newClauses); + return new _AST(newClauses); } } -export const Ast = Object.freeze({ +export const AST = Object.freeze({ Match, Term, Field, Is, - create: (clauses) => new _Ast(clauses) + create: (clauses) => new _AST(clauses) }); diff --git a/src/services/query/ast_to_es.js b/src/components/search_bar/query/ast_to_es.js similarity index 94% rename from src/services/query/ast_to_es.js rename to src/components/search_bar/query/ast_to_es.js index 9fcb263db0d..2f2815f10bd 100644 --- a/src/services/query/ast_to_es.js +++ b/src/components/search_bar/query/ast_to_es.js @@ -1,5 +1,5 @@ -import { Ast } from './ast'; -import { isArray } from '../predicate'; +import { AST } from './ast'; +import { isArray } from '../../../services/predicate'; export const _termValuesToQuery = (values, options) => { const body = { @@ -74,7 +74,7 @@ export const _isFlagToQuery = (flag, on) => { const collectTerms = (ast) => { return ast.getTermClauses().reduce((values, clause) => { - if (Ast.Match.isMustClause(clause)) { + if (AST.Match.isMustClause(clause)) { values.must.push(clause.value); } else { values.mustNot.push(clause.value); @@ -93,7 +93,7 @@ const collectFields = (ast) => { }; return ast.getFieldClauses().reduce((fields, clause) => { - if (Ast.Match.isMustClause(clause)) { + if (AST.Match.isMustClause(clause)) { if (isArray(clause.value)) { fieldArray(fields.must.or, clause.field).push(...clause.value); } else { @@ -141,7 +141,7 @@ export const astToEs = (ast, options = {}) => { return fieldValuesToQuery(field, fields.must.or[field], 'or'); })); must.push(...ast.getIsClauses().map(clause => { - return isFlagToQuery(clause.flag, Ast.Match.isMustClause(clause)); + return isFlagToQuery(clause.flag, AST.Match.isMustClause(clause)); })); const mustNot = []; diff --git a/src/components/search_bar/query/ast_to_es.test.js b/src/components/search_bar/query/ast_to_es.test.js new file mode 100644 index 00000000000..a180dfc4a04 --- /dev/null +++ b/src/components/search_bar/query/ast_to_es.test.js @@ -0,0 +1,69 @@ +import { astToEs } from './ast_to_es'; +import { AST } from './ast'; + +describe('astToEs', () => { + + test(`ast - ''`, () => { + const query = astToEs(AST.create([])); + expect(query).toMatchSnapshot(); + }); + + test(`ast - 'john -sales'`, () => { + const query = astToEs(AST.create([ + AST.Term.must('john'), + AST.Term.mustNot('sales'), + ])); + expect(query).toMatchSnapshot(); + }); + + test(`ast - '-group:es group:kibana -group:beats group:logstash'`, () => { + const query = astToEs(AST.create([ + AST.Field.mustNot('group', 'es'), + AST.Field.must('group', 'kibana'), + AST.Field.mustNot('group', 'beats'), + AST.Field.must('group', 'logstash') + ])); + expect(query).toMatchSnapshot(); + }); + + test(`ast - 'is:online group:kibana john'`, () => { + const query = astToEs(AST.create([ + AST.Is.must('online'), + AST.Field.must('group', 'kibana'), + AST.Term.must('john') + ])); + expect(query).toMatchSnapshot(); + }); + + test(`ast - 'john -doe is:online group:eng group:es -group:kibana -is:active'`, () => { + const query = astToEs(AST.create([ + AST.Term.must('john'), + AST.Term.mustNot('doe'), + AST.Is.must('online'), + AST.Field.must('group', 'eng'), + AST.Field.must('group', 'es'), + AST.Field.mustNot('group', 'kibana'), + AST.Is.mustNot('active') + ])); + expect(query).toMatchSnapshot(); + }); + + test(`ast - 'john group:(eng or es) -group:kibana'`, () => { + const query = astToEs(AST.create([ + AST.Term.must('john'), + AST.Field.must('group', ['eng', 'es']), + AST.Field.mustNot('group', 'kibana') + ])); + expect(query).toMatchSnapshot(); + }); + + test(`ast - 'john group:(eng or "marketing org") -group:"kibana team"`, () => { + const query = astToEs(AST.create([ + AST.Term.must('john'), + AST.Field.must('group', ['eng', 'marketing org']), + AST.Field.mustNot('group', 'kibana team') + ])); + expect(query).toMatchSnapshot(); + }); + +}); diff --git a/src/services/query/default_syntax.js b/src/components/search_bar/query/default_syntax.js similarity index 75% rename from src/services/query/default_syntax.js rename to src/components/search_bar/query/default_syntax.js index 482906fb47e..b66d84fc374 100644 --- a/src/services/query/default_syntax.js +++ b/src/components/search_bar/query/default_syntax.js @@ -1,5 +1,5 @@ -import { Ast } from './ast'; -import { isArray } from '../predicate'; +import { AST } from './ast'; +import { isArray } from '../../../services/predicate'; import peg from 'pegjs-inline-precompile'; // eslint-disable-line import/no-unresolved const unescapeValue = (value) => { @@ -12,7 +12,7 @@ const escapeValue = (value) => { const parser = peg` { - const { Ast, unescapeValue } = options; + const { AST, unescapeValue } = options; } Query @@ -32,19 +32,19 @@ Clause / TermClause TermClause - = space? "-" value:termValue { return Ast.Term.mustNot(value); } - / space? value:termValue { return Ast.Term.must(value); } + = space? "-" value:termValue { return AST.Term.mustNot(value); } + / space? value:termValue { return AST.Term.must(value); } IsClause - = space? "-" value:IsValue { return Ast.Is.mustNot(value); } - / space? value:IsValue { return Ast.Is.must(value); } + = space? "-" value:IsValue { return AST.Is.mustNot(value); } + / space? value:IsValue { return AST.Is.must(value); } IsValue = "is:" value:value { return value; } FieldClause - = space? "-" fv:FieldAndValue { return Ast.Field.mustNot(fv.field, fv.value); } - / space? fv:FieldAndValue { return Ast.Field.must(fv.field, fv.value); } + = space? "-" fv:FieldAndValue { return AST.Field.mustNot(fv.field, fv.value); } + / space? fv:FieldAndValue { return AST.Field.must(fv.field, fv.value); } FieldAndValue = field:fieldName ":" value:fieldValue { return {field, value}; } @@ -105,22 +105,22 @@ const printValue = (value) => { export const defaultSyntax = Object.freeze({ parse: (query) => { - const clauses = parser.parse(query, { Ast, unescapeValue }); - return Ast.create(clauses); + const clauses = parser.parse(query, { AST, unescapeValue }); + return AST.create(clauses); }, print: (ast) => { return ast.clauses.reduce((text, clause) => { - const prefix = Ast.Match.isMustClause(clause) ? '' : '-'; + const prefix = AST.Match.isMustClause(clause) ? '' : '-'; switch (clause.type) { - case Ast.Field.TYPE: + case AST.Field.TYPE: if (isArray(clause.value)) { return `${text} ${prefix}${escapeValue(clause.field)}:(${clause.value.map(val => printValue(val)).join(' or ')})`; } return `${text} ${prefix}${escapeValue(clause.field)}:${printValue(clause.value)}`; - case Ast.Is.TYPE: + case AST.Is.TYPE: return `${text} ${prefix}is:${escapeValue(clause.flag)}`; - case Ast.Term.TYPE: + case AST.Term.TYPE: return `${text} ${prefix}${printValue(clause.value)}`; default: return text; diff --git a/src/services/query/default_syntax.test.js b/src/components/search_bar/query/default_syntax.test.js similarity index 73% rename from src/services/query/default_syntax.test.js rename to src/components/search_bar/query/default_syntax.test.js index e018a52cc55..c0c0a1f3537 100644 --- a/src/services/query/default_syntax.test.js +++ b/src/components/search_bar/query/default_syntax.test.js @@ -1,5 +1,5 @@ import { defaultSyntax } from './default_syntax'; -import { Ast } from './ast'; +import { AST } from './ast'; describe('defaultSyntax', () => { @@ -25,20 +25,20 @@ describe('defaultSyntax', () => { let clause = ast.getTermClause('-'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.value).toBe('-'); clause = ast.getTermClause(':'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(false); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(false); expect(clause.value).toBe(':'); clause = ast.getTermClause('\\'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.value).toBe('\\'); const printedQuery = defaultSyntax.print(ast); @@ -55,8 +55,8 @@ describe('defaultSyntax', () => { const clause = ast.getSimpleFieldClause('name', 'john'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('name'); expect(clause.value).toBe('john'); @@ -74,8 +74,8 @@ describe('defaultSyntax', () => { const clause = ast.getSimpleFieldClause('n:ame', 'jo:hn'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('n:ame'); expect(clause.value).toBe('jo:hn'); @@ -93,15 +93,15 @@ describe('defaultSyntax', () => { let clause = ast.getSimpleFieldClause('name', 'john'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('name'); expect(clause.value).toBe('john'); clause = ast.getSimpleFieldClause('age', '6'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('age'); expect(clause.value).toBe('6'); @@ -119,22 +119,22 @@ describe('defaultSyntax', () => { let clause = ast.getSimpleFieldClause('name', 'john'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('name'); expect(clause.value).toBe('john'); clause = ast.getSimpleFieldClause('age', '6'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('age'); expect(clause.value).toBe('6'); clause = ast.getSimpleFieldClause('age', '5'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('age'); expect(clause.value).toBe('5'); @@ -152,22 +152,22 @@ describe('defaultSyntax', () => { let clause = ast.getSimpleFieldClause('name', 'john'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('name'); expect(clause.value).toBe('john'); clause = ast.getSimpleFieldClause('age', '6'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('age'); expect(clause.value).toBe('6'); clause = ast.getSimpleFieldClause('age', '5'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(false); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(false); expect(clause.field).toBe('age'); expect(clause.value).toBe('5'); @@ -185,14 +185,14 @@ describe('defaultSyntax', () => { let clause = ast.getTermClause('foo'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.value).toBe('foo'); clause = ast.getTermClause('bar'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.value).toBe('bar'); const printedQuery = defaultSyntax.print(ast); @@ -209,14 +209,14 @@ describe('defaultSyntax', () => { let clause = ast.getTermClause('foo'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.value).toBe('foo'); clause = ast.getTermClause('bar'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(false); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(false); expect(clause.value).toBe('bar'); const printedQuery = defaultSyntax.print(ast); @@ -233,34 +233,34 @@ describe('defaultSyntax', () => { let clause = ast.getTermClause('foo'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.value).toBe('foo'); clause = ast.getSimpleFieldClause('name', 'john'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(false); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(false); expect(clause.field).toBe('name'); expect(clause.value).toBe('john'); clause = ast.getTermClause('bar'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(false); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(false); expect(clause.value).toBe('bar'); clause = ast.getSimpleFieldClause('age', '5'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('age'); expect(clause.value).toBe('5'); clause = ast.getSimpleFieldClause('name', 'joe'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('name'); expect(clause.value).toBe('joe'); @@ -278,47 +278,47 @@ describe('defaultSyntax', () => { let clause = ast.getTermClause('foo'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.value).toBe('foo'); clause = ast.getSimpleFieldClause('name', 'john'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(false); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(false); expect(clause.field).toBe('name'); expect(clause.value).toBe('john'); clause = ast.getTermClause('bar'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(false); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(false); expect(clause.value).toBe('bar'); clause = ast.getSimpleFieldClause('age', '5'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('age'); expect(clause.value).toBe('5'); clause = ast.getSimpleFieldClause('name', 'joe'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('name'); expect(clause.value).toBe('joe'); clause = ast.getIsClause('open'); expect(clause).toBeDefined(); - expect(Ast.Is.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Is.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.flag).toBe('open'); clause = ast.getIsClause('liberal'); expect(clause).toBeDefined(); - expect(Ast.Is.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(false); + expect(AST.Is.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(false); expect(clause.flag).toBe('liberal'); const printedQuery = defaultSyntax.print(ast); @@ -335,8 +335,8 @@ describe('defaultSyntax', () => { const clause = ast.getTermClause('foo bar'); expect(clause).toBeDefined(); - expect(Ast.Term.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Term.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.value).toBe('foo bar'); const printedQuery = defaultSyntax.print(ast); @@ -353,8 +353,8 @@ describe('defaultSyntax', () => { const clause = ast.getSimpleFieldClause('field', 'foo bar'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('field'); expect(clause.value).toBe('foo bar'); @@ -372,8 +372,8 @@ describe('defaultSyntax', () => { const clause = ast.getOrFieldClause('field'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('field'); expect(clause.value).toHaveLength(2); expect(clause.value).toContain('foo'); @@ -393,8 +393,8 @@ describe('defaultSyntax', () => { let clause = ast.getOrFieldClause('field1'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('field1'); expect(clause.value).toHaveLength(2); expect(clause.value).toContain('foo'); @@ -402,8 +402,8 @@ describe('defaultSyntax', () => { clause = ast.getSimpleFieldClause('field2'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(false); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(false); expect(clause.field).toBe('field2'); expect(clause.value).toBe('baz'); @@ -421,8 +421,8 @@ describe('defaultSyntax', () => { const clause = ast.getSimpleFieldClause('f'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('f'); expect(clause.value).toBe('this is a relaxed phrase'); @@ -440,8 +440,8 @@ describe('defaultSyntax', () => { const clause = ast.getOrFieldClause('f'); expect(clause).toBeDefined(); - expect(Ast.Field.isInstance(clause)).toBe(true); - expect(Ast.Match.isMustClause(clause)).toBe(true); + expect(AST.Field.isInstance(clause)).toBe(true); + expect(AST.Match.isMustClause(clause)).toBe(true); expect(clause.field).toBe('f'); expect(clause.value).toHaveLength(1); expect(clause.value).toContain('foo'); diff --git a/src/services/query/execute_ast.js b/src/components/search_bar/query/execute_ast.js similarity index 92% rename from src/services/query/execute_ast.js rename to src/components/search_bar/query/execute_ast.js index 60cbbeae1c0..f825f40c73c 100644 --- a/src/services/query/execute_ast.js +++ b/src/components/search_bar/query/execute_ast.js @@ -1,20 +1,20 @@ import { get } from 'lodash'; -import { isString, isArray } from '../predicate'; +import { isString, isArray } from '../../../services/predicate'; import { must } from './must'; import { mustNot } from './must_not'; -import { Ast } from './ast'; +import { AST } from './ast'; const EXPLAIN_FIELD = '__explain'; const matchers = { - [Ast.Match.MUST]: must, - [Ast.Match.MUST_NOT]: mustNot + [AST.Match.MUST]: must, + [AST.Match.MUST_NOT]: mustNot }; const defaultIsClauseMatcher = (record, clause, explain) => { const { type, flag, match } = clause; const value = get(record, clause.flag); - const must = Ast.Match.isMustClause(clause); + const must = AST.Match.isMustClause(clause); const hit = !!value === must; if (explain && hit) { explain.push({ hit, type, flag, match }); @@ -57,7 +57,7 @@ const termClauseMatcher = (record, fields, clauses = [], explain) => { if (!matcher) { // unknown matcher return true; } - if (Ast.Match.isMustClause(clause)) { + if (AST.Match.isMustClause(clause)) { return fields.some(field => { const recordValue = get(record, field); const hit = matcher(recordValue, value); diff --git a/src/services/query/execute_ast.test.js b/src/components/search_bar/query/execute_ast.test.js similarity index 73% rename from src/services/query/execute_ast.test.js rename to src/components/search_bar/query/execute_ast.test.js index 0541481bef6..58af1721059 100644 --- a/src/services/query/execute_ast.test.js +++ b/src/components/search_bar/query/execute_ast.test.js @@ -1,6 +1,6 @@ -import { Ast } from './ast'; +import { AST } from './ast'; import { executeAst } from './execute_ast'; -import { Random } from '../random'; +import { Random } from '../../../services/random'; const random = new Random(); @@ -11,8 +11,8 @@ describe('execute ast', () => { { name: 'john doe' }, { name: 'joe' } ]; - const result = executeAst(Ast.create([ - Ast.Field.must('name', 'john') + const result = executeAst(AST.create([ + AST.Field.must('name', 'john') ]), items); expect(result).toHaveLength(1); expect(result[0].name).toBe('john doe'); @@ -23,8 +23,8 @@ describe('execute ast', () => { { name: 'john' }, { name: 'joe' } ]; - const result = executeAst(Ast.create([ - Ast.Field.mustNot('name', 'john') + const result = executeAst(AST.create([ + AST.Field.mustNot('name', 'john') ]), items); expect(result).toHaveLength(1); expect(result[0].name).toBe('joe'); @@ -35,20 +35,20 @@ describe('execute ast', () => { { name: 'john' }, { name: 'joe' } ]; - let result = executeAst(Ast.create([ - Ast.Field.must('name', ['john', 'doe']) + let result = executeAst(AST.create([ + AST.Field.must('name', ['john', 'doe']) ]), items); expect(result).toHaveLength(1); expect(result[0].name).toBe('john'); - result = executeAst(Ast.create([ - Ast.Field.must('name', ['joe', 'doe']) + result = executeAst(AST.create([ + AST.Field.must('name', ['joe', 'doe']) ]), items); expect(result).toHaveLength(1); expect(result[0].name).toBe('joe'); - result = executeAst(Ast.create([ - Ast.Field.must('name', ['foo', 'bar']) + result = executeAst(AST.create([ + AST.Field.must('name', ['foo', 'bar']) ]), items); expect(result).toHaveLength(0); }); @@ -58,8 +58,8 @@ describe('execute ast', () => { { name: 'john' }, { name: 'joe' } ]; - const result = executeAst(Ast.create([ - Ast.Field.must('name', 'foo') + const result = executeAst(AST.create([ + AST.Field.must('name', 'foo') ]), items); expect(result).toHaveLength(0); }); @@ -69,9 +69,9 @@ describe('execute ast', () => { { name: 'john' }, { name: 'joe' } ]; - const result = executeAst(Ast.create([ - Ast.Field.must('name', 'john'), - Ast.Field.must('name', 'joe') + const result = executeAst(AST.create([ + AST.Field.must('name', 'john'), + AST.Field.must('name', 'joe') ]), items); expect(result).toHaveLength(0); }); @@ -81,9 +81,9 @@ describe('execute ast', () => { { name: 'john', age: 5 }, { name: 'joe' } ]; - const result = executeAst(Ast.create([ - Ast.Field.must('name', 'foo'), - Ast.Field.must('age', '7') + const result = executeAst(AST.create([ + AST.Field.must('name', 'foo'), + AST.Field.must('age', '7') ]), items); expect(result).toHaveLength(0); }); @@ -93,9 +93,9 @@ describe('execute ast', () => { { name: 'john', age: 5 }, { name: 'joe' } ]; - const result = executeAst(Ast.create([ - Ast.Field.must('name', 'john'), - Ast.Field.must('age', '5') + const result = executeAst(AST.create([ + AST.Field.must('name', 'john'), + AST.Field.must('age', '5') ]), items); expect(result).toHaveLength(1); }); @@ -106,8 +106,8 @@ describe('execute ast', () => { { name: 'joe' } ]; const value = random.oneOf(['john', 'doe']); - const result = executeAst(Ast.create([ - Ast.Term.must(value) + const result = executeAst(AST.create([ + AST.Term.must(value) ]), items); expect(result).toHaveLength(1); }); @@ -119,8 +119,8 @@ describe('execute ast', () => { { name: 'john', age: 5 }, { name: 'joe' } ]; - const result = executeAst(Ast.create([ - Ast.Term.must('5') + const result = executeAst(AST.create([ + AST.Term.must('5') ]), items); expect(result).toHaveLength(0); }); @@ -130,8 +130,8 @@ describe('execute ast', () => { { name: 'john', description: 'doe', age: 5 }, { name: 'joe' } ]; - const result = executeAst(Ast.create([ - Ast.Term.must('john') + const result = executeAst(AST.create([ + AST.Term.must('john') ]), items, { defaultFields: [ 'name' ] }); expect(result).toHaveLength(1); }); @@ -141,8 +141,8 @@ describe('execute ast', () => { { name: 'john', description: 'doe', age: 5 }, { name: 'joe' } ]; - const result = executeAst(Ast.create([ - Ast.Term.must('doe') + const result = executeAst(AST.create([ + AST.Term.must('doe') ]), items, { defaultFields: [ 'name' ] }); expect(result).toHaveLength(0); }); @@ -152,8 +152,8 @@ describe('execute ast', () => { { name: 'john', description: 'doe', age: 5 }, { name: 'joe' } ]; - const result = executeAst(Ast.create([ - Ast.Term.mustNot('john') + const result = executeAst(AST.create([ + AST.Term.mustNot('john') ]), items, { defaultFields: [ 'name' ] }); expect(result).toHaveLength(1); expect(result[0].name).toBe('joe'); @@ -164,8 +164,8 @@ describe('execute ast', () => { { name: 'john', open: true }, { name: 'joe', open: false } ]; - const result = executeAst(Ast.create([ - Ast.Is.must('open') + const result = executeAst(AST.create([ + AST.Is.must('open') ]), items); expect(result).toHaveLength(1); expect(result[0].name).toBe('john'); @@ -176,8 +176,8 @@ describe('execute ast', () => { { name: 'john', open: true }, { name: 'joe', open: false } ]; - const result = executeAst(Ast.create([ - Ast.Is.mustNot('open') + const result = executeAst(AST.create([ + AST.Is.mustNot('open') ]), items); expect(result).toHaveLength(1); expect(result[0].name).toBe('joe'); @@ -188,8 +188,8 @@ describe('execute ast', () => { { name: 'john', open: true }, { name: 'joe', open: false } ]; - const result = executeAst(Ast.create([ - Ast.Is.must('closed') + const result = executeAst(AST.create([ + AST.Is.must('closed') ]), items); expect(result).toHaveLength(0); }); @@ -199,8 +199,8 @@ describe('execute ast', () => { { name: 'john', open: true }, { name: 'joe', open: false } ]; - const result = executeAst(Ast.create([ - Ast.Is.mustNot('closed') + const result = executeAst(AST.create([ + AST.Is.mustNot('closed') ]), items); expect(result).toHaveLength(2); }); @@ -212,11 +212,11 @@ describe('execute ast', () => { { text: 'foo bar', age: 7 }, { text: 'bar', age: 7 }, ]; - const result = executeAst(Ast.create([ - Ast.Is.mustNot('open'), - Ast.Field.must('age', '7'), - Ast.Term.must('bar'), - Ast.Term.mustNot('foo') + const result = executeAst(AST.create([ + AST.Is.mustNot('open'), + AST.Field.must('age', '7'), + AST.Term.must('bar'), + AST.Term.mustNot('foo') ]), items); expect(result).toHaveLength(1); expect(result[0].text).toBe('bar'); @@ -230,8 +230,8 @@ describe('execute ast', () => { { text: 'foo bar', age: 7 }, { text: 'bar', age: 7 }, ]; - const result = executeAst(Ast.create([ - Ast.Field.must('name', 'John Doe'), + const result = executeAst(AST.create([ + AST.Field.must('name', 'John Doe'), ]), items); expect(result).toHaveLength(1); expect(result[0].name).toBe('john doe'); @@ -244,8 +244,8 @@ describe('execute ast', () => { { name: 'foo bar', age: 7 }, { name: 'bar', age: 7 }, ]; - const result = executeAst(Ast.create([ - Ast.Field.must('name', [ 'john', 'bar' ]), + const result = executeAst(AST.create([ + AST.Field.must('name', [ 'john', 'bar' ]), ]), items); expect(result).toHaveLength(3); const names = result.map(item => item.name); diff --git a/src/services/query/index.js b/src/components/search_bar/query/index.js similarity index 53% rename from src/services/query/index.js rename to src/components/search_bar/query/index.js index f6ddbd44ea2..60e75e781d8 100644 --- a/src/services/query/index.js +++ b/src/components/search_bar/query/index.js @@ -1,2 +1,2 @@ export { Query } from './query'; -export { Ast } from './ast'; +export { AST } from './ast'; diff --git a/src/services/query/must.js b/src/components/search_bar/query/must.js similarity index 95% rename from src/services/query/must.js rename to src/components/search_bar/query/must.js index 6d0db460a63..7c02950cc90 100644 --- a/src/services/query/must.js +++ b/src/components/search_bar/query/must.js @@ -1,7 +1,7 @@ import { isArray, isBoolean, isNumber, isString -} from '../predicate'; +} from '../../../services/predicate'; import moment from 'moment/moment'; const defaultOptions = { diff --git a/src/services/query/must_not.js b/src/components/search_bar/query/must_not.js similarity index 95% rename from src/services/query/must_not.js rename to src/components/search_bar/query/must_not.js index 1f37ee18a9c..eadd7e410b5 100644 --- a/src/services/query/must_not.js +++ b/src/components/search_bar/query/must_not.js @@ -1,7 +1,7 @@ import { isArray, isBoolean, isNumber, isString -} from '../predicate'; +} from '../../../services/predicate'; import moment from 'moment/moment'; const defaultOptions = { diff --git a/src/services/query/query.js b/src/components/search_bar/query/query.js similarity index 93% rename from src/services/query/query.js rename to src/components/search_bar/query/query.js index 4da9733b464..8d1d40b4589 100644 --- a/src/services/query/query.js +++ b/src/components/search_bar/query/query.js @@ -1,8 +1,8 @@ import { defaultSyntax } from './default_syntax'; import { executeAst } from './execute_ast'; -import { isNil, isString } from '../predicate'; +import { isNil, isString } from '../../../services/predicate'; import { astToEs } from './ast_to_es'; -import { Ast } from './ast'; +import { AST } from './ast'; /** * This is the consumer interface for the query - it's effectively a wrapper construct around @@ -10,26 +10,27 @@ import { Ast } from './ast'; * It is immutable - all mutating operations return a new (mutated) query instance. */ export class Query { + static parse(text, syntax = defaultSyntax) { return new Query(syntax.parse(text), syntax, text); } static isMust(clause) { - return Ast.Match.isMustClause(clause); + return AST.Match.isMustClause(clause); } static MATCH_ALL = Query.parse(''); static isTerm(clause) { - return Ast.Term.isInstance(clause); + return AST.Term.isInstance(clause); } static isIs(clause) { - return Ast.Is.isInstance(clause); + return AST.Is.isInstance(clause); } static isField(clause) { - return Ast.Field.isInstance(clause); + return AST.Field.isInstance(clause); } constructor(ast, syntax = defaultSyntax, text = undefined) { @@ -93,12 +94,12 @@ export class Query { } addMustIsClause(flag) { - const ast = this.ast.addClause(Ast.Is.must(flag)); + const ast = this.ast.addClause(AST.Is.must(flag)); return new Query(ast, this.syntax); } addMustNotIsClause(flag) { - const ast = this.ast.addClause(Ast.Is.mustNot(flag)); + const ast = this.ast.addClause(AST.Is.mustNot(flag)); return new Query(ast, this.syntax); } @@ -164,4 +165,5 @@ export class Query { const q = isString(query) ? Query.parse(query) : query; return astToEs(q.ast, options); } + } diff --git a/src/components/search_bar/search_bar.js b/src/components/search_bar/search_bar.js index 24f29684d61..4496fe7ecfe 100644 --- a/src/components/search_bar/search_bar.js +++ b/src/components/search_bar/search_bar.js @@ -1,5 +1,4 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { isString } from '../../services/predicate'; import { EuiFlexGroup } from '../flex/flex_group'; import { @@ -10,7 +9,8 @@ import { EuiSearchFilters, SearchFiltersFiltersType } from './search_filters'; -import { Query } from '../../services/query'; +import PropTypes from 'prop-types'; +import { Query } from './query'; import { EuiFlexItem } from '../flex/flex_item'; export const QueryType = PropTypes.oneOfType([ PropTypes.instanceOf(Query), PropTypes.string ]); @@ -58,6 +58,7 @@ const resolveQuery = (query) => { }; export class EuiSearchBar extends Component { + static propTypes = SearchBoxConfigPropTypes; constructor(props) { diff --git a/src/components/search_bar/search_box.js b/src/components/search_bar/search_box.js index 0e39fe2399d..1c64b75104a 100644 --- a/src/components/search_bar/search_box.js +++ b/src/components/search_bar/search_box.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { EuiFieldSearch } from '../form/field_search/field_search'; +import PropTypes from 'prop-types'; export const SearchBoxConfigPropTypes = { placeholder: PropTypes.string, @@ -8,6 +8,7 @@ export const SearchBoxConfigPropTypes = { }; export class EuiSearchBox extends Component { + static propTypes = { query: PropTypes.string.isRequired, onSearch: PropTypes.func.isRequired, // (queryText) => void diff --git a/src/components/search_bar/search_filters.js b/src/components/search_bar/search_filters.js index 91984c51ddf..5586c434180 100644 --- a/src/components/search_bar/search_filters.js +++ b/src/components/search_bar/search_filters.js @@ -1,12 +1,13 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { createFilter, FilterConfigType } from './filters'; -import { Query } from '../../services/query'; +import { Query } from './query'; import { EuiFilterGroup } from '../../components/filter_group'; export const SearchFiltersFiltersType = PropTypes.arrayOf(FilterConfigType); export class EuiSearchFilters extends Component { + static propTypes = { query: PropTypes.instanceOf(Query).isRequired, onChange: PropTypes.func.isRequired, diff --git a/src/components/search_bar/search_filters.test.js b/src/components/search_bar/search_filters.test.js index 5ce46061082..1751c0dd5f8 100644 --- a/src/components/search_bar/search_filters.test.js +++ b/src/components/search_bar/search_filters.test.js @@ -2,7 +2,7 @@ import React from 'react'; import { requiredProps } from '../../test'; import { shallow } from 'enzyme'; import { EuiSearchFilters } from './search_filters'; -import { Query } from '../../services/query'; +import { Query } from './query'; describe('EuiSearchFilters', () => { diff --git a/src/components/table_of_records/collapsed_record_actions.js b/src/components/table_of_records/collapsed_record_actions.js index 0971980f3d8..dd637e91e02 100644 --- a/src/components/table_of_records/collapsed_record_actions.js +++ b/src/components/table_of_records/collapsed_record_actions.js @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { Component } from 'react'; import { EuiContextMenuItem, EuiContextMenuPanel } from '../context_menu'; import { EuiPopover } from '../popover'; import { EuiButtonIcon } from '../button'; -export class CollapsedRecordActions extends React.Component { +export class CollapsedRecordActions extends Component { constructor(props) { super(props); diff --git a/src/components/table_of_records/custom_record_action.js b/src/components/table_of_records/custom_record_action.js index 9e390332b02..b218a2a40cb 100644 --- a/src/components/table_of_records/custom_record_action.js +++ b/src/components/table_of_records/custom_record_action.js @@ -1,6 +1,6 @@ -import React, { cloneElement } from 'react'; +import React, { Component, cloneElement } from 'react'; -export class CustomRecordAction extends React.Component { +export class CustomRecordAction extends Component { constructor(props) { super(props); diff --git a/src/components/table_of_records/default_record_action.js b/src/components/table_of_records/default_record_action.js index d77556cf9e4..1e748acc107 100644 --- a/src/components/table_of_records/default_record_action.js +++ b/src/components/table_of_records/default_record_action.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import { isString } from '../../services/predicate'; import { EuiButton, EuiButtonIcon } from '../button'; @@ -6,7 +6,7 @@ const defaults = { color: 'primary' }; -export class DefaultRecordAction extends React.Component { +export class DefaultRecordAction extends Component { constructor(props) { super(props); diff --git a/src/components/table_of_records/table_of_records.js b/src/components/table_of_records/table_of_records.js index c56d3451df8..90ed801da96 100644 --- a/src/components/table_of_records/table_of_records.js +++ b/src/components/table_of_records/table_of_records.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import _ from 'lodash'; import { isString } from '../../services/predicate'; import classNames from 'classnames'; @@ -149,7 +149,7 @@ const EuiTableOfRecordsPropTypes = { className: PropTypes.string }; -export class EuiTableOfRecords extends React.Component { +export class EuiTableOfRecords extends Component { static propTypes = EuiTableOfRecordsPropTypes; diff --git a/src/components/tooltip/tooltip_trigger.js b/src/components/tooltip/tooltip_trigger.js index c1fca3c89bd..509e3b3e96c 100644 --- a/src/components/tooltip/tooltip_trigger.js +++ b/src/components/tooltip/tooltip_trigger.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import classnames from 'classnames'; @@ -6,7 +6,7 @@ import { Tooltip } from './tooltip'; import { SIZE } from './tooltip_constants'; import { noOverflowPlacement } from '../../services'; -export class TooltipTrigger extends React.Component { +export class TooltipTrigger extends Component { static propTypes = { display: PropTypes.bool, title: PropTypes.string, diff --git a/src/services/index.js b/src/services/index.js index f95bee4f9ea..2226e139dc0 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -36,10 +36,11 @@ export { checkHrefAndOnClick, } from './prop_types'; +// TODO: Migrate these services into the services directory. export { - Ast, Query, -} from './query'; + AST as Ast, +} from '../components/search_bar/query'; export { Random @@ -60,4 +61,3 @@ export { export { noOverflowPlacement, } from './overflow'; - diff --git a/src/services/query/ast_to_es.test.js b/src/services/query/ast_to_es.test.js deleted file mode 100644 index 61c4f3a8995..00000000000 --- a/src/services/query/ast_to_es.test.js +++ /dev/null @@ -1,67 +0,0 @@ -import { astToEs } from './ast_to_es'; -import { Ast } from './ast'; - -describe('astToEs', () => { - test(`ast - ''`, () => { - const query = astToEs(Ast.create([])); - expect(query).toMatchSnapshot(); - }); - - test(`ast - 'john -sales'`, () => { - const query = astToEs(Ast.create([ - Ast.Term.must('john'), - Ast.Term.mustNot('sales'), - ])); - expect(query).toMatchSnapshot(); - }); - - test(`ast - '-group:es group:kibana -group:beats group:logstash'`, () => { - const query = astToEs(Ast.create([ - Ast.Field.mustNot('group', 'es'), - Ast.Field.must('group', 'kibana'), - Ast.Field.mustNot('group', 'beats'), - Ast.Field.must('group', 'logstash') - ])); - expect(query).toMatchSnapshot(); - }); - - test(`ast - 'is:online group:kibana john'`, () => { - const query = astToEs(Ast.create([ - Ast.Is.must('online'), - Ast.Field.must('group', 'kibana'), - Ast.Term.must('john') - ])); - expect(query).toMatchSnapshot(); - }); - - test(`ast - 'john -doe is:online group:eng group:es -group:kibana -is:active'`, () => { - const query = astToEs(Ast.create([ - Ast.Term.must('john'), - Ast.Term.mustNot('doe'), - Ast.Is.must('online'), - Ast.Field.must('group', 'eng'), - Ast.Field.must('group', 'es'), - Ast.Field.mustNot('group', 'kibana'), - Ast.Is.mustNot('active') - ])); - expect(query).toMatchSnapshot(); - }); - - test(`ast - 'john group:(eng or es) -group:kibana'`, () => { - const query = astToEs(Ast.create([ - Ast.Term.must('john'), - Ast.Field.must('group', ['eng', 'es']), - Ast.Field.mustNot('group', 'kibana') - ])); - expect(query).toMatchSnapshot(); - }); - - test(`ast - 'john group:(eng or "marketing org") -group:"kibana team"`, () => { - const query = astToEs(Ast.create([ - Ast.Term.must('john'), - Ast.Field.must('group', ['eng', 'marketing org']), - Ast.Field.mustNot('group', 'kibana team') - ])); - expect(query).toMatchSnapshot(); - }); -});