Skip to content

Commit 9ff3d0c

Browse files
committed
Revisit multi-fields logic in ECS
1 parent 38db8df commit 9ff3d0c

File tree

2 files changed

+95
-23
lines changed

2 files changed

+95
-23
lines changed

internal/fields/mappings.go

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,29 @@ func (v *MappingValidator) validateMappingInECSSchema(currentPath string, defini
315315
return err
316316
}
317317

318-
// Compare multifields
318+
return compareMultiFieldsInECSSchema(currentPath, found, definition)
319+
}
320+
321+
func compareFieldDefinitionWithECS(currentPath string, ecs *FieldDefinition, actual map[string]any) error {
322+
actualType := mappingParameter("type", actual)
323+
if ecs.Type != actualType {
324+
// exceptions related to numbers
325+
if !isNumberTypeField(ecs.Type, actualType) {
326+
return fmt.Errorf("actual mapping type (%s) does not match with ECS definition type: %s", actualType, ecs.Type)
327+
} else {
328+
logger.Debugf("Allowed number fields with different types (ECS %s - actual %s)", string(ecs.Type), string(actualType))
329+
}
330+
}
331+
332+
// Compare other parameters
333+
metricType := mappingParameter("time_series_metric", actual)
334+
if ecs.MetricType != metricType {
335+
return fmt.Errorf("actual mapping \"time_series_metric\" (%s) does not match with ECS definition value: %s", metricType, ecs.MetricType)
336+
}
337+
return nil
338+
}
339+
340+
func compareMultiFieldsInECSSchema(currentPath string, found *FieldDefinition, definition map[string]any) error {
319341
var ecsMultiFields []FieldDefinition
320342
// Filter multi-fields added by appendECSMappingMultifields
321343
for _, f := range found.MultiFields {
@@ -342,21 +364,34 @@ func (v *MappingValidator) validateMappingInECSSchema(currentPath string, defini
342364
return fmt.Errorf("invalid multi_field mapping %q: %w", currentPath, err)
343365
}
344366

367+
missingMultiFields := []string{}
345368
for key, value := range actualMultiFields {
346369
multiFieldPath := currentMappingPath(currentPath, key)
347370

348371
def, ok := value.(map[string]any)
349372
if !ok {
350373
return fmt.Errorf("invalid multi_field mapping type: %q", multiFieldPath)
351374
}
375+
found := false
352376
for _, ecsMultiField := range ecsMultiFields {
377+
if ecsMultiField.Name != key {
378+
continue
379+
}
353380
err := compareFieldDefinitionWithECS(multiFieldPath, &ecsMultiField, def)
354381
if err == nil {
355-
return nil
382+
found = true
383+
break
356384
}
357385
}
386+
if !found {
387+
missingMultiFields = append(missingMultiFields, key)
388+
}
389+
}
390+
if len(missingMultiFields) > 0 {
391+
return fmt.Errorf("missing definition for multi-fields in ECS: %q", strings.Join(missingMultiFields, ", "))
358392
}
359-
return fmt.Errorf("not found multi-field definition in ECS for %q", currentPath)
393+
394+
return nil
360395

361396
// for _, ecsMultiField := range ecsMultiFields {
362397
// multiFieldPath := currentMappingPath(currentPath, ecsMultiField.Name)
@@ -379,25 +414,6 @@ func (v *MappingValidator) validateMappingInECSSchema(currentPath string, defini
379414
// return nil
380415
}
381416

382-
func compareFieldDefinitionWithECS(currentPath string, ecs *FieldDefinition, actual map[string]any) error {
383-
actualType := mappingParameter("type", actual)
384-
if ecs.Type != actualType {
385-
// exceptions related to numbers
386-
if !isNumberTypeField(ecs.Type, actualType) {
387-
return fmt.Errorf("actual mapping type (%s) does not match with ECS definition type: %s", actualType, ecs.Type)
388-
} else {
389-
logger.Debugf("Allowed number fields with different types (ECS %s - actual %s)", string(ecs.Type), string(actualType))
390-
}
391-
}
392-
393-
// Compare other parameters
394-
metricType := mappingParameter("time_series_metric", actual)
395-
if ecs.MetricType != metricType {
396-
return fmt.Errorf("actual mapping \"time_series_metric\" (%s) does not match with ECS definition value: %s", metricType, ecs.MetricType)
397-
}
398-
return nil
399-
}
400-
401417
// flattenMappings returns all the mapping definitions found at "path" flattened including
402418
// specific entries for multi fields too.
403419
func flattenMappings(path string, definition map[string]any) (map[string]any, error) {
@@ -670,7 +686,7 @@ func (v *MappingValidator) matchingWithDynamicTemplates(currentPath string, defi
670686
return nil
671687
}
672688

673-
logger.Debugf(">> No template matching for path: %q", currentPath)
689+
// logger.Debugf(">> No template matching for path: %q", currentPath)
674690
return fmt.Errorf("no template matching for path: %q", currentPath)
675691
}
676692

internal/fields/mappings_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,24 @@ func TestComparingMappings(t *testing.T) {
9999
"foo": map[string]any{
100100
"type": "keyword",
101101
},
102+
"user": map[string]any{
103+
"type": "keyword",
104+
},
105+
"time": map[string]any{
106+
"type": "keyword",
107+
"fields": map[string]any{
108+
"text": map[string]any{
109+
"type": "match_only_text",
110+
},
111+
"other": map[string]any{
112+
"type": "match_only_text",
113+
},
114+
},
115+
},
116+
// Should this fail since it has no multi-fields as in the ECS definition?
117+
"name": map[string]any{
118+
"type": "keyword",
119+
},
102120
},
103121
schema: []FieldDefinition{
104122
{
@@ -116,9 +134,35 @@ func TestComparingMappings(t *testing.T) {
116134
Type: "keyword",
117135
External: "",
118136
},
137+
{
138+
Name: "time",
139+
Type: "keyword",
140+
External: "ecs",
141+
MultiFields: []FieldDefinition{
142+
{
143+
Name: "text",
144+
Type: "match_only_text",
145+
External: "ecs",
146+
},
147+
},
148+
},
149+
{
150+
Name: "name",
151+
Type: "keyword",
152+
External: "ecs",
153+
MultiFields: []FieldDefinition{
154+
{
155+
Name: "text",
156+
Type: "match_only_text",
157+
External: "ecs",
158+
},
159+
},
160+
},
119161
},
120162
expectedErrors: []string{
121163
`field "metrics" is undefined: actual mapping type (long) does not match with ECS definition type: keyword`,
164+
`field "user" is undefined: missing definition for path (not in ECS)`,
165+
`field "time" is undefined: missing definition for multi-fields in ECS: "other"`,
122166
},
123167
},
124168
{
@@ -230,6 +274,14 @@ func TestComparingMappings(t *testing.T) {
230274
{
231275
title: "validate multifields failure",
232276
preview: map[string]any{
277+
"time": map[string]any{
278+
"type": "keyword",
279+
"fields": map[string]any{
280+
"text": map[string]any{
281+
"type": "match_only_text",
282+
},
283+
},
284+
},
233285
"foo": map[string]any{
234286
"type": "keyword",
235287
"fields": map[string]any{
@@ -255,6 +307,10 @@ func TestComparingMappings(t *testing.T) {
255307
},
256308
},
257309
actual: map[string]any{
310+
// Should this fail since it has no multi-fields as in the preview?
311+
"time": map[string]any{
312+
"type": "keyword",
313+
},
258314
"foo": map[string]any{
259315
"type": "keyword",
260316
"fields": map[string]any{

0 commit comments

Comments
 (0)