diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts index e5c323de62dfd..f0459b10c8570 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts @@ -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', diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts index e55cad3f80a9c..36e13ae331f58 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts @@ -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': @@ -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; @@ -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) {