[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:
Sloane Perrault 2023-06-23 09:23:25 -04:00 committed by GitHub
parent 6b7ec77cc2
commit 566d4ddc5a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 744 additions and 5 deletions

View file

@ -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`,
},

View file

@ -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;
};

View file

@ -138,6 +138,7 @@ export const applicationUsageSchema = {
enterpriseSearchAnalytics: commonSchema,
enterpriseSearchApplications: commonSchema,
enterpriseSearchEsre: commonSchema,
enterpriseSearchVectorSearch: commonSchema,
elasticsearch: commonSchema,
appSearch: commonSchema,
workplaceSearch: commonSchema,

View file

@ -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": {

View file

@ -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 = {

View file

@ -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;

View file

@ -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]);

View file

@ -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]);

View file

@ -15,4 +15,5 @@ export {
SetWorkplaceSearchChrome,
SetSearchExperiencesChrome,
SetEnterpriseSearchApplicationsChrome,
SetVectorSearchChrome,
} from './set_chrome';

View file

@ -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();

View file

@ -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',

View file

@ -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', {

View file

@ -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>
);
};

View file

@ -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 />);
});
});
});

View file

@ -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>
);

View file

@ -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="Dont 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>
);
};

View file

@ -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>
);
};

View file

@ -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 = '/';

View file

@ -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,

View file

@ -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,

View file

@ -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',

View file

@ -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'

View file

@ -32,6 +32,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) {
'enterpriseSearchAnalytics',
'enterpriseSearchApplications',
'enterpriseSearchEsre',
'enterpriseSearchVectorSearch',
'elasticsearch',
'appSearch',
'workplaceSearch',

View file

@ -24,6 +24,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
'enterpriseSearchAnalytics',
'enterpriseSearchApplications',
'enterpriseSearchEsre',
'enterpriseSearchVectorSearch',
'appSearch',
'workplaceSearch',
];