[8.10] [Enterprise Search] Make single target field editable in multi-field editor (#162512)

## Summary

This PR enhances the multi-field configuration screen for creating an ML
inference pipeline. It adds different behavior to the target field based
on the selected model:
- For ELSER pipelines the target field is not editable, and the output
field names are automatically generated as `ml.inference.<source
field>_expanded`
- For non-ELSER pipelines the target field is editable if there's a
single source field selected. For multiple source fields the names are
automatically generated as `ml.inference.<source field>`

The mapping auto-updater process now receives `model_id` and only
changes the mapping for ELSER pipelines.

In order to keep the scope of changes smaller, we're NOT switching over
to the multi-field selector for non-ELSER pipelines just yet. We're
making the logic ready for this here, but the actual switchover will
happen in a following PR.

Non-ELSER pipelines

![field_config_non_elser](2f74b01d-ac40-4656-9fd2-2843657d40c1)

ELSER pipelines

![field_config_elser](57f5d06a-fa61-4139-9ecf-007595670469)


### Checklist

Delete any items that are not applicable to this PR.
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Adam Demjen 2023-07-27 11:25:08 -04:00 committed by GitHub
parent 11e56ec4c4
commit c82b679c24
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 250 additions and 38 deletions

View file

@ -86,6 +86,7 @@ export interface CreateMlInferencePipelineParameters {
export interface CreateMLInferencePipelineDefinition {
field_mappings: FieldMapping[];
inference_config?: InferencePipelineInferenceConfig;
model_id: string;
pipeline_definition: MlInferencePipeline;
pipeline_name: string;
}

View file

@ -32,6 +32,7 @@ describe('CreateMlInferencePipelineApiLogic', () => {
},
],
indexName: 'my-index',
modelId: 'my-model-id',
pipelineName: 'my-pipeline',
pipelineDefinition: { processors: [], version: 1 },
};
@ -39,7 +40,7 @@ describe('CreateMlInferencePipelineApiLogic', () => {
expect(http.post).toHaveBeenCalledWith(
'/internal/enterprise_search/indices/my-index/ml_inference/pipeline_processors',
{
body: '{"field_mappings":[{"sourceField":"my_source_field","targetField":"my_target_field"}],"pipeline_definition":{"processors":[],"version":1},"pipeline_name":"my-pipeline"}',
body: '{"field_mappings":[{"sourceField":"my_source_field","targetField":"my_target_field"}],"model_id":"my-model-id","pipeline_definition":{"processors":[],"version":1},"pipeline_name":"my-pipeline"}',
}
);
expect(result).toEqual({

View file

@ -18,6 +18,7 @@ export interface CreateMlInferencePipelineApiLogicArgs {
fieldMappings: FieldMapping[];
indexName: string;
inferenceConfig?: InferencePipelineInferenceConfig;
modelId: string;
pipelineDefinition: MlInferencePipeline;
pipelineName: string;
}
@ -33,6 +34,7 @@ export const createMlInferencePipeline = async (
const params: CreateMLInferencePipelineDefinition = {
field_mappings: args.fieldMappings,
inference_config: args.inferenceConfig,
model_id: args.modelId,
pipeline_definition: args.pipelineDefinition,
pipeline_name: args.pipelineName,
};

View file

@ -26,7 +26,7 @@ describe('ConfigureFields', () => {
addInferencePipelineModal: { configuration: { existingPipeline: false } },
};
it('renders multi-field selector components if non-text expansion model is selected', () => {
it('renders single field selector component if non-text expansion model is selected', () => {
setMockValues(mockValues);
const wrapper = shallow(<ConfigureFields />);
expect(wrapper.find(SingleFieldMapping)).toHaveLength(1);

View file

@ -172,6 +172,7 @@ describe('MlInferenceLogic', () => {
},
],
indexName: 'test',
modelId: 'test-model',
pipelineDefinition: {},
pipelineName: 'unit-test',
});
@ -340,6 +341,7 @@ describe('MlInferenceLogic', () => {
pipelineName: 'unit-test',
sourceField: '',
fieldMappings: [],
targetField: '',
});
expect(MLInferenceLogic.values.mlInferencePipeline).toBeUndefined();
@ -352,6 +354,7 @@ describe('MlInferenceLogic', () => {
pipelineName: 'unit-test',
sourceField: 'body',
fieldMappings: [],
targetField: '',
});
expect(MLInferenceLogic.values.mlInferencePipeline).not.toBeUndefined();
@ -364,6 +367,7 @@ describe('MlInferenceLogic', () => {
pipelineName: '',
sourceField: '',
fieldMappings: [],
targetField: '',
});
expect(MLInferenceLogic.values.mlInferencePipeline).toBeUndefined();
});
@ -383,6 +387,7 @@ describe('MlInferenceLogic', () => {
pipelineName: 'unit-test',
sourceField: '',
fieldMappings: [],
targetField: '',
});
expect(MLInferenceLogic.values.mlInferencePipeline).not.toBeUndefined();
expect(MLInferenceLogic.values.mlInferencePipeline).toEqual(existingPipeline);
@ -563,12 +568,13 @@ describe('MlInferenceLogic', () => {
MLModelsApiLogic.actions.apiSuccess([textExpansionModel]);
MLInferenceLogic.actions.selectFields(['my_source_field1', 'my_source_field2']);
MLInferenceLogic.actions.addSelectedFieldsToMapping();
MLInferenceLogic.actions.addSelectedFieldsToMapping(true);
MLInferenceLogic.actions.createPipeline();
expect(MLInferenceLogic.actions.makeCreatePipelineRequest).toHaveBeenCalledWith({
indexName: mockModelConfiguration.indexName,
inferenceConfig: undefined,
modelId: textExpansionModel.model_id,
fieldMappings: [
{
sourceField: 'my_source_field1',
@ -609,6 +615,7 @@ describe('MlInferenceLogic', () => {
targetField: `ml.inference.${mockModelConfiguration.configuration.destinationField}`,
},
],
modelId: nerModel.model_id,
pipelineDefinition: expect.any(Object), // Generation logic is tested elsewhere
pipelineName: mockModelConfiguration.configuration.pipelineName,
});

View file

@ -93,11 +93,23 @@ export const EMPTY_PIPELINE_CONFIGURATION: InferencePipelineConfiguration = {
modelID: '',
pipelineName: '',
sourceField: '',
targetField: '',
};
const API_REQUEST_COMPLETE_STATUSES = [Status.SUCCESS, Status.ERROR];
const DEFAULT_CONNECTOR_FIELDS = ['body', 'title', 'id', 'type', 'url'];
const getFullTargetFieldName = (
sourceField: string,
targetField: string | undefined,
isTextExpansionModelSelected: boolean
) => {
const suffixedTargetField = `${targetField || sourceField}${
isTextExpansionModelSelected ? '_expanded' : ''
}`;
return getMlInferencePrefixedFieldName(suffixedTargetField);
};
export interface MLInferencePipelineOption {
disabled: boolean;
disabledReason?: string;
@ -109,7 +121,9 @@ export interface MLInferencePipelineOption {
}
interface MLInferenceProcessorsActions {
addSelectedFieldsToMapping: () => void;
addSelectedFieldsToMapping: (isTextExpansionModelSelected: boolean) => {
isTextExpansionModelSelected: boolean;
};
attachApiError: Actions<
AttachMlInferencePipelineApiLogicArgs,
AttachMlInferencePipelineResponse
@ -166,6 +180,7 @@ interface MLInferenceProcessorsActions {
setInferencePipelineConfiguration: (configuration: InferencePipelineConfiguration) => {
configuration: InferencePipelineConfiguration;
};
setTargetField: (targetFieldName: string) => { targetFieldName: string };
startTextExpansionModelSuccess: StartTextExpansionModelApiLogicActions['apiSuccess'];
}
@ -203,7 +218,9 @@ export const MLInferenceLogic = kea<
MakeLogicType<MLInferenceProcessorsValues, MLInferenceProcessorsActions>
>({
actions: {
addSelectedFieldsToMapping: true,
addSelectedFieldsToMapping: (isTextExpansionModelSelected: string) => ({
isTextExpansionModelSelected,
}),
attachPipeline: true,
clearFormErrors: true,
createPipeline: true,
@ -217,6 +234,7 @@ export const MLInferenceLogic = kea<
setInferencePipelineConfiguration: (configuration: InferencePipelineConfiguration) => ({
configuration,
}),
setTargetField: (targetFieldName: string) => ({ targetFieldName }),
},
connect: {
actions: [
@ -298,6 +316,7 @@ export const MLInferenceLogic = kea<
targetField: getMlInferencePrefixedFieldName(configuration.destinationField),
},
],
modelId: configuration.modelID,
pipelineDefinition: mlInferencePipeline!,
pipelineName: configuration.pipelineName,
});
@ -314,6 +333,7 @@ export const MLInferenceLogic = kea<
pipelineName,
sourceField: params.source_field,
fieldMappings: params.field_mappings,
targetField: params.destination_field ?? '',
});
},
setIndexName: ({ indexName }) => {
@ -370,17 +390,21 @@ export const MLInferenceLogic = kea<
step: AddInferencePipelineSteps.Configuration,
},
{
addSelectedFieldsToMapping: (modal) => {
addSelectedFieldsToMapping: (modal, { isTextExpansionModelSelected }) => {
const {
configuration: { fieldMappings },
configuration: { fieldMappings, targetField },
selectedSourceFields,
} = modal;
const mergedFieldMappings: FieldMapping[] = [
...(fieldMappings || []),
...(selectedSourceFields || []).map((fieldName) => ({
...(selectedSourceFields || []).map((fieldName: string) => ({
sourceField: fieldName,
targetField: getMlInferencePrefixedFieldName(`${fieldName}_expanded`),
targetField: getFullTargetFieldName(
fieldName,
targetField,
isTextExpansionModelSelected
),
})),
];
@ -389,6 +413,7 @@ export const MLInferenceLogic = kea<
configuration: {
...modal.configuration,
fieldMappings: mergedFieldMappings,
targetField: '',
},
selectedSourceFields: [],
};
@ -437,6 +462,13 @@ export const MLInferenceLogic = kea<
...modal,
configuration,
}),
setTargetField: (modal, { targetFieldName }) => ({
...modal,
configuration: {
...modal.configuration,
targetField: targetFieldName,
},
}),
},
],
createErrors: [

View file

@ -11,7 +11,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { EuiBasicTable, EuiButton, EuiComboBox } from '@elastic/eui';
import { EuiBasicTable, EuiButton, EuiComboBox, EuiFieldText } from '@elastic/eui';
import { MultiFieldMapping, SelectedFieldMappings } from './multi_field_selector';
@ -92,6 +92,53 @@ describe('MultiFieldMapping', () => {
const button = wrapper.find(EuiButton);
expect(button.prop('disabled')).toBe(false);
});
it('disables target field text field if no source fields are selected', () => {
setMockValues(DEFAULT_VALUES);
const wrapper = shallow(<MultiFieldMapping />);
expect(wrapper.find(EuiFieldText)).toHaveLength(1);
const textField = wrapper.find(EuiFieldText);
expect(textField.prop('disabled')).toBe(true);
});
it('disables target field text field if multiple source fields are selected', () => {
setMockValues({
...DEFAULT_VALUES,
addInferencePipelineModal: {
...DEFAULT_VALUES.addInferencePipelineModal,
selectedSourceFields: ['my-source-field1', 'my-source-field2'],
},
});
const wrapper = shallow(<MultiFieldMapping />);
expect(wrapper.find(EuiFieldText)).toHaveLength(1);
const textField = wrapper.find(EuiFieldText);
expect(textField.prop('disabled')).toBe(true);
});
it('disables target field text field if text expansion model is selected', () => {
setMockValues({
...DEFAULT_VALUES,
isTextExpansionModelSelected: true,
});
const wrapper = shallow(<MultiFieldMapping />);
expect(wrapper.find(EuiFieldText)).toHaveLength(1);
const textField = wrapper.find(EuiFieldText);
expect(textField.prop('disabled')).toBe(true);
});
it('enables target field text field if a single source field is selected', () => {
setMockValues({
...DEFAULT_VALUES,
addInferencePipelineModal: {
...DEFAULT_VALUES.addInferencePipelineModal,
selectedSourceFields: ['my-source-field1'],
},
});
const wrapper = shallow(<MultiFieldMapping />);
expect(wrapper.find(EuiFieldText)).toHaveLength(1);
const textField = wrapper.find(EuiFieldText);
expect(textField.prop('disabled')).toBe(false);
});
});
describe('SelectedFieldMappings', () => {

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React from 'react';
import React, { useState } from 'react';
import { useValues, useActions } from 'kea';
@ -31,13 +31,62 @@ import { MLInferenceLogic } from './ml_inference_logic';
type FieldNames = Array<{ label: string }>;
const TARGET_FIELD_PLACEHOLDER_TEXT_NO_FIELDS = i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.placeholder.noFields',
{
defaultMessage: 'Select a source field',
}
);
const TARGET_FIELD_PLACEHOLDER_TEXT_MULTIPLE_FIELDS = i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.placeholder.multipleFields',
{
defaultMessage: 'Automatically created for multi-select',
}
);
const TARGET_FIELD_PLACEHOLDER_TEXT_TEXT_EXPANSION_MODEL = i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.placeholder.textExpansionModel',
{
defaultMessage: 'Automatically created',
}
);
const TARGET_FIELD_HELP_TEXT_DEFAULT = i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.helpText',
{
defaultMessage: 'Optional. Field name where inference results should be saved.',
}
);
const TARGET_FIELD_HELP_TEXT_TEXT_EXPANSION_MODEL = i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.helpTextTextExpansionModel',
{
defaultMessage: 'ELSER target fields are created automatically.',
}
);
const getInitialTargetFieldPlaceholderText = (isTextExpansionModelSelected: boolean) =>
isTextExpansionModelSelected
? TARGET_FIELD_PLACEHOLDER_TEXT_TEXT_EXPANSION_MODEL
: TARGET_FIELD_PLACEHOLDER_TEXT_NO_FIELDS;
const getTargetFieldHelpText = (isTextExpansionModelSelected: boolean) =>
isTextExpansionModelSelected
? TARGET_FIELD_HELP_TEXT_TEXT_EXPANSION_MODEL
: TARGET_FIELD_HELP_TEXT_DEFAULT;
export const MultiFieldMapping: React.FC = () => {
const {
addInferencePipelineModal: { configuration, selectedSourceFields = [] },
isTextExpansionModelSelected,
sourceFields,
} = useValues(MLInferenceLogic);
const { ingestionMethod } = useValues(IndexViewLogic);
const { addSelectedFieldsToMapping, selectFields } = useActions(MLInferenceLogic);
const { addSelectedFieldsToMapping, selectFields, setTargetField } = useActions(MLInferenceLogic);
const [placeholderText, setPlaceholderText] = useState<string>(
getInitialTargetFieldPlaceholderText(isTextExpansionModelSelected)
);
const mappedSourceFields =
configuration.fieldMappings?.map(({ sourceField }) => sourceField) ?? [];
@ -50,9 +99,25 @@ export const MultiFieldMapping: React.FC = () => {
const selectedFields = selectedSourceFields.map((fieldName) => ({
label: fieldName,
}));
const targetField = configuration.targetField;
const isExactlyOneSourceFieldSelected = selectedSourceFields.length === 1;
const onChangeSelectedFields = (selectedFieldNames: FieldNames) => {
selectFields(selectedFieldNames.map(({ label }) => label));
setTargetField(
!isTextExpansionModelSelected && selectedFieldNames.length === 1
? selectedFieldNames[0].label
: ''
);
setPlaceholderText(
isTextExpansionModelSelected
? TARGET_FIELD_PLACEHOLDER_TEXT_TEXT_EXPANSION_MODEL
: selectedFieldNames.length === 0
? TARGET_FIELD_PLACEHOLDER_TEXT_NO_FIELDS
: selectedFieldNames.length === 1
? selectedFieldNames[0].label
: TARGET_FIELD_PLACEHOLDER_TEXT_MULTIPLE_FIELDS
);
};
const onCreateField = (fieldName: string) => {
@ -63,6 +128,12 @@ export const MultiFieldMapping: React.FC = () => {
selectFields([...selectedSourceFields, fieldName]);
};
const onAddSelectedFields = () => {
addSelectedFieldsToMapping(isTextExpansionModelSelected);
setTargetField('');
setPlaceholderText(getInitialTargetFieldPlaceholderText(isTextExpansionModelSelected));
};
return (
<>
<EuiFlexGroup>
@ -109,23 +180,16 @@ export const MultiFieldMapping: React.FC = () => {
defaultMessage: 'Target field',
}
)}
helpText={i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.helpText',
{
defaultMessage: 'This name is automatically created based on your source field.',
}
)}
helpText={getTargetFieldHelpText(isTextExpansionModelSelected)}
fullWidth
>
<EuiFieldText
prepend="ml.inference."
onChange={(e) => setTargetField(e.target.value)}
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-configureFields-targetField`}
disabled
value={i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.defaultValue',
{
defaultMessage: 'This is automatically created',
}
)}
disabled={isTextExpansionModelSelected || !isExactlyOneSourceFieldSelected}
value={targetField}
placeholder={placeholderText}
fullWidth
/>
</EuiFormRow>
@ -136,7 +200,7 @@ export const MultiFieldMapping: React.FC = () => {
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-configureFields-addSelectedFieldsToMapping`}
disabled={selectedFields.length === 0}
iconType="plusInCircle"
onClick={addSelectedFieldsToMapping}
onClick={onAddSelectedFields}
style={{ width: '60px' }}
>
{i18n.translate(

View file

@ -25,6 +25,7 @@ const DEFAULT_VALUES: TestPipelineValues = {
modelID: '',
pipelineName: '',
sourceField: '',
targetField: '',
},
indexName: '',
step: AddInferencePipelineSteps.Configuration,
@ -70,6 +71,7 @@ describe('TestPipelineLogic', () => {
modelID: '',
pipelineName: '',
sourceField: '',
targetField: '',
},
indexName: '',
step: AddInferencePipelineSteps.Configuration,

View file

@ -17,6 +17,7 @@ export interface InferencePipelineConfiguration {
pipelineName: string;
sourceField: string;
fieldMappings?: FieldMapping[];
targetField: string;
}
export interface AddInferencePipelineFormErrors {

View file

@ -38,7 +38,7 @@ export const preparePipelineAndIndexForMlInference = async (
indexName: string,
pipelineName: string,
pipelineDefinition: IngestPipeline | undefined,
modelId: string | undefined,
modelId: string,
sourceField: string | undefined,
destinationField: string | null | undefined,
fieldMappings: FieldMapping[] | undefined,
@ -62,7 +62,7 @@ export const preparePipelineAndIndexForMlInference = async (
);
const mappingResponse = fieldMappings
? (await updateMlInferenceMappings(indexName, fieldMappings, esClient)).acknowledged
? (await updateMlInferenceMappings(indexName, modelId, fieldMappings, esClient)).acknowledged
: false;
return {

View file

@ -13,11 +13,27 @@ import { updateMlInferenceMappings } from './update_ml_inference_mappings';
describe('updateMlInferenceMappings', () => {
const indexName = 'my-index';
const modelId = 'my-model-id';
const mockClient = elasticsearchServiceMock.createScopedClusterClient();
beforeEach(() => {
jest.clearAllMocks();
mockClient.asCurrentUser.ml.getTrainedModels.mockResolvedValue({
count: 1,
trained_model_configs: [
{
inference_config: {
text_expansion: {},
},
input: {
field_names: [],
},
model_id: modelId,
tags: [],
},
],
});
});
const expectedMapping = {
@ -62,14 +78,42 @@ describe('updateMlInferenceMappings', () => {
},
];
it('should update mappings for default output', async () => {
await updateMlInferenceMappings(indexName, fieldMappings, mockClient.asCurrentUser);
it('should update mappings for text expansion pipelines', async () => {
await updateMlInferenceMappings(indexName, modelId, fieldMappings, mockClient.asCurrentUser);
expect(mockClient.asCurrentUser.indices.putMapping).toHaveBeenLastCalledWith({
index: indexName,
properties: expectedMapping,
});
});
it('should not update mappings for pipelines other than text expansion', async () => {
const nonTextExpansionModelId = 'some-other-model-id';
mockClient.asCurrentUser.ml.getTrainedModels.mockResolvedValue({
count: 1,
trained_model_configs: [
{
inference_config: {
ner: {},
},
input: {
field_names: [],
},
model_id: nonTextExpansionModelId,
tags: [],
},
],
});
await updateMlInferenceMappings(
indexName,
nonTextExpansionModelId,
fieldMappings,
mockClient.asCurrentUser
);
expect(mockClient.asCurrentUser.indices.putMapping).not.toHaveBeenCalled();
});
it('should raise an error if the update fails', async () => {
mockClient.asCurrentUser.indices.putMapping.mockImplementation(() =>
Promise.reject({
@ -84,7 +128,7 @@ describe('updateMlInferenceMappings', () => {
})
);
await expect(
updateMlInferenceMappings(indexName, fieldMappings, mockClient.asCurrentUser)
updateMlInferenceMappings(indexName, modelId, fieldMappings, mockClient.asCurrentUser)
).rejects.toThrowError(ErrorCode.MAPPING_UPDATE_FAILED);
});
});

View file

@ -21,9 +21,17 @@ import { isIllegalArgumentException } from '../../../../utils/identify_exception
*/
export const updateMlInferenceMappings = async (
indexName: string,
modelId: string,
fieldMappings: FieldMapping[],
esClient: ElasticsearchClient
) => {
// Check if the model is of text_expansion type, if not, skip the mapping update
if (!(await isTextExpansionModel(modelId, esClient))) {
return {
acknowledged: false,
};
}
const sourceFields = fieldMappings.map(({ sourceField }) => sourceField);
const nonDefaultTargetFields = fieldMappings
@ -97,3 +105,9 @@ const formDefaultElserMappingProps = (sourceFields: string[]) => {
{}
);
};
const isTextExpansionModel = async (modelId: string, esClient: ElasticsearchClient) => {
const models = await esClient.ml.getTrainedModels({ model_id: modelId });
return models.trained_model_configs[0]?.inference_config?.text_expansion !== undefined;
};

View file

@ -409,7 +409,7 @@ export function registerIndexRoutes({
),
})
),
model_id: schema.maybe(schema.string()),
model_id: schema.string(),
pipeline_definition: schema.maybe(
schema.object({
description: schema.maybe(schema.string()),
@ -437,14 +437,14 @@ export function registerIndexRoutes({
} = request.body;
// additional validations
if ((pipelineDefinition || fieldMappings) && (sourceField || destinationField || modelId)) {
if ((pipelineDefinition || fieldMappings) && (sourceField || destinationField)) {
return createError({
errorCode: ErrorCode.PARAMETER_CONFLICT,
message: i18n.translate(
'xpack.enterpriseSearch.server.routes.createMlInferencePipeline.ParameterConflictError',
{
defaultMessage:
'pipeline_definition and field_mappings should only be provided if source_field and destination_field and model_id are not provided',
'pipeline_definition and field_mappings should only be provided if source_field and destination_field are not provided',
}
),
response,

View file

@ -13596,7 +13596,6 @@
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.sourceField.helpText": "Sélectionnez un champ existant ou tapez un nom de champ.",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.sourceField.placeholder": "Sélectionner un champ de schéma",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.sourceFieldLabel": "Champ source",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.defaultValue": "Ceci est créé automatiquement",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.helpText": "Ce nom est créé automatiquement en fonction de votre champ source.",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.label": "Champ cible (facultatif)",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetFieldLabel": "Champ cible",

View file

@ -13610,7 +13610,6 @@
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.sourceField.helpText": "既存のフィールドを選択するか、フィールド名を入力してください。",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.sourceField.placeholder": "スキーマフィールドを選択",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.sourceFieldLabel": "ソースフィールド",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.defaultValue": "これは自動的に作成されます",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.helpText": "この名前は、ソースフィールドに基づいて自動的に作成されます。",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.label": "ターゲットフィールド(任意)",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetFieldLabel": "ターゲットフィールド",

View file

@ -13609,7 +13609,6 @@
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.sourceField.helpText": "选择现有字段或键入字段名称。",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.sourceField.placeholder": "选择架构字段",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.sourceFieldLabel": "源字段",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.defaultValue": "这是自动创建的内容",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.helpText": "此名称基于您的源字段自动创建。",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetField.label": "目标字段(可选)",
"xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.targetFieldLabel": "目标字段",