diff --git a/src/ui/public/field_editor/components/scripting_call_outs/help_flyout.js b/src/ui/public/field_editor/components/scripting_call_outs/help_flyout.js deleted file mode 100644 index 3d7c9b2025867..0000000000000 --- a/src/ui/public/field_editor/components/scripting_call_outs/help_flyout.js +++ /dev/null @@ -1,113 +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 React from 'react'; -import { getDocLink } from 'ui/documentation_links'; - -import { - EuiCode, - EuiFlyout, - EuiFlyoutBody, - EuiIcon, - EuiLink, - EuiText, -} from '@elastic/eui'; - -export const ScriptingHelpFlyout = ({ - isVisible = false, - onClose = () => {}, -}) => { - return isVisible ? ( - - - -

Scripting help

-

- By default, Kibana scripted fields use {( - - Painless - - )}, a simple and secure scripting language designed specifically for use with Elasticsearch, - to access values in the document use the following format: -

-

- doc['some_field'].value -

-

- Painless is powerful but easy to use. It provides access to many {( - - native Java APIs - - )}. Read up on its {( - - syntax - - )} and you'll be up to speed in no time! -

-

- Kibana currently imposes one special limitation on the painless scripts you write. They cannot contain named functions. -

-

- Coming from an older version of Kibana? The {( - - Lucene Expressions - - )} you know and love are still available. Lucene expressions are a lot like JavaScript, - but limited to basic arithmetic, bitwise and comparison operations. -

-

- There are a few limitations when using Lucene Expressions: -

-
    -
  • Only numeric, boolean, date, and geo_point fields may be accessed
  • -
  • Stored fields are not available
  • -
  • If a field is sparse (only some documents contain a value), documents missing the field will have a value of 0
  • -
-

- Here are all the operations available to lucene expressions: -

-
    -
  • Arithmetic operators: + - * / %
  • -
  • Bitwise operators: | & ^ ~ << >> >>>
  • -
  • Boolean operators (including the ternary operator): && || ! ?:
  • -
  • Comparison operators: < <= == >= >
  • -
  • Common mathematic functions: abs ceil exp floor ln log10 logn max min sqrt pow
  • -
  • Trigonometric library functions: acosh acos asinh asin atanh atan atan2 cosh cos sinh sin tanh tan
  • -
  • Distance functions: haversin
  • -
  • Miscellaneous functions: min, max
  • -
-
-
-
- ) : null; -}; - -ScriptingHelpFlyout.displayName = 'ScriptingHelpFlyout'; diff --git a/src/ui/public/field_editor/components/scripting_call_outs/index.js b/src/ui/public/field_editor/components/scripting_call_outs/index.js index 44da62f3e22f2..1ecf919ce6b16 100644 --- a/src/ui/public/field_editor/components/scripting_call_outs/index.js +++ b/src/ui/public/field_editor/components/scripting_call_outs/index.js @@ -19,4 +19,3 @@ export { ScriptingDisabledCallOut } from './disabled_call_out'; export { ScriptingWarningCallOut } from './warning_call_out'; -export { ScriptingHelpFlyout } from './help_flyout'; diff --git a/src/ui/public/field_editor/components/scripting_help/help_flyout.js b/src/ui/public/field_editor/components/scripting_help/help_flyout.js new file mode 100644 index 0000000000000..fa92145311f21 --- /dev/null +++ b/src/ui/public/field_editor/components/scripting_help/help_flyout.js @@ -0,0 +1,76 @@ +/* + * 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 React from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlyout, + EuiFlyoutBody, + EuiTabbedContent, +} from '@elastic/eui'; + +import { ScriptingSyntax } from './scripting_syntax'; +import { TestScript } from './test_script'; + +export const ScriptingHelpFlyout = ({ + isVisible = false, + onClose = () => {}, + indexPattern, + lang, + name, + script, +}) => { + const tabs = [{ + id: 'syntax', + name: 'Syntax', + content: , + }, { + id: 'test', + name: 'Preview results', + content: ( + + ), + }]; + + return isVisible ? ( + + + + + + ) : null; +}; + +ScriptingHelpFlyout.displayName = 'ScriptingHelpFlyout'; + +ScriptingHelpFlyout.propTypes = { + indexPattern: PropTypes.object.isRequired, + lang: PropTypes.string.isRequired, + name: PropTypes.string, + script: PropTypes.string, +}; diff --git a/src/ui/public/field_editor/components/scripting_call_outs/help_flyout.test.js b/src/ui/public/field_editor/components/scripting_help/help_flyout.test.js similarity index 100% rename from src/ui/public/field_editor/components/scripting_call_outs/help_flyout.test.js rename to src/ui/public/field_editor/components/scripting_help/help_flyout.test.js diff --git a/src/ui/public/field_editor/components/scripting_help/index.js b/src/ui/public/field_editor/components/scripting_help/index.js new file mode 100644 index 0000000000000..13b3b10fb5859 --- /dev/null +++ b/src/ui/public/field_editor/components/scripting_help/index.js @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { ScriptingHelpFlyout } from './help_flyout'; diff --git a/src/ui/public/field_editor/components/scripting_help/scripting_syntax.js b/src/ui/public/field_editor/components/scripting_help/scripting_syntax.js new file mode 100644 index 0000000000000..892bf5376ea1b --- /dev/null +++ b/src/ui/public/field_editor/components/scripting_help/scripting_syntax.js @@ -0,0 +1,104 @@ +/* + * 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 React, { Fragment } from 'react'; +import { getDocLink } from 'ui/documentation_links'; + +import { + EuiCode, + EuiIcon, + EuiLink, + EuiText, + EuiSpacer, +} from '@elastic/eui'; + +export const ScriptingSyntax = () => ( + + + +

Syntax

+

+ By default, Kibana scripted fields use {( + + Painless + + )}, a simple and secure scripting language designed specifically for use with Elasticsearch, + to access values in the document use the following format: +

+

+ doc['some_field'].value +

+

+ Painless is powerful but easy to use. It provides access to many {( + + native Java APIs + + )}. Read up on its {( + + syntax + + )} and you'll be up to speed in no time! +

+

+ Kibana currently imposes one special limitation on the painless scripts you write. They cannot contain named functions. +

+

+ Coming from an older version of Kibana? The {( + + Lucene Expressions + + )} you know and love are still available. Lucene expressions are a lot like JavaScript, + but limited to basic arithmetic, bitwise and comparison operations. +

+

+ There are a few limitations when using Lucene Expressions: +

+
    +
  • Only numeric, boolean, date, and geo_point fields may be accessed
  • +
  • Stored fields are not available
  • +
  • If a field is sparse (only some documents contain a value), documents missing the field will have a value of 0
  • +
+

+ Here are all the operations available to lucene expressions: +

+
    +
  • Arithmetic operators: + - * / %
  • +
  • Bitwise operators: | & ^ ~ << >> >>>
  • +
  • Boolean operators (including the ternary operator): && || ! ?:
  • +
  • Comparison operators: < <= == >= >
  • +
  • Common mathematic functions: abs ceil exp floor ln log10 logn max min sqrt pow
  • +
  • Trigonometric library functions: acosh acos asinh asin atanh atan atan2 cosh cos sinh sin tanh tan
  • +
  • Distance functions: haversin
  • +
  • Miscellaneous functions: min, max
  • +
+
+
+); diff --git a/src/ui/public/field_editor/script_editor.js b/src/ui/public/field_editor/components/scripting_help/test_script.js similarity index 60% rename from src/ui/public/field_editor/script_editor.js rename to src/ui/public/field_editor/components/scripting_help/test_script.js index cb2faf900ff36..e05da7609fcdd 100644 --- a/src/ui/public/field_editor/script_editor.js +++ b/src/ui/public/field_editor/components/scripting_help/test_script.js @@ -16,29 +16,32 @@ * specific language governing permissions and limitations * under the License. */ -import './script_editor.less'; + +import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; -import React, { Component } from 'react'; import { kfetch } from 'ui/kfetch'; import { - EuiFlexGroup, - EuiFlexItem, EuiButton, - EuiTextArea, - EuiLink, EuiCodeBlock, - EuiCallOut, EuiComboBox, - EuiFieldNumber, + EuiFormRow, + EuiText, + EuiSpacer, + EuiTitle, + EuiCallOut, } from '@elastic/eui'; -export class ScriptEditor extends Component { - +export class TestScript extends Component { state = { isLoading: false, sourceFields: [], - size: 10, + } + + componentDidMount() { + if (this.props.script) { + this.executeScript(); + } } executeScript = async () => { @@ -46,7 +49,7 @@ export class ScriptEditor extends Component { indexPattern, lang, name, - script + script, } = this.props; this.setState({ @@ -59,6 +62,7 @@ export class ScriptEditor extends Component { ignore_unavailable: true, timeout: 30000 }; + const search = { query: { match_all: {} @@ -66,20 +70,20 @@ export class ScriptEditor extends Component { script_fields: { [name]: { script: { - lang: lang, + lang, source: script } } - } + }, + size: 10, }; - if (!isNaN(this.state.size)) { - search.size = this.state.size; - } + if (this.state.sourceFields.length > 0) { search._source = this.state.sourceFields.map(option => { return option.value; }); } + const body = `${JSON.stringify(header)}\n${JSON.stringify(search)}\n`; const esResp = await kfetch({ method: 'POST', pathname: '/elasticsearch/_msearch', body }); // unwrap _msearch response @@ -88,16 +92,18 @@ export class ScriptEditor extends Component { if (scriptResponse.status !== 200) { this.setState({ isLoading: false, - previewData: scriptResponse.error + previewData: scriptResponse }); return; } this.setState({ isLoading: false, - previewData: scriptResponse.hits.hits.map(hit => { - return Object.assign({ _id: hit._id }, hit._source, hit.fields); - }) + previewData: scriptResponse.hits.hits.map(hit => ({ + _id: hit._id, + ...hit._source, + ...hit.fields, + })), }); } @@ -107,34 +113,42 @@ export class ScriptEditor extends Component { }); } - onSizeChange = (e) => { - this.setState({ - size: parseInt(e.target.value, 10) - }); - }; - renderPreview() { - if (!this.state.previewData) { + const { previewData } = this.state; + + if (!previewData) { + return null; + } + + if (previewData.error) { return ( -

- Click the play button to preview the results of your script. -

+ + {JSON.stringify(previewData.error, null, ' ')} +
); } return ( - - {JSON.stringify(this.state.previewData, null, ' ')} - + +

First 10 results

+ + + {JSON.stringify(previewData, null, ' ')} + +
); } + renderToolbar() { const fieldsByTypeMap = new Map(); const fields = []; + this.props.indexPattern.fields .filter(field => { return !field.name.startsWith('_'); @@ -165,88 +179,56 @@ export class ScriptEditor extends Component { }); return ( - - - - - - - - + + - - + + + + Run script + + ); } render() { - const { - script, - onScriptChange, - showScriptingHelp - } = this.props; - - const isInvalid = !script || !script.trim(); - return ( -
- - - + + + +

Preview results

+

+ Run your script to preview the first 10 results. You can also select some + additional fields to include in your results to gain more context. +

+
+ {this.renderToolbar()} - - - - -
- Scripting help -
-
- - {this.renderPreview()} - -
-
+ + {this.renderPreview()} + ); } } -ScriptEditor.propTypes = { +TestScript.propTypes = { indexPattern: PropTypes.object.isRequired, lang: PropTypes.string.isRequired, name: PropTypes.string, script: PropTypes.string, - onScriptChange: PropTypes.func.isRequired, - showScriptingHelp: PropTypes.func.isRequired, }; -ScriptEditor.defaultProps = { +TestScript.defaultProps = { name: 'myScriptedField', }; diff --git a/src/ui/public/field_editor/field_editor.js b/src/ui/public/field_editor/field_editor.js index 5795791590acb..1fa2853dbaa3b 100644 --- a/src/ui/public/field_editor/field_editor.js +++ b/src/ui/public/field_editor/field_editor.js @@ -39,8 +39,6 @@ import { toastNotifications } from 'ui/notify'; -import { ScriptEditor } from './script_editor'; - import { EuiButton, EuiButtonEmpty, @@ -58,15 +56,19 @@ import { EuiSelect, EuiSpacer, EuiText, + EuiTextArea, EUI_MODAL_CONFIRM_BUTTON, } from '@elastic/eui'; import { ScriptingDisabledCallOut, ScriptingWarningCallOut, - ScriptingHelpFlyout, } from './components/scripting_call_outs'; +import { + ScriptingHelpFlyout, +} from './components/scripting_help'; + import { FieldFormatEditor } from './components/field_format_editor'; @@ -342,16 +344,34 @@ export class FieldEditor extends PureComponent { renderScript() { const { field } = this.state; + const isInvalid = !field.script || !field.script.trim(); return field.scripted ? ( - { this.onFieldChange('script', e.target.value); }} - showScriptingHelp={this.showScriptingHelp} - /> + + + + + + + + Get help with the syntax and preview the results of your script + + + + )} + isInvalid={isInvalid} + error={isInvalid ? 'Script is required' : null} + > + { this.onFieldChange('script', e.target.value); }} + isInvalid={isInvalid} + /> + ) : null; } @@ -516,6 +536,10 @@ export class FieldEditor extends PureComponent { {this.renderName()} {this.renderLanguage()} diff --git a/src/ui/public/field_editor/script_editor.less b/src/ui/public/field_editor/script_editor.less deleted file mode 100644 index 40186c04c9ce2..0000000000000 --- a/src/ui/public/field_editor/script_editor.less +++ /dev/null @@ -1,7 +0,0 @@ -.scriptEditorFormRow { - max-width: 850px; -} - -.scriptPreviewCodeBlock { - max-width: 400px; -}