mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Enterprise Search] Vector search landing page (#160206)
## Summary
d43fa0f0
-1491-411f-8aa9-0767b08edd20
### Checklist
Delete any items that are not applicable to this PR.
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
This commit is contained in:
parent
6b7ec77cc2
commit
566d4ddc5a
24 changed files with 744 additions and 5 deletions
|
@ -178,6 +178,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
|
|||
supportedNlpModels: `${MACHINE_LEARNING_DOCS}ml-nlp-model-ref.html`,
|
||||
syncRules: `${ENTERPRISE_SEARCH_DOCS}sync-rules.html`,
|
||||
trainedModels: `${MACHINE_LEARNING_DOCS}ml-trained-models.html`,
|
||||
textEmbedding: `${MACHINE_LEARNING_DOCS}ml-nlp-model-ref.html#ml-nlp-model-ref-text-embedding`,
|
||||
troubleshootSetup: `${ENTERPRISE_SEARCH_DOCS}troubleshoot-setup.html`,
|
||||
usersAccess: `${ENTERPRISE_SEARCH_DOCS}users-access.html`,
|
||||
},
|
||||
|
|
|
@ -162,6 +162,7 @@ export interface DocLinks {
|
|||
readonly supportedNlpModels: string;
|
||||
readonly syncRules: string;
|
||||
readonly trainedModels: string;
|
||||
readonly textEmbedding: string;
|
||||
readonly troubleshootSetup: string;
|
||||
readonly usersAccess: string;
|
||||
};
|
||||
|
|
|
@ -138,6 +138,7 @@ export const applicationUsageSchema = {
|
|||
enterpriseSearchAnalytics: commonSchema,
|
||||
enterpriseSearchApplications: commonSchema,
|
||||
enterpriseSearchEsre: commonSchema,
|
||||
enterpriseSearchVectorSearch: commonSchema,
|
||||
elasticsearch: commonSchema,
|
||||
appSearch: commonSchema,
|
||||
workplaceSearch: commonSchema,
|
||||
|
|
|
@ -2491,6 +2491,137 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"enterpriseSearchVectorSearch": {
|
||||
"properties": {
|
||||
"appId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The application being tracked"
|
||||
}
|
||||
},
|
||||
"viewId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "Always `main`"
|
||||
}
|
||||
},
|
||||
"clicks_total": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application since we started counting them"
|
||||
}
|
||||
},
|
||||
"clicks_7_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application over the last 7 days"
|
||||
}
|
||||
},
|
||||
"clicks_30_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application over the last 30 days"
|
||||
}
|
||||
},
|
||||
"clicks_90_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application over the last 90 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_total": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen since we started counting them."
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_7_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen over the last 7 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_30_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen over the last 30 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_90_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen over the last 90 days"
|
||||
}
|
||||
},
|
||||
"views": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"properties": {
|
||||
"appId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The application being tracked"
|
||||
}
|
||||
},
|
||||
"viewId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The application view being tracked"
|
||||
}
|
||||
},
|
||||
"clicks_total": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application sub view since we started counting them"
|
||||
}
|
||||
},
|
||||
"clicks_7_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the active application sub view over the last 7 days"
|
||||
}
|
||||
},
|
||||
"clicks_30_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the active application sub view over the last 30 days"
|
||||
}
|
||||
},
|
||||
"clicks_90_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the active application sub view over the last 90 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_total": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application sub view is active and on-screen since we started counting them."
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_7_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen active application sub view over the last 7 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_30_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen active application sub view over the last 30 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_90_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen active application sub view over the last 90 days"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"elasticsearch": {
|
||||
"properties": {
|
||||
"appId": {
|
||||
|
|
|
@ -147,6 +147,23 @@ export const APPLICATIONS_PLUGIN = {
|
|||
URL: '/app/enterprise_search/applications',
|
||||
};
|
||||
|
||||
export const VECTOR_SEARCH_PLUGIN = {
|
||||
DESCRIPTION: i18n.translate('xpack.enterpriseSearch.vectorSearch.description', {
|
||||
defaultMessage:
|
||||
'Elasticsearch can be used as a vector database and search along with other semantic search methods.',
|
||||
}),
|
||||
ID: 'enterpriseSearchVectorSearch',
|
||||
LOGO: 'logoEnterpriseSearch',
|
||||
NAME: i18n.translate('xpack.enterpriseSearch.vectorSearch.productName', {
|
||||
defaultMessage: 'Vector Search',
|
||||
}),
|
||||
NAV_TITLE: i18n.translate('xpack.enterpriseSearch.vectorSearch.navTitle', {
|
||||
defaultMessage: 'Vector Search',
|
||||
}),
|
||||
SUPPORT_URL: 'https://discuss.elastic.co/c/enterprise-search/',
|
||||
URL: '/app/enterprise_search/vector_search',
|
||||
};
|
||||
|
||||
export const LICENSED_SUPPORT_URL = 'https://support.elastic.co';
|
||||
|
||||
export const JSON_HEADER = {
|
||||
|
|
|
@ -121,6 +121,7 @@ class DocLinks {
|
|||
public supportedNlpModels: string;
|
||||
public syncRules: string;
|
||||
public trainedModels: string;
|
||||
public textEmbedding: string;
|
||||
public workplaceSearchApiKeys: string;
|
||||
public workplaceSearchBox: string;
|
||||
public workplaceSearchConfluenceCloud: string;
|
||||
|
@ -271,6 +272,7 @@ class DocLinks {
|
|||
this.supportedNlpModels = '';
|
||||
this.syncRules = '';
|
||||
this.trainedModels = '';
|
||||
this.textEmbedding = '';
|
||||
this.workplaceSearchApiKeys = '';
|
||||
this.workplaceSearchBox = '';
|
||||
this.workplaceSearchConfluenceCloud = '';
|
||||
|
@ -423,6 +425,7 @@ class DocLinks {
|
|||
this.supportedNlpModels = docLinks.links.enterpriseSearch.supportedNlpModels;
|
||||
this.syncRules = docLinks.links.enterpriseSearch.syncRules;
|
||||
this.trainedModels = docLinks.links.enterpriseSearch.trainedModels;
|
||||
this.textEmbedding = docLinks.links.enterpriseSearch.textEmbedding;
|
||||
this.workplaceSearchApiKeys = docLinks.links.workplaceSearch.apiKeys;
|
||||
this.workplaceSearchBox = docLinks.links.workplaceSearch.box;
|
||||
this.workplaceSearchConfluenceCloud = docLinks.links.workplaceSearch.confluenceCloud;
|
||||
|
|
|
@ -10,13 +10,14 @@ import { useValues } from 'kea';
|
|||
import { EuiBreadcrumb } from '@elastic/eui';
|
||||
|
||||
import {
|
||||
ENTERPRISE_SEARCH_OVERVIEW_PLUGIN,
|
||||
ANALYTICS_PLUGIN,
|
||||
APP_SEARCH_PLUGIN,
|
||||
WORKPLACE_SEARCH_PLUGIN,
|
||||
ENTERPRISE_SEARCH_CONTENT_PLUGIN,
|
||||
SEARCH_EXPERIENCES_PLUGIN,
|
||||
ENTERPRISE_SEARCH_OVERVIEW_PLUGIN,
|
||||
ESRE_PLUGIN,
|
||||
SEARCH_EXPERIENCES_PLUGIN,
|
||||
VECTOR_SEARCH_PLUGIN,
|
||||
WORKPLACE_SEARCH_PLUGIN,
|
||||
} from '../../../../common/constants';
|
||||
|
||||
import { stripLeadingSlash } from '../../../../common/strip_slashes';
|
||||
|
@ -143,3 +144,6 @@ export const useEnterpriseSearchApplicationsBreadcrumbs = (breadcrumbs: Breadcru
|
|||
|
||||
export const useEsreBreadcrumbs = (breadcrumbs: Breadcrumbs = []) =>
|
||||
useEnterpriseSearchBreadcrumbs([{ text: ESRE_PLUGIN.NAME, path: '/' }, ...breadcrumbs]);
|
||||
|
||||
export const useVectorSearchBreadcrumbs = (breadcrumbs: Breadcrumbs = []) =>
|
||||
useEnterpriseSearchBreadcrumbs([{ text: VECTOR_SEARCH_PLUGIN.NAME, path: '/' }, ...breadcrumbs]);
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
WORKPLACE_SEARCH_PLUGIN,
|
||||
SEARCH_EXPERIENCES_PLUGIN,
|
||||
ESRE_PLUGIN,
|
||||
VECTOR_SEARCH_PLUGIN,
|
||||
} from '../../../../common/constants';
|
||||
|
||||
/**
|
||||
|
@ -50,3 +51,6 @@ export const searchExperiencesTitle = (page: Title = []) =>
|
|||
generateTitle([...page, SEARCH_EXPERIENCES_PLUGIN.NAME]);
|
||||
|
||||
export const esreTitle = (page: Title = []) => generateTitle([...page, ESRE_PLUGIN.NAME]);
|
||||
|
||||
export const vectorSearchTitle = (page: Title = []) =>
|
||||
generateTitle([...page, VECTOR_SEARCH_PLUGIN.NAME]);
|
||||
|
|
|
@ -15,4 +15,5 @@ export {
|
|||
SetWorkplaceSearchChrome,
|
||||
SetSearchExperiencesChrome,
|
||||
SetEnterpriseSearchApplicationsChrome,
|
||||
SetVectorSearchChrome,
|
||||
} from './set_chrome';
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, { useEffect } from 'react';
|
|||
|
||||
import { useValues } from 'kea';
|
||||
|
||||
import { APPLICATIONS_PLUGIN } from '../../../../common/constants';
|
||||
import { APPLICATIONS_PLUGIN, VECTOR_SEARCH_PLUGIN } from '../../../../common/constants';
|
||||
|
||||
import { KibanaLogic } from '../kibana';
|
||||
|
||||
|
@ -25,6 +25,7 @@ import {
|
|||
useWorkplaceSearchBreadcrumbs,
|
||||
BreadcrumbTrail,
|
||||
useSearchExperiencesBreadcrumbs,
|
||||
useVectorSearchBreadcrumbs,
|
||||
} from './generate_breadcrumbs';
|
||||
import {
|
||||
enterpriseSearchTitle,
|
||||
|
@ -34,6 +35,7 @@ import {
|
|||
workplaceSearchTitle,
|
||||
searchExperiencesTitle,
|
||||
esreTitle,
|
||||
vectorSearchTitle,
|
||||
} from './generate_title';
|
||||
|
||||
/**
|
||||
|
@ -209,5 +211,23 @@ export const SetEnterpriseSearchApplicationsChrome: React.FC<SetChromeProps> = (
|
|||
return null;
|
||||
};
|
||||
|
||||
export const SetVectorSearchChrome: React.FC<SetChromeProps> = ({ trail = [] }) => {
|
||||
const { setBreadcrumbs, setDocTitle } = useValues(KibanaLogic);
|
||||
|
||||
const title = reverseArray(trail);
|
||||
const docTitle = vectorSearchTitle(title);
|
||||
|
||||
const breadcrumbs = useVectorSearchBreadcrumbs(
|
||||
useGenerateBreadcrumbs([VECTOR_SEARCH_PLUGIN.NAV_TITLE, ...trail])
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setBreadcrumbs(breadcrumbs);
|
||||
setDocTitle(docTitle);
|
||||
}, [trail]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Small util - performantly reverses an array without mutating the original array
|
||||
const reverseArray = (array: string[]) => array.slice().reverse();
|
||||
|
|
|
@ -56,6 +56,11 @@ describe('useEnterpriseSearchContentNav', () => {
|
|||
id: 'esre',
|
||||
name: 'ESRE',
|
||||
},
|
||||
{
|
||||
href: '/app/enterprise_search/vector_search',
|
||||
id: 'vectorSearch',
|
||||
name: 'Vector Search',
|
||||
},
|
||||
{
|
||||
href: '/app/enterprise_search/search_experiences',
|
||||
id: 'searchExperiences',
|
||||
|
@ -220,6 +225,11 @@ describe('useEnterpriseSearchApplicationNav', () => {
|
|||
id: 'esre',
|
||||
name: 'ESRE',
|
||||
},
|
||||
{
|
||||
href: '/app/enterprise_search/vector_search',
|
||||
id: 'vectorSearch',
|
||||
name: 'Vector Search',
|
||||
},
|
||||
{
|
||||
href: '/app/enterprise_search/search_experiences',
|
||||
id: 'searchExperiences',
|
||||
|
@ -412,6 +422,11 @@ describe('useEnterpriseSearchAnalyticsNav', () => {
|
|||
id: 'esre',
|
||||
name: 'ESRE',
|
||||
},
|
||||
{
|
||||
href: '/app/enterprise_search/vector_search',
|
||||
id: 'vectorSearch',
|
||||
name: 'Vector Search',
|
||||
},
|
||||
{
|
||||
href: '/app/enterprise_search/search_experiences',
|
||||
id: 'searchExperiences',
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
ENTERPRISE_SEARCH_OVERVIEW_PLUGIN,
|
||||
ESRE_PLUGIN,
|
||||
SEARCH_EXPERIENCES_PLUGIN,
|
||||
VECTOR_SEARCH_PLUGIN,
|
||||
WORKPLACE_SEARCH_PLUGIN,
|
||||
} from '../../../../common/constants';
|
||||
import { SEARCH_APPLICATIONS_PATH, SearchApplicationViewTabs } from '../../applications/routes';
|
||||
|
@ -64,6 +65,14 @@ export const useEnterpriseSearchNav = () => {
|
|||
to: ESRE_PLUGIN.URL,
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'vectorSearch',
|
||||
name: VECTOR_SEARCH_PLUGIN.NAME,
|
||||
...generateNavLink({
|
||||
shouldNotCreateHref: true,
|
||||
to: VECTOR_SEARCH_PLUGIN.URL,
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'searchExperiences',
|
||||
name: i18n.translate('xpack.enterpriseSearch.nav.searchExperiencesTitle', {
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { useValues } from 'kea';
|
||||
import { compressToEncodedURIComponent } from 'lz-string';
|
||||
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiCodeBlock,
|
||||
EuiCodeBlockProps,
|
||||
EuiCopy,
|
||||
EuiFlexGroup,
|
||||
EuiHorizontalRule,
|
||||
EuiPanel,
|
||||
EuiThemeProvider,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { KibanaLogic } from '../../../shared/kibana';
|
||||
|
||||
export type DevToolsConsoleCodeBlockProps = EuiCodeBlockProps & {
|
||||
children: string;
|
||||
};
|
||||
|
||||
export const DevToolsConsoleCodeBlock: React.FC<DevToolsConsoleCodeBlockProps> = ({
|
||||
children,
|
||||
...props
|
||||
}) => {
|
||||
const {
|
||||
application,
|
||||
share: { url },
|
||||
} = useValues(KibanaLogic);
|
||||
|
||||
const consolePreviewLink =
|
||||
!!application?.capabilities?.dev_tools?.show &&
|
||||
url.locators
|
||||
.get('CONSOLE_APP_LOCATOR')
|
||||
?.useUrl(
|
||||
{ loadFrom: `data:text/plain,${compressToEncodedURIComponent(children)}` },
|
||||
undefined,
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiThemeProvider colorMode="dark">
|
||||
<EuiPanel hasShadow={false}>
|
||||
<EuiFlexGroup direction="column" gutterSize="xs">
|
||||
<EuiFlexGroup direction="rowReverse" gutterSize="s">
|
||||
{consolePreviewLink && (
|
||||
<EuiButtonEmpty
|
||||
iconType="popout"
|
||||
color="success"
|
||||
href={consolePreviewLink}
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.component.devToolsConsoleCodeBlock.tryInConsole"
|
||||
defaultMessage="Try in Console"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
)}
|
||||
<EuiCopy textToCopy={children}>
|
||||
{(copy) => (
|
||||
<EuiButtonEmpty iconType="copyClipboard" onClick={copy} color="text">
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.component.devToolsConsoleCodeBlock.copy"
|
||||
defaultMessage="Copy"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
)}
|
||||
</EuiCopy>
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule margin="xs" />
|
||||
<EuiCodeBlock
|
||||
paddingSize="s"
|
||||
fontSize="m"
|
||||
transparentBackground
|
||||
color="subduedText"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</EuiCodeBlock>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
</EuiThemeProvider>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
jest.mock('../../../shared/layout/nav', () => ({
|
||||
useEnterpriseSearchNav: () => [],
|
||||
}));
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { SetVectorSearchChrome } from '../../../shared/kibana_chrome';
|
||||
import { EnterpriseSearchPageTemplateWrapper } from '../../../shared/layout';
|
||||
import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry';
|
||||
|
||||
import { EnterpriseSearchVectorSearchPageTemplate } from './page_template';
|
||||
|
||||
describe('EnterpriseSearchVectorSearchPageTemplate', () => {
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(
|
||||
<EnterpriseSearchVectorSearchPageTemplate>
|
||||
<div className="hello">world</div>
|
||||
</EnterpriseSearchVectorSearchPageTemplate>
|
||||
);
|
||||
|
||||
expect(wrapper.type()).toEqual(EnterpriseSearchPageTemplateWrapper);
|
||||
expect(wrapper.prop('solutionNav')).toEqual({ items: [], name: 'Vector Search' });
|
||||
expect(wrapper.find('.hello').text()).toEqual('world');
|
||||
});
|
||||
|
||||
describe('page chrome', () => {
|
||||
it('takes a breadcrumb array & renders a product-specific page chrome', () => {
|
||||
const wrapper = shallow(
|
||||
<EnterpriseSearchVectorSearchPageTemplate pageChrome={['Some page']} />
|
||||
);
|
||||
const setPageChrome = wrapper
|
||||
.find(EnterpriseSearchPageTemplateWrapper)
|
||||
.prop('setPageChrome') as any;
|
||||
|
||||
expect(setPageChrome.type).toEqual(SetVectorSearchChrome);
|
||||
expect(setPageChrome.props.trail).toEqual(['Some page']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('page telemetry', () => {
|
||||
it('takes a metric & renders product-specific telemetry viewed event', () => {
|
||||
const wrapper = shallow(
|
||||
<EnterpriseSearchVectorSearchPageTemplate pageViewTelemetry="some_page" />
|
||||
);
|
||||
|
||||
expect(wrapper.find(SendEnterpriseSearchTelemetry).prop('action')).toEqual('viewed');
|
||||
expect(wrapper.find(SendEnterpriseSearchTelemetry).prop('metric')).toEqual('some_page');
|
||||
});
|
||||
});
|
||||
|
||||
describe('props', () => {
|
||||
it('passes down any ...pageTemplateProps that EnterpriseSearchPageTemplateWrapper accepts', () => {
|
||||
const wrapper = shallow(
|
||||
<EnterpriseSearchVectorSearchPageTemplate
|
||||
pageHeader={{ pageTitle: 'hello world' }}
|
||||
isLoading={false}
|
||||
emptyState={<div />}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find(EnterpriseSearchPageTemplateWrapper).prop('pageHeader')!.pageTitle
|
||||
).toEqual('hello world');
|
||||
expect(wrapper.find(EnterpriseSearchPageTemplateWrapper).prop('isLoading')).toEqual(false);
|
||||
expect(wrapper.find(EnterpriseSearchPageTemplateWrapper).prop('emptyState')).toEqual(<div />);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { VECTOR_SEARCH_PLUGIN } from '../../../../../common/constants';
|
||||
import { SetVectorSearchChrome } from '../../../shared/kibana_chrome';
|
||||
import {
|
||||
EnterpriseSearchPageTemplateWrapper,
|
||||
PageTemplateProps,
|
||||
useEnterpriseSearchNav,
|
||||
} from '../../../shared/layout';
|
||||
import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry';
|
||||
|
||||
export const EnterpriseSearchVectorSearchPageTemplate: React.FC<PageTemplateProps> = ({
|
||||
children,
|
||||
pageChrome,
|
||||
pageViewTelemetry,
|
||||
...pageTemplateProps
|
||||
}) => (
|
||||
<EnterpriseSearchPageTemplateWrapper
|
||||
{...pageTemplateProps}
|
||||
solutionNav={{
|
||||
items: useEnterpriseSearchNav(),
|
||||
name: VECTOR_SEARCH_PLUGIN.NAME,
|
||||
}}
|
||||
setPageChrome={pageChrome && <SetVectorSearchChrome trail={pageChrome} />}
|
||||
>
|
||||
{pageViewTelemetry && (
|
||||
<SendEnterpriseSearchTelemetry action="viewed" metric={pageViewTelemetry} />
|
||||
)}
|
||||
{children}
|
||||
</EnterpriseSearchPageTemplateWrapper>
|
||||
);
|
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { useValues } from 'kea';
|
||||
|
||||
import {
|
||||
EuiCard,
|
||||
EuiCode,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiHorizontalRule,
|
||||
EuiIcon,
|
||||
EuiLink,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { ESRE_PLUGIN } from '../../../../../common/constants';
|
||||
import elserIllustration from '../../../../assets/images/elser.svg';
|
||||
import nlpIllustration from '../../../../assets/images/nlp.svg';
|
||||
import { docLinks } from '../../../shared/doc_links';
|
||||
import { KibanaLogic } from '../../../shared/kibana';
|
||||
import { SetVectorSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
|
||||
import { DevToolsConsoleCodeBlock } from '../dev_tools_console_code_block/dev_tools_console_code_block';
|
||||
import { EnterpriseSearchVectorSearchPageTemplate } from '../layout/page_template';
|
||||
|
||||
const CREATE_INDEX_SNIPPET = `PUT /image-index
|
||||
{
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"image-vector": {
|
||||
"type": "dense_vector",
|
||||
"dims": 3,
|
||||
"index": true,
|
||||
"similarity": "l2_norm"
|
||||
},
|
||||
"title-vector": {
|
||||
"type": "dense_vector",
|
||||
"dims": 5,
|
||||
"index": true,
|
||||
"similarity": "l2_norm"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"file-type": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const INGEST_SNIPPET = `POST /image-index/_bulk?refresh=true
|
||||
{ "index": { "_id": "1" } }
|
||||
{ "image-vector": [1, 5, -20], "title-vector": [12, 50, -10, 0, 1], "title": "moose family", "file-type": "jpg" }
|
||||
{ "index": { "_id": "2" } }
|
||||
{ "image-vector": [42, 8, -15], "title-vector": [25, 1, 4, -12, 2], "title": "alpine lake", "file-type": "png" }
|
||||
{ "index": { "_id": "3" } }
|
||||
{ "image-vector": [15, 11, 23], "title-vector": [1, 5, 25, 50, 20], "title": "full moon", "file-type": "jpg" }`;
|
||||
|
||||
const QUERY_SNIPPET = `POST /image-index/_search
|
||||
{
|
||||
"knn": {
|
||||
"field": "image-vector",
|
||||
"query_vector": [-5, 9, -12],
|
||||
"k": 10,
|
||||
"num_candidates": 100
|
||||
},
|
||||
"fields": [ "title", "file-type" ]
|
||||
}`;
|
||||
|
||||
export const VectorSearchGuide: React.FC = () => {
|
||||
const { application } = useValues(KibanaLogic);
|
||||
|
||||
return (
|
||||
<EnterpriseSearchVectorSearchPageTemplate
|
||||
restrictWidth
|
||||
pageHeader={{
|
||||
description: (
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.description"
|
||||
defaultMessage="Elasticsearch can be used as a vector database and search along with other semantic search methods."
|
||||
/>{' '}
|
||||
<EuiLink href={docLinks.knnSearch} target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.descriptionLink"
|
||||
defaultMessage="Learn more about vector searches."
|
||||
/>
|
||||
</EuiLink>
|
||||
</p>
|
||||
),
|
||||
pageTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.pageTitle"
|
||||
defaultMessage="Get started with vector search"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<SetPageChrome />
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem grow={4}>
|
||||
<EuiTitle>
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.createIndex.title"
|
||||
defaultMessage="Create an index"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.createIndex.description"
|
||||
defaultMessage="Start by creating an index with one or more {denseVector} fields."
|
||||
values={{ denseVector: <EuiCode>dense_vector</EuiCode> }}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={6}>
|
||||
<DevToolsConsoleCodeBlock>{CREATE_INDEX_SNIPPET}</DevToolsConsoleCodeBlock>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem grow={4}>
|
||||
<EuiTitle>
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.ingest.title"
|
||||
defaultMessage="Ingest your data"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.ingest.description"
|
||||
defaultMessage="Add data to your index to make it searchable."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={6}>
|
||||
<DevToolsConsoleCodeBlock>{INGEST_SNIPPET}</DevToolsConsoleCodeBlock>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem grow={4}>
|
||||
<EuiTitle>
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.query.title"
|
||||
defaultMessage="Build your vector search query"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.query.description"
|
||||
defaultMessage="Now you're ready to explore your data with searches and aggregations."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={6}>
|
||||
<DevToolsConsoleCodeBlock>{QUERY_SNIPPET}</DevToolsConsoleCodeBlock>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem grow={4}>
|
||||
<EuiTitle>
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.deployedModel.title"
|
||||
defaultMessage="Don’t have a model deployed?"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.deployedModel.description"
|
||||
defaultMessage="Elastic can help you generate embeddings."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={6}>
|
||||
<EuiFlexGroup gutterSize="l" direction="column">
|
||||
<EuiCard
|
||||
onClick={() =>
|
||||
application.navigateToApp(ESRE_PLUGIN.URL.replace(/^(?:\/app\/)?(.*)$/, '$1'))
|
||||
}
|
||||
layout="horizontal"
|
||||
titleSize="s"
|
||||
icon={<EuiIcon type={elserIllustration} size="xxl" />}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.deployedModel.elser.title"
|
||||
defaultMessage="Elastic Learned Sparse Encoder (ELSER)"
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.deployedModel.elser.description"
|
||||
defaultMessage="Learn about the configuration-free semantic search"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<EuiCard
|
||||
href={docLinks.textEmbedding}
|
||||
target="_blank"
|
||||
layout="horizontal"
|
||||
titleSize="s"
|
||||
icon={<EuiIcon type={nlpIllustration} size="xxl" />}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.deployedModel.byoModel.title"
|
||||
defaultMessage="Run your models in Elastic"
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.vectorSearch.guide.deployedModel.byoModel.description"
|
||||
defaultMessage="Learn how to load in compatible third-party models"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EnterpriseSearchVectorSearchPageTemplate>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Switch } from 'react-router-dom';
|
||||
|
||||
import { Route } from '@kbn/shared-ux-router';
|
||||
|
||||
import { isVersionMismatch } from '../../../common/is_version_mismatch';
|
||||
import { InitialAppData } from '../../../common/types';
|
||||
import { VersionMismatchPage } from '../shared/version_mismatch';
|
||||
|
||||
import { VectorSearchGuide } from './components/vector_search_guide/vector_search_guide';
|
||||
|
||||
import { ROOT_PATH } from './routes';
|
||||
|
||||
export const EnterpriseSearchVectorSearch: React.FC<InitialAppData> = (props) => {
|
||||
const { enterpriseSearchVersion, kibanaVersion } = props;
|
||||
const incompatibleVersions = isVersionMismatch(enterpriseSearchVersion, kibanaVersion);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path={ROOT_PATH}>
|
||||
{incompatibleVersions ? (
|
||||
<VersionMismatchPage
|
||||
enterpriseSearchVersion={enterpriseSearchVersion}
|
||||
kibanaVersion={kibanaVersion}
|
||||
/>
|
||||
) : (
|
||||
<VectorSearchGuide />
|
||||
)}
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const ROOT_PATH = '/';
|
|
@ -33,8 +33,9 @@ import {
|
|||
ESRE_PLUGIN,
|
||||
ENTERPRISE_SEARCH_CONTENT_PLUGIN,
|
||||
ENTERPRISE_SEARCH_OVERVIEW_PLUGIN,
|
||||
WORKPLACE_SEARCH_PLUGIN,
|
||||
SEARCH_EXPERIENCES_PLUGIN,
|
||||
VECTOR_SEARCH_PLUGIN,
|
||||
WORKPLACE_SEARCH_PLUGIN,
|
||||
} from '../common/constants';
|
||||
import { ClientConfigType, InitialAppData } from '../common/types';
|
||||
|
||||
|
@ -163,6 +164,27 @@ export class EnterpriseSearchPlugin implements Plugin {
|
|||
title: ESRE_PLUGIN.NAV_TITLE,
|
||||
});
|
||||
|
||||
core.application.register({
|
||||
appRoute: VECTOR_SEARCH_PLUGIN.URL,
|
||||
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
|
||||
euiIconType: VECTOR_SEARCH_PLUGIN.LOGO,
|
||||
id: VECTOR_SEARCH_PLUGIN.ID,
|
||||
mount: async (params: AppMountParameters) => {
|
||||
const kibanaDeps = await this.getKibanaDeps(core, params, cloud);
|
||||
const { chrome, http } = kibanaDeps.core;
|
||||
chrome.docTitle.change(VECTOR_SEARCH_PLUGIN.NAME);
|
||||
|
||||
this.getInitialData(http);
|
||||
const pluginData = this.getPluginData();
|
||||
|
||||
const { renderApp } = await import('./applications');
|
||||
const { EnterpriseSearchVectorSearch } = await import('./applications/vector_search');
|
||||
|
||||
return renderApp(EnterpriseSearchVectorSearch, kibanaDeps, pluginData);
|
||||
},
|
||||
title: VECTOR_SEARCH_PLUGIN.NAV_TITLE,
|
||||
});
|
||||
|
||||
core.application.register({
|
||||
appRoute: ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL,
|
||||
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
|
||||
|
|
|
@ -186,6 +186,7 @@ export class EnterpriseSearchPlugin implements Plugin {
|
|||
enterpriseSearchAnalytics: showEnterpriseSearch,
|
||||
enterpriseSearchApplications: showEnterpriseSearch,
|
||||
enterpriseSearchEsre: showEnterpriseSearch,
|
||||
enterpriseSearchVectorSearch: showEnterpriseSearch,
|
||||
elasticsearch: showEnterpriseSearch,
|
||||
appSearch: hasAppSearchAccess && config.canDeployEntSearch,
|
||||
workplaceSearch: hasWorkplaceSearchAccess && config.canDeployEntSearch,
|
||||
|
@ -197,6 +198,7 @@ export class EnterpriseSearchPlugin implements Plugin {
|
|||
enterpriseSearchAnalytics: showEnterpriseSearch,
|
||||
enterpriseSearchApplications: showEnterpriseSearch,
|
||||
enterpriseSearchEsre: showEnterpriseSearch,
|
||||
enterpriseSearchVectorSearch: showEnterpriseSearch,
|
||||
elasticsearch: showEnterpriseSearch,
|
||||
appSearch: hasAppSearchAccess && config.canDeployEntSearch,
|
||||
workplaceSearch: hasWorkplaceSearchAccess && config.canDeployEntSearch,
|
||||
|
|
|
@ -68,6 +68,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'enterpriseSearchEsre',
|
||||
'enterpriseSearchVectorSearch',
|
||||
'elasticsearch',
|
||||
'appSearch',
|
||||
'workplaceSearch',
|
||||
|
@ -97,6 +98,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'enterpriseSearchEsre',
|
||||
'enterpriseSearchVectorSearch',
|
||||
'elasticsearch',
|
||||
'appSearch',
|
||||
'workplaceSearch',
|
||||
|
|
|
@ -55,6 +55,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'enterpriseSearchEsre',
|
||||
'enterpriseSearchVectorSearch',
|
||||
'appSearch',
|
||||
'workplaceSearch'
|
||||
)
|
||||
|
@ -73,6 +74,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'enterpriseSearchEsre',
|
||||
'enterpriseSearchVectorSearch',
|
||||
'appSearch',
|
||||
'workplaceSearch',
|
||||
'guidedOnboardingFeature'
|
||||
|
|
|
@ -32,6 +32,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'enterpriseSearchEsre',
|
||||
'enterpriseSearchVectorSearch',
|
||||
'elasticsearch',
|
||||
'appSearch',
|
||||
'workplaceSearch',
|
||||
|
|
|
@ -24,6 +24,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'enterpriseSearchEsre',
|
||||
'enterpriseSearchVectorSearch',
|
||||
'appSearch',
|
||||
'workplaceSearch',
|
||||
];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue