diff --git a/libbeat/common/schema/mapstriface/mapstriface.go b/libbeat/common/schema/mapstriface/mapstriface.go index d3c51eea2dae..a903f5c092ad 100644 --- a/libbeat/common/schema/mapstriface/mapstriface.go +++ b/libbeat/common/schema/mapstriface/mapstriface.go @@ -193,9 +193,43 @@ func toInteger(key string, data map[string]interface{}) (interface{}, error) { if err == nil { return int64(f64), nil } - return 0, fmt.Errorf("Expected integer, found json.Number (%v) that cannot be converted", num) + return 0, fmt.Errorf("expected integer, found json.Number (%v) that cannot be converted", num) default: - return 0, fmt.Errorf("Expected integer, found %T", emptyIface) + return 0, fmt.Errorf("expected integer, found %T", emptyIface) + } +} + +// Float creates a Conv object for converting floats. Acceptable input +// types are int64, int, and float64. +func Float(key string, opts ...schema.SchemaOption) schema.Conv { + return schema.SetOptions(schema.Conv{Key: key, Func: toFloat}, opts) +} + +func toFloat(key string, data map[string]interface{}) (interface{}, error) { + emptyIface, exists := data[key] + if !exists { + return 0, fmt.Errorf("key %s not found", key) + } + switch emptyIface.(type) { + case float64: + return emptyIface.(float64), nil + case int: + return float64(emptyIface.(int)), nil + case int64: + return float64(emptyIface.(int64)), nil + case json.Number: + num := emptyIface.(json.Number) + i64, err := num.Float64() + if err == nil { + return i64, nil + } + f64, err := num.Float64() + if err == nil { + return f64, nil + } + return 0, fmt.Errorf("expected float, found json.Number (%v) that cannot be converted", num) + default: + return 0, fmt.Errorf("expected float, found %T", emptyIface) } } diff --git a/libbeat/common/schema/mapstriface/mapstriface_test.go b/libbeat/common/schema/mapstriface/mapstriface_test.go index 3671e1bf0507..d2bc20e489de 100644 --- a/libbeat/common/schema/mapstriface/mapstriface_test.go +++ b/libbeat/common/schema/mapstriface/mapstriface_test.go @@ -17,13 +17,16 @@ func TestConversions(t *testing.T) { cTs := common.Time{} input := map[string]interface{}{ - "testString": "hello", - "testInt": 42, - "testIntFromFloat": 42.0, - "testIntFromInt32": int32(32), - "testIntFromInt64": int64(42), - "testJsonNumber": json.Number("3910564293633576924"), - "testBool": true, + "testString": "hello", + "testInt": 42, + "testIntFromFloat": 42.2, + "testFloat": 42.7, + "testFloatFromInt": 43, + "testIntFromInt32": int32(32), + "testIntFromInt64": int64(42), + "testJsonNumber": json.Number("3910564293633576924"), + "testJsonNumberFloat": json.Number("43.7"), + "testBool": true, "testObj": map[string]interface{}{ "testObjString": "hello, object", }, @@ -49,7 +52,10 @@ func TestConversions(t *testing.T) { "test_int": Int("testInt"), "test_int_from_float": Int("testIntFromFloat"), "test_int_from_int64": Int("testIntFromInt64"), + "test_float": Float("testFloat"), + "test_float_from_int": Float("testFloatFromInt"), "test_int_from_json": Int("testJsonNumber"), + "test_float_from_json": Float("testJsonNumberFloat"), "test_string_from_num": StrFromNum("testIntFromInt32"), "test_string_from_json_num": StrFromNum("testJsonNumber"), "test_bool": Bool("testBool"), @@ -74,7 +80,10 @@ func TestConversions(t *testing.T) { "test_int": int64(42), "test_int_from_float": int64(42), "test_int_from_int64": int64(42), + "test_float": float64(42.7), + "test_float_from_int": float64(43), "test_int_from_json": int64(3910564293633576924), + "test_float_from_json": float64(43.7), "test_string_from_num": "32", "test_string_from_json_num": "3910564293633576924", "test_bool": true,