diff --git a/.babelrc b/.babelrc
index d9073a59..2a3340e9 100644
--- a/.babelrc
+++ b/.babelrc
@@ -14,3 +14,4 @@
["@babel/plugin-transform-runtime", { "regenerator": true }]
]
}
+
diff --git a/common/index.ts b/common/index.ts
index fcb74f26..a670499d 100644
--- a/common/index.ts
+++ b/common/index.ts
@@ -8,6 +8,9 @@ export const PLUGIN_NAME = 'Search Relevance';
export enum ServiceEndpoints {
GetIndexes = '/api/relevancy/search/indexes',
+ GetPipelines = '/api/relevancy/search/pipelines',
GetSearchResults = '/api/relevancy/search',
GetStats = '/api/relevancy/stats',
}
+
+export const SEARCH_API = '/_search';
diff --git a/public/components/query_compare/home.tsx b/public/components/query_compare/home.tsx
index 26582fa4..0ef866c0 100644
--- a/public/components/query_compare/home.tsx
+++ b/public/components/query_compare/home.tsx
@@ -4,7 +4,7 @@
*/
import React, { useEffect } from 'react';
-import { NavigationPublicPluginStart } from 'src/plugins/navigation/public';
+import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public';
import { CoreStart, ChromeBreadcrumb } from '../../../../../src/core/public';
import '../../ace-themes/sql_console';
import { CreateIndex } from './create_index';
@@ -25,7 +25,6 @@ interface QueryExplorerProps {
setToast: (title: string, color?: string, text?: any, side?: string) => void;
chrome: CoreStart['chrome'];
}
-
export const Home = ({
parentBreadCrumbs,
notifications,
@@ -35,19 +34,28 @@ export const Home = ({
setToast,
chrome,
}: QueryExplorerProps) => {
- const { documentsIndexes, setDocumentsIndexes, showFlyout } = useSearchRelevanceContext();
+ const {
+ documentsIndexes,
+ setDocumentsIndexes,
+ pipelines,
+ setPipelines,
+ showFlyout,
+ } = useSearchRelevanceContext();
useEffect(() => {
setBreadcrumbs([...parentBreadCrumbs]);
}, [setBreadcrumbs, parentBreadCrumbs]);
- // Get Indexes
+ // Get Indexes and Pipelines
useEffect(() => {
http.get(ServiceEndpoints.GetIndexes).then((res: DocumentsIndex[]) => {
setDocumentsIndexes(res);
});
- }, [http, setDocumentsIndexes]);
+ http.get(ServiceEndpoints.GetPipelines).then((res: {}) => {
+ setPipelines(res);
+ });
+ }, [http, setDocumentsIndexes, setPipelines]);
return (
<>
diff --git a/public/components/query_compare/search_result/index.tsx b/public/components/query_compare/search_result/index.tsx
index 4d27fb10..7e6de1ad 100644
--- a/public/components/query_compare/search_result/index.tsx
+++ b/public/components/query_compare/search_result/index.tsx
@@ -41,6 +41,8 @@ export const SearchResult = ({ http }: SearchResultProps) => {
updateComparedResult2,
selectedIndex1,
selectedIndex2,
+ pipeline1,
+ pipeline2,
} = useSearchRelevanceContext();
const onClickSearch = () => {
@@ -86,6 +88,7 @@ export const SearchResult = ({ http }: SearchResultProps) => {
const handleQuery = (
queryError: QueryError,
selectedIndex: string,
+ pipeline: string,
jsonQuery: any,
updateComparedResult: (result: SearchResults) => void,
setQueryResult: React.Dispatch
>,
@@ -97,7 +100,7 @@ export const SearchResult = ({ http }: SearchResultProps) => {
updateComparedResult({} as any);
} else if (!queryError.queryString.length && !queryError.selectIndex) {
setQueryError(initialQueryErrorState);
- return { index: selectedIndex, ...jsonQuery };
+ return { index: selectedIndex, pipeline, ...jsonQuery };
}
};
@@ -106,6 +109,7 @@ export const SearchResult = ({ http }: SearchResultProps) => {
query1: handleQuery(
queryErrors[0],
selectedIndex1,
+ pipeline1,
jsonQueries[0],
updateComparedResult1,
setQueryResult1,
@@ -114,6 +118,7 @@ export const SearchResult = ({ http }: SearchResultProps) => {
query2: handleQuery(
queryErrors[1],
selectedIndex2,
+ pipeline2,
jsonQueries[1],
updateComparedResult2,
setQueryResult2,
diff --git a/public/components/query_compare/search_result/result_components/result_components.tsx b/public/components/query_compare/search_result/result_components/result_components.tsx
index 8844caf1..c64886e9 100644
--- a/public/components/query_compare/search_result/result_components/result_components.tsx
+++ b/public/components/query_compare/search_result/result_components/result_components.tsx
@@ -29,9 +29,7 @@ const InitialState = () => {
No results}
- body={
- Add at least one query to display search results.
- }
+ body={Add at least one query to display search results.
}
/>
);
diff --git a/public/components/query_compare/search_result/search_components/__tests__/__snapshots__/search_config.test.tsx.snap b/public/components/query_compare/search_result/search_components/__tests__/__snapshots__/search_config.test.tsx.snap
index 36d1e463..5f19165d 100644
--- a/public/components/query_compare/search_result/search_components/__tests__/__snapshots__/search_config.test.tsx.snap
+++ b/public/components/query_compare/search_result/search_components/__tests__/__snapshots__/search_config.test.tsx.snap
@@ -3,6 +3,7 @@
exports[`Flyout component Renders flyout component 1`] = `
-
+
-
-
-
-
-
-
-
+
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Optional
-
+
-
-
-
+
+
+
-
+
-
+
-
-
-
-
-
-
-
+
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Optional
-
+
-
-
-
+
+
+
-
+
-
+
-
-
-
-
-
-
-
+
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Optional
-
+
-
-
-
+
+
+
-
+
{
it('Renders flyout component', async () => {
const setQueryString = jest.fn();
const setSelectedIndex = jest.fn();
+ const setPipeline = jest.fn();
const setQueryError = jest.fn();
const wrapper = mount(
@@ -27,6 +28,8 @@ describe('Flyout component', () => {
setQueryString={setQueryString}
selectedIndex={''}
setSelectedIndex={setSelectedIndex}
+ pipeline={''}
+ setPipeline={setPipeline}
queryError={initialQueryErrorState}
setQueryError={setQueryError}
/>
@@ -39,9 +42,14 @@ describe('Flyout component', () => {
wrapper.find('EuiCodeEditor').prop('onChange')?.({ target: { value: '' } });
wrapper.find('EuiSelect').prop('onChange')?.({ target: {} });
wrapper.find('EuiSelect').prop('onBlur')?.({ target: {} });
+ wrapper.find('EuiComboBox').prop('onChange')?.({ target: { selectedPipelineOptions: [] } });
+ wrapper.find('EuiComboBox').prop('onChange')?.({
+ target: { selectedPipelineOptions: [{ label: '_none' }] },
+ });
});
expect(setQueryString).toHaveBeenCalledTimes(1);
expect(setSelectedIndex).toHaveBeenCalledTimes(1);
+ expect(setPipeline).toHaveBeenCalledTimes(2);
expect(setQueryError).toHaveBeenCalledTimes(3);
});
});
diff --git a/public/components/query_compare/search_result/search_components/search_configs/search_config.tsx b/public/components/query_compare/search_result/search_components/search_configs/search_config.tsx
index 58180d0d..654ad012 100644
--- a/public/components/query_compare/search_result/search_components/search_configs/search_config.tsx
+++ b/public/components/query_compare/search_result/search_components/search_configs/search_config.tsx
@@ -12,6 +12,9 @@ import {
EuiCodeEditor,
EuiText,
EuiButtonEmpty,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiComboBox,
} from '@elastic/eui';
import { useSearchRelevanceContext } from '../../../../../contexts';
@@ -25,6 +28,8 @@ interface SearchConfigProps {
setSelectedIndex: React.Dispatch>;
queryError: QueryError;
setQueryError: React.Dispatch>;
+ pipeline: string;
+ setPipeline: React.Dispatch>;
}
export const SearchConfig: FunctionComponent = ({
@@ -35,8 +40,10 @@ export const SearchConfig: FunctionComponent = ({
setSelectedIndex,
queryError,
setQueryError,
+ pipeline,
+ setPipeline,
}) => {
- const { documentsIndexes, setShowFlyout } = useSearchRelevanceContext();
+ const { documentsIndexes, pipelines, setShowFlyout } = useSearchRelevanceContext();
// On select index
const onChangeSelectedIndex: React.ChangeEventHandler = (e) => {
setSelectedIndex(e.target.value);
@@ -47,6 +54,20 @@ export const SearchConfig: FunctionComponent = ({
}));
};
+ // Sort search pipelines based off of each individual pipeline name.
+ const sortedPipelines = [...Object.keys(pipelines)]
+ .sort((a, b) => a.localeCompare(b))
+ .map((searchPipeline) => ({
+ label: searchPipeline,
+ }));
+ // Add the '_none' option to the pipeline dropdown (runs the index without a pipeline).
+ sortedPipelines.push({ label: '_none' });
+
+ // On select pipeline for ComboBox
+ const onChangePipeline = (selectedPipelineOptions: string | any[]) => {
+ setPipeline(selectedPipelineOptions[0]?.label || '');
+ };
+
// Select index on blur
const selectIndexOnBlur = () => {
// If Index Select on blur without selecting an index, show error
@@ -88,24 +109,39 @@ export const SearchConfig: FunctionComponent = ({
Query {queryNumber}
- {queryError.selectIndex}}
- isInvalid={!!queryError.selectIndex.length}
- >
- ({
- value: index,
- text: index,
- }))}
- aria-label="Search Index"
- onChange={onChangeSelectedIndex}
- value={selectedIndex}
- onBlur={selectIndexOnBlur}
- />
-
+
+
+ {queryError.selectIndex}}
+ isInvalid={!!queryError.selectIndex.length}
+ >
+ ({
+ value: index,
+ text: index,
+ }))}
+ aria-label="Search Index"
+ onChange={onChangeSelectedIndex}
+ value={selectedIndex}
+ onBlur={selectIndexOnBlur}
+ />
+
+
+
+
+
+
+
+
@@ -70,6 +76,8 @@ export const SearchConfigsPanel = ({
setSelectedIndex={setSelectedIndex2}
queryError={queryError2}
setQueryError={setQueryError2}
+ pipeline={pipeline2}
+ setPipeline={setPipeline2}
/>
diff --git a/public/contexts/index.tsx b/public/contexts/index.tsx
index d15f1de6..d0fd077f 100644
--- a/public/contexts/index.tsx
+++ b/public/contexts/index.tsx
@@ -21,6 +21,12 @@ export interface SearchRelevanceContextProps {
setSelectedIndex1: React.Dispatch>;
selectedIndex2: string;
setSelectedIndex2: React.Dispatch>;
+ pipelines: {};
+ setPipelines: React.Dispatch>;
+ pipeline1: string;
+ setPipeline1: React.Dispatch>;
+ pipeline2: string;
+ setPipeline2: React.Dispatch>;
}
export const SearchRelevanceContext = createContext(null);
@@ -42,6 +48,9 @@ export const SearchRelevanceContextProvider = ({ children }: { children: React.R
const [comparedResult2, setComparedResult2] = useState({});
const [selectedIndex1, setSelectedIndex1] = useState('');
const [selectedIndex2, setSelectedIndex2] = useState('');
+ const [pipelines, setPipelines] = useState<{}>({});
+ const [pipeline1, setPipeline1] = useState('');
+ const [pipeline2, setPipeline2] = useState('');
const updateComparedResult1 = (result: SearchResults) => {
setComparedResult1(getDocumentRank(result?.hits?.hits));
@@ -66,6 +75,12 @@ export const SearchRelevanceContextProvider = ({ children }: { children: React.R
setSelectedIndex1,
selectedIndex2,
setSelectedIndex2,
+ pipelines,
+ setPipelines,
+ pipeline1,
+ setPipeline1,
+ pipeline2,
+ setPipeline2,
}}
>
{children}
diff --git a/server/metrics/index.ts b/server/metrics/index.ts
index 11302658..7b874b27 100644
--- a/server/metrics/index.ts
+++ b/server/metrics/index.ts
@@ -20,4 +20,5 @@ export enum METRIC_ACTION {
COMPARISON_SEARCH = 'comparison_search',
SINGLE_SEARCH = 'single_search',
FETCH_INDEX = 'fetch_index',
+ FETCH_PIPELINE = 'fetch_pipeline',
}
diff --git a/server/routes/dsl_route.ts b/server/routes/dsl_route.ts
index bd64a8b6..94671f8c 100644
--- a/server/routes/dsl_route.ts
+++ b/server/routes/dsl_route.ts
@@ -8,7 +8,7 @@ import { RequestParams } from '@opensearch-project/opensearch';
import { IRouter } from '../../../../src/core/server';
import { METRIC_NAME, METRIC_ACTION } from '../metrics';
-import { ServiceEndpoints } from '../../common';
+import { ServiceEndpoints, SEARCH_API } from '../../common';
interface SearchResultsResponse {
result1?: Object;
@@ -29,15 +29,23 @@ export function registerDslRoute(router: IRouter) {
const { query1, query2 } = request.body;
const actionName =
query1 && query2 ? METRIC_ACTION.COMPARISON_SEARCH : METRIC_ACTION.SINGLE_SEARCH;
- let resBody: SearchResultsResponse = {};
+ const resBody: SearchResultsResponse = {};
if (query1) {
- const { index, size, ...rest } = query1;
- const params: RequestParams.Search = {
- index,
- size,
- body: rest,
- };
+ const { index, pipeline, size, ...rest } = query1;
+ const params: RequestParams.Search =
+ pipeline !== ''
+ ? {
+ index,
+ size,
+ body: rest,
+ search_pipeline: pipeline,
+ }
+ : {
+ index,
+ size,
+ body: rest,
+ };
const start = performance.now();
try {
@@ -75,12 +83,20 @@ export function registerDslRoute(router: IRouter) {
}
if (query2) {
- const { index, size, ...rest } = query2;
- const params: RequestParams.Search = {
- index,
- size,
- body: rest,
- };
+ const { index, pipeline, size, ...rest } = query2;
+ const params: RequestParams.Search =
+ pipeline !== ''
+ ? {
+ index,
+ size,
+ body: rest,
+ search_pipeline: pipeline,
+ }
+ : {
+ index,
+ size,
+ body: rest,
+ };
const start = performance.now();
try {
@@ -122,6 +138,7 @@ export function registerDslRoute(router: IRouter) {
}
);
+ // Get Indices
router.get(
{
path: ServiceEndpoints.GetIndexes,
@@ -163,4 +180,46 @@ export function registerDslRoute(router: IRouter) {
}
}
);
+
+ // Get Pipelines
+ router.get(
+ {
+ path: ServiceEndpoints.GetPipelines,
+ validate: {},
+ },
+ async (context, request, response) => {
+ const start = performance.now();
+ let resBody: any = {};
+ try {
+ const resp = await context.core.opensearch.client.asCurrentUser.transport.request({
+ method: 'GET',
+ path: `${SEARCH_API}/pipeline`,
+ });
+ resBody = resp.body;
+ const end = performance.now();
+ context.searchRelevance.metricsService.addMetric(
+ METRIC_NAME.SEARCH_RELEVANCE,
+ METRIC_ACTION.FETCH_PIPELINE,
+ 200,
+ end - start
+ );
+ return response.ok({
+ body: resBody,
+ });
+ } catch (error) {
+ const end = performance.now();
+ context.searchRelevance.metricsService.addMetric(
+ METRIC_NAME.SEARCH_RELEVANCE,
+ METRIC_ACTION.FETCH_PIPELINE,
+ error.statusCode,
+ end - start
+ );
+ if (error.statusCode !== 404) console.error(error);
+ return response.custom({
+ statusCode: error.statusCode || 500,
+ body: error.message,
+ });
+ }
+ }
+ );
}