Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.x] Add JSONPath examples / details in relevant modals #543

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export const NORMALIZATION_PROCESSOR_LINK =
'https://opensearch.org/docs/latest/search-plugins/search-pipelines/normalization-processor/';
export const GITHUB_FEEDBACK_LINK =
'https://github.com/opensearch-project/dashboards-flow-framework/issues/new/choose';
export const JSONPATH_DOCS_LINK = 'https://www.npmjs.com/package/jsonpath';

/**
* Text chunking algorithm constants
Expand Down
63 changes: 59 additions & 4 deletions public/general_components/jsonpath_examples_table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import {
EuiText,
EuiFlexGroup,
EuiFlexItem,
EuiLink,
EuiSpacer,
} from '@elastic/eui';
import { JSONPATH_DOCS_LINK } from '../../common';

interface JsonPathExamplesTableProps {
headerText?: string;
Expand All @@ -25,9 +28,55 @@ type JSONPathExample = {

const examples = [
{
expression: '$.data',
meaning: 'The entire input',
example: '$.data',
expression: '$',
meaning: 'The root object / element',
example: '$.my_field',
},
{
expression: '.',
meaning: 'Child member operator',
example: 'my_field.my_sub_field',
},
{
expression: '..',
meaning:
'Recursive descendant operator, to specify an object field in an array of objects',
example: '$..my_field',
},
{
expression: '*',
meaning: 'Wildcard matching all objects',
example: 'my_array.*',
},
{
expression: '[]',
meaning: 'Subscript operator',
example: 'my_array[0]',
},
{
expression: '[,]',
meaning: 'Union operator for alternate names or array indices as a set',
example: 'my_array[0,1]',
},
{
expression: '[start:end:step]',
meaning: 'Array slice operator borrowed from ES4 / Python',
example: 'my_array[0:5]',
},
{
expression: '@',
meaning: 'The current object / element in an array',
example: 'my_array[?(@.price<10)]',
},
{
expression: '()',
meaning: 'Script expression via static evaluation',
example: 'my_array[?(@.price<10)]',
},
{
expression: '?()',
meaning: 'Applies a filter (script) expression via static evaluation',
example: 'my_array[?(@.price<10)]',
},
] as JSONPathExample[];

Expand Down Expand Up @@ -61,7 +110,7 @@ const columns = [
*/
export function JsonPathExamplesTable(props: JsonPathExamplesTableProps) {
return (
<EuiFlexItem style={{ width: '20vw' }}>
<EuiFlexItem style={{ width: '40vw' }}>
<EuiFlexGroup direction="column" gutterSize="xs">
{!isEmpty(props.headerText) && (
<EuiFlexItem grow={false}>
Expand All @@ -77,6 +126,12 @@ export function JsonPathExamplesTable(props: JsonPathExamplesTableProps) {
hasActions={false}
/>
</EuiFlexItem>
<EuiSpacer size="s" />
<EuiFlexItem grow={false}>
<EuiLink href={JSONPATH_DOCS_LINK} target="_blank">
More examples & documentation
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,19 +194,11 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIconTip
content={`Specify a ${
content={`Specify how to transform your input ${
props.context === PROCESSOR_CONTEXT.SEARCH_REQUEST
? 'query'
: 'document'
} field or define JSONPath to transform the ${
props.context === PROCESSOR_CONTEXT.SEARCH_REQUEST
? 'query'
: 'document'
} to map to a model input field.${
props.context === PROCESSOR_CONTEXT.SEARCH_RESPONSE
? ` Or, if you'd like to include data from the the original query request, prefix your mapping with "${REQUEST_PREFIX}" or "${REQUEST_PREFIX_WITH_JSONPATH_ROOT_SELECTOR}" - for example, "_request.query.match.my_field"`
: ''
}`}
: 'documents'
} and map to your model input fields`}
position="right"
/>
</EuiFlexItem>
Expand All @@ -222,13 +214,23 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
isDataFetchingAvailable={isInputPreviewAvailable}
/>
<EuiSpacer size="l" />
<EuiFlexGroup direction="row" justifyContent="spaceBetween">
<EuiFlexGroup direction="row" gutterSize="xs">
<EuiFlexItem grow={false}>
<EuiText
size="m"
style={{ marginTop: '4px' }}
>{`Outputs`}</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIconTip
content={`Specify how to transform your model outputs to new ${
props.context === PROCESSOR_CONTEXT.SEARCH_REQUEST
? 'query'
: 'document'
} fields`}
position="right"
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="s" />
<ModelOutputs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
EuiSmallButtonEmpty,
EuiSpacer,
EuiIconTip,
EuiPopover,
} from '@elastic/eui';
import {
customStringify,
Expand All @@ -38,7 +39,6 @@ import {
WorkflowConfig,
WorkflowFormValues,
REQUEST_PREFIX,
REQUEST_PREFIX_WITH_JSONPATH_ROOT_SELECTOR,
QueryParam,
} from '../../../../../../../common';
import {
Expand All @@ -61,7 +61,10 @@ import {
useAppDispatch,
} from '../../../../../../store';
import { getCore } from '../../../../../../services';
import { QueryParamsList } from '../../../../../../general_components';
import {
JsonPathExamplesTable,
QueryParamsList,
} from '../../../../../../general_components';

interface ConfigureExpressionModalProps {
uiConfig: WorkflowConfig;
Expand Down Expand Up @@ -135,6 +138,9 @@ export function ConfigureExpressionModal(props: ConfigureExpressionModalProps) {
// button updating state
const [isUpdating, setIsUpdating] = useState<boolean>(false);

// JSONPath details popover state
const [popoverOpen, setPopoverOpen] = useState<boolean>(false);

// validation state utilizing the model interface, if applicable. undefined if
// there is no model interface and/or no source input
const [isValid, setIsValid] = useState<boolean | undefined>(undefined);
Expand Down Expand Up @@ -306,27 +312,39 @@ export function ConfigureExpressionModal(props: ConfigureExpressionModalProps) {
<EuiFlexItem grow={false}>
<EuiFlexGroup direction="row" gutterSize="m">
<EuiFlexItem grow={KEY_FLEX_RATIO}>
<EuiFlexGroup direction="row" gutterSize="none">
<EuiFlexGroup
direction="row"
justifyContent="spaceBetween"
>
<EuiFlexItem grow={false}>
<EuiText size="xs" color="subdued">
<EuiText size="s" color="subdued">
{`Expression`}
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIconTip
content={`Define JSONPath to transform the ${
props.context ===
PROCESSOR_CONTEXT.SEARCH_REQUEST
? 'query'
: 'document'
} to map to a model input field.${
props.context ===
PROCESSOR_CONTEXT.SEARCH_RESPONSE
? ` Or, if you'd like to include data from the the original query request, prefix your mapping with "${REQUEST_PREFIX}" or "${REQUEST_PREFIX_WITH_JSONPATH_ROOT_SELECTOR}" - for example, "_request.query.match.my_field"`
: ''
}`}
position="right"
/>
<EuiPopover
isOpen={popoverOpen}
initialFocus={false}
anchorPosition="downCenter"
closePopover={() => setPopoverOpen(false)}
button={
<EuiSmallButtonEmpty
style={{ marginTop: '-4px' }}
onClick={() => setPopoverOpen(!popoverOpen)}
>
Learn more
</EuiSmallButtonEmpty>
}
>
<JsonPathExamplesTable
headerText={`Define JSONPath to transform your input ${
props.context ===
PROCESSOR_CONTEXT.SEARCH_REQUEST
? 'query'
: 'documents'
} to map to a model input field.`}
/>
</EuiPopover>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
Expand All @@ -339,18 +357,29 @@ export function ConfigureExpressionModal(props: ConfigureExpressionModalProps) {
<EuiSpacer size="s" />
<EuiFlexGroup direction="row" gutterSize="m">
<EuiFlexItem grow={KEY_FLEX_RATIO}>
<TextField
fullWidth={true}
fieldPath={`expression`}
placeholder={`$.data`}
showError={true}
/>
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexItem grow={false}>
<TextField
fullWidth={true}
fieldPath={`expression`}
placeholder={`$.data`}
showError={true}
/>
</EuiFlexItem>
{props.context ===
PROCESSOR_CONTEXT.SEARCH_RESPONSE && (
<EuiFlexItem grow={false}>
<EuiText size="xs" color="subdued">
{`Tip: to include data from the the original query request, prefix your expression with "${REQUEST_PREFIX}" - for example, "_request.query.match.my_field"`}
</EuiText>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={VALUE_FLEX_RATIO}>
<EuiText>{props.modelInputFieldName}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="s" />
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
EuiSmallButtonEmpty,
EuiSpacer,
EuiSmallButtonIcon,
EuiIconTip,
EuiPopover,
} from '@elastic/eui';
import {
customStringify,
Expand Down Expand Up @@ -59,7 +59,10 @@ import {
useAppDispatch,
} from '../../../../../../store';
import { getCore } from '../../../../../../services';
import { QueryParamsList } from '../../../../../../general_components';
import {
JsonPathExamplesTable,
QueryParamsList,
} from '../../../../../../general_components';

interface ConfigureMultiExpressionModalProps {
uiConfig: WorkflowConfig;
Expand Down Expand Up @@ -143,6 +146,9 @@ export function ConfigureMultiExpressionModal(
// button updating state
const [isUpdating, setIsUpdating] = useState<boolean>(false);

// JSONPath details popover state
const [popoverOpen, setPopoverOpen] = useState<boolean>(false);

// source input / transformed input state
const [sourceInput, setSourceInput] = useState<string>('{}');
const [transformedInput, setTransformedInput] = useState<string>('{}');
Expand Down Expand Up @@ -278,18 +284,39 @@ export function ConfigureMultiExpressionModal(
<EuiFlexItem grow={false}>
<EuiFlexGroup direction="row" gutterSize="s">
<EuiFlexItem grow={KEY_FLEX_RATIO}>
<EuiFlexGroup direction="row" gutterSize="xs">
<EuiFlexGroup
direction="row"
justifyContent="spaceBetween"
>
<EuiFlexItem grow={false}>
<EuiText size="s" color="subdued">
{`Expression`}
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIconTip
content={`Define any number of JSONPath transforms to extract out parts of the model output's source data and
store in new document fields.`}
position="right"
/>
<EuiPopover
isOpen={popoverOpen}
initialFocus={false}
anchorPosition="downCenter"
closePopover={() => setPopoverOpen(false)}
button={
<EuiSmallButtonEmpty
style={{ marginTop: '-4px' }}
onClick={() => setPopoverOpen(!popoverOpen)}
>
Learn more
</EuiSmallButtonEmpty>
}
>
<JsonPathExamplesTable
headerText={`Define JSONPath to transform your model outputs to new ${
props.context ===
PROCESSOR_CONTEXT.SEARCH_REQUEST
? 'query'
: 'document'
} fields.`}
/>
</EuiPopover>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
Expand Down
Loading
Loading