kibana/x-pack/plugins/lens/public/search_provider.ts
Pierre Gayvallet 73fbf2a703
[GS] add tag and dashboard suggestion results (#85144)
* initial draft

* polish

* fix mocks

* add tests

* tests on suggestions

* add comment

* add FTR tests

* factorize getSearchableTypes

* move to bottom
2020-12-09 11:05:59 +01:00

84 lines
2.9 KiB
TypeScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import levenshtein from 'js-levenshtein';
import { ApplicationStart } from 'kibana/public';
import { from, of } from 'rxjs';
import { i18n } from '@kbn/i18n';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
import { GlobalSearchResultProvider } from '../../global_search/public';
import { getFullPath } from '../common';
/**
* Global search provider adding a Lens entry.
* This is necessary because Lens does not show up in the nav bar and is filtered out by the
* default app provider.
*
* It is inlining the same search term matching logic as the application search provider.
*
* TODO: This is a workaround and can be removed once there is a generic way to register sub features
* of apps. In this case, Lens should be considered a feature of Visualize.
*/
export const getSearchProvider: (
uiCapabilities: Promise<ApplicationStart['capabilities']>
) => GlobalSearchResultProvider = (uiCapabilities) => ({
id: 'lens',
find: ({ term = '', types, tags }) => {
if (tags || (types && !types.includes('application'))) {
return of([]);
}
return from(
uiCapabilities.then(({ navLinks: { visualize: visualizeNavLink } }) => {
if (!visualizeNavLink) {
return [];
}
const title = i18n.translate('xpack.lens.searchTitle', {
defaultMessage: 'Lens: create visualizations',
description: 'Lens is a product name and should not be translated',
});
const searchableTitle = title.toLowerCase();
term = term.toLowerCase();
let score = 0;
// shortcuts to avoid calculating the distance when there is an exact match somewhere.
if (searchableTitle === term) {
score = 100;
} else if (searchableTitle.startsWith(term)) {
score = 90;
} else if (searchableTitle.includes(term)) {
score = 75;
} else {
const length = Math.max(term.length, searchableTitle.length);
const distance = levenshtein(term, searchableTitle);
// maximum lev distance is length, we compute the match ratio (lower distance is better)
const ratio = Math.floor((1 - distance / length) * 100);
if (ratio >= 60) {
score = ratio;
}
}
if (score === 0) return [];
return [
{
id: 'lens',
title,
type: 'application',
icon: 'logoKibana',
meta: {
categoryId: DEFAULT_APP_CATEGORIES.kibana.id,
categoryLabel: DEFAULT_APP_CATEGORIES.kibana.label,
},
score,
url: getFullPath(),
},
];
})
);
},
getSearchableTypes: () => ['application'],
});