Skip to content

Commit

Permalink
Add JSONPath examples / details in relevant modals (#542)
Browse files Browse the repository at this point in the history
* Tune wording on jsonarray too large

Signed-off-by: Tyler Ohlsen <[email protected]>

* Add JSONPath details table to all relevant modals

Signed-off-by: Tyler Ohlsen <[email protected]>

---------

Signed-off-by: Tyler Ohlsen <[email protected]>
(cherry picked from commit 67c0bf9)
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
github-actions[bot] committed Dec 18, 2024
1 parent 05bc411 commit 63b6612
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 54 deletions.
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

0 comments on commit 63b6612

Please sign in to comment.