Skip to content

Commit

Permalink
[8.10][ESRE] Parse multiple fields when attaching (#161295)
Browse files Browse the repository at this point in the history
## Summary

This PR adds support for parsing multiple field mappings from an
existing pipeline configuration. This is only relevant when listing
attachable pipelines; the new pipeline creation process is not affected.

This change is a pre-requisite for allowing ELSER pipelines to be
attached.

Tested for backward compatibility:
* Existing pipeline selection dropdown (temporarily disabled filtering
of ELSER pipelines)
![Screenshot 2023-07-05 at 15 53
52](https://github.com/elastic/kibana/assets/14224983/00d673f7-70b4-4cbb-919c-4fe17eedaca2)

Note the "Source field" and "Destination field" attributes show the
first mapping's fields only (even if the pipeline is configured with
multiple mappings). This is pending redesign and will be addressed in a
later PR.

* Fields - ELSER pipeline (temporarily removed field modification
widgets)
![Screenshot 2023-07-05 at 15 54
34](https://github.com/elastic/kibana/assets/14224983/8bdd49f9-1682-4ccb-a066-233e0809b883)

* Fields - non-ELSER pipeline
![Screenshot 2023-07-05 at 15 54
42](https://github.com/elastic/kibana/assets/14224983/5c70eb9b-e9a9-4213-9d24-ac9303f64b54)

### Checklist
- [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
  • Loading branch information
demjened authored Jul 6, 2023
1 parent 7709670 commit 060cc5b
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -381,12 +381,59 @@ describe('parseMlInferenceParametersFromPipeline', () => {
model_id: 'test-model',
pipeline_name: 'unit-test',
source_field: 'body',
field_mappings: [
{
sourceField: 'body',
targetField: 'ml.inference.test',
},
],
});
});
it('returns pipeline parameters from ingest pipeline with multiple inference processors', () => {
expect(
parseMlInferenceParametersFromPipeline('unit-test', {
processors: [
{
inference: {
field_map: {
body: 'text_field',
},
model_id: 'test-model',
target_field: 'ml.inference.body',
},
},
{
inference: {
field_map: {
title: 'text_field',
},
model_id: 'test-model',
target_field: 'ml.inference.title',
},
},
],
})
).toEqual({
destination_field: 'body',
model_id: 'test-model',
pipeline_name: 'unit-test',
source_field: 'body',
field_mappings: [
{
sourceField: 'body',
targetField: 'ml.inference.body',
},
{
sourceField: 'title',
targetField: 'ml.inference.title',
},
],
});
});
it('return null if pipeline missing inference processor', () => {
it('return null if pipeline is missing inference processor', () => {
expect(parseMlInferenceParametersFromPipeline('unit-test', { processors: [] })).toBeNull();
});
it('return null if pipeline missing field_map', () => {
it('return null if pipeline is missing field_map', () => {
expect(
parseMlInferenceParametersFromPipeline('unit-test', {
processors: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,24 +216,37 @@ export const parseMlInferenceParametersFromPipeline = (
name: string,
pipeline: IngestPipeline
): CreateMlInferencePipelineParameters | null => {
const processor = pipeline?.processors?.find((proc) => proc.inference !== undefined);
if (!processor || processor?.inference === undefined) {
const inferenceProcessors = pipeline?.processors
?.filter((p) => p.inference)
.map((p) => p.inference) as IngestInferenceProcessor[];
if (!inferenceProcessors || inferenceProcessors.length === 0) {
return null;
}
const { inference: inferenceProcessor } = processor;
const sourceFields = Object.keys(inferenceProcessor.field_map ?? {});
const sourceField = sourceFields.length === 1 ? sourceFields[0] : null;
if (!sourceField) {
return null;
}
return {
destination_field: inferenceProcessor.target_field
? stripMlInferencePrefix(inferenceProcessor.target_field)
: inferenceProcessor.target_field,
model_id: inferenceProcessor.model_id,
pipeline_name: name,
source_field: sourceField,
};

// Extract source -> target field mappings from all inference processors in pipeline
const fieldMappings = inferenceProcessors
.map((p) => {
const sourceFields = Object.keys(p.field_map ?? {});
// We assume that there is only one source field per inference processor
const sourceField = sourceFields.length >= 1 ? sourceFields[0] : null;
return {
sourceField,
targetField: p.target_field, // Prefixed target field
};
})
.filter((f) => f.sourceField) as FieldMapping[];

return fieldMappings.length === 0
? null
: {
destination_field: fieldMappings[0].targetField // Backward compatibility - TODO: remove after multi-field selector is implemented for all inference types
? stripMlInferencePrefix(fieldMappings[0].targetField)
: '',
model_id: inferenceProcessors[0].model_id,
pipeline_name: name,
source_field: fieldMappings[0].sourceField, // Backward compatibility - TODO: remove after multi-field selector is implemented for all inference types
field_mappings: fieldMappings,
};
};

export const parseModelStateFromStats = (
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/enterprise_search/common/types/pipelines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export interface CreateMlInferencePipelineParameters {
model_id: string;
pipeline_name: string;
source_field: string;
field_mappings?: FieldMapping[];
}

export interface CreateMLInferencePipelineDefinition {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ export const MLInferenceLogic = kea<
modelID: params.model_id,
pipelineName,
sourceField: params.source_field,
fieldMappings: params.field_mappings,
});
},
setIndexName: ({ indexName }) => {
Expand Down

0 comments on commit 060cc5b

Please sign in to comment.