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] JSONPath bug fix & improved wording in input/output transforms #480

Merged
merged 1 commit into from
Nov 14, 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
2 changes: 1 addition & 1 deletion common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ export const ERROR_GETTING_WORKFLOW_MSG = 'Failed to retrieve template';
export const NO_TEMPLATES_FOUND_MSG = 'There are no templates';
export const NO_MODIFICATIONS_FOUND_TEXT =
'Template does not contain any modifications';
export const JSONPATH_ROOT_SELECTOR = '$.';
export const JSONPATH_ROOT_SELECTOR = '$';
export enum SORT_ORDER {
ASC = 'asc',
DESC = 'desc',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ import { MapField } from './map_field';

interface MapArrayFieldProps {
fieldPath: string; // the full path in string-form to the field (e.g., 'ingest.enrich.processors.text_embedding_processor.inputField')
helpText?: string;
keyTitle?: string;
keyPlaceholder?: string;
keyHelpText?: string;
valueTitle?: string;
valuePlaceholder?: string;
valueHelpText?: string;
onMapAdd?: (curArray: MapArrayFormValue) => void;
onMapDelete?: (idxToDelete: number) => void;
keyOptions?: { label: string }[];
Expand Down Expand Up @@ -123,11 +124,12 @@ export function MapArrayField(props: MapArrayFieldProps) {
<EuiPanel grow={true}>
<MapField
fieldPath={`${props.fieldPath}.${idx}`}
helpText={props.helpText}
keyTitle={props.keyTitle}
keyPlaceholder={props.keyPlaceholder}
keyHelpText={props.keyHelpText}
valueTitle={props.valueTitle}
valuePlaceholder={props.valuePlaceholder}
valueHelpText={props.valueHelpText}
keyOptions={props.keyOptions}
valueOptions={props.valueOptions}
addEntryButtonText={props.addMapEntryButtonText}
Expand All @@ -144,13 +146,14 @@ export function MapArrayField(props: MapArrayFieldProps) {
<EuiPanel grow={true}>
<MapField
fieldPath={`${props.fieldPath}.0`}
helpText={props.helpText}
keyTitle={props.keyTitle}
keyPlaceholder={props.keyPlaceholder}
keyHelpText={props.keyHelpText}
valueTitle={props.valueTitle}
valuePlaceholder={props.valuePlaceholder}
keyOptions={props.keyOptions}
valueOptions={props.valueOptions}
valueHelpText={props.valueHelpText}
addEntryButtonText={props.addMapEntryButtonText}
mappingDirection={props.mappingDirection}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ interface MapFieldProps {
fieldPath: string; // the full path in string-form to the field (e.g., 'ingest.enrich.processors.text_embedding_processor.inputField')
label?: string;
helpLink?: string;
helpText?: string;
keyTitle?: string;
keyPlaceholder?: string;
keyHelpText?: string;
valueTitle?: string;
valuePlaceholder?: string;
valueHelpText?: string;
keyOptions?: { label: string }[];
valueOptions?: { label: string }[];
addEntryButtonText?: string;
Expand Down Expand Up @@ -111,9 +112,21 @@ export function MapField(props: MapFieldProps) {
<EuiFlexItem style={{ marginBottom: '0px' }}>
<EuiFlexGroup direction="row" gutterSize="xs">
<EuiFlexItem grow={KEY_FLEX_RATIO}>
<EuiText size="s" color="subdued">
{props.keyTitle || 'Key'}
</EuiText>
<EuiFlexGroup direction="row" gutterSize="xs">
<EuiFlexItem grow={false}>
<EuiText size="s" color="subdued">
{props.keyTitle || 'Key'}
</EuiText>
</EuiFlexItem>
{props.keyHelpText && (
<EuiFlexItem grow={false}>
<EuiIconTip
content={props.keyHelpText}
position="right"
/>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={VALUE_FLEX_RATIO}>
<EuiFlexGroup direction="row" gutterSize="xs">
Expand All @@ -122,10 +135,10 @@ export function MapField(props: MapFieldProps) {
{props.valueTitle || 'Value'}
</EuiText>
</EuiFlexItem>
{props.helpText && (
{props.valueHelpText && (
<EuiFlexItem grow={false}>
<EuiIconTip
content={props.helpText}
content={props.valueHelpText}
position="right"
/>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,6 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
<EuiSpacer size="s" />
<MapArrayField
fieldPath={inputMapFieldPath}
helpText={`An array specifying how to map fields from the ingested document to the model’s input. Dot notation is used by default. To explicitly use JSONPath, please ensure to prepend with the
root object selector "${JSONPATH_ROOT_SELECTOR}"`}
keyTitle="Name"
keyPlaceholder="Name"
keyOptions={parseModelInputs(modelInterface)}
Expand All @@ -393,6 +391,7 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
? 'Specify a query field'
: 'Define a document field'
}
valueHelpText={`Specify a document field or define JSONPath to transform the document to map to a model input field.`}
valueOptions={
props.context === PROCESSOR_CONTEXT.INGEST
? docFields
Expand Down Expand Up @@ -435,10 +434,9 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
<EuiSpacer size="s" />
<MapArrayField
fieldPath={outputMapFieldPath}
helpText={`An array specifying how to map the model’s output to new document fields. Dot notation is used by default. To explicitly use JSONPath, please ensure to prepend with the
root object selector "${JSONPATH_ROOT_SELECTOR}"`}
keyTitle="Name"
keyPlaceholder="Name"
keyHelpText={`Specify a model output field or define JSONPath to transform the model output to map to a new document field.`}
keyOptions={
fullResponsePath
? undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import {
IngestPipelineConfig,
InputTransformFormValues,
InputTransformSchema,
JSONPATH_ROOT_SELECTOR,
MapArrayFormValue,
ModelInterface,
PROCESSOR_CONTEXT,
Expand Down Expand Up @@ -291,8 +290,6 @@ export function InputTransformModal(props: InputTransformModalProps) {
const InputMap = (
<MapArrayField
fieldPath={'input_map'}
helpText={`An array specifying how to map fields from the ingested document to the model’s input. Dot notation is used by default. To explicitly use JSONPath, please ensure to prepend with the
root object selector "${JSONPATH_ROOT_SELECTOR}"`}
keyTitle="Name"
keyPlaceholder="Name"
keyOptions={parseModelInputs(props.modelInterface)}
Expand All @@ -306,6 +303,7 @@ export function InputTransformModal(props: InputTransformModalProps) {
? 'Specify a query field'
: 'Define a document field'
}
valueHelpText={`Specify a document field or define JSONPath to transform the document to map to a model input field.`}
valueOptions={props.valueOptions}
// If the map we are adding is the first one, populate the selected option to index 0
onMapAdd={(curArray) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import {
IConfigField,
IProcessorConfig,
IngestPipelineConfig,
JSONPATH_ROOT_SELECTOR,
MapArrayFormValue,
MapFormValue,
ModelInterface,
Expand Down Expand Up @@ -213,10 +212,9 @@ export function OutputTransformModal(props: OutputTransformModalProps) {
const OutputMap = (
<MapArrayField
fieldPath={'output_map'}
helpText={`An array specifying how to map the model’s output to new fields. Dot notation is used by default. To explicitly use JSONPath, please ensure to prepend with the
root object selector "${JSONPATH_ROOT_SELECTOR}"`}
keyTitle="Name"
keyPlaceholder="Name"
keyHelpText={`Specify a model output field or define JSONPath to transform the model output to map to a new document field.`}
keyOptions={
tempFullResponsePath
? undefined
Expand Down
19 changes: 7 additions & 12 deletions public/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,18 +229,13 @@ function getTransformedResult(
// the entire value. This may happen if full_response_path=false
// and the input is the entire result with nothing else to parse out.
// get() does not cover this case, so we override manually.
const result =
path === '.'
? input
: mapEntry.value.startsWith(JSONPATH_ROOT_SELECTOR)
? // JSONPath transform
jsonpath.query(input, path)
: // Standard dot notation
get(input, path);

// ML processors dynamically handle arrays vs. single values differently when indexing.
// We replicate that logic here to get consistent results
return Array.isArray(result) && result.length === 1 ? result[0] : result;
return path === '.'
? input
: mapEntry.value.startsWith(JSONPATH_ROOT_SELECTOR)
? // JSONPath transform
jsonpath.query(input, path)
: // Standard dot notation
get(input, path);
}

// Derive the collection of model inputs from the model interface JSONSchema into a form-ready list
Expand Down
Loading