Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use json number when unmarshalling data from ES #1618

Merged
merged 3 commits into from
Jun 21, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions plugin/storage/es/spanstore/dbmodel/to_domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package dbmodel

import (
"encoding/hex"
"encoding/json"
"fmt"
"strconv"
"strings"
Expand Down Expand Up @@ -161,12 +162,9 @@ func (td ToDomain) convertTagFields(tagsMap map[string]interface{}) ([]model.Key

func (td ToDomain) convertTagField(k string, v interface{}) (model.KeyValue, error) {
dKey := td.ReplaceDotReplacement(k)
// The number is always a float64 therefore type assertion on int (v.(int/64/32)) does not work.
// If 1.0, 2.0.. was stored as float it will be read as int
if pInt, err := strconv.ParseInt(fmt.Sprintf("%v", v), 10, 64); err == nil {
return model.Int64(dKey, pInt), nil
}
switch val := v.(type) {
case int64:
return model.Int64(dKey, val), nil
case float64:
return model.Float64(dKey, val), nil
case bool:
Expand All @@ -176,6 +174,18 @@ func (td ToDomain) convertTagField(k string, v interface{}) (model.KeyValue, err
// the binary is never returned, ES returns it as string with base64 encoding
case []byte:
return model.Binary(dKey, val), nil
// in spans are decoded using json.UseNumber() to preserve the type
// however note that float(1) will be parsed as int as ES does not store decimal point
case json.Number:
n, err := val.Int64()
if err == nil {
return model.Int64(dKey, n), nil
}
f, err := val.Float64()
if err == nil {
return model.Float64(dKey, f), nil
}
return model.String("", ""), err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to return an error similar to the next line, to provide more context?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 I will wrap it

default:
return model.String("", ""), fmt.Errorf("invalid tag type in %+v", v)
}
Expand Down
22 changes: 16 additions & 6 deletions plugin/storage/es/spanstore/dbmodel/to_domain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,12 @@ func TestTagsMap(t *testing.T) {
{fieldTags: map[string]interface{}{"int.int": int64(1)}, expected: []model.KeyValue{model.Int64("int.int", 1)}},
{fieldTags: map[string]interface{}{"int:int": int64(2)}, expected: []model.KeyValue{model.Int64("int.int", 2)}},
{fieldTags: map[string]interface{}{"float": float64(1.1)}, expected: []model.KeyValue{model.Float64("float", 1.1)}},
// we are not able to reproduce type for float 123 or any N.0 number therefore returning int
{fieldTags: map[string]interface{}{"float": float64(123)}, expected: []model.KeyValue{model.Int64("float", 123)}},
{fieldTags: map[string]interface{}{"float": float64(123.0)}, expected: []model.KeyValue{model.Int64("float", 123)}},
{fieldTags: map[string]interface{}{"float:float": float64(123)}, expected: []model.KeyValue{model.Int64("float.float", 123)}},
{fieldTags: map[string]interface{}{"float": float64(123)}, expected: []model.KeyValue{model.Float64("float", float64(123))}},
{fieldTags: map[string]interface{}{"float": float64(123.0)}, expected: []model.KeyValue{model.Float64("float", float64(123.0))}},
{fieldTags: map[string]interface{}{"float:float": float64(123)}, expected: []model.KeyValue{model.Float64("float.float", float64(123))}},
{fieldTags: map[string]interface{}{"json_number:int": json.Number("123")}, expected: []model.KeyValue{model.Int64("json_number.int", 123)}},
{fieldTags: map[string]interface{}{"json_number:float": json.Number("123.0")}, expected: []model.KeyValue{model.Float64("json_number.float", float64(123.0))}},
{fieldTags: map[string]interface{}{"json_number:err": json.Number("foo")}, err: fmt.Errorf("strconv.ParseFloat: parsing \"foo\": invalid syntax")},
{fieldTags: map[string]interface{}{"str": "foo"}, expected: []model.KeyValue{model.String("str", "foo")}},
{fieldTags: map[string]interface{}{"str:str": "foo"}, expected: []model.KeyValue{model.String("str.str", "foo")}},
{fieldTags: map[string]interface{}{"binary": []byte("foo")}, expected: []model.KeyValue{model.Binary("binary", []byte("foo"))}},
Expand All @@ -333,8 +335,16 @@ func TestTagsMap(t *testing.T) {
for i, test := range tests {
t.Run(fmt.Sprintf("%d, %s", i, test.fieldTags), func(t *testing.T) {
tags, err := converter.convertTagFields(test.fieldTags)
assert.Equal(t, test.expected, tags)
assert.Equal(t, test.err, err)
if err != nil {
fmt.Println(err.Error())
}
if test.err != nil {
assert.Equal(t, test.err.Error(), err.Error())
require.Nil(t, tags)
} else {
require.NoError(t, err)
assert.Equal(t, test.expected, tags)
}
})
}
}
Expand Down
6 changes: 5 additions & 1 deletion plugin/storage/es/spanstore/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package spanstore

import (
"bytes"
"context"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -218,7 +219,10 @@ func (s *SpanReader) unmarshalJSONSpan(esSpanRaw *elastic.SearchHit) (*dbmodel.S
esSpanInByteArray := esSpanRaw.Source

var jsonSpan dbmodel.Span
if err := json.Unmarshal(*esSpanInByteArray, &jsonSpan); err != nil {

d := json.NewDecoder(bytes.NewReader(*esSpanInByteArray))
d.UseNumber()
if err := d.Decode(&jsonSpan); err != nil {
return nil, err
}
return &jsonSpan, nil
Expand Down