Skip to content

Commit

Permalink
Add parameterized search (#509)
Browse files Browse the repository at this point in the history
Signed-off-by: Tyler Ohlsen <[email protected]>
  • Loading branch information
ohltyler authored Dec 5, 2024
1 parent 4f99083 commit ba2aff3
Show file tree
Hide file tree
Showing 21 changed files with 999 additions and 252 deletions.
29 changes: 29 additions & 0 deletions common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,3 +522,32 @@ export enum SOURCE_OPTIONS {
UPLOAD = 'upload',
EXISTING_INDEX = 'existing_index',
}
export enum INSPECTOR_TAB_ID {
INGEST = 'ingest',
QUERY = 'query',
ERRORS = 'errors',
RESOURCES = 'resources',
}

export const INSPECTOR_TABS = [
{
id: INSPECTOR_TAB_ID.INGEST,
name: 'Ingest response',
disabled: false,
},
{
id: INSPECTOR_TAB_ID.QUERY,
name: 'Search response',
disabled: false,
},
{
id: INSPECTOR_TAB_ID.ERRORS,
name: 'Errors',
disabled: false,
},
{
id: INSPECTOR_TAB_ID.RESOURCES,
name: 'Resources',
disabled: false,
},
];
8 changes: 8 additions & 0 deletions common/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,14 @@ export type QuickConfigureFields = {
llmResponseField?: string;
};

export type QueryParamType = 'Text' | 'Binary';

export type QueryParam = {
name: string;
type: QueryParamType;
value: string;
};

/**
********** OPENSEARCH TYPES/INTERFACES ************
*/
Expand Down
1 change: 1 addition & 0 deletions public/general_components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
export { MultiSelectFilter } from './multi_select_filter';
export { ProcessorsTitle } from './processors_title';
export { ExperimentalBadge } from './experimental_badge';
export { QueryParamsList } from './query_params_list';
export * from './service_card';
152 changes: 152 additions & 0 deletions public/general_components/query_params_list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { get } from 'lodash';
import {
EuiFlexGroup,
EuiFlexItem,
EuiText,
EuiFieldText,
EuiComboBox,
EuiCompressedFilePicker,
} from '@elastic/eui';
import { QueryParam, QueryParamType } from '../../common';

interface QueryParamsListProps {
queryParams: QueryParam[];
setQueryParams: (params: QueryParam[]) => void;
}

// The keys will be more static in general. Give more space for values where users
// will typically be writing out more complex transforms/configuration (in the case of ML inference processors).
const KEY_FLEX_RATIO = 3;
const TYPE_FLEX_RATIO = 2;
const VALUE_FLEX_RATIO = 5;

const OPTIONS = [
{
label: 'Text' as QueryParamType,
},
{
label: 'Binary' as QueryParamType,
},
];

/**
* Basic, reusable component for displaying a list of query parameters, and allowing
* users to freely enter values for each.
*/
export function QueryParamsList(props: QueryParamsListProps) {
return (
<>
{props.queryParams?.length > 0 && (
<EuiFlexItem grow={false}>
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexItem grow={false}>
<EuiFlexGroup direction="row" gutterSize="s">
<EuiFlexItem grow={KEY_FLEX_RATIO}>
<EuiText size="s" color="subdued">
Parameter
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={TYPE_FLEX_RATIO}>
<EuiText size="s" color="subdued">
Type
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={VALUE_FLEX_RATIO}>
<EuiText size="s" color="subdued">
Value
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
{props.queryParams.map((queryParam, idx) => {
return (
<EuiFlexItem grow={false} key={idx}>
<EuiFlexGroup direction="row" gutterSize="s">
<EuiFlexItem grow={KEY_FLEX_RATIO}>
<EuiText size="s" style={{ paddingTop: '4px' }}>
{queryParam.name}
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={TYPE_FLEX_RATIO}>
<EuiComboBox
fullWidth={true}
compressed={true}
placeholder={`Type`}
singleSelection={{ asPlainText: true }}
isClearable={false}
options={OPTIONS}
selectedOptions={[{ label: queryParam.type || 'Text' }]}
onChange={(options) => {
props.setQueryParams(
props.queryParams.map((qp, i) =>
i === idx
? { ...qp, type: get(options, '0.label') }
: qp
)
);
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={VALUE_FLEX_RATIO}>
{queryParam.type === 'Binary' ? (
// For binary filetypes, accept images
<EuiCompressedFilePicker
accept="image/*"
multiple={false}
initialPromptText="Select or drag and drop an image"
onChange={(files) => {
if (files && files.length > 0) {
const fileReader = new FileReader();
fileReader.onload = (e) => {
try {
const binaryData = e.target?.result as string;
const base64Str = binaryData.split(',')[1];
props.setQueryParams(
props.queryParams.map((qp, i) =>
i === idx
? { ...qp, value: base64Str }
: qp
)
);
} catch {}
};
fileReader.readAsDataURL(files[0]);
}
}}
display="default"
/>
) : (
// Default to freeform text input
<EuiFieldText
compressed={true}
fullWidth={true}
placeholder={`Value`}
value={queryParam.value}
onChange={(e) => {
props.setQueryParams(
props.queryParams.map((qp, i) =>
i === idx
? { ...qp, value: e?.target?.value }
: qp
)
);
}}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
);
})}
</EuiFlexGroup>
</EuiFlexItem>
)}
</>
);
}
6 changes: 5 additions & 1 deletion public/pages/workflow_detail/components/export_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ export function ExportModal(props: ExportModalProps) {
}, [props.workflow, selectedOption]);

return (
<EuiModal onClose={() => props.setIsExportModalOpen(false)}>
<EuiModal
maxWidth={false}
style={{ width: '70vw' }}
onClose={() => props.setIsExportModalOpen(false)}
>
<EuiModalHeader>
<EuiModalHeaderTitle>
<p>{`Export ${getCharacterLimitedString(
Expand Down
17 changes: 16 additions & 1 deletion public/pages/workflow_detail/resizable_workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '@elastic/eui';
import {
CONFIG_STEP,
INSPECTOR_TAB_ID,
Workflow,
WorkflowConfig,
customStringify,
Expand Down Expand Up @@ -78,9 +79,13 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
setIsToolsPanelOpen(!isToolsPanelOpen);
};

// ingest / search response states to be populated in the Tools panel
// Inspector panel state vars. Actions taken in the form can update the Inspector panel,
// hence we keep top-level vars here to pass to both form and inspector components.
const [ingestResponse, setIngestResponse] = useState<string>('');
const [queryResponse, setQueryResponse] = useState<string>('');
const [selectedInspectorTabId, setSelectedInspectorTabId] = useState<
INSPECTOR_TAB_ID
>(INSPECTOR_TAB_ID.INGEST);

// is valid workflow state, + associated hook to set it as such
const [isValidWorkflow, setIsValidWorkflow] = useState<boolean>(true);
Expand Down Expand Up @@ -132,6 +137,12 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
setSelectedStep={props.setSelectedStep}
setUnsavedIngestProcessors={props.setUnsavedIngestProcessors}
setUnsavedSearchProcessors={props.setUnsavedSearchProcessors}
displaySearchPanel={() => {
if (!isToolsPanelOpen) {
onToggleToolsChange();
}
setSelectedInspectorTabId(INSPECTOR_TAB_ID.QUERY);
}}
/>
</EuiResizablePanel>
<EuiResizableButton />
Expand Down Expand Up @@ -198,6 +209,10 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
workflow={props.workflow}
ingestResponse={ingestResponse}
queryResponse={queryResponse}
setQueryResponse={setQueryResponse}
selectedTabId={selectedInspectorTabId}
setSelectedTabId={setSelectedInspectorTabId}
selectedStep={props.selectedStep}
/>
</EuiResizablePanel>
</>
Expand Down
45 changes: 30 additions & 15 deletions public/pages/workflow_detail/tools/ingest/ingest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
*/

import React from 'react';
import { EuiCodeEditor } from '@elastic/eui';
import { isEmpty } from 'lodash';
import { EuiCodeEditor, EuiEmptyPrompt, EuiText } from '@elastic/eui';

interface IngestProps {
ingestResponse: string;
Expand All @@ -19,19 +20,33 @@ export function Ingest(props: IngestProps) {
// TODO: known issue with the editor where resizing the resizablecontainer does not
// trigger vertical scroll updates. Updating the window, or reloading the component
// by switching tabs etc. will refresh it correctly
<EuiCodeEditor
mode="json"
theme="textmate"
width="100%"
height="100%"
value={props.ingestResponse}
readOnly={true}
setOptions={{
fontSize: '12px',
autoScrollEditorIntoView: true,
wrap: true,
}}
tabSize={2}
/>
<>
{isEmpty(props.ingestResponse) ? (
<EuiEmptyPrompt
title={<h2>No data</h2>}
titleSize="s"
body={
<>
<EuiText size="s">Run ingest and view the response here.</EuiText>
</>
}
/>
) : (
<EuiCodeEditor
mode="json"
theme="textmate"
width="100%"
height="100%"
value={props.ingestResponse}
readOnly={true}
setOptions={{
fontSize: '12px',
autoScrollEditorIntoView: true,
wrap: true,
}}
tabSize={2}
/>
)}
</>
);
}
Loading

0 comments on commit ba2aff3

Please sign in to comment.