mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[UnifiedSearch][Lens] Make sure adHoc dataViews are receiving the right query suggestions (#143389)
* 🐛 Resolve title and id strings as dataViews * 🐛 Make sure dataViews are passed by id when available * 🐛 Forgot to spread results * ✅ Fix and add more tests * 🐛 Fix test * ⚡ refactor as suggested for performance * 🏷️ reuse type * ✅ Fix one more test * 🐛 Fix mock name * Update x-pack/plugins/graph/public/components/search_bar.test.tsx * Update x-pack/plugins/graph/public/components/search_bar.test.tsx * 🐛 Fix instanceof issue * 🐛 Use a mock function for dataview Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
This commit is contained in:
parent
e363b7c9ba
commit
6522bbb453
9 changed files with 102 additions and 30 deletions
|
@ -7,26 +7,47 @@
|
|||
*/
|
||||
|
||||
import { isEmpty } from 'lodash';
|
||||
import { DataViewsContract } from '@kbn/data-views-plugin/public';
|
||||
import type { DataViewsContract, DataView } from '@kbn/data-views-plugin/public';
|
||||
|
||||
export interface DataViewByIdOrTitle {
|
||||
type: 'title' | 'id';
|
||||
value: string;
|
||||
}
|
||||
|
||||
export async function fetchIndexPatterns(
|
||||
indexPatternsService: DataViewsContract,
|
||||
indexPatternStrings: string[]
|
||||
) {
|
||||
indexPatternStrings: DataViewByIdOrTitle[]
|
||||
): Promise<DataView[]> {
|
||||
if (!indexPatternStrings || isEmpty(indexPatternStrings)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const searchString = indexPatternStrings.map((string) => `"${string}"`).join(' | ');
|
||||
const searchStringList: string[] = [];
|
||||
const searchIdsList: string[] = [];
|
||||
for (const { type, value } of indexPatternStrings) {
|
||||
if (type === 'title') {
|
||||
searchStringList.push(value);
|
||||
} else {
|
||||
searchIdsList.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
const exactMatches = (await indexPatternsService.find(searchString)).filter((ip) =>
|
||||
indexPatternStrings.includes(ip.title)
|
||||
);
|
||||
const searchString = searchStringList.map((value) => `"${value}"`).join(' | ');
|
||||
|
||||
const [searchMatches, ...matchesById] = await Promise.all([
|
||||
indexPatternsService.find(searchString),
|
||||
...searchIdsList.map((id) => indexPatternsService.get(id)),
|
||||
]);
|
||||
|
||||
const exactMatches = [
|
||||
...searchMatches.filter((ip) => searchStringList.includes(ip.title)),
|
||||
...matchesById,
|
||||
];
|
||||
|
||||
const allMatches =
|
||||
exactMatches.length === indexPatternStrings.length
|
||||
? exactMatches
|
||||
: [...exactMatches, await indexPatternsService.getDefault()];
|
||||
|
||||
return allMatches;
|
||||
return allMatches.filter((d: DataView | null): d is DataView => d != null);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ export const FilterEditorWrapper = React.memo(function FilterEditorWrapper({
|
|||
|
||||
const objectPatternsFromStrings = (await fetchIndexPatterns(
|
||||
data.dataViews,
|
||||
stringPatterns
|
||||
stringPatterns.map((value) => ({ type: 'title', value }))
|
||||
)) as DataView[];
|
||||
setDataviews([...objectPatterns, ...objectPatternsFromStrings]);
|
||||
const [dataView] = [...objectPatterns, ...objectPatternsFromStrings];
|
||||
|
|
|
@ -361,7 +361,46 @@ describe('QueryStringInput', () => {
|
|||
disableAutoFocus: true,
|
||||
})
|
||||
);
|
||||
expect(mockFetchIndexPatterns.mock.calls[0][1]).toStrictEqual(patternStrings);
|
||||
expect(mockFetchIndexPatterns.mock.calls[0][1]).toEqual(
|
||||
patternStrings.map((value) => ({ type: 'title', value }))
|
||||
);
|
||||
});
|
||||
|
||||
it('Should accept index pattern ids and fetch the full object', () => {
|
||||
const idStrings = [{ type: 'id', value: '1' }];
|
||||
mockFetchIndexPatterns.mockClear();
|
||||
mount(
|
||||
wrapQueryStringInputInContext({
|
||||
query: kqlQuery,
|
||||
onSubmit: noop,
|
||||
indexPatterns: idStrings,
|
||||
disableAutoFocus: true,
|
||||
})
|
||||
);
|
||||
expect(mockFetchIndexPatterns.mock.calls[0][1]).toEqual(idStrings);
|
||||
});
|
||||
|
||||
it('Should accept a mix of full objects, title and ids and fetch only missing index pattern objects', () => {
|
||||
const patternStrings = [
|
||||
'logstash-*',
|
||||
{ type: 'id', value: '1' },
|
||||
{ type: 'title', value: 'my-fake-index-pattern' },
|
||||
stubIndexPattern,
|
||||
];
|
||||
mockFetchIndexPatterns.mockClear();
|
||||
mount(
|
||||
wrapQueryStringInputInContext({
|
||||
query: kqlQuery,
|
||||
onSubmit: noop,
|
||||
indexPatterns: patternStrings,
|
||||
disableAutoFocus: true,
|
||||
})
|
||||
);
|
||||
expect(mockFetchIndexPatterns.mock.calls[0][1]).toEqual([
|
||||
{ type: 'title', value: 'logstash-*' },
|
||||
{ type: 'id', value: '1' },
|
||||
{ type: 'title', value: 'my-fake-index-pattern' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('Should convert non-breaking spaces into regular spaces', () => {
|
||||
|
|
|
@ -27,11 +27,11 @@ import {
|
|||
toSentenceCase,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { compact, debounce, isEmpty, isEqual, isFunction } from 'lodash';
|
||||
import { compact, debounce, isEmpty, isEqual, isFunction, partition } from 'lodash';
|
||||
import { CoreStart, DocLinksStart, Toast } from '@kbn/core/public';
|
||||
import type { Query } from '@kbn/es-query';
|
||||
import { DataPublicPluginStart, getQueryLog } from '@kbn/data-plugin/public';
|
||||
import { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { type DataView, DataView as KibanaDataView } from '@kbn/data-views-plugin/public';
|
||||
import type { PersistedLog } from '@kbn/data-plugin/public';
|
||||
import { getFieldSubtypeNested, KIBANA_USER_QUERY_LANGUAGE_KEY } from '@kbn/data-plugin/common';
|
||||
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
|
||||
|
@ -40,7 +40,7 @@ import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
|
|||
import { matchPairs } from './match_pairs';
|
||||
import { toUser } from './to_user';
|
||||
import { fromUser } from './from_user';
|
||||
import { fetchIndexPatterns } from './fetch_index_patterns';
|
||||
import { type DataViewByIdOrTitle, fetchIndexPatterns } from './fetch_index_patterns';
|
||||
import { QueryLanguageSwitcher } from './language_switcher';
|
||||
import type { SuggestionsListSize } from '../typeahead/suggestions_component';
|
||||
import { SuggestionsComponent } from '../typeahead';
|
||||
|
@ -64,7 +64,7 @@ export interface QueryStringInputDependencies {
|
|||
}
|
||||
|
||||
export interface QueryStringInputProps {
|
||||
indexPatterns: Array<DataView | string>;
|
||||
indexPatterns: Array<DataView | string | DataViewByIdOrTitle>;
|
||||
query: Query;
|
||||
disableAutoFocus?: boolean;
|
||||
screenTitle?: string;
|
||||
|
@ -179,12 +179,15 @@ export default class QueryStringInputUI extends PureComponent<QueryStringInputPr
|
|||
};
|
||||
|
||||
private fetchIndexPatterns = debounce(async () => {
|
||||
const stringPatterns = this.props.indexPatterns.filter(
|
||||
(indexPattern) => typeof indexPattern === 'string'
|
||||
) as string[];
|
||||
const objectPatterns = this.props.indexPatterns.filter(
|
||||
(indexPattern) => typeof indexPattern !== 'string'
|
||||
) as DataView[];
|
||||
const [objectPatterns = [], stringPatterns = []] = partition<
|
||||
QueryStringInputProps['indexPatterns'][number],
|
||||
DataView
|
||||
>(this.props.indexPatterns || [], (indexPattern): indexPattern is DataView => {
|
||||
return indexPattern instanceof KibanaDataView;
|
||||
});
|
||||
const idOrTitlePatterns = stringPatterns.map((sp) =>
|
||||
typeof sp === 'string' ? { type: 'title', value: sp } : sp
|
||||
) as DataViewByIdOrTitle[];
|
||||
|
||||
// abort the previous fetch to avoid overriding with outdated data
|
||||
// issue https://github.com/elastic/kibana/issues/80831
|
||||
|
@ -192,10 +195,10 @@ export default class QueryStringInputUI extends PureComponent<QueryStringInputPr
|
|||
this.fetchIndexPatternsAbortController = new AbortController();
|
||||
const currentAbortController = this.fetchIndexPatternsAbortController;
|
||||
|
||||
const objectPatternsFromStrings = (await fetchIndexPatterns(
|
||||
const objectPatternsFromStrings = await fetchIndexPatterns(
|
||||
this.props.deps.data.indexPatterns,
|
||||
stringPatterns
|
||||
)) as DataView[];
|
||||
idOrTitlePatterns
|
||||
);
|
||||
|
||||
if (!currentAbortController.signal.aborted) {
|
||||
this.setState({
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
} from '@kbn/core/public';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { QueryStringInput } from '@kbn/unified-search-plugin/public';
|
||||
import { createStubDataView } from '@kbn/data-views-plugin/common/mocks';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { I18nProvider, InjectedIntl } from '@kbn/i18n-react';
|
||||
|
@ -90,7 +91,7 @@ describe('search_bar', () => {
|
|||
isLoading: false,
|
||||
indexPatternProvider: {
|
||||
get: jest.fn(() =>
|
||||
Promise.resolve({ fields: [], getName: () => 'Test Name' } as unknown as DataView)
|
||||
Promise.resolve(createStubDataView({ spec: { fields: {}, name: 'Test Name' } }))
|
||||
),
|
||||
},
|
||||
confirmWipeWorkspace: (callback: () => void) => {
|
||||
|
|
|
@ -94,7 +94,7 @@ describe('filter popover', () => {
|
|||
expect(instance.find(QueryStringInput).props()).toEqual(
|
||||
expect.objectContaining({
|
||||
dataTestSubj: 'indexPattern-filters-queryStringInput',
|
||||
indexPatterns: ['my-fake-index-pattern'],
|
||||
indexPatterns: [{ type: 'id', value: '1' }],
|
||||
isInvalid: false,
|
||||
query: { language: 'kuery', query: 'bytes >= 1' },
|
||||
})
|
||||
|
|
|
@ -60,7 +60,11 @@ export const FilterPopover = ({
|
|||
<QueryInput
|
||||
isInvalid={!isQueryValid(filter.input, indexPattern)}
|
||||
value={filter.input}
|
||||
indexPatternTitle={indexPattern.title}
|
||||
indexPattern={
|
||||
indexPattern.id
|
||||
? { type: 'id', value: indexPattern.id }
|
||||
: { type: 'title', value: indexPattern.title }
|
||||
}
|
||||
disableAutoFocus
|
||||
onChange={setFilterQuery}
|
||||
onSubmit={() => {
|
||||
|
|
|
@ -129,7 +129,11 @@ export function FilterQueryInput({
|
|||
data-test-subj="indexPattern-filter-by-input"
|
||||
>
|
||||
<QueryInput
|
||||
indexPatternTitle={indexPattern.title}
|
||||
indexPattern={
|
||||
indexPattern.id
|
||||
? { type: 'id', value: indexPattern.id }
|
||||
: { type: 'title', value: indexPattern.title }
|
||||
}
|
||||
disableAutoFocus={true}
|
||||
value={queryInput}
|
||||
onChange={setQueryInput}
|
||||
|
|
|
@ -17,7 +17,7 @@ import { LensAppServices } from '../../app_plugin/types';
|
|||
export const QueryInput = ({
|
||||
value,
|
||||
onChange,
|
||||
indexPatternTitle,
|
||||
indexPattern,
|
||||
isInvalid,
|
||||
onSubmit,
|
||||
disableAutoFocus,
|
||||
|
@ -26,7 +26,7 @@ export const QueryInput = ({
|
|||
}: {
|
||||
value: Query;
|
||||
onChange: (input: Query) => void;
|
||||
indexPatternTitle: string;
|
||||
indexPattern: string | { type: 'title' | 'id'; value: string };
|
||||
isInvalid: boolean;
|
||||
onSubmit: () => void;
|
||||
disableAutoFocus?: boolean;
|
||||
|
@ -46,7 +46,7 @@ export const QueryInput = ({
|
|||
disableAutoFocus={disableAutoFocus}
|
||||
isInvalid={isInvalid}
|
||||
bubbleSubmitEvent={false}
|
||||
indexPatterns={[indexPatternTitle]}
|
||||
indexPatterns={[indexPattern]}
|
||||
query={inputValue}
|
||||
onChange={(newQuery) => {
|
||||
if (!isEqual(newQuery, inputValue)) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue