Skip to content

Commit

Permalink
[Fleet] Add support for additional types for dynamic mappings (#168842)
Browse files Browse the repository at this point in the history
Add support for fields that generate dynamic mappings of the following
types:
- match_only_text
- scaled_float
- aggregate_metric_double
- half_float
- ip
- flattened
- integer (mapped as long as in other cases)
- group (child fields are installed as dynamic mappings)

Default `match_matching_type` has been corrected to provide always a
valid value. For example for floats it was using `float`, what is invalid, for
this case it is changed to `double`.

When the type is not known, an error is raised now instead of silently
ignoring the field definition, this helps package developers discover earlier
if their mappings are not well supported.

Tested with the `install_all_packages` and no current package fails
because of this.

Fix #168823
  • Loading branch information
jsoriano committed Oct 17, 2023
1 parent 0c1f828 commit d2165f3
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,105 @@ describe('EPM template', () => {
expect(mappings).toEqual(runtimeFieldMapping);
});

it('tests processing scaled_float fields in a dynamic template', () => {
const textWithRuntimeFieldsLiteralYml = `
- name: numeric_labels
type: object
object_type: scaled_float
`;
const runtimeFieldMapping = {
properties: {},
dynamic_templates: [
{
numeric_labels: {
match_mapping_type: '*',
path_match: 'numeric_labels.*',
mapping: {
type: 'scaled_float',
scaling_factor: 1000,
},
},
},
],
};
const fields: Field[] = safeLoad(textWithRuntimeFieldsLiteralYml);
const processedFields = processFields(fields);
const mappings = generateMappings(processedFields);
expect(mappings).toEqual(runtimeFieldMapping);
});

it('tests processing aggregate_metric_double fields in a dynamic template', () => {
const textWithRuntimeFieldsLiteralYml = `
- name: aggregate.*
type: aggregate_metric_double
metrics: ["min", "max", "sum", "value_count"]
default_metric: "max"
`;
const runtimeFieldMapping = {
properties: {},
dynamic_templates: [
{
'aggregate.*': {
match_mapping_type: '*',
path_match: 'aggregate.*',
mapping: {
type: 'aggregate_metric_double',
metrics: ['min', 'max', 'sum', 'value_count'],
default_metric: 'max',
},
},
},
],
};
const fields: Field[] = safeLoad(textWithRuntimeFieldsLiteralYml);
const processedFields = processFields(fields);
const mappings = generateMappings(processedFields);
expect(mappings).toEqual(runtimeFieldMapping);
});

it('tests processing groub sub fields in a dynamic template', () => {
const textWithRuntimeFieldsLiteralYml = `
- name: group.*.network
type: group
fields:
- name: bytes
type: integer
metric_type: counter
`;
const runtimeFieldMapping = {
properties: {},
dynamic_templates: [
{
'group.*.network.bytes': {
match_mapping_type: 'long',
path_match: 'group.*.network.bytes',
mapping: {
type: 'long',
time_series_metric: 'counter',
},
},
},
],
};
const fields: Field[] = safeLoad(textWithRuntimeFieldsLiteralYml);
const processedFields = processFields(fields);
const mappings = generateMappings(processedFields);
expect(mappings).toEqual(runtimeFieldMapping);
});

it('tests unexpected type for field as dynamic template fails', () => {
const textWithRuntimeFieldsLiteralYml = `
- name: labels.*
type: object
object_type: constant_keyword
`;
const fields: Field[] = safeLoad(textWithRuntimeFieldsLiteralYml);
expect(() => {
const processedFields = processFields(fields);
generateMappings(processedFields);
}).toThrow();
});

it('tests priority and index pattern for data stream without dataset_is_prefix', () => {
const dataStreamDatasetIsPrefixUnset = {
type: 'metrics',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ function _generateMappings(
if (type === 'object' && field.object_type) {
const pathMatch = path.includes('*') ? path : `${path}.*`;

let dynProperties: Properties = getDefaultProperties(field);
const dynProperties: Properties = getDefaultProperties(field);
let matchingType: string | undefined;
switch (field.object_type) {
case 'keyword':
Expand All @@ -216,10 +216,8 @@ function _generateMappings(
case 'double':
case 'long':
case 'boolean':
dynProperties = {
type: field.object_type,
time_series_metric: field.metric_type,
};
dynProperties.type = field.object_type;
dynProperties.time_series_metric = field.metric_type;
matchingType = field.object_type_mapping_type ?? field.object_type;
default:
break;
Expand Down Expand Up @@ -258,27 +256,74 @@ function _generateMappings(
dynProperties = histogram(field);
matchingType = field.object_type_mapping_type ?? '*';
break;
case 'ip':
case 'keyword':
case 'match_only_text':
case 'text':
case 'wildcard':
dynProperties.type = field.object_type;
matchingType = field.object_type_mapping_type ?? 'string';
break;
case 'keyword':
case 'scaled_float':
dynProperties = scaledFloat(field);
matchingType = field.object_type_mapping_type ?? '*';
break;
case 'aggregate_metric_double':
dynProperties.type = field.object_type;
matchingType = field.object_type_mapping_type ?? 'string';
dynProperties.metrics = field.metrics;
dynProperties.default_metric = field.default_metric;
matchingType = field.object_type_mapping_type ?? '*';
break;
case 'byte':
case 'double':
case 'float':
case 'half_float':
dynProperties.type = field.object_type;
dynProperties.time_series_metric = field.metric_type;
matchingType = field.object_type_mapping_type ?? 'double';
break;
case 'byte':
case 'long':
case 'short':
case 'unsigned_long':
dynProperties.type = field.object_type;
dynProperties.time_series_metric = field.metric_type;
matchingType = field.object_type_mapping_type ?? 'long';
break;
case 'integer':
// Map integers as long, as in other cases.
dynProperties.type = 'long';
dynProperties.time_series_metric = field.metric_type;
matchingType = field.object_type_mapping_type ?? 'long';
break;
case 'boolean':
dynProperties = {
type: field.object_type,
time_series_metric: field.metric_type,
};
dynProperties.type = field.object_type;
dynProperties.time_series_metric = field.metric_type;
matchingType = field.object_type_mapping_type ?? field.object_type;
default:
break;
case 'group':
if (!field?.fields) {
break;
}
const subFields = field.fields.map((subField) => ({
...subField,
type: 'object',
object_type: subField.object_type ?? subField.type,
}));
_generateMappings(subFields, {
...ctx,
groupFieldName: ctx.groupFieldName
? `${ctx.groupFieldName}.${field.name}`
: field.name,
});
break;
case 'flattened':
dynProperties.type = field.object_type;
matchingType = field.object_type_mapping_type ?? 'object';
break;
default:
throw new Error(
`no dynamic mapping generated for field ${path} of type ${field.object_type}`
);
}

if (dynProperties && matchingType) {
Expand Down

0 comments on commit d2165f3

Please sign in to comment.