[data.search.searchSource] Update SearchSource to use Fields API. (#82383)
This commit is contained in:
parent
e83bbfd289
commit
7393c230a4
|
@ -21,7 +21,8 @@ getFields(): {
|
|||
size?: number | undefined;
|
||||
source?: string | boolean | string[] | undefined;
|
||||
version?: boolean | undefined;
|
||||
fields?: string | boolean | string[] | undefined;
|
||||
fields?: SearchFieldValue[] | undefined;
|
||||
fieldsFromSource?: string | boolean | string[] | undefined;
|
||||
index?: import("../..").IndexPattern | undefined;
|
||||
searchAfter?: import("./types").EsQuerySearchAfter | undefined;
|
||||
timeout?: string | undefined;
|
||||
|
@ -42,7 +43,8 @@ getFields(): {
|
|||
size?: number | undefined;
|
||||
source?: string | boolean | string[] | undefined;
|
||||
version?: boolean | undefined;
|
||||
fields?: string | boolean | string[] | undefined;
|
||||
fields?: SearchFieldValue[] | undefined;
|
||||
fieldsFromSource?: string | boolean | string[] | undefined;
|
||||
index?: import("../..").IndexPattern | undefined;
|
||||
searchAfter?: import("./types").EsQuerySearchAfter | undefined;
|
||||
timeout?: string | undefined;
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
|
||||
## SearchSourceFields.fields property
|
||||
|
||||
Retrieve fields via the search Fields API
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
fields?: NameList;
|
||||
fields?: SearchFieldValue[];
|
||||
```
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) > [fieldsFromSource](./kibana-plugin-plugins-data-public.searchsourcefields.fieldsfromsource.md)
|
||||
|
||||
## SearchSourceFields.fieldsFromSource property
|
||||
|
||||
> Warning: This API is now obsolete.
|
||||
>
|
||||
> It is recommended to use `fields` wherever possible.
|
||||
>
|
||||
|
||||
Retreive fields directly from \_source (legacy behavior)
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
fieldsFromSource?: NameList;
|
||||
```
|
|
@ -17,7 +17,8 @@ export interface SearchSourceFields
|
|||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [aggs](./kibana-plugin-plugins-data-public.searchsourcefields.aggs.md) | <code>any</code> | [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) |
|
||||
| [fields](./kibana-plugin-plugins-data-public.searchsourcefields.fields.md) | <code>NameList</code> | |
|
||||
| [fields](./kibana-plugin-plugins-data-public.searchsourcefields.fields.md) | <code>SearchFieldValue[]</code> | Retrieve fields via the search Fields API |
|
||||
| [fieldsFromSource](./kibana-plugin-plugins-data-public.searchsourcefields.fieldsfromsource.md) | <code>NameList</code> | Retreive fields directly from \_source (legacy behavior) |
|
||||
| [filter](./kibana-plugin-plugins-data-public.searchsourcefields.filter.md) | <code>Filter[] | Filter | (() => Filter[] | Filter | undefined)</code> | [Filter](./kibana-plugin-plugins-data-public.filter.md) |
|
||||
| [from](./kibana-plugin-plugins-data-public.searchsourcefields.from.md) | <code>number</code> | |
|
||||
| [highlight](./kibana-plugin-plugins-data-public.searchsourcefields.highlight.md) | <code>any</code> | |
|
||||
|
|
|
@ -23,7 +23,8 @@ import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
|
|||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCodeBlock,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
|
@ -32,6 +33,7 @@ import {
|
|||
EuiTitle,
|
||||
EuiText,
|
||||
EuiFlexGrid,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiCheckbox,
|
||||
EuiSpacer,
|
||||
|
@ -68,6 +70,11 @@ interface SearchExamplesAppDeps {
|
|||
data: DataPublicPluginStart;
|
||||
}
|
||||
|
||||
function getNumeric(fields?: IndexPatternField[]) {
|
||||
if (!fields) return [];
|
||||
return fields?.filter((f) => f.type === 'number' && f.aggregatable);
|
||||
}
|
||||
|
||||
function formatFieldToComboBox(field?: IndexPatternField | null) {
|
||||
if (!field) return [];
|
||||
return formatFieldsToComboBox([field]);
|
||||
|
@ -95,8 +102,13 @@ export const SearchExamplesApp = ({
|
|||
const [getCool, setGetCool] = useState<boolean>(false);
|
||||
const [timeTook, setTimeTook] = useState<number | undefined>();
|
||||
const [indexPattern, setIndexPattern] = useState<IndexPattern | null>();
|
||||
const [numericFields, setNumericFields] = useState<IndexPatternField[]>();
|
||||
const [selectedField, setSelectedField] = useState<IndexPatternField | null | undefined>();
|
||||
const [fields, setFields] = useState<IndexPatternField[]>();
|
||||
const [selectedFields, setSelectedFields] = useState<IndexPatternField[]>([]);
|
||||
const [selectedNumericField, setSelectedNumericField] = useState<
|
||||
IndexPatternField | null | undefined
|
||||
>();
|
||||
const [request, setRequest] = useState<Record<string, any>>({});
|
||||
const [response, setResponse] = useState<Record<string, any>>({});
|
||||
|
||||
// Fetch the default index pattern using the `data.indexPatterns` service, as the component is mounted.
|
||||
useEffect(() => {
|
||||
|
@ -110,24 +122,23 @@ export const SearchExamplesApp = ({
|
|||
|
||||
// Update the fields list every time the index pattern is modified.
|
||||
useEffect(() => {
|
||||
const fields = indexPattern?.fields.filter(
|
||||
(field) => field.type === 'number' && field.aggregatable
|
||||
);
|
||||
setNumericFields(fields);
|
||||
setSelectedField(fields?.length ? fields[0] : null);
|
||||
setFields(indexPattern?.fields);
|
||||
}, [indexPattern]);
|
||||
useEffect(() => {
|
||||
setSelectedNumericField(fields?.length ? getNumeric(fields)[0] : null);
|
||||
}, [fields]);
|
||||
|
||||
const doAsyncSearch = async (strategy?: string) => {
|
||||
if (!indexPattern || !selectedField) return;
|
||||
if (!indexPattern || !selectedNumericField) return;
|
||||
|
||||
// Constuct the query portion of the search request
|
||||
const query = data.query.getEsQuery(indexPattern);
|
||||
|
||||
// Constuct the aggregations portion of the search request by using the `data.search.aggs` service.
|
||||
const aggs = [{ type: 'avg', params: { field: selectedField.name } }];
|
||||
const aggs = [{ type: 'avg', params: { field: selectedNumericField!.name } }];
|
||||
const aggsDsl = data.search.aggs.createAggConfigs(indexPattern, aggs).toDsl();
|
||||
|
||||
const request = {
|
||||
const req = {
|
||||
params: {
|
||||
index: indexPattern.title,
|
||||
body: {
|
||||
|
@ -140,23 +151,26 @@ export const SearchExamplesApp = ({
|
|||
};
|
||||
|
||||
// Submit the search request using the `data.search` service.
|
||||
setRequest(req.params.body);
|
||||
const searchSubscription$ = data.search
|
||||
.search(request, {
|
||||
.search(req, {
|
||||
strategy,
|
||||
})
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
if (isCompleteResponse(response)) {
|
||||
setTimeTook(response.rawResponse.took);
|
||||
const avgResult: number | undefined = response.rawResponse.aggregations
|
||||
? response.rawResponse.aggregations[1].value
|
||||
next: (res) => {
|
||||
if (isCompleteResponse(res)) {
|
||||
setResponse(res.rawResponse);
|
||||
setTimeTook(res.rawResponse.took);
|
||||
const avgResult: number | undefined = res.rawResponse.aggregations
|
||||
? res.rawResponse.aggregations[1].value
|
||||
: undefined;
|
||||
const message = (
|
||||
<EuiText>
|
||||
Searched {response.rawResponse.hits.total} documents. <br />
|
||||
The average of {selectedField.name} is {avgResult ? Math.floor(avgResult) : 0}.
|
||||
Searched {res.rawResponse.hits.total} documents. <br />
|
||||
The average of {selectedNumericField!.name} is{' '}
|
||||
{avgResult ? Math.floor(avgResult) : 0}.
|
||||
<br />
|
||||
Is it Cool? {String((response as IMyStrategyResponse).cool)}
|
||||
Is it Cool? {String((res as IMyStrategyResponse).cool)}
|
||||
</EuiText>
|
||||
);
|
||||
notifications.toasts.addSuccess({
|
||||
|
@ -164,7 +178,7 @@ export const SearchExamplesApp = ({
|
|||
text: mountReactNode(message),
|
||||
});
|
||||
searchSubscription$.unsubscribe();
|
||||
} else if (isErrorResponse(response)) {
|
||||
} else if (isErrorResponse(res)) {
|
||||
// TODO: Make response error status clearer
|
||||
notifications.toasts.addWarning('An error has occurred');
|
||||
searchSubscription$.unsubscribe();
|
||||
|
@ -176,6 +190,50 @@ export const SearchExamplesApp = ({
|
|||
});
|
||||
};
|
||||
|
||||
const doSearchSourceSearch = async () => {
|
||||
if (!indexPattern) return;
|
||||
|
||||
const query = data.query.queryString.getQuery();
|
||||
const filters = data.query.filterManager.getFilters();
|
||||
const timefilter = data.query.timefilter.timefilter.createFilter(indexPattern);
|
||||
if (timefilter) {
|
||||
filters.push(timefilter);
|
||||
}
|
||||
|
||||
try {
|
||||
const searchSource = await data.search.searchSource.create();
|
||||
|
||||
searchSource
|
||||
.setField('index', indexPattern)
|
||||
.setField('filter', filters)
|
||||
.setField('query', query)
|
||||
.setField('fields', selectedFields.length ? selectedFields.map((f) => f.name) : ['*']);
|
||||
|
||||
if (selectedNumericField) {
|
||||
searchSource.setField('aggs', () => {
|
||||
return data.search.aggs
|
||||
.createAggConfigs(indexPattern, [
|
||||
{ type: 'avg', params: { field: selectedNumericField.name } },
|
||||
])
|
||||
.toDsl();
|
||||
});
|
||||
}
|
||||
|
||||
setRequest(await searchSource.getSearchRequestBody());
|
||||
const res = await searchSource.fetch();
|
||||
setResponse(res);
|
||||
|
||||
const message = <EuiText>Searched {res.hits.total} documents.</EuiText>;
|
||||
notifications.toasts.addSuccess({
|
||||
title: 'Query result',
|
||||
text: mountReactNode(message),
|
||||
});
|
||||
} catch (e) {
|
||||
setResponse(e.body);
|
||||
notifications.toasts.addWarning(`An error has occurred: ${e.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
const onClickHandler = () => {
|
||||
doAsyncSearch();
|
||||
};
|
||||
|
@ -185,22 +243,24 @@ export const SearchExamplesApp = ({
|
|||
};
|
||||
|
||||
const onServerClickHandler = async () => {
|
||||
if (!indexPattern || !selectedField) return;
|
||||
if (!indexPattern || !selectedNumericField) return;
|
||||
try {
|
||||
const response = await http.get(SERVER_SEARCH_ROUTE_PATH, {
|
||||
const res = await http.get(SERVER_SEARCH_ROUTE_PATH, {
|
||||
query: {
|
||||
index: indexPattern.title,
|
||||
field: selectedField.name,
|
||||
field: selectedNumericField!.name,
|
||||
},
|
||||
});
|
||||
|
||||
notifications.toasts.addSuccess(`Server returned ${JSON.stringify(response)}`);
|
||||
notifications.toasts.addSuccess(`Server returned ${JSON.stringify(res)}`);
|
||||
} catch (e) {
|
||||
notifications.toasts.addDanger('Failed to run search');
|
||||
}
|
||||
};
|
||||
|
||||
if (!indexPattern) return null;
|
||||
const onSearchSourceClickHandler = () => {
|
||||
doSearchSourceSearch();
|
||||
};
|
||||
|
||||
return (
|
||||
<Router basename={basename}>
|
||||
|
@ -212,7 +272,7 @@ export const SearchExamplesApp = ({
|
|||
useDefaultBehaviors={true}
|
||||
indexPatterns={indexPattern ? [indexPattern] : undefined}
|
||||
/>
|
||||
<EuiPage restrictWidth="1000px">
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageHeader>
|
||||
<EuiTitle size="l">
|
||||
|
@ -227,106 +287,178 @@ export const SearchExamplesApp = ({
|
|||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentBody>
|
||||
<EuiText>
|
||||
<EuiFlexGrid columns={1}>
|
||||
<EuiFlexItem>
|
||||
<EuiFormLabel>Index Pattern</EuiFormLabel>
|
||||
<IndexPatternSelect
|
||||
placeholder={i18n.translate(
|
||||
'backgroundSessionExample.selectIndexPatternPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Select index pattern',
|
||||
}
|
||||
)}
|
||||
indexPatternId={indexPattern?.id || ''}
|
||||
onChange={async (newIndexPatternId: any) => {
|
||||
const newIndexPattern = await data.indexPatterns.get(newIndexPatternId);
|
||||
setIndexPattern(newIndexPattern);
|
||||
}}
|
||||
isClearable={false}
|
||||
<EuiFlexGrid columns={3}>
|
||||
<EuiFlexItem style={{ width: '40%' }}>
|
||||
<EuiText>
|
||||
<EuiFlexGrid columns={2}>
|
||||
<EuiFlexItem>
|
||||
<EuiFormLabel>Index Pattern</EuiFormLabel>
|
||||
<IndexPatternSelect
|
||||
placeholder={i18n.translate(
|
||||
'backgroundSessionExample.selectIndexPatternPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Select index pattern',
|
||||
}
|
||||
)}
|
||||
indexPatternId={indexPattern?.id || ''}
|
||||
onChange={async (newIndexPatternId: any) => {
|
||||
const newIndexPattern = await data.indexPatterns.get(
|
||||
newIndexPatternId
|
||||
);
|
||||
setIndexPattern(newIndexPattern);
|
||||
}}
|
||||
isClearable={false}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFormLabel>Numeric Field to Aggregate</EuiFormLabel>
|
||||
<EuiComboBox
|
||||
options={formatFieldsToComboBox(getNumeric(fields))}
|
||||
selectedOptions={formatFieldToComboBox(selectedNumericField)}
|
||||
singleSelection={true}
|
||||
onChange={(option) => {
|
||||
const fld = indexPattern?.getFieldByName(option[0].label);
|
||||
setSelectedNumericField(fld || null);
|
||||
}}
|
||||
sortMatchesBy="startsWith"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGrid>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiFormLabel>
|
||||
Fields to query (leave blank to include all fields)
|
||||
</EuiFormLabel>
|
||||
<EuiComboBox
|
||||
options={formatFieldsToComboBox(fields)}
|
||||
selectedOptions={formatFieldsToComboBox(selectedFields)}
|
||||
singleSelection={false}
|
||||
onChange={(option) => {
|
||||
const flds = option
|
||||
.map((opt) => indexPattern?.getFieldByName(opt?.label))
|
||||
.filter((f) => f);
|
||||
setSelectedFields(flds.length ? (flds as IndexPatternField[]) : []);
|
||||
}}
|
||||
sortMatchesBy="startsWith"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
Searching Elasticsearch using <EuiCode>data.search</EuiCode>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
If you want to fetch data from Elasticsearch, you can use the different
|
||||
services provided by the <EuiCode>data</EuiCode> plugin. These help you get
|
||||
the index pattern and search bar configuration, format them into a DSL query
|
||||
and send it to Elasticsearch.
|
||||
<EuiSpacer />
|
||||
<EuiButtonEmpty size="xs" onClick={onClickHandler} iconType="play">
|
||||
<FormattedMessage
|
||||
id="searchExamples.buttonText"
|
||||
defaultMessage="Request from low-level client (data.search.search)"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
onClick={onSearchSourceClickHandler}
|
||||
iconType="play"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="searchExamples.searchSource.buttonText"
|
||||
defaultMessage="Request from high-level client (data.search.searchSource)"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
<EuiTitle size="s">
|
||||
<h3>Writing a custom search strategy</h3>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
If you want to do some pre or post processing on the server, you might want
|
||||
to create a custom search strategy. This example uses such a strategy,
|
||||
passing in custom input and receiving custom output back.
|
||||
<EuiSpacer />
|
||||
<EuiCheckbox
|
||||
id="GetCool"
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="searchExamples.getCoolCheckbox"
|
||||
defaultMessage="Get cool parameter?"
|
||||
/>
|
||||
}
|
||||
checked={getCool}
|
||||
onChange={(event) => setGetCool(event.target.checked)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFormLabel>Numeric Fields</EuiFormLabel>
|
||||
<EuiComboBox
|
||||
options={formatFieldsToComboBox(numericFields)}
|
||||
selectedOptions={formatFieldToComboBox(selectedField)}
|
||||
singleSelection={true}
|
||||
onChange={(option) => {
|
||||
const field = indexPattern.getFieldByName(option[0].label);
|
||||
setSelectedField(field || null);
|
||||
}}
|
||||
sortMatchesBy="startsWith"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGrid>
|
||||
</EuiText>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="searchExamples.timestampText"
|
||||
defaultMessage="Last query took: {time} ms"
|
||||
values={{ time: timeTook || 'Unknown' }}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
Searching Elasticsearch using <EuiCode>data.search</EuiCode>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
If you want to fetch data from Elasticsearch, you can use the different services
|
||||
provided by the <EuiCode>data</EuiCode> plugin. These help you get the index
|
||||
pattern and search bar configuration, format them into a DSL query and send it
|
||||
to Elasticsearch.
|
||||
<EuiSpacer />
|
||||
<EuiButton type="primary" size="s" onClick={onClickHandler}>
|
||||
<FormattedMessage id="searchExamples.buttonText" defaultMessage="Get data" />
|
||||
</EuiButton>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
<EuiTitle size="s">
|
||||
<h3>Writing a custom search strategy</h3>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
If you want to do some pre or post processing on the server, you might want to
|
||||
create a custom search strategy. This example uses such a strategy, passing in
|
||||
custom input and receiving custom output back.
|
||||
<EuiSpacer />
|
||||
<EuiCheckbox
|
||||
id="GetCool"
|
||||
label={
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
onClick={onMyStrategyClickHandler}
|
||||
iconType="play"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="searchExamples.myStrategyButtonText"
|
||||
defaultMessage="Request from low-level client via My Strategy"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
<EuiTitle size="s">
|
||||
<h3>Using search on the server</h3>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
You can also run your search request from the server, without registering a
|
||||
search strategy. This request does not take the configuration of{' '}
|
||||
<EuiCode>TopNavMenu</EuiCode> into account, but you could pass those down to
|
||||
the server as well.
|
||||
<EuiSpacer />
|
||||
<EuiButtonEmpty size="xs" onClick={onServerClickHandler} iconType="play">
|
||||
<FormattedMessage
|
||||
id="searchExamples.myServerButtonText"
|
||||
defaultMessage="Request from low-level client on the server"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem style={{ width: '30%' }}>
|
||||
<EuiTitle size="xs">
|
||||
<h4>Request</h4>
|
||||
</EuiTitle>
|
||||
<EuiText size="xs">Search body sent to ES</EuiText>
|
||||
<EuiCodeBlock
|
||||
language="json"
|
||||
fontSize="s"
|
||||
paddingSize="s"
|
||||
overflowHeight={450}
|
||||
isCopyable
|
||||
>
|
||||
{JSON.stringify(request, null, 2)}
|
||||
</EuiCodeBlock>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem style={{ width: '30%' }}>
|
||||
<EuiTitle size="xs">
|
||||
<h4>Response</h4>
|
||||
</EuiTitle>
|
||||
<EuiText size="xs">
|
||||
<FormattedMessage
|
||||
id="searchExamples.getCoolCheckbox"
|
||||
defaultMessage="Get cool parameter?"
|
||||
id="searchExamples.timestampText"
|
||||
defaultMessage="Took: {time} ms"
|
||||
values={{ time: timeTook || 'Unknown' }}
|
||||
/>
|
||||
}
|
||||
checked={getCool}
|
||||
onChange={(event) => setGetCool(event.target.checked)}
|
||||
/>
|
||||
<EuiButton type="primary" size="s" onClick={onMyStrategyClickHandler}>
|
||||
<FormattedMessage
|
||||
id="searchExamples.myStrategyButtonText"
|
||||
defaultMessage="Get data via My Strategy"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
<EuiTitle size="s">
|
||||
<h3>Using search on the server</h3>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
You can also run your search request from the server, without registering a
|
||||
search strategy. This request does not take the configuration of{' '}
|
||||
<EuiCode>TopNavMenu</EuiCode> into account, but you could pass those down to the
|
||||
server as well.
|
||||
<EuiButton type="primary" size="s" onClick={onServerClickHandler}>
|
||||
<FormattedMessage
|
||||
id="searchExamples.myServerButtonText"
|
||||
defaultMessage="Get data on the server"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiText>
|
||||
</EuiText>
|
||||
<EuiCodeBlock
|
||||
language="json"
|
||||
fontSize="s"
|
||||
paddingSize="s"
|
||||
overflowHeight={450}
|
||||
isCopyable
|
||||
>
|
||||
{JSON.stringify(response, null, 2)}
|
||||
</EuiCodeBlock>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGrid>
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { filterDocvalueFields } from './filter_docvalue_fields';
|
||||
|
||||
test('Should exclude docvalue_fields that are not contained in fields', () => {
|
||||
const docvalueFields = [
|
||||
'my_ip_field',
|
||||
{ field: 'my_keyword_field' },
|
||||
{ field: 'my_date_field', format: 'epoch_millis' },
|
||||
];
|
||||
const out = filterDocvalueFields(docvalueFields, ['my_ip_field', 'my_keyword_field']);
|
||||
expect(out).toEqual(['my_ip_field', { field: 'my_keyword_field' }]);
|
||||
});
|
|
@ -1,33 +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.
|
||||
*/
|
||||
|
||||
interface DocvalueField {
|
||||
field: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export function filterDocvalueFields(
|
||||
docvalueFields: Array<string | DocvalueField>,
|
||||
fields: string[]
|
||||
) {
|
||||
return docvalueFields.filter((docValue) => {
|
||||
const docvalueFieldName = typeof docValue === 'string' ? docValue : docValue.field;
|
||||
return fields.includes(docvalueFieldName);
|
||||
});
|
||||
}
|
|
@ -29,7 +29,7 @@ jest.mock('./legacy', () => ({
|
|||
|
||||
const getComputedFields = () => ({
|
||||
storedFields: [],
|
||||
scriptFields: [],
|
||||
scriptFields: {},
|
||||
docvalueFields: [],
|
||||
});
|
||||
|
||||
|
@ -51,6 +51,7 @@ const indexPattern2 = ({
|
|||
describe('SearchSource', () => {
|
||||
let mockSearchMethod: any;
|
||||
let searchSourceDependencies: SearchSourceDependencies;
|
||||
let searchSource: SearchSource;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSearchMethod = jest.fn().mockReturnValue(of({ rawResponse: '' }));
|
||||
|
@ -64,19 +65,12 @@ describe('SearchSource', () => {
|
|||
loadingCount$: new BehaviorSubject(0),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('#setField()', () => {
|
||||
test('sets the value for the property', () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
searchSource.setField('aggs', 5);
|
||||
expect(searchSource.getField('aggs')).toBe(5);
|
||||
});
|
||||
searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
});
|
||||
|
||||
describe('#getField()', () => {
|
||||
test('gets the value for the property', () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
searchSource.setField('aggs', 5);
|
||||
expect(searchSource.getField('aggs')).toBe(5);
|
||||
});
|
||||
|
@ -84,52 +78,391 @@ describe('SearchSource', () => {
|
|||
|
||||
describe('#removeField()', () => {
|
||||
test('remove property', () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
searchSource.setField('aggs', 5);
|
||||
searchSource.removeField('aggs');
|
||||
expect(searchSource.getField('aggs')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe(`#setField('index')`, () => {
|
||||
describe('auto-sourceFiltering', () => {
|
||||
describe('new index pattern assigned', () => {
|
||||
test('generates a searchSource filter', async () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
expect(searchSource.getField('index')).toBe(undefined);
|
||||
expect(searchSource.getField('source')).toBe(undefined);
|
||||
searchSource.setField('index', indexPattern);
|
||||
expect(searchSource.getField('index')).toBe(indexPattern);
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toBe(mockSource);
|
||||
});
|
||||
describe('#setField() / #flatten', () => {
|
||||
test('sets the value for the property', () => {
|
||||
searchSource.setField('aggs', 5);
|
||||
expect(searchSource.getField('aggs')).toBe(5);
|
||||
});
|
||||
|
||||
test('removes created searchSource filter on removal', async () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
searchSource.setField('index', indexPattern);
|
||||
searchSource.setField('index', undefined);
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toBe(undefined);
|
||||
describe('computed fields handling', () => {
|
||||
test('still provides computed fields when no fields are specified', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: ['hello'],
|
||||
scriptFields: { world: {} },
|
||||
docvalueFields: ['@timestamp'],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request.stored_fields).toEqual(['hello']);
|
||||
expect(request.script_fields).toEqual({ world: {} });
|
||||
expect(request.fields).toEqual(['@timestamp']);
|
||||
});
|
||||
|
||||
test('never includes docvalue_fields', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: {},
|
||||
docvalueFields: ['@timestamp'],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fields', ['@timestamp']);
|
||||
searchSource.setField('fieldsFromSource', ['foo']);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request).not.toHaveProperty('docvalue_fields');
|
||||
});
|
||||
|
||||
test('overrides computed docvalue fields with ones that are provided', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: {},
|
||||
docvalueFields: ['hello'],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
// @ts-expect-error TS won't like using this field name, but technically it's possible.
|
||||
searchSource.setField('docvalue_fields', ['world']);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request).toHaveProperty('docvalue_fields');
|
||||
expect(request.docvalue_fields).toEqual(['world']);
|
||||
});
|
||||
|
||||
test('allows explicitly provided docvalue fields to override fields API when fetching fieldsFromSource', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: {},
|
||||
docvalueFields: [{ field: 'a', format: 'date_time' }],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
// @ts-expect-error TS won't like using this field name, but technically it's possible.
|
||||
searchSource.setField('docvalue_fields', [{ field: 'b', format: 'date_time' }]);
|
||||
searchSource.setField('fields', ['c']);
|
||||
searchSource.setField('fieldsFromSource', ['a', 'b', 'd']);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request).toHaveProperty('docvalue_fields');
|
||||
expect(request._source.includes).toEqual(['c', 'a', 'b', 'd']);
|
||||
expect(request.docvalue_fields).toEqual([{ field: 'b', format: 'date_time' }]);
|
||||
expect(request.fields).toEqual(['c', { field: 'a', format: 'date_time' }]);
|
||||
});
|
||||
|
||||
test('allows you to override computed fields if you provide a format', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: {},
|
||||
docvalueFields: [{ field: 'hello', format: 'date_time' }],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fields', [{ field: 'hello', format: 'strict_date_time' }]);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request).toHaveProperty('fields');
|
||||
expect(request.fields).toEqual([{ field: 'hello', format: 'strict_date_time' }]);
|
||||
});
|
||||
|
||||
test('injects a date format for computed docvalue fields if none is provided', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: {},
|
||||
docvalueFields: [{ field: 'hello', format: 'date_time' }],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fields', ['hello']);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request).toHaveProperty('fields');
|
||||
expect(request.fields).toEqual([{ field: 'hello', format: 'date_time' }]);
|
||||
});
|
||||
|
||||
test('injects a date format for computed docvalue fields while merging other properties', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: {},
|
||||
docvalueFields: [{ field: 'hello', format: 'date_time', a: 'test', b: 'test' }],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fields', [{ field: 'hello', a: 'a', c: 'c' }]);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request).toHaveProperty('fields');
|
||||
expect(request.fields).toEqual([
|
||||
{ field: 'hello', format: 'date_time', a: 'a', b: 'test', c: 'c' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('merges provided script fields with computed fields', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: { hello: {} },
|
||||
docvalueFields: [],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
// @ts-expect-error TS won't like using this field name, but technically it's possible.
|
||||
searchSource.setField('script_fields', { world: {} });
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request).toHaveProperty('script_fields');
|
||||
expect(request.script_fields).toEqual({
|
||||
hello: {},
|
||||
world: {},
|
||||
});
|
||||
});
|
||||
|
||||
describe('new index pattern assigned over another', () => {
|
||||
test('replaces searchSource filter with new', async () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
searchSource.setField('index', indexPattern);
|
||||
searchSource.setField('index', indexPattern2);
|
||||
expect(searchSource.getField('index')).toBe(indexPattern2);
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toBe(mockSource2);
|
||||
test(`requests any fields that aren't script_fields from stored_fields`, async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: { hello: {} },
|
||||
docvalueFields: [],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fields', ['hello', 'a', { field: 'c' }]);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request.script_fields).toEqual({ hello: {} });
|
||||
expect(request.stored_fields).toEqual(['a', 'c']);
|
||||
});
|
||||
|
||||
test('ignores objects without a `field` property when setting stored_fields', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: { hello: {} },
|
||||
docvalueFields: [],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fields', ['hello', 'a', { foo: 'c' }]);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request.script_fields).toEqual({ hello: {} });
|
||||
expect(request.stored_fields).toEqual(['a']);
|
||||
});
|
||||
|
||||
test(`requests any fields that aren't script_fields from stored_fields with fieldsFromSource`, async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: { hello: {} },
|
||||
docvalueFields: [],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fieldsFromSource', ['hello', 'a']);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request.script_fields).toEqual({ hello: {} });
|
||||
expect(request.stored_fields).toEqual(['a']);
|
||||
});
|
||||
|
||||
test('defaults to * for stored fields when no fields are provided', async () => {
|
||||
const requestA = await searchSource.getSearchRequestBody();
|
||||
expect(requestA.stored_fields).toEqual(['*']);
|
||||
|
||||
searchSource.setField('fields', ['*']);
|
||||
const requestB = await searchSource.getSearchRequestBody();
|
||||
expect(requestB.stored_fields).toEqual(['*']);
|
||||
});
|
||||
|
||||
test('defaults to * for stored fields when no fields are provided with fieldsFromSource', async () => {
|
||||
searchSource.setField('fieldsFromSource', ['*']);
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request.stored_fields).toEqual(['*']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('source filters handling', () => {
|
||||
test('excludes docvalue fields based on source filtering', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: {},
|
||||
docvalueFields: ['@timestamp', 'exclude-me'],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
// @ts-expect-error Typings for excludes filters need to be fixed.
|
||||
searchSource.setField('source', { excludes: ['exclude-*'] });
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request.fields).toEqual(['@timestamp']);
|
||||
});
|
||||
|
||||
test('defaults to source filters from index pattern', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: {},
|
||||
docvalueFields: ['@timestamp', 'foo-bar', 'foo-baz'],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request.fields).toEqual(['@timestamp']);
|
||||
});
|
||||
|
||||
test('filters script fields to only include specified fields', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: { hello: {}, world: {} },
|
||||
docvalueFields: [],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fields', ['hello']);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request.script_fields).toEqual({ hello: {} });
|
||||
});
|
||||
});
|
||||
|
||||
describe('handling for when specific fields are provided', () => {
|
||||
test('fieldsFromSource will request any fields outside of script_fields from _source & stored fields', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: [],
|
||||
scriptFields: { hello: {}, world: {} },
|
||||
docvalueFields: ['@timestamp'],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fieldsFromSource', [
|
||||
'hello',
|
||||
'world',
|
||||
'@timestamp',
|
||||
'foo-a',
|
||||
'bar-b',
|
||||
]);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toEqual({
|
||||
includes: ['@timestamp', 'bar-b'],
|
||||
});
|
||||
expect(request.stored_fields).toEqual(['@timestamp', 'bar-b']);
|
||||
});
|
||||
|
||||
test('filters request when a specific list of fields is provided', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: ['*'],
|
||||
scriptFields: { hello: {}, world: {} },
|
||||
docvalueFields: ['@timestamp', 'date'],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fields', ['hello', '@timestamp', 'foo-a', 'bar']);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request.fields).toEqual(['hello', '@timestamp', 'bar']);
|
||||
expect(request.script_fields).toEqual({ hello: {} });
|
||||
expect(request.stored_fields).toEqual(['@timestamp', 'bar']);
|
||||
});
|
||||
|
||||
test('filters request when a specific list of fields is provided with fieldsFromSource', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: ['*'],
|
||||
scriptFields: { hello: {}, world: {} },
|
||||
docvalueFields: ['@timestamp', 'date'],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fieldsFromSource', ['hello', '@timestamp', 'foo-a', 'bar']);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toEqual({
|
||||
includes: ['@timestamp', 'bar'],
|
||||
});
|
||||
expect(request.fields).toEqual(['@timestamp']);
|
||||
expect(request.script_fields).toEqual({ hello: {} });
|
||||
expect(request.stored_fields).toEqual(['@timestamp', 'bar']);
|
||||
});
|
||||
|
||||
test('filters request when a specific list of fields is provided with fieldsFromSource or fields', async () => {
|
||||
searchSource.setField('index', ({
|
||||
...indexPattern,
|
||||
getComputedFields: () => ({
|
||||
storedFields: ['*'],
|
||||
scriptFields: { hello: {}, world: {} },
|
||||
docvalueFields: ['@timestamp', 'date', 'time'],
|
||||
}),
|
||||
} as unknown) as IndexPattern);
|
||||
searchSource.setField('fields', ['hello', '@timestamp', 'foo-a', 'bar']);
|
||||
searchSource.setField('fieldsFromSource', ['foo-b', 'date', 'baz']);
|
||||
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toEqual({
|
||||
includes: ['@timestamp', 'bar', 'date', 'baz'],
|
||||
});
|
||||
expect(request.fields).toEqual(['hello', '@timestamp', 'bar', 'date']);
|
||||
expect(request.script_fields).toEqual({ hello: {} });
|
||||
expect(request.stored_fields).toEqual(['@timestamp', 'bar', 'date', 'baz']);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`#setField('index')`, () => {
|
||||
describe('auto-sourceFiltering', () => {
|
||||
describe('new index pattern assigned', () => {
|
||||
test('generates a searchSource filter', async () => {
|
||||
expect(searchSource.getField('index')).toBe(undefined);
|
||||
expect(searchSource.getField('source')).toBe(undefined);
|
||||
searchSource.setField('index', indexPattern);
|
||||
expect(searchSource.getField('index')).toBe(indexPattern);
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toBe(mockSource);
|
||||
});
|
||||
|
||||
test('removes created searchSource filter on removal', async () => {
|
||||
searchSource.setField('index', indexPattern);
|
||||
searchSource.setField('index', undefined);
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test('removes created searchSource filter on removal', async () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
searchSource.setField('index', indexPattern);
|
||||
searchSource.setField('index', indexPattern2);
|
||||
searchSource.setField('index', undefined);
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toBe(undefined);
|
||||
describe('new index pattern assigned over another', () => {
|
||||
test('replaces searchSource filter with new', async () => {
|
||||
searchSource.setField('index', indexPattern);
|
||||
searchSource.setField('index', indexPattern2);
|
||||
expect(searchSource.getField('index')).toBe(indexPattern2);
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toBe(mockSource2);
|
||||
});
|
||||
|
||||
test('removes created searchSource filter on removal', async () => {
|
||||
searchSource.setField('index', indexPattern);
|
||||
searchSource.setField('index', indexPattern2);
|
||||
searchSource.setField('index', undefined);
|
||||
const request = await searchSource.getSearchRequestBody();
|
||||
expect(request._source).toBe(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -137,7 +470,7 @@ describe('SearchSource', () => {
|
|||
|
||||
describe('#onRequestStart()', () => {
|
||||
test('should be called when starting a request', async () => {
|
||||
const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
|
||||
searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
|
||||
const fn = jest.fn();
|
||||
searchSource.onRequestStart(fn);
|
||||
const options = {};
|
||||
|
@ -147,7 +480,7 @@ describe('SearchSource', () => {
|
|||
|
||||
test('should not be called on parent searchSource', async () => {
|
||||
const parent = new SearchSource({}, searchSourceDependencies);
|
||||
const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
|
||||
searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
|
||||
|
||||
const fn = jest.fn();
|
||||
searchSource.onRequestStart(fn);
|
||||
|
@ -162,12 +495,12 @@ describe('SearchSource', () => {
|
|||
|
||||
test('should be called on parent searchSource if callParentStartHandlers is true', async () => {
|
||||
const parent = new SearchSource({}, searchSourceDependencies);
|
||||
const searchSource = new SearchSource(
|
||||
{ index: indexPattern },
|
||||
searchSourceDependencies
|
||||
).setParent(parent, {
|
||||
callParentStartHandlers: true,
|
||||
});
|
||||
searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies).setParent(
|
||||
parent,
|
||||
{
|
||||
callParentStartHandlers: true,
|
||||
}
|
||||
);
|
||||
|
||||
const fn = jest.fn();
|
||||
searchSource.onRequestStart(fn);
|
||||
|
@ -192,7 +525,7 @@ describe('SearchSource', () => {
|
|||
});
|
||||
|
||||
test('should call msearch', async () => {
|
||||
const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
|
||||
searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
|
||||
const options = {};
|
||||
await searchSource.fetch(options);
|
||||
expect(fetchSoon).toBeCalledTimes(1);
|
||||
|
@ -201,7 +534,7 @@ describe('SearchSource', () => {
|
|||
|
||||
describe('#search service fetch()', () => {
|
||||
test('should call msearch', async () => {
|
||||
const searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
|
||||
searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies);
|
||||
const options = {};
|
||||
|
||||
await searchSource.fetch(options);
|
||||
|
@ -212,7 +545,6 @@ describe('SearchSource', () => {
|
|||
describe('#serialize', () => {
|
||||
test('should reference index patterns', () => {
|
||||
const indexPattern123 = { id: '123' } as IndexPattern;
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
searchSource.setField('index', indexPattern123);
|
||||
const { searchSourceJSON, references } = searchSource.serialize();
|
||||
expect(references[0].id).toEqual('123');
|
||||
|
@ -221,7 +553,6 @@ describe('SearchSource', () => {
|
|||
});
|
||||
|
||||
test('should add other fields', () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
searchSource.setField('highlightAll', true);
|
||||
searchSource.setField('from', 123456);
|
||||
const { searchSourceJSON } = searchSource.serialize();
|
||||
|
@ -230,7 +561,6 @@ describe('SearchSource', () => {
|
|||
});
|
||||
|
||||
test('should omit sort and size', () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
searchSource.setField('highlightAll', true);
|
||||
searchSource.setField('from', 123456);
|
||||
searchSource.setField('sort', { field: SortDirection.asc });
|
||||
|
@ -240,7 +570,6 @@ describe('SearchSource', () => {
|
|||
});
|
||||
|
||||
test('should serialize filters', () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
const filter = [
|
||||
{
|
||||
query: 'query',
|
||||
|
@ -257,7 +586,6 @@ describe('SearchSource', () => {
|
|||
});
|
||||
|
||||
test('should reference index patterns in filters separately from index field', () => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
const indexPattern123 = { id: '123' } as IndexPattern;
|
||||
searchSource.setField('index', indexPattern123);
|
||||
const filter = [
|
||||
|
|
|
@ -70,14 +70,18 @@
|
|||
*/
|
||||
|
||||
import { setWith } from '@elastic/safer-lodash-set';
|
||||
import { uniqueId, uniq, extend, pick, difference, omit, isObject, keys, isFunction } from 'lodash';
|
||||
import { uniqueId, keyBy, pick, difference, omit, isObject, isFunction } from 'lodash';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { normalizeSortRequest } from './normalize_sort_request';
|
||||
import { filterDocvalueFields } from './filter_docvalue_fields';
|
||||
import { fieldWildcardFilter } from '../../../../kibana_utils/common';
|
||||
import { IIndexPattern } from '../../index_patterns';
|
||||
import { ISearchGeneric, ISearchOptions } from '../..';
|
||||
import type { ISearchSource, SearchSourceOptions, SearchSourceFields } from './types';
|
||||
import type {
|
||||
ISearchSource,
|
||||
SearchFieldValue,
|
||||
SearchSourceOptions,
|
||||
SearchSourceFields,
|
||||
} from './types';
|
||||
import { FetchHandlers, RequestFailure, getSearchParamsFromRequest, SearchRequest } from './fetch';
|
||||
|
||||
import { getEsQueryConfig, buildEsQuery, Filter, UI_SETTINGS } from '../../../common';
|
||||
|
@ -404,7 +408,11 @@ export class SearchSource {
|
|||
case 'query':
|
||||
return addToRoot(key, (data[key] || []).concat(val));
|
||||
case 'fields':
|
||||
const fields = uniq((data[key] || []).concat(val));
|
||||
// uses new Fields API
|
||||
return addToBody('fields', val);
|
||||
case 'fieldsFromSource':
|
||||
// preserves legacy behavior
|
||||
const fields = [...new Set((data[key] || []).concat(val))];
|
||||
return addToRoot(key, fields);
|
||||
case 'index':
|
||||
case 'type':
|
||||
|
@ -451,49 +459,127 @@ export class SearchSource {
|
|||
}
|
||||
|
||||
private flatten() {
|
||||
const { getConfig } = this.dependencies;
|
||||
const searchRequest = this.mergeProps();
|
||||
|
||||
searchRequest.body = searchRequest.body || {};
|
||||
const { body, index, fields, query, filters, highlightAll } = searchRequest;
|
||||
const { body, index, query, filters, highlightAll } = searchRequest;
|
||||
searchRequest.indexType = this.getIndexType(index);
|
||||
|
||||
const computedFields = index ? index.getComputedFields() : {};
|
||||
// get some special field types from the index pattern
|
||||
const { docvalueFields, scriptFields, storedFields } = index
|
||||
? index.getComputedFields()
|
||||
: {
|
||||
docvalueFields: [],
|
||||
scriptFields: {},
|
||||
storedFields: ['*'],
|
||||
};
|
||||
|
||||
body.stored_fields = computedFields.storedFields;
|
||||
body.script_fields = body.script_fields || {};
|
||||
extend(body.script_fields, computedFields.scriptFields);
|
||||
const fieldListProvided = !!body.fields;
|
||||
const getFieldName = (fld: string | Record<string, any>): string =>
|
||||
typeof fld === 'string' ? fld : fld.field;
|
||||
|
||||
const defaultDocValueFields = computedFields.docvalueFields
|
||||
? computedFields.docvalueFields
|
||||
: [];
|
||||
body.docvalue_fields = body.docvalue_fields || defaultDocValueFields;
|
||||
// set defaults
|
||||
let fieldsFromSource = searchRequest.fieldsFromSource || [];
|
||||
body.fields = body.fields || [];
|
||||
body.script_fields = {
|
||||
...body.script_fields,
|
||||
...scriptFields,
|
||||
};
|
||||
body.stored_fields = storedFields;
|
||||
|
||||
if (!body.hasOwnProperty('_source') && index) {
|
||||
body._source = index.getSourceFiltering();
|
||||
// apply source filters from index pattern if specified by the user
|
||||
let filteredDocvalueFields = docvalueFields;
|
||||
if (index) {
|
||||
const sourceFilters = index.getSourceFiltering();
|
||||
if (!body.hasOwnProperty('_source')) {
|
||||
body._source = sourceFilters;
|
||||
}
|
||||
if (body._source.excludes) {
|
||||
const filter = fieldWildcardFilter(
|
||||
body._source.excludes,
|
||||
getConfig(UI_SETTINGS.META_FIELDS)
|
||||
);
|
||||
// also apply filters to provided fields & default docvalueFields
|
||||
body.fields = body.fields.filter((fld: SearchFieldValue) => filter(getFieldName(fld)));
|
||||
fieldsFromSource = fieldsFromSource.filter((fld: SearchFieldValue) =>
|
||||
filter(getFieldName(fld))
|
||||
);
|
||||
filteredDocvalueFields = filteredDocvalueFields.filter((fld: SearchFieldValue) =>
|
||||
filter(getFieldName(fld))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const { getConfig } = this.dependencies;
|
||||
|
||||
if (body._source) {
|
||||
// exclude source fields for this index pattern specified by the user
|
||||
const filter = fieldWildcardFilter(body._source.excludes, getConfig(UI_SETTINGS.META_FIELDS));
|
||||
body.docvalue_fields = body.docvalue_fields.filter((docvalueField: any) =>
|
||||
filter(docvalueField.field)
|
||||
// specific fields were provided, so we need to exclude any others
|
||||
if (fieldListProvided || fieldsFromSource.length) {
|
||||
const bodyFieldNames = body.fields.map((field: string | Record<string, any>) =>
|
||||
getFieldName(field)
|
||||
);
|
||||
}
|
||||
const uniqFieldNames = [...new Set([...bodyFieldNames, ...fieldsFromSource])];
|
||||
|
||||
// if we only want to search for certain fields
|
||||
if (fields) {
|
||||
// filter out the docvalue_fields, and script_fields to only include those that we are concerned with
|
||||
body.docvalue_fields = filterDocvalueFields(body.docvalue_fields, fields);
|
||||
body.script_fields = pick(body.script_fields, fields);
|
||||
|
||||
// request the remaining fields from both stored_fields and _source
|
||||
const remainingFields = difference(fields, keys(body.script_fields));
|
||||
body.stored_fields = remainingFields;
|
||||
setWith(body, '_source.includes', remainingFields, (nsValue) =>
|
||||
isObject(nsValue) ? {} : nsValue
|
||||
// filter down script_fields to only include items specified
|
||||
body.script_fields = pick(
|
||||
body.script_fields,
|
||||
Object.keys(body.script_fields).filter((f) => uniqFieldNames.includes(f))
|
||||
);
|
||||
|
||||
// request the remaining fields from stored_fields just in case, since the
|
||||
// fields API does not handle stored fields
|
||||
const remainingFields = difference(uniqFieldNames, Object.keys(body.script_fields)).filter(
|
||||
Boolean
|
||||
);
|
||||
|
||||
// only include unique values
|
||||
body.stored_fields = [...new Set(remainingFields)];
|
||||
|
||||
if (fieldsFromSource.length) {
|
||||
// include remaining fields in _source
|
||||
setWith(body, '_source.includes', remainingFields, (nsValue) =>
|
||||
isObject(nsValue) ? {} : nsValue
|
||||
);
|
||||
|
||||
// if items that are in the docvalueFields are provided, we should
|
||||
// make sure those are added to the fields API unless they are
|
||||
// already set in docvalue_fields
|
||||
body.fields = [
|
||||
...body.fields,
|
||||
...filteredDocvalueFields.filter((fld: SearchFieldValue) => {
|
||||
return (
|
||||
fieldsFromSource.includes(getFieldName(fld)) &&
|
||||
!(body.docvalue_fields || [])
|
||||
.map((d: string | Record<string, any>) => getFieldName(d))
|
||||
.includes(getFieldName(fld))
|
||||
);
|
||||
}),
|
||||
];
|
||||
|
||||
// delete fields array if it is still set to the empty default
|
||||
if (!fieldListProvided && body.fields.length === 0) delete body.fields;
|
||||
} else {
|
||||
// remove _source, since everything's coming from fields API, scripted, or stored fields
|
||||
body._source = false;
|
||||
|
||||
// if items that are in the docvalueFields are provided, we should
|
||||
// inject the format from the computed fields if one isn't given
|
||||
const docvaluesIndex = keyBy(filteredDocvalueFields, 'field');
|
||||
body.fields = body.fields.map((fld: SearchFieldValue) => {
|
||||
const fieldName = getFieldName(fld);
|
||||
if (Object.keys(docvaluesIndex).includes(fieldName)) {
|
||||
// either provide the field object from computed docvalues,
|
||||
// or merge the user-provided field with the one in docvalues
|
||||
return typeof fld === 'string'
|
||||
? docvaluesIndex[fld]
|
||||
: {
|
||||
...docvaluesIndex[fieldName],
|
||||
...fld,
|
||||
};
|
||||
}
|
||||
return fld;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
body.fields = filteredDocvalueFields;
|
||||
}
|
||||
|
||||
const esQueryConfigs = getEsQueryConfig({ get: getConfig });
|
||||
|
|
|
@ -59,6 +59,13 @@ export interface SortDirectionNumeric {
|
|||
|
||||
export type EsQuerySortValue = Record<string, SortDirection | SortDirectionNumeric>;
|
||||
|
||||
interface SearchField {
|
||||
[key: string]: SearchFieldValue;
|
||||
}
|
||||
|
||||
// @internal
|
||||
export type SearchFieldValue = string | SearchField;
|
||||
|
||||
/**
|
||||
* search source fields
|
||||
*/
|
||||
|
@ -86,7 +93,16 @@ export interface SearchSourceFields {
|
|||
size?: number;
|
||||
source?: NameList;
|
||||
version?: boolean;
|
||||
fields?: NameList;
|
||||
/**
|
||||
* Retrieve fields via the search Fields API
|
||||
*/
|
||||
fields?: SearchFieldValue[];
|
||||
/**
|
||||
* Retreive fields directly from _source (legacy behavior)
|
||||
*
|
||||
* @deprecated It is recommended to use `fields` wherever possible.
|
||||
*/
|
||||
fieldsFromSource?: NameList;
|
||||
/**
|
||||
* {@link IndexPatternService}
|
||||
*/
|
||||
|
|
|
@ -2171,7 +2171,8 @@ export class SearchSource {
|
|||
size?: number | undefined;
|
||||
source?: string | boolean | string[] | undefined;
|
||||
version?: boolean | undefined;
|
||||
fields?: string | boolean | string[] | undefined;
|
||||
fields?: SearchFieldValue[] | undefined;
|
||||
fieldsFromSource?: string | boolean | string[] | undefined;
|
||||
index?: import("../..").IndexPattern | undefined;
|
||||
searchAfter?: import("./types").EsQuerySearchAfter | undefined;
|
||||
timeout?: string | undefined;
|
||||
|
@ -2205,8 +2206,9 @@ export class SearchSource {
|
|||
export interface SearchSourceFields {
|
||||
// (undocumented)
|
||||
aggs?: any;
|
||||
// (undocumented)
|
||||
fields?: NameList;
|
||||
fields?: SearchFieldValue[];
|
||||
// @deprecated
|
||||
fieldsFromSource?: NameList;
|
||||
// (undocumented)
|
||||
filter?: Filter[] | Filter | (() => Filter[] | Filter | undefined);
|
||||
// (undocumented)
|
||||
|
@ -2406,6 +2408,7 @@ export const UI_SETTINGS: {
|
|||
// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:64:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:135:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/common/search/aggs/types.ts:113:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/common/search/search_source/search_source.ts:197:7 - (ae-forgotten-export) The symbol "SearchFieldValue" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/field_formats/field_formats_service.ts:67:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts
|
||||
|
|
|
@ -46,10 +46,8 @@ describe('getSharingData', () => {
|
|||
],
|
||||
"searchRequest": Object {
|
||||
"body": Object {
|
||||
"_source": Object {
|
||||
"includes": Array [],
|
||||
},
|
||||
"docvalue_fields": Array [],
|
||||
"_source": Object {},
|
||||
"fields": undefined,
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [],
|
||||
|
@ -60,7 +58,7 @@ describe('getSharingData', () => {
|
|||
},
|
||||
"script_fields": Object {},
|
||||
"sort": Array [],
|
||||
"stored_fields": Array [],
|
||||
"stored_fields": undefined,
|
||||
},
|
||||
"index": "the-index-pattern-title",
|
||||
},
|
||||
|
|
|
@ -63,7 +63,7 @@ export async function getSharingData(
|
|||
index.timeFieldName || '',
|
||||
config.get(DOC_HIDE_TIME_COLUMN_SETTING)
|
||||
);
|
||||
searchSource.setField('fields', searchFields);
|
||||
searchSource.setField('fieldsFromSource', searchFields);
|
||||
searchSource.setField(
|
||||
'sort',
|
||||
getSortForSearchSource(state.sort as SortOrder[], index, config.get(SORT_DEFAULT_ORDER_SETTING))
|
||||
|
|
|
@ -8,7 +8,7 @@ exports[`AddFilter should ignore strings with just spaces 1`] = `
|
|||
<EuiFieldText
|
||||
fullWidth={true}
|
||||
onChange={[Function]}
|
||||
placeholder="source filter, accepts wildcards (e.g., \`user*\` to filter fields starting with 'user')"
|
||||
placeholder="field filter, accepts wildcards (e.g., \`user*\` to filter fields starting with 'user')"
|
||||
value=""
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
@ -35,7 +35,7 @@ exports[`AddFilter should render normally 1`] = `
|
|||
<EuiFieldText
|
||||
fullWidth={true}
|
||||
onChange={[Function]}
|
||||
placeholder="source filter, accepts wildcards (e.g., \`user*\` to filter fields starting with 'user')"
|
||||
placeholder="field filter, accepts wildcards (e.g., \`user*\` to filter fields starting with 'user')"
|
||||
value=""
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -31,7 +31,7 @@ const sourcePlaceholder = i18n.translate(
|
|||
'indexPatternManagement.editIndexPattern.sourcePlaceholder',
|
||||
{
|
||||
defaultMessage:
|
||||
"source filter, accepts wildcards (e.g., `user*` to filter fields starting with 'user')",
|
||||
"field filter, accepts wildcards (e.g., `user*` to filter fields starting with 'user')",
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ exports[`Header should render normally 1`] = `
|
|||
onConfirm={[Function]}
|
||||
title={
|
||||
<FormattedMessage
|
||||
defaultMessage="Delete source filter '{value}'?"
|
||||
defaultMessage="Delete field filter '{value}'?"
|
||||
id="indexPatternManagement.editIndexPattern.source.deleteSourceFilterLabel"
|
||||
values={
|
||||
Object {
|
||||
|
|
|
@ -42,7 +42,7 @@ export const DeleteFilterConfirmationModal = ({
|
|||
title={
|
||||
<FormattedMessage
|
||||
id="indexPatternManagement.editIndexPattern.source.deleteSourceFilterLabel"
|
||||
defaultMessage="Delete source filter '{value}'?"
|
||||
defaultMessage="Delete field filter '{value}'?"
|
||||
values={{
|
||||
value: filterToDeleteValue,
|
||||
}}
|
||||
|
|
|
@ -7,7 +7,7 @@ exports[`Header should render normally 1`] = `
|
|||
>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Source filters"
|
||||
defaultMessage="Field filters"
|
||||
id="indexPatternManagement.editIndexPattern.sourceHeader"
|
||||
values={Object {}}
|
||||
/>
|
||||
|
@ -16,7 +16,7 @@ exports[`Header should render normally 1`] = `
|
|||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Source filters can be used to exclude one or more fields when fetching the document source. This happens when viewing a document in the Discover app, or with a table displaying results from a saved search in the Dashboard app. Each row is built using the source of a single document, and if you have documents with large or unimportant fields you may benefit from filtering those out at this lower level."
|
||||
defaultMessage="Field filters can be used to exclude one or more fields when fetching a document. This happens when viewing a document in the Discover app, or with a table displaying results from a saved search in the Dashboard app. If you have documents with large or unimportant fields you may benefit from filtering those out at this lower level."
|
||||
id="indexPatternManagement.editIndexPattern.sourceLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
|
|
|
@ -28,7 +28,7 @@ export const Header = () => (
|
|||
<h3>
|
||||
<FormattedMessage
|
||||
id="indexPatternManagement.editIndexPattern.sourceHeader"
|
||||
defaultMessage="Source filters"
|
||||
defaultMessage="Field filters"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
|
@ -36,10 +36,9 @@ export const Header = () => (
|
|||
<p>
|
||||
<FormattedMessage
|
||||
id="indexPatternManagement.editIndexPattern.sourceLabel"
|
||||
defaultMessage="Source filters can be used to exclude one or more fields when fetching the document source. This happens when
|
||||
viewing a document in the Discover app, or with a table displaying results from a saved search in the Dashboard app. Each row is
|
||||
built using the source of a single document, and if you have documents with large or unimportant fields you may benefit from
|
||||
filtering those out at this lower level."
|
||||
defaultMessage="Field filters can be used to exclude one or more fields when fetching a document. This happens when
|
||||
viewing a document in the Discover app, or with a table displaying results from a saved search in the Dashboard app.
|
||||
If you have documents with large or unimportant fields you may benefit from filtering those out at this lower level."
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
|
|
|
@ -67,7 +67,7 @@ function getTitle(type: string, filteredCount: Dictionary<number>, totalCount: D
|
|||
break;
|
||||
case 'sourceFilters':
|
||||
title = i18n.translate('indexPatternManagement.editIndexPattern.tabs.sourceHeader', {
|
||||
defaultMessage: 'Source filters',
|
||||
defaultMessage: 'Field filters',
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ describe('ESSearchSource', () => {
|
|||
});
|
||||
const urlTemplateWithMeta = await esSearchSource.getUrlTemplateWithMeta(searchFilters);
|
||||
expect(urlTemplateWithMeta.urlTemplate).toBe(
|
||||
`rootdir/api/maps/mvt/getTile;?x={x}&y={y}&z={z}&geometryFieldName=bar&index=foobar-title-*&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:(),title:'foobar-title-*')),'1':('0':size,'1':1000),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:(),title:'foobar-title-*')),'5':('0':query,'1':(language:KQL,query:'tooltipField: foobar',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':fields,'1':!(tooltipField,styleField)),'7':('0':source,'1':!(tooltipField,styleField))))&geoFieldType=geo_shape`
|
||||
`rootdir/api/maps/mvt/getTile;?x={x}&y={y}&z={z}&geometryFieldName=bar&index=foobar-title-*&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:(),title:'foobar-title-*')),'1':('0':size,'1':1000),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:(),title:'foobar-title-*')),'5':('0':query,'1':(language:KQL,query:'tooltipField: foobar',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':fieldsFromSource,'1':!(tooltipField,styleField)),'7':('0':source,'1':!(tooltipField,styleField))))&geoFieldType=geo_shape`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -375,7 +375,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
|
|||
maxResultWindow,
|
||||
initialSearchContext
|
||||
);
|
||||
searchSource.setField('fields', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields
|
||||
searchSource.setField('fieldsFromSource', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields
|
||||
if (sourceOnlyFields.length === 0) {
|
||||
searchSource.setField('source', false); // do not need anything from _source
|
||||
} else {
|
||||
|
@ -505,7 +505,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
|
|||
};
|
||||
|
||||
searchSource.setField('query', query);
|
||||
searchSource.setField('fields', this._getTooltipPropertyNames());
|
||||
searchSource.setField('fieldsFromSource', this._getTooltipPropertyNames());
|
||||
|
||||
const resp = await searchSource.fetch();
|
||||
|
||||
|
@ -708,7 +708,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
|
|||
indexSettings.maxResultWindow,
|
||||
initialSearchContext
|
||||
);
|
||||
searchSource.setField('fields', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields
|
||||
searchSource.setField('fieldsFromSource', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields
|
||||
if (sourceOnlyFields.length === 0) {
|
||||
searchSource.setField('source', false); // do not need anything from _source
|
||||
} else {
|
||||
|
|
|
@ -32,7 +32,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
//Source should be correct
|
||||
expect(mapboxStyle.sources[MB_VECTOR_SOURCE_ID].tiles[0]).to.equal(
|
||||
"/api/maps/mvt/getGridTile?x={x}&y={y}&z={z}&geometryFieldName=geo.coordinates&index=logstash-*&requestBody=(_source:(excludes:!()),aggs:(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:geo.coordinates)),max_of_bytes:(max:(field:bytes))),geotile_grid:(bounds:!n,field:geo.coordinates,precision:!n,shard_size:65535,size:65535))),docvalue_fields:!((field:'@timestamp',format:date_time),(field:'relatedContent.article:modified_time',format:date_time),(field:'relatedContent.article:published_time',format:date_time),(field:utc_time,format:date_time)),query:(bool:(filter:!((match_all:()),(range:('@timestamp':(format:strict_date_optional_time,gte:'2015-09-20T00:00:00.000Z',lte:'2015-09-20T01:00:00.000Z')))),must:!(),must_not:!(),should:!())),script_fields:(hour_of_day:(script:(lang:painless,source:'doc[!'@timestamp!'].value.getHour()'))),size:0,stored_fields:!('*'))&requestType=grid&geoFieldType=geo_point"
|
||||
"/api/maps/mvt/getGridTile?x={x}&y={y}&z={z}&geometryFieldName=geo.coordinates&index=logstash-*&requestBody=(_source:(excludes:!()),aggs:(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:geo.coordinates)),max_of_bytes:(max:(field:bytes))),geotile_grid:(bounds:!n,field:geo.coordinates,precision:!n,shard_size:65535,size:65535))),fields:!((field:'@timestamp',format:date_time),(field:'relatedContent.article:modified_time',format:date_time),(field:'relatedContent.article:published_time',format:date_time),(field:utc_time,format:date_time)),query:(bool:(filter:!((match_all:()),(range:('@timestamp':(format:strict_date_optional_time,gte:'2015-09-20T00:00:00.000Z',lte:'2015-09-20T01:00:00.000Z')))),must:!(),must_not:!(),should:!())),script_fields:(hour_of_day:(script:(lang:painless,source:'doc[!'@timestamp!'].value.getHour()'))),size:0,stored_fields:!('*'))&requestType=grid&geoFieldType=geo_point"
|
||||
);
|
||||
|
||||
//Should correctly load meta for style-rule (sigma is set to 1, opacity to 1)
|
||||
|
|
Loading…
Reference in a new issue