mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* Enable use of KQL and autocomplete in filters agg editor (#37287) This PR updates the filters agg editor to use the full QueryBar component, enabling use of KQL and autocomplete inside the editor for this aggregation in Visualize. * remove unused translation
This commit is contained in:
parent
2fd33c9371
commit
e1a19ccd57
22 changed files with 714 additions and 549 deletions
|
@ -18,11 +18,17 @@
|
|||
*/
|
||||
|
||||
// Creates a filter corresponding to a raw Elasticsearch query DSL object
|
||||
export function buildQueryFilter(query, index) {
|
||||
return {
|
||||
export function buildQueryFilter(query, index, alias) {
|
||||
const filter = {
|
||||
query: query,
|
||||
meta: {
|
||||
index: index
|
||||
index,
|
||||
}
|
||||
};
|
||||
|
||||
if (alias) {
|
||||
filter.meta.alias = alias;
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
|
|
@ -92,3 +92,4 @@ export { ExpressionRenderer, ExpressionRendererProps, ExpressionRunner } from '.
|
|||
|
||||
/** @public types */
|
||||
export { IndexPattern, StaticIndexPattern, StaticIndexPatternField, Field } from './index_patterns';
|
||||
export { Query } from './query';
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { QueryService, QuerySetup } from './query_service';
|
||||
export { QueryService, QuerySetup, Query } from './query_service';
|
||||
|
|
|
@ -170,174 +170,170 @@ exports[`QueryBarInput Should disable autoFocus on EuiFieldText when disableAuto
|
|||
}
|
||||
}
|
||||
>
|
||||
<form
|
||||
name="queryBarForm"
|
||||
<div
|
||||
role="search"
|
||||
>
|
||||
<div
|
||||
role="search"
|
||||
className="kuiLocalSearchAssistedInput"
|
||||
>
|
||||
<div
|
||||
className="kuiLocalSearchAssistedInput"
|
||||
<EuiFieldText
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language="kuery"
|
||||
onSelectLanguage={[Function]}
|
||||
/>
|
||||
}
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={false}
|
||||
compressed={false}
|
||||
data-test-subj="queryInput"
|
||||
fullWidth={true}
|
||||
inputRef={[Function]}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
>
|
||||
<EuiFieldText
|
||||
<EuiFormControlLayout
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language="kuery"
|
||||
onSelectLanguage={[Function]}
|
||||
/>
|
||||
}
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={false}
|
||||
compressed={false}
|
||||
data-test-subj="queryInput"
|
||||
fullWidth={true}
|
||||
inputRef={[Function]}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
>
|
||||
<EuiFormControlLayout
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language="kuery"
|
||||
onSelectLanguage={[Function]}
|
||||
/>
|
||||
}
|
||||
compressed={false}
|
||||
fullWidth={true}
|
||||
isLoading={false}
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--fullWidth euiFormControlLayout--group"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--fullWidth euiFormControlLayout--group"
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
<EuiValidatableControl
|
||||
className="undefined euiFormControlLayout__child--noStyle"
|
||||
>
|
||||
<EuiValidatableControl
|
||||
className="undefined euiFormControlLayout__child--noStyle"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={false}
|
||||
className="euiFieldText euiFieldText--fullWidth euiFieldText--inGroup"
|
||||
data-test-subj="queryInput"
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
/>
|
||||
</EuiValidatableControl>
|
||||
<EuiFormControlLayoutIcons
|
||||
isLoading={false}
|
||||
<input
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={false}
|
||||
className="euiFieldText euiFieldText--fullWidth euiFieldText--inGroup"
|
||||
data-test-subj="queryInput"
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
/>
|
||||
</div>
|
||||
<QueryLanguageSwitcher
|
||||
className="euiFormControlLayout__append"
|
||||
language="kuery"
|
||||
onSelectLanguage={[Function]}
|
||||
</EuiValidatableControl>
|
||||
<EuiFormControlLayoutIcons
|
||||
isLoading={false}
|
||||
/>
|
||||
</div>
|
||||
<QueryLanguageSwitcher
|
||||
className="euiFormControlLayout__append"
|
||||
language="kuery"
|
||||
onSelectLanguage={[Function]}
|
||||
>
|
||||
<EuiPopover
|
||||
anchorPosition="downRight"
|
||||
button={
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="KQL"
|
||||
id="data.query.queryBar.kqlLanguageName"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
}
|
||||
className="eui-displayBlock"
|
||||
closePopover={[Function]}
|
||||
hasArrow={true}
|
||||
id="popover"
|
||||
isOpen={false}
|
||||
ownFocus={true}
|
||||
panelPaddingSize="m"
|
||||
withTitle={true}
|
||||
>
|
||||
<EuiPopover
|
||||
anchorPosition="downRight"
|
||||
button={
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="KQL"
|
||||
id="data.query.queryBar.kqlLanguageName"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
}
|
||||
className="eui-displayBlock"
|
||||
closePopover={[Function]}
|
||||
hasArrow={true}
|
||||
id="popover"
|
||||
isOpen={false}
|
||||
ownFocus={true}
|
||||
panelPaddingSize="m"
|
||||
withTitle={true}
|
||||
<EuiOutsideClickDetector
|
||||
isDisabled={true}
|
||||
onOutsideClick={[Function]}
|
||||
>
|
||||
<EuiOutsideClickDetector
|
||||
isDisabled={true}
|
||||
onOutsideClick={[Function]}
|
||||
<div
|
||||
className="euiPopover euiPopover--anchorDownRight euiPopover--withTitle eui-displayBlock"
|
||||
id="popover"
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
>
|
||||
<div
|
||||
className="euiPopover euiPopover--anchorDownRight euiPopover--withTitle eui-displayBlock"
|
||||
id="popover"
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
className="euiPopover__anchor"
|
||||
>
|
||||
<div
|
||||
className="euiPopover__anchor"
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
<button
|
||||
className="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--xSmall"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
className="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--xSmall"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
className="euiButtonEmpty__text"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__text"
|
||||
<FormattedMessage
|
||||
defaultMessage="KQL"
|
||||
id="data.query.queryBar.kqlLanguageName"
|
||||
values={Object {}}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="KQL"
|
||||
id="data.query.queryBar.kqlLanguageName"
|
||||
values={Object {}}
|
||||
>
|
||||
KQL
|
||||
</FormattedMessage>
|
||||
</span>
|
||||
KQL
|
||||
</FormattedMessage>
|
||||
</span>
|
||||
</button>
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
</span>
|
||||
</button>
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
</EuiOutsideClickDetector>
|
||||
</EuiPopover>
|
||||
</QueryLanguageSwitcher>
|
||||
</div>
|
||||
</EuiFormControlLayout>
|
||||
</EuiFieldText>
|
||||
</div>
|
||||
</div>
|
||||
</EuiOutsideClickDetector>
|
||||
</EuiPopover>
|
||||
</QueryLanguageSwitcher>
|
||||
</div>
|
||||
</EuiFormControlLayout>
|
||||
</EuiFieldText>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<SuggestionsComponent
|
||||
index={null}
|
||||
loadMore={[Function]}
|
||||
|
@ -520,174 +516,170 @@ exports[`QueryBarInput Should pass the query language to the language switcher 1
|
|||
}
|
||||
}
|
||||
>
|
||||
<form
|
||||
name="queryBarForm"
|
||||
<div
|
||||
role="search"
|
||||
>
|
||||
<div
|
||||
role="search"
|
||||
className="kuiLocalSearchAssistedInput"
|
||||
>
|
||||
<div
|
||||
className="kuiLocalSearchAssistedInput"
|
||||
<EuiFieldText
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language="lucene"
|
||||
onSelectLanguage={[Function]}
|
||||
/>
|
||||
}
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={true}
|
||||
compressed={false}
|
||||
data-test-subj="queryInput"
|
||||
fullWidth={true}
|
||||
inputRef={[Function]}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
>
|
||||
<EuiFieldText
|
||||
<EuiFormControlLayout
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language="lucene"
|
||||
onSelectLanguage={[Function]}
|
||||
/>
|
||||
}
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={true}
|
||||
compressed={false}
|
||||
data-test-subj="queryInput"
|
||||
fullWidth={true}
|
||||
inputRef={[Function]}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
>
|
||||
<EuiFormControlLayout
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language="lucene"
|
||||
onSelectLanguage={[Function]}
|
||||
/>
|
||||
}
|
||||
compressed={false}
|
||||
fullWidth={true}
|
||||
isLoading={false}
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--fullWidth euiFormControlLayout--group"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--fullWidth euiFormControlLayout--group"
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
<EuiValidatableControl
|
||||
className="undefined euiFormControlLayout__child--noStyle"
|
||||
>
|
||||
<EuiValidatableControl
|
||||
className="undefined euiFormControlLayout__child--noStyle"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={true}
|
||||
className="euiFieldText euiFieldText--fullWidth euiFieldText--inGroup"
|
||||
data-test-subj="queryInput"
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
/>
|
||||
</EuiValidatableControl>
|
||||
<EuiFormControlLayoutIcons
|
||||
isLoading={false}
|
||||
<input
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={true}
|
||||
className="euiFieldText euiFieldText--fullWidth euiFieldText--inGroup"
|
||||
data-test-subj="queryInput"
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
/>
|
||||
</div>
|
||||
<QueryLanguageSwitcher
|
||||
className="euiFormControlLayout__append"
|
||||
language="lucene"
|
||||
onSelectLanguage={[Function]}
|
||||
</EuiValidatableControl>
|
||||
<EuiFormControlLayoutIcons
|
||||
isLoading={false}
|
||||
/>
|
||||
</div>
|
||||
<QueryLanguageSwitcher
|
||||
className="euiFormControlLayout__append"
|
||||
language="lucene"
|
||||
onSelectLanguage={[Function]}
|
||||
>
|
||||
<EuiPopover
|
||||
anchorPosition="downRight"
|
||||
button={
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Lucene"
|
||||
id="data.query.queryBar.luceneLanguageName"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
}
|
||||
className="eui-displayBlock"
|
||||
closePopover={[Function]}
|
||||
hasArrow={true}
|
||||
id="popover"
|
||||
isOpen={false}
|
||||
ownFocus={true}
|
||||
panelPaddingSize="m"
|
||||
withTitle={true}
|
||||
>
|
||||
<EuiPopover
|
||||
anchorPosition="downRight"
|
||||
button={
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Lucene"
|
||||
id="data.query.queryBar.luceneLanguageName"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
}
|
||||
className="eui-displayBlock"
|
||||
closePopover={[Function]}
|
||||
hasArrow={true}
|
||||
id="popover"
|
||||
isOpen={false}
|
||||
ownFocus={true}
|
||||
panelPaddingSize="m"
|
||||
withTitle={true}
|
||||
<EuiOutsideClickDetector
|
||||
isDisabled={true}
|
||||
onOutsideClick={[Function]}
|
||||
>
|
||||
<EuiOutsideClickDetector
|
||||
isDisabled={true}
|
||||
onOutsideClick={[Function]}
|
||||
<div
|
||||
className="euiPopover euiPopover--anchorDownRight euiPopover--withTitle eui-displayBlock"
|
||||
id="popover"
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
>
|
||||
<div
|
||||
className="euiPopover euiPopover--anchorDownRight euiPopover--withTitle eui-displayBlock"
|
||||
id="popover"
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
className="euiPopover__anchor"
|
||||
>
|
||||
<div
|
||||
className="euiPopover__anchor"
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
<button
|
||||
className="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--xSmall"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
className="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--xSmall"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
className="euiButtonEmpty__text"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__text"
|
||||
<FormattedMessage
|
||||
defaultMessage="Lucene"
|
||||
id="data.query.queryBar.luceneLanguageName"
|
||||
values={Object {}}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Lucene"
|
||||
id="data.query.queryBar.luceneLanguageName"
|
||||
values={Object {}}
|
||||
>
|
||||
Lucene
|
||||
</FormattedMessage>
|
||||
</span>
|
||||
Lucene
|
||||
</FormattedMessage>
|
||||
</span>
|
||||
</button>
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
</span>
|
||||
</button>
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
</EuiOutsideClickDetector>
|
||||
</EuiPopover>
|
||||
</QueryLanguageSwitcher>
|
||||
</div>
|
||||
</EuiFormControlLayout>
|
||||
</EuiFieldText>
|
||||
</div>
|
||||
</div>
|
||||
</EuiOutsideClickDetector>
|
||||
</EuiPopover>
|
||||
</QueryLanguageSwitcher>
|
||||
</div>
|
||||
</EuiFormControlLayout>
|
||||
</EuiFieldText>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<SuggestionsComponent
|
||||
index={null}
|
||||
loadMore={[Function]}
|
||||
|
@ -870,174 +862,170 @@ exports[`QueryBarInput Should render the given query 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<form
|
||||
name="queryBarForm"
|
||||
<div
|
||||
role="search"
|
||||
>
|
||||
<div
|
||||
role="search"
|
||||
className="kuiLocalSearchAssistedInput"
|
||||
>
|
||||
<div
|
||||
className="kuiLocalSearchAssistedInput"
|
||||
<EuiFieldText
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language="kuery"
|
||||
onSelectLanguage={[Function]}
|
||||
/>
|
||||
}
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={true}
|
||||
compressed={false}
|
||||
data-test-subj="queryInput"
|
||||
fullWidth={true}
|
||||
inputRef={[Function]}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
>
|
||||
<EuiFieldText
|
||||
<EuiFormControlLayout
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language="kuery"
|
||||
onSelectLanguage={[Function]}
|
||||
/>
|
||||
}
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={true}
|
||||
compressed={false}
|
||||
data-test-subj="queryInput"
|
||||
fullWidth={true}
|
||||
inputRef={[Function]}
|
||||
isLoading={false}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
>
|
||||
<EuiFormControlLayout
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language="kuery"
|
||||
onSelectLanguage={[Function]}
|
||||
/>
|
||||
}
|
||||
compressed={false}
|
||||
fullWidth={true}
|
||||
isLoading={false}
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--fullWidth euiFormControlLayout--group"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--fullWidth euiFormControlLayout--group"
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
<EuiValidatableControl
|
||||
className="undefined euiFormControlLayout__child--noStyle"
|
||||
>
|
||||
<EuiValidatableControl
|
||||
className="undefined euiFormControlLayout__child--noStyle"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={true}
|
||||
className="euiFieldText euiFieldText--fullWidth euiFieldText--inGroup"
|
||||
data-test-subj="queryInput"
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
/>
|
||||
</EuiValidatableControl>
|
||||
<EuiFormControlLayoutIcons
|
||||
isLoading={false}
|
||||
<input
|
||||
aria-activedescendant=""
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-label="You are on search box of Another Screen page. Start typing to search and filter the discover"
|
||||
autoComplete="off"
|
||||
autoFocus={true}
|
||||
className="euiFieldText euiFieldText--fullWidth euiFieldText--inGroup"
|
||||
data-test-subj="queryInput"
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
placeholder="Search"
|
||||
role="textbox"
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="response:200"
|
||||
/>
|
||||
</div>
|
||||
<QueryLanguageSwitcher
|
||||
className="euiFormControlLayout__append"
|
||||
language="kuery"
|
||||
onSelectLanguage={[Function]}
|
||||
</EuiValidatableControl>
|
||||
<EuiFormControlLayoutIcons
|
||||
isLoading={false}
|
||||
/>
|
||||
</div>
|
||||
<QueryLanguageSwitcher
|
||||
className="euiFormControlLayout__append"
|
||||
language="kuery"
|
||||
onSelectLanguage={[Function]}
|
||||
>
|
||||
<EuiPopover
|
||||
anchorPosition="downRight"
|
||||
button={
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="KQL"
|
||||
id="data.query.queryBar.kqlLanguageName"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
}
|
||||
className="eui-displayBlock"
|
||||
closePopover={[Function]}
|
||||
hasArrow={true}
|
||||
id="popover"
|
||||
isOpen={false}
|
||||
ownFocus={true}
|
||||
panelPaddingSize="m"
|
||||
withTitle={true}
|
||||
>
|
||||
<EuiPopover
|
||||
anchorPosition="downRight"
|
||||
button={
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="KQL"
|
||||
id="data.query.queryBar.kqlLanguageName"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
}
|
||||
className="eui-displayBlock"
|
||||
closePopover={[Function]}
|
||||
hasArrow={true}
|
||||
id="popover"
|
||||
isOpen={false}
|
||||
ownFocus={true}
|
||||
panelPaddingSize="m"
|
||||
withTitle={true}
|
||||
<EuiOutsideClickDetector
|
||||
isDisabled={true}
|
||||
onOutsideClick={[Function]}
|
||||
>
|
||||
<EuiOutsideClickDetector
|
||||
isDisabled={true}
|
||||
onOutsideClick={[Function]}
|
||||
<div
|
||||
className="euiPopover euiPopover--anchorDownRight euiPopover--withTitle eui-displayBlock"
|
||||
id="popover"
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
>
|
||||
<div
|
||||
className="euiPopover euiPopover--anchorDownRight euiPopover--withTitle eui-displayBlock"
|
||||
id="popover"
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
className="euiPopover__anchor"
|
||||
>
|
||||
<div
|
||||
className="euiPopover__anchor"
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
color="primary"
|
||||
iconSide="left"
|
||||
<button
|
||||
className="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--xSmall"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
className="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--xSmall"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__content"
|
||||
className="euiButtonEmpty__text"
|
||||
>
|
||||
<span
|
||||
className="euiButtonEmpty__text"
|
||||
<FormattedMessage
|
||||
defaultMessage="KQL"
|
||||
id="data.query.queryBar.kqlLanguageName"
|
||||
values={Object {}}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="KQL"
|
||||
id="data.query.queryBar.kqlLanguageName"
|
||||
values={Object {}}
|
||||
>
|
||||
KQL
|
||||
</FormattedMessage>
|
||||
</span>
|
||||
KQL
|
||||
</FormattedMessage>
|
||||
</span>
|
||||
</button>
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
</span>
|
||||
</button>
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
</EuiOutsideClickDetector>
|
||||
</EuiPopover>
|
||||
</QueryLanguageSwitcher>
|
||||
</div>
|
||||
</EuiFormControlLayout>
|
||||
</EuiFieldText>
|
||||
</div>
|
||||
</div>
|
||||
</EuiOutsideClickDetector>
|
||||
</EuiPopover>
|
||||
</QueryLanguageSwitcher>
|
||||
</div>
|
||||
</EuiFormControlLayout>
|
||||
</EuiFieldText>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<SuggestionsComponent
|
||||
index={null}
|
||||
loadMore={[Function]}
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
EuiSpacer,
|
||||
EuiSwitch,
|
||||
EuiText,
|
||||
PopoverAnchorPosition,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { Component } from 'react';
|
||||
|
@ -41,6 +42,7 @@ interface State {
|
|||
interface Props {
|
||||
language: string;
|
||||
onSelectLanguage: (newLanguage: string) => void;
|
||||
anchorPosition?: PopoverAnchorPosition;
|
||||
}
|
||||
|
||||
export class QueryLanguageSwitcher extends Component<Props, State> {
|
||||
|
@ -73,7 +75,7 @@ export class QueryLanguageSwitcher extends Component<Props, State> {
|
|||
id="popover"
|
||||
className="eui-displayBlock"
|
||||
ownFocus
|
||||
anchorPosition="downRight"
|
||||
anchorPosition={this.props.anchorPosition || 'downRight'}
|
||||
button={button}
|
||||
isOpen={this.state.isPopoverOpen}
|
||||
closePopover={this.closePopover}
|
||||
|
|
|
@ -40,14 +40,10 @@ import { PersistedLog } from 'ui/persisted_log';
|
|||
import { QueryBarInput } from './query_bar_input';
|
||||
|
||||
import { getQueryLog } from '../lib/get_query_log';
|
||||
import { Query } from '../index';
|
||||
|
||||
const config = chrome.getUiSettingsClient();
|
||||
|
||||
interface Query {
|
||||
query: string;
|
||||
language: string;
|
||||
}
|
||||
|
||||
interface DateRange {
|
||||
from: string;
|
||||
to: string;
|
||||
|
@ -338,6 +334,7 @@ export class QueryBarUI extends Component<Props, State> {
|
|||
const { query, language } = this.state.query;
|
||||
if (
|
||||
language === 'kuery' &&
|
||||
typeof query === 'string' &&
|
||||
!store.get('kibana.luceneSyntaxWarningOptOut') &&
|
||||
doesKueryExpressionHaveLuceneSyntaxError(query)
|
||||
) {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import { Component } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { EuiFieldText, EuiOutsideClickDetector } from '@elastic/eui';
|
||||
import { EuiFieldText, EuiOutsideClickDetector, PopoverAnchorPosition } from '@elastic/eui';
|
||||
|
||||
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import {
|
||||
|
@ -28,34 +28,32 @@ import {
|
|||
AutocompleteSuggestionType,
|
||||
getAutocompleteProvider,
|
||||
} from 'ui/autocomplete_providers';
|
||||
import { debounce, compact, isEqual } from 'lodash';
|
||||
import { debounce, compact, isEqual, omit } from 'lodash';
|
||||
import { IndexPattern, StaticIndexPattern } from 'ui/index_patterns';
|
||||
import { PersistedLog } from 'ui/persisted_log';
|
||||
import chrome from 'ui/chrome';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { Storage } from 'ui/storage';
|
||||
import { localStorage } from 'ui/storage/storage_service';
|
||||
import { Query } from '../index';
|
||||
import { fromUser, matchPairs, toUser } from '../lib';
|
||||
import { QueryLanguageSwitcher } from './language_switcher';
|
||||
import { SuggestionsComponent } from './typeahead/suggestions_component';
|
||||
import { getQueryLog } from '../lib/get_query_log';
|
||||
import { fetchIndexPatterns } from '../lib/fetch_index_patterns';
|
||||
|
||||
interface Query {
|
||||
query: string;
|
||||
language: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
indexPatterns: Array<IndexPattern | string>;
|
||||
intl: InjectedIntl;
|
||||
query: Query;
|
||||
appName: string;
|
||||
id?: string;
|
||||
disableAutoFocus?: boolean;
|
||||
screenTitle?: string;
|
||||
prepend?: any;
|
||||
store: Storage;
|
||||
store?: Storage;
|
||||
persistedLog?: PersistedLog;
|
||||
bubbleSubmitEvent?: boolean;
|
||||
languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition;
|
||||
onChange?: (query: Query) => void;
|
||||
onSubmit?: (query: Query) => void;
|
||||
}
|
||||
|
@ -125,10 +123,10 @@ export class QueryBarInputUI extends Component<Props, State> {
|
|||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
query: { query, language },
|
||||
} = this.props;
|
||||
const recentSearchSuggestions = this.getRecentSearchSuggestions(query);
|
||||
const language = this.props.query.language;
|
||||
const queryString = this.getQueryString();
|
||||
|
||||
const recentSearchSuggestions = this.getRecentSearchSuggestions(queryString);
|
||||
|
||||
const autocompleteProvider = getAutocompleteProvider(language);
|
||||
if (
|
||||
|
@ -148,7 +146,7 @@ export class QueryBarInputUI extends Component<Props, State> {
|
|||
}
|
||||
|
||||
const suggestions: AutocompleteSuggestion[] = await getAutocompleteSuggestions({
|
||||
query,
|
||||
query: queryString,
|
||||
selectionStart,
|
||||
selectionEnd,
|
||||
});
|
||||
|
@ -258,8 +256,11 @@ export class QueryBarInputUI extends Component<Props, State> {
|
|||
}
|
||||
break;
|
||||
case KEY_CODES.ENTER:
|
||||
event.preventDefault();
|
||||
if (!this.props.bubbleSubmitEvent) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (isSuggestionsVisible && index !== null && this.state.suggestions[index]) {
|
||||
event.preventDefault();
|
||||
this.selectSuggestion(this.state.suggestions[index]);
|
||||
} else {
|
||||
this.onSubmit(this.props.query);
|
||||
|
@ -358,7 +359,11 @@ export class QueryBarInputUI extends Component<Props, State> {
|
|||
body: JSON.stringify({ opt_in: language === 'kuery' }),
|
||||
});
|
||||
|
||||
this.props.store.set('kibana.userQueryLanguage', language);
|
||||
if (this.props.store) {
|
||||
this.props.store.set('kibana.userQueryLanguage', language);
|
||||
} else {
|
||||
localStorage.set('kibana.userQueryLanguage', language);
|
||||
}
|
||||
|
||||
const newQuery = { query: '', language };
|
||||
this.onChange(newQuery);
|
||||
|
@ -421,6 +426,22 @@ export class QueryBarInputUI extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const rest = omit(this.props, [
|
||||
'indexPatterns',
|
||||
'intl',
|
||||
'query',
|
||||
'appName',
|
||||
'disableAutoFocus',
|
||||
'screenTitle',
|
||||
'prepend',
|
||||
'store',
|
||||
'persistedLog',
|
||||
'bubbleSubmitEvent',
|
||||
'languageSwitcherPopoverAnchorPosition',
|
||||
'onChange',
|
||||
'onSubmit',
|
||||
]);
|
||||
|
||||
return (
|
||||
<EuiOutsideClickDetector onOutsideClick={this.onOutsideClick}>
|
||||
<div
|
||||
|
@ -431,63 +452,62 @@ export class QueryBarInputUI extends Component<Props, State> {
|
|||
aria-owns="kbnTypeahead__items"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
>
|
||||
<form name="queryBarForm">
|
||||
<div role="search">
|
||||
<div className="kuiLocalSearchAssistedInput">
|
||||
<EuiFieldText
|
||||
id={this.props.id}
|
||||
placeholder={this.props.intl.formatMessage({
|
||||
id: 'data.query.queryBar.searchInputPlaceholder',
|
||||
defaultMessage: 'Search',
|
||||
})}
|
||||
value={this.getQueryString()}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={this.onKeyUp}
|
||||
onChange={this.onInputChange}
|
||||
onClick={this.onClickInput}
|
||||
fullWidth
|
||||
autoFocus={!this.props.disableAutoFocus}
|
||||
inputRef={node => {
|
||||
if (node) {
|
||||
this.inputRef = node;
|
||||
}
|
||||
}}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
aria-label={
|
||||
this.props.screenTitle
|
||||
? this.props.intl.formatMessage(
|
||||
{
|
||||
id: 'data.query.queryBar.searchInputAriaLabel',
|
||||
defaultMessage:
|
||||
'You are on search box of {previouslyTranslatedPageTitle} page. Start typing to search and filter the {pageType}',
|
||||
},
|
||||
{
|
||||
previouslyTranslatedPageTitle: this.props.screenTitle,
|
||||
pageType: this.props.appName,
|
||||
}
|
||||
)
|
||||
: undefined
|
||||
<div role="search">
|
||||
<div className="kuiLocalSearchAssistedInput">
|
||||
<EuiFieldText
|
||||
placeholder={this.props.intl.formatMessage({
|
||||
id: 'data.query.queryBar.searchInputPlaceholder',
|
||||
defaultMessage: 'Search',
|
||||
})}
|
||||
value={this.getQueryString()}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={this.onKeyUp}
|
||||
onChange={this.onInputChange}
|
||||
onClick={this.onClickInput}
|
||||
fullWidth
|
||||
autoFocus={!this.props.disableAutoFocus}
|
||||
inputRef={node => {
|
||||
if (node) {
|
||||
this.inputRef = node;
|
||||
}
|
||||
type="text"
|
||||
data-test-subj="queryInput"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-activedescendant={
|
||||
this.state.isSuggestionsVisible ? 'suggestion-' + this.state.index : ''
|
||||
}
|
||||
role="textbox"
|
||||
prepend={this.props.prepend}
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language={this.props.query.language}
|
||||
onSelectLanguage={this.onSelectLanguage}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
aria-label={
|
||||
this.props.screenTitle
|
||||
? this.props.intl.formatMessage(
|
||||
{
|
||||
id: 'data.query.queryBar.searchInputAriaLabel',
|
||||
defaultMessage:
|
||||
'You are on search box of {previouslyTranslatedPageTitle} page. Start typing to search and filter the {pageType}',
|
||||
},
|
||||
{
|
||||
previouslyTranslatedPageTitle: this.props.screenTitle,
|
||||
pageType: this.props.appName,
|
||||
}
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
type="text"
|
||||
data-test-subj="queryInput"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="kbnTypeahead__items"
|
||||
aria-activedescendant={
|
||||
this.state.isSuggestionsVisible ? 'suggestion-' + this.state.index : ''
|
||||
}
|
||||
role="textbox"
|
||||
prepend={this.props.prepend}
|
||||
append={
|
||||
<QueryLanguageSwitcher
|
||||
language={this.props.query.language}
|
||||
anchorPosition={this.props.languageSwitcherPopoverAnchorPosition}
|
||||
onSelectLanguage={this.onSelectLanguage}
|
||||
/>
|
||||
}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<SuggestionsComponent
|
||||
show={this.state.isSuggestionsVisible}
|
||||
|
|
|
@ -20,6 +20,12 @@
|
|||
export { QueryBar, QueryBarInput } from './components';
|
||||
export { fromUser } from './lib/from_user';
|
||||
export { toUser } from './lib/to_user';
|
||||
export { getQueryLog } from './lib/get_query_log';
|
||||
|
||||
// @ts-ignore
|
||||
export { setupDirective } from './directive';
|
||||
|
||||
export interface Query {
|
||||
query: string | { [key: string]: any };
|
||||
language: string;
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@ import angular from 'angular';
|
|||
|
||||
/**
|
||||
* Take text from the model and present it to the user as a string
|
||||
* @param {text} model value
|
||||
* @param text model value
|
||||
* @returns {string}
|
||||
*/
|
||||
export function toUser(text: ToUserQuery | string): string {
|
||||
export function toUser(text: { [key: string]: any } | string): string {
|
||||
if (text == null) {
|
||||
return '';
|
||||
}
|
||||
|
@ -39,12 +39,3 @@ export function toUser(text: ToUserQuery | string): string {
|
|||
}
|
||||
return '' + text;
|
||||
}
|
||||
|
||||
interface ToUserQuery {
|
||||
match_all: object;
|
||||
query_string: ToUserQueryString;
|
||||
}
|
||||
|
||||
interface ToUserQueryString {
|
||||
query: string;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
QueryBarInput,
|
||||
fromUser,
|
||||
toUser,
|
||||
getQueryLog,
|
||||
setupDirective as setupQueryBarDirective,
|
||||
} from './query_bar';
|
||||
|
||||
|
@ -38,6 +39,7 @@ export class QueryService {
|
|||
helpers: {
|
||||
fromUser,
|
||||
toUser,
|
||||
getQueryLog,
|
||||
},
|
||||
ui: {
|
||||
QueryBar,
|
||||
|
@ -53,3 +55,5 @@ export class QueryService {
|
|||
|
||||
/** @public */
|
||||
export type QuerySetup = ReturnType<QueryService['setup']>;
|
||||
|
||||
export { Query } from './query_bar';
|
||||
|
|
|
@ -27,14 +27,9 @@ import ResizeObserver from 'resize-observer-polyfill';
|
|||
import { IndexPattern } from 'ui/index_patterns';
|
||||
import { Storage } from 'ui/storage';
|
||||
|
||||
import { QueryBar } from '../../../query/query_bar';
|
||||
import { Query, QueryBar } from '../../../query/query_bar';
|
||||
import { FilterBar } from '../../../filter/filter_bar';
|
||||
|
||||
interface Query {
|
||||
query: string;
|
||||
language: string;
|
||||
}
|
||||
|
||||
interface DateRange {
|
||||
from: string;
|
||||
to: string;
|
||||
|
@ -45,10 +40,7 @@ interface DateRange {
|
|||
* See [search_bar\directive\index.js] file
|
||||
*/
|
||||
interface Props {
|
||||
query: {
|
||||
query: string;
|
||||
language: string;
|
||||
};
|
||||
query: Query;
|
||||
onQuerySubmit: (payload: { dateRange: DateRange; query: Query }) => void;
|
||||
disableAutoFocus?: boolean;
|
||||
appName: string;
|
||||
|
|
|
@ -278,8 +278,40 @@ function transformFilterStringToQueryObject(doc) {
|
|||
}
|
||||
return newDoc;
|
||||
}
|
||||
|
||||
function migrateFiltersAggQuery(doc) {
|
||||
const visStateJSON = get(doc, 'attributes.visState');
|
||||
|
||||
if (visStateJSON) {
|
||||
try {
|
||||
const visState = JSON.parse(visStateJSON);
|
||||
if (visState && visState.aggs) {
|
||||
visState.aggs.forEach((agg) => {
|
||||
if (agg.type !== 'filters') return;
|
||||
|
||||
agg.params.filters.forEach((filter) => {
|
||||
if (filter.input.language) return filter;
|
||||
filter.input.language = 'lucene';
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
...doc,
|
||||
attributes: {
|
||||
...doc.attributes,
|
||||
visState: JSON.stringify(visState),
|
||||
},
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
// Let it go, the data is invalid and we'll leave it as is
|
||||
}
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
const executeMigrations720 = flow(migratePercentileRankAggregation, migrateDateHistogramAggregation);
|
||||
const executeMigrations730 = flow(migrateGaugeVerticalSplitToAlignment, transformFilterStringToQueryObject);
|
||||
const executeMigrations730 = flow(migrateGaugeVerticalSplitToAlignment, transformFilterStringToQueryObject, migrateFiltersAggQuery);
|
||||
|
||||
export const migrations = {
|
||||
'index-pattern': {
|
||||
|
@ -383,7 +415,7 @@ export const migrations = {
|
|||
},
|
||||
'7.0.1': removeDateHistogramTimeZones,
|
||||
'7.2.0': doc => executeMigrations720(doc),
|
||||
'7.3.0': executeMigrations730
|
||||
'7.3.0': executeMigrations730,
|
||||
},
|
||||
dashboard: {
|
||||
'7.0.0': (doc) => {
|
||||
|
|
|
@ -890,6 +890,90 @@ Object {
|
|||
}
|
||||
`);
|
||||
});
|
||||
|
||||
describe('filters agg query migration', () => {
|
||||
const doc = {
|
||||
attributes: {
|
||||
visState: JSON.stringify({
|
||||
aggs: [
|
||||
{
|
||||
type: 'filters',
|
||||
params: {
|
||||
filters: [
|
||||
{
|
||||
input: {
|
||||
query: 'response:200',
|
||||
},
|
||||
label: '',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
query: 'response:404',
|
||||
},
|
||||
label: 'bad response',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
query: {
|
||||
exists: {
|
||||
field: 'phpmemory',
|
||||
},
|
||||
},
|
||||
},
|
||||
label: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
it('should add language property to filters without one, assuming lucene', () => {
|
||||
const migrationResult = migrate(doc);
|
||||
expect(migrationResult).toEqual({
|
||||
attributes: {
|
||||
visState: JSON.stringify({
|
||||
aggs: [
|
||||
{
|
||||
type: 'filters',
|
||||
params: {
|
||||
filters: [
|
||||
{
|
||||
input: {
|
||||
query: 'response:200',
|
||||
language: 'lucene',
|
||||
},
|
||||
label: '',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
query: 'response:404',
|
||||
language: 'lucene',
|
||||
},
|
||||
label: 'bad response',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
query: {
|
||||
exists: {
|
||||
field: 'phpmemory',
|
||||
},
|
||||
},
|
||||
language: 'lucene',
|
||||
},
|
||||
label: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('7.3.0 tsvb', () => {
|
||||
const migrate = doc => migrations.visualization['7.3.0'](doc);
|
||||
|
|
|
@ -43,8 +43,8 @@ describe('AggConfig Filters', function () {
|
|||
schema: 'segment',
|
||||
params: {
|
||||
filters: [
|
||||
{ input: { query: { query_string: { query: 'type:apache' } } } },
|
||||
{ input: { query: { query_string: { query: 'type:nginx' } } } }
|
||||
{ input: { query: 'type:apache', language: 'lucene' } },
|
||||
{ input: { query: 'type:nginx', language: 'lucene' } }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -53,9 +53,9 @@ describe('AggConfig Filters', function () {
|
|||
|
||||
const aggConfig = vis.aggs.byTypeName.filters[0];
|
||||
const filter = createFilterFilters(aggConfig, 'type:nginx');
|
||||
expect(filter.query.query_string.query).to.be('type:nginx');
|
||||
expect(filter.query.bool.must[0].query_string.query).to.be('type:nginx');
|
||||
expect(filter.meta).to.have.property('index', indexPattern.id);
|
||||
|
||||
expect(filter.meta).to.have.property('alias', 'type:nginx');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,6 +26,6 @@ export function createFilterFilters(aggConfig, key) {
|
|||
const filter = dslFilters[key];
|
||||
|
||||
if (filter) {
|
||||
return buildQueryFilter(filter.query, aggConfig.getIndexPattern().id);
|
||||
return buildQueryFilter(filter.query, aggConfig.getIndexPattern().id, key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,15 @@ import angular from 'angular';
|
|||
|
||||
import { BucketAggType } from './_bucket_agg_type';
|
||||
import { createFilterFilters } from './create_filter/filters';
|
||||
import { decorateQuery, luceneStringToDsl } from '@kbn/es-query';
|
||||
import { FiltersParamEditor } from '../controls/filters';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { buildEsQuery } from '@kbn/es-query';
|
||||
import { data } from 'plugins/data';
|
||||
|
||||
const { getQueryLog } = data.query.helpers;
|
||||
const config = chrome.getUiSettingsClient();
|
||||
|
||||
export const filtersBucketAgg = new BucketAggType({
|
||||
name: 'filters',
|
||||
|
@ -40,32 +44,36 @@ export const filtersBucketAgg = new BucketAggType({
|
|||
{
|
||||
name: 'filters',
|
||||
editorComponent: FiltersParamEditor,
|
||||
default: [ { input: {}, label: '' } ],
|
||||
default: [ { input: { query: '', language: config.get('search:queryLanguage') }, label: '' } ],
|
||||
write: function (aggConfig, output) {
|
||||
const inFilters = aggConfig.params.filters;
|
||||
if (!_.size(inFilters)) return;
|
||||
|
||||
const outFilters = _.transform(inFilters, function (filters, filter) {
|
||||
const input = _.cloneDeep(filter.input);
|
||||
inFilters.forEach((filter) => {
|
||||
const persistedLog = getQueryLog('filtersAgg', filter.input.language);
|
||||
persistedLog.add(filter.input.query);
|
||||
});
|
||||
|
||||
if (!input) {
|
||||
const outFilters = _.transform(inFilters, function (filters, filter) {
|
||||
let input = _.cloneDeep(filter.input);
|
||||
|
||||
if (!input || !input.query) {
|
||||
console.log('malformed filter agg params, missing "input" query'); // eslint-disable-line no-console
|
||||
return;
|
||||
}
|
||||
|
||||
const query = input.query = luceneStringToDsl(input.query);
|
||||
const query = input = buildEsQuery(aggConfig.getIndexPattern(), [input], [], config);
|
||||
|
||||
if (!query) {
|
||||
console.log('malformed filter agg params, missing "query" on input'); // eslint-disable-line no-console
|
||||
return;
|
||||
}
|
||||
const config = chrome.getUiSettingsClient();
|
||||
const queryStringOptions = config.get('query:queryString:options');
|
||||
|
||||
decorateQuery(query, queryStringOptions);
|
||||
|
||||
const matchAllLabel = (filter.input.query === '' && _.has(query, 'match_all')) ? '*' : '';
|
||||
const label = filter.label || matchAllLabel || _.get(query, 'query_string.query') || angular.toJson(query);
|
||||
filters[label] = input;
|
||||
const matchAllLabel = filter.input.query === '' ? '*' : '';
|
||||
const label = filter.label
|
||||
|| matchAllLabel
|
||||
|| (typeof filter.input.query === 'string' ? filter.input.query : angular.toJson(filter.input.query));
|
||||
filters[label] = { query: input };
|
||||
}, {});
|
||||
|
||||
if (!_.size(outFilters)) return;
|
||||
|
|
|
@ -27,17 +27,22 @@ import {
|
|||
EuiFormRow,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { AggConfig } from 'ui/vis';
|
||||
import { Query, data } from 'plugins/data';
|
||||
|
||||
const { QueryBarInput } = data.query.ui;
|
||||
|
||||
interface FilterRowProps {
|
||||
id: string;
|
||||
arrayIndex: number;
|
||||
customLabel: string;
|
||||
value: string;
|
||||
value: Query;
|
||||
autoFocus: boolean;
|
||||
disableRemove: boolean;
|
||||
dataTestSubj: string;
|
||||
onChangeValue(id: string, query: string, label: string): void;
|
||||
onChangeValue(id: string, query: Query, label: string): void;
|
||||
onRemoveFilter(id: string): void;
|
||||
agg: AggConfig;
|
||||
}
|
||||
|
||||
function FilterRow({
|
||||
|
@ -48,6 +53,7 @@ function FilterRow({
|
|||
autoFocus,
|
||||
disableRemove,
|
||||
dataTestSubj,
|
||||
agg,
|
||||
onChangeValue,
|
||||
onRemoveFilter,
|
||||
}: FilterRowProps) {
|
||||
|
@ -94,15 +100,15 @@ function FilterRow({
|
|||
fullWidth={true}
|
||||
className="visEditorSidebar__aggParamFormRow"
|
||||
>
|
||||
<EuiFieldText
|
||||
value={value}
|
||||
placeholder={i18n.translate('common.ui.aggTypes.filters.filterPlaceholder', {
|
||||
defaultMessage: 'Lucene or Query DSL',
|
||||
})}
|
||||
<QueryBarInput
|
||||
query={value}
|
||||
indexPatterns={[agg.getIndexPattern()]}
|
||||
appName="filtersAgg"
|
||||
onChange={query => onChangeValue(id, query, customLabel)}
|
||||
disableAutoFocus={!autoFocus}
|
||||
data-test-subj={dataTestSubj}
|
||||
onChange={ev => onChangeValue(id, ev.target.value, customLabel)}
|
||||
fullWidth={true}
|
||||
autoFocus={autoFocus}
|
||||
bubbleSubmitEvent={true}
|
||||
languageSwitcherPopoverAnchorPosition="leftDown"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{showCustomLabel ? (
|
||||
|
|
|
@ -22,14 +22,15 @@ import { omit, isEqual } from 'lodash';
|
|||
import { htmlIdGenerator, EuiButton, EuiSpacer } from '@elastic/eui';
|
||||
import { AggParamEditorProps } from 'ui/vis/editors/default';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { data } from 'plugins/data';
|
||||
import chrome from 'ui/chrome';
|
||||
import { Query } from 'plugins/data';
|
||||
import { FilterRow } from './filter';
|
||||
|
||||
const { toUser, fromUser } = data.query.helpers;
|
||||
const generateId = htmlIdGenerator();
|
||||
const config = chrome.getUiSettingsClient();
|
||||
|
||||
interface FilterValue {
|
||||
input: any;
|
||||
input: Query;
|
||||
label: string;
|
||||
id: string;
|
||||
}
|
||||
|
@ -41,11 +42,7 @@ function FiltersParamEditor({ agg, value, setValue }: AggParamEditorProps<Filter
|
|||
|
||||
useEffect(() => {
|
||||
// set parsed values into model after initialization
|
||||
setValue(
|
||||
filters.map(filter =>
|
||||
omit({ ...filter, input: { query: fromUser(filter.input.query) } }, 'id')
|
||||
)
|
||||
);
|
||||
setValue(filters.map(filter => omit({ ...filter, input: filter.input }, 'id')));
|
||||
}, []);
|
||||
|
||||
useEffect(
|
||||
|
@ -68,15 +65,22 @@ function FiltersParamEditor({ agg, value, setValue }: AggParamEditorProps<Filter
|
|||
};
|
||||
|
||||
const onAddFilter = () =>
|
||||
updateFilters([...filters, { input: { query: '' }, label: '', id: generateId() }]);
|
||||
updateFilters([
|
||||
...filters,
|
||||
{
|
||||
input: { query: '', language: config.get('search:queryLanguage') },
|
||||
label: '',
|
||||
id: generateId(),
|
||||
},
|
||||
]);
|
||||
const onRemoveFilter = (id: string) => updateFilters(filters.filter(filter => filter.id !== id));
|
||||
const onChangeValue = (id: string, query: string, label: string) =>
|
||||
const onChangeValue = (id: string, query: Query, label: string) =>
|
||||
updateFilters(
|
||||
filters.map(filter =>
|
||||
filter.id === id
|
||||
? {
|
||||
...filter,
|
||||
input: { query: fromUser(query) },
|
||||
input: query,
|
||||
label,
|
||||
}
|
||||
: filter
|
||||
|
@ -91,10 +95,11 @@ function FiltersParamEditor({ agg, value, setValue }: AggParamEditorProps<Filter
|
|||
id={id}
|
||||
arrayIndex={arrayIndex}
|
||||
customLabel={label}
|
||||
value={toUser(input.query)}
|
||||
value={input}
|
||||
autoFocus={arrayIndex === filters.length - 1}
|
||||
disableRemove={arrayIndex === 0 && filters.length === 1}
|
||||
dataTestSubj={`visEditorFilterInput_${agg.id}_${arrayIndex}`}
|
||||
agg={agg}
|
||||
onChangeValue={onChangeValue}
|
||||
onRemoveFilter={onRemoveFilter}
|
||||
/>
|
||||
|
|
23
src/legacy/ui/public/storage/storage_service.ts
Normal file
23
src/legacy/ui/public/storage/storage_service.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 { Storage } from './storage';
|
||||
|
||||
export const localStorage = new Storage(window.localStorage);
|
||||
export const sessionStorage = new Storage(window.sessionStorage);
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import { has } from 'lodash';
|
||||
import { Query } from 'plugins/data';
|
||||
|
||||
/**
|
||||
* Creates a standardized query object from old queries that were either strings or pure ES query DSL
|
||||
|
@ -25,11 +26,12 @@ import { has } from 'lodash';
|
|||
* @param query - a legacy query, what used to be stored in SearchSource's query property
|
||||
* @return Object
|
||||
*/
|
||||
export function migrateLegacyQuery(query: object): object {
|
||||
|
||||
export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query {
|
||||
// Lucene was the only option before, so language-less queries are all lucene
|
||||
if (!has(query, 'language')) {
|
||||
return { query, language: 'lucene' };
|
||||
}
|
||||
|
||||
return query;
|
||||
return query as Query;
|
||||
}
|
||||
|
|
|
@ -140,7 +140,6 @@
|
|||
"common.ui.aggTypes.filters.addFilterButtonLabel": "フィルターを追加",
|
||||
"common.ui.aggTypes.filters.definiteFilterLabel": "{index} ラベルでフィルタリング",
|
||||
"common.ui.aggTypes.filters.filterLabel": "{index} でフィルタリング",
|
||||
"common.ui.aggTypes.filters.filterPlaceholder": "Lucene またはクエリ DSL",
|
||||
"common.ui.aggTypes.filters.labelPlaceholder": "ラベル",
|
||||
"common.ui.aggTypes.filters.removeFilterButtonAriaLabel": "このフィルターを削除",
|
||||
"common.ui.aggTypes.filters.toggleFilterButtonAriaLabel": "フィルターラベルを切り替える",
|
||||
|
@ -10001,4 +10000,4 @@
|
|||
"xpack.watcher.watchActionsTitle": "条件が満たされた際に {watchActionsCount, plural, one{# アクション} other {# アクション}} を実行します",
|
||||
"xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -139,7 +139,6 @@
|
|||
"common.ui.aggTypes.filters.addFilterButtonLabel": "添加筛选",
|
||||
"common.ui.aggTypes.filters.definiteFilterLabel": "筛选 {index} 标签",
|
||||
"common.ui.aggTypes.filters.filterLabel": "筛选 {index}",
|
||||
"common.ui.aggTypes.filters.filterPlaceholder": "Lucene 或查询 DSL",
|
||||
"common.ui.aggTypes.filters.labelPlaceholder": "标签",
|
||||
"common.ui.aggTypes.filters.removeFilterButtonAriaLabel": "移除此筛选",
|
||||
"common.ui.aggTypes.filters.toggleFilterButtonAriaLabel": "切换筛选标签",
|
||||
|
@ -10011,4 +10010,4 @@
|
|||
"xpack.watcher.watchActionsTitle": "满足后将执行 {watchActionsCount, plural, one{# 个操作} other {# 个操作}}",
|
||||
"xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue