Skip to content

Commit

Permalink
Implement MarshalJSON for query results (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
xichen2020 committed Feb 9, 2019
1 parent 593c801 commit e5ab829
Show file tree
Hide file tree
Showing 18 changed files with 842 additions and 210 deletions.
30 changes: 15 additions & 15 deletions calculation/calculation_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,32 +132,32 @@ var (

// For operators that do not require fields.
newResultFnsByOpsNoType = map[Op]newResultFn{
Count: newCountResult,
Count: NewCountResult,
}

// For operators that require fields.
newResultFnsByOpsAndType = map[Op]map[field.ValueType]newResultFn{
Sum: map[field.ValueType]newResultFn{
field.IntType: newSumResult,
field.DoubleType: newSumResult,
field.TimeType: newSumResult,
field.IntType: NewSumResult,
field.DoubleType: NewSumResult,
field.TimeType: NewSumResult,
},
Avg: map[field.ValueType]newResultFn{
field.IntType: newAvgResult,
field.DoubleType: newAvgResult,
field.TimeType: newAvgResult,
field.IntType: NewAvgResult,
field.DoubleType: NewAvgResult,
field.TimeType: NewAvgResult,
},
Min: map[field.ValueType]newResultFn{
field.IntType: newMinNumberResult,
field.DoubleType: newMinNumberResult,
field.StringType: newMinStringResult,
field.TimeType: newMinNumberResult,
field.IntType: NewMinNumberResult,
field.DoubleType: NewMinNumberResult,
field.StringType: NewMinStringResult,
field.TimeType: NewMinNumberResult,
},
Max: map[field.ValueType]newResultFn{
field.IntType: newMaxNumberResult,
field.DoubleType: newMaxNumberResult,
field.StringType: newMaxStringResult,
field.TimeType: newMaxNumberResult,
field.IntType: NewMaxNumberResult,
field.DoubleType: NewMaxNumberResult,
field.StringType: NewMaxStringResult,
field.TimeType: NewMaxNumberResult,
},
}

Expand Down
87 changes: 50 additions & 37 deletions calculation/result.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package calculation

import (
"encoding/json"
"errors"
"fmt"
"math"
Expand All @@ -25,6 +26,9 @@ type Result interface {

// Value returns the result value.
Value() ValueUnion

// MarshalJSON marshals the result as a JSON object.
MarshalJSON() ([]byte, error)
}

var (
Expand All @@ -43,11 +47,12 @@ type countResult struct {
v int
}

func newCountResult() Result {
// NewCountResult creates a new count result.
func NewCountResult() Result {
return &countResult{}
}

func (r *countResult) New() Result { return newCountResult() }
func (r *countResult) New() Result { return NewCountResult() }

func (r *countResult) Add(ValueUnion) { r.v++ }

Expand All @@ -60,17 +65,18 @@ func (r *countResult) MergeInPlace(other Result) error {
return nil
}

func (r *countResult) Value() ValueUnion { return newNumberUnion(float64(r.v)) }
func (r *countResult) Value() ValueUnion { return NewNumberUnion(float64(r.v)) }

func (r *countResult) MarshalJSON() ([]byte, error) { return json.Marshal(r.Value()) }

type sumResult struct {
v float64
}

func newSumResult() Result {
return &sumResult{}
}
// NewSumResult creates a new sum result.
func NewSumResult() Result { return &sumResult{} }

func (r *sumResult) New() Result { return newSumResult() }
func (r *sumResult) New() Result { return NewSumResult() }

func (r *sumResult) Add(v ValueUnion) { r.v += v.NumberVal }

Expand All @@ -83,18 +89,19 @@ func (r *sumResult) MergeInPlace(other Result) error {
return nil
}

func (r *sumResult) Value() ValueUnion { return newNumberUnion(r.v) }
func (r *sumResult) Value() ValueUnion { return NewNumberUnion(r.v) }

func (r *sumResult) MarshalJSON() ([]byte, error) { return json.Marshal(r.Value()) }

type avgResult struct {
s float64
c int
}

func newAvgResult() Result {
return &avgResult{}
}
// NewAvgResult creates a new average result.
func NewAvgResult() Result { return &avgResult{} }

func (r *avgResult) New() Result { return newAvgResult() }
func (r *avgResult) New() Result { return NewAvgResult() }

func (r *avgResult) Add(v ValueUnion) {
r.s += v.NumberVal
Expand All @@ -111,18 +118,19 @@ func (r *avgResult) MergeInPlace(other Result) error {
return nil
}

func (r *avgResult) Value() ValueUnion { return newNumberUnion(r.s / float64(r.c)) }
func (r *avgResult) Value() ValueUnion { return NewNumberUnion(r.s / float64(r.c)) }

func (r *avgResult) MarshalJSON() ([]byte, error) { return json.Marshal(r.Value()) }

type minNumberResult struct {
hasValues bool
v float64
}

func newMinNumberResult() Result {
return &minNumberResult{}
}
// NewMinNumberResult creates a new minimum number result.
func NewMinNumberResult() Result { return &minNumberResult{} }

func (r *minNumberResult) New() Result { return newMinNumberResult() }
func (r *minNumberResult) New() Result { return NewMinNumberResult() }

func (r *minNumberResult) Add(v ValueUnion) {
if !r.hasValues {
Expand Down Expand Up @@ -155,21 +163,22 @@ func (r *minNumberResult) MergeInPlace(other Result) error {

func (r *minNumberResult) Value() ValueUnion {
if !r.hasValues {
return newNumberUnion(nan)
return NewNumberUnion(nan)
}
return newNumberUnion(r.v)
return NewNumberUnion(r.v)
}

func (r *minNumberResult) MarshalJSON() ([]byte, error) { return json.Marshal(r.Value()) }

type minStringResult struct {
hasValues bool
v string
}

func newMinStringResult() Result {
return &minStringResult{}
}
// NewMinStringResult creates a new minimum string result.
func NewMinStringResult() Result { return &minStringResult{} }

func (r *minStringResult) New() Result { return newMinStringResult() }
func (r *minStringResult) New() Result { return NewMinStringResult() }

func (r *minStringResult) Add(v ValueUnion) {
if !r.hasValues {
Expand Down Expand Up @@ -202,21 +211,22 @@ func (r *minStringResult) MergeInPlace(other Result) error {

func (r *minStringResult) Value() ValueUnion {
if !r.hasValues {
return newStringUnion(emptyString)
return NewStringUnion(emptyString)
}
return newStringUnion(r.v)
return NewStringUnion(r.v)
}

func (r *minStringResult) MarshalJSON() ([]byte, error) { return json.Marshal(r.Value()) }

type maxNumberResult struct {
hasValues bool
v float64
}

func newMaxNumberResult() Result {
return &maxNumberResult{}
}
// NewMaxNumberResult creates a new maximum number result.
func NewMaxNumberResult() Result { return &maxNumberResult{} }

func (r *maxNumberResult) New() Result { return newMaxNumberResult() }
func (r *maxNumberResult) New() Result { return NewMaxNumberResult() }

func (r *maxNumberResult) Add(v ValueUnion) {
if !r.hasValues {
Expand Down Expand Up @@ -249,21 +259,22 @@ func (r *maxNumberResult) MergeInPlace(other Result) error {

func (r *maxNumberResult) Value() ValueUnion {
if !r.hasValues {
return newNumberUnion(nan)
return NewNumberUnion(nan)
}
return newNumberUnion(r.v)
return NewNumberUnion(r.v)
}

func (r *maxNumberResult) MarshalJSON() ([]byte, error) { return json.Marshal(r.Value()) }

type maxStringResult struct {
hasValues bool
v string
}

func newMaxStringResult() Result {
return &maxStringResult{}
}
// NewMaxStringResult creates a new maximum string result.
func NewMaxStringResult() Result { return &maxStringResult{} }

func (r *maxStringResult) New() Result { return newMaxStringResult() }
func (r *maxStringResult) New() Result { return NewMaxStringResult() }

func (r *maxStringResult) Add(v ValueUnion) {
if !r.hasValues {
Expand Down Expand Up @@ -296,11 +307,13 @@ func (r *maxStringResult) MergeInPlace(other Result) error {

func (r *maxStringResult) Value() ValueUnion {
if !r.hasValues {
return newStringUnion(emptyString)
return NewStringUnion(emptyString)
}
return newStringUnion(r.v)
return NewStringUnion(r.v)
}

func (r *maxStringResult) MarshalJSON() ([]byte, error) { return json.Marshal(r.Value()) }

// ResultArray is an array of calculation result.
type ResultArray []Result

Expand Down
40 changes: 32 additions & 8 deletions calculation/value.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package calculation

import (
"encoding/json"
"fmt"
"math"

"github.com/xichen2020/eventdb/document/field"
"github.com/xichen2020/eventdb/x/compare"
Expand All @@ -16,18 +18,40 @@ const (
StringType
)

var (
nullBytes = []byte("null")
)

// ValueUnion is a value union.
type ValueUnion struct {
Type ValueType
NumberVal float64
StringVal string
}

func newNumberUnion(v float64) ValueUnion {
// MarshalJSON marshals value as a JSON object.
func (u ValueUnion) MarshalJSON() ([]byte, error) {
switch u.Type {
case NumberType:
// NaN cannot be marshalled as JSON, so marshal it as null.
if math.IsNaN(u.NumberVal) {
return nullBytes, nil
}
return json.Marshal(u.NumberVal)
case StringType:
return json.Marshal(u.StringVal)
default:
return nil, fmt.Errorf("unexpected value type %v", u.Type)
}
}

// NewNumberUnion creates a new number union.
func NewNumberUnion(v float64) ValueUnion {
return ValueUnion{Type: NumberType, NumberVal: v}
}

func newStringUnion(v string) ValueUnion {
// NewStringUnion creates a new string union.
func NewStringUnion(v string) ValueUnion {
return ValueUnion{Type: StringType, StringVal: v}
}

Expand Down Expand Up @@ -100,31 +124,31 @@ func AsValueFns(fieldTypes field.OptionalTypeArray) ([]FieldValueToValueFn, erro
}

func nullToValue(v *field.ValueUnion) ValueUnion {
return newNumberUnion(0)
return NewNumberUnion(0)
}

func boolToValue(v *field.ValueUnion) ValueUnion {
var val float64
if v.BoolVal {
val = 1
}
return newNumberUnion(val)
return NewNumberUnion(val)
}

func intToValue(v *field.ValueUnion) ValueUnion {
return newNumberUnion(float64(v.IntVal))
return NewNumberUnion(float64(v.IntVal))
}

func doubleToValue(v *field.ValueUnion) ValueUnion {
return newNumberUnion(v.DoubleVal)
return NewNumberUnion(v.DoubleVal)
}

func stringToValue(v *field.ValueUnion) ValueUnion {
return newStringUnion(v.StringVal)
return NewStringUnion(v.StringVal)
}

func timeToValue(v *field.ValueUnion) ValueUnion {
return newNumberUnion(float64(v.TimeNanosVal))
return NewNumberUnion(float64(v.TimeNanosVal))
}

var (
Expand Down
23 changes: 23 additions & 0 deletions document/field/value.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package field

import (
"encoding/json"
"fmt"
"math"

Expand All @@ -27,6 +28,8 @@ const (
var (
// NumValidFieldTypes returns the number of valid field types.
NumValidFieldTypes = len(validTypes)

nullBytes = []byte("null")
)

// IsValid returns true if this is a valid value type.
Expand Down Expand Up @@ -197,6 +200,26 @@ func NewTimeUnion(v int64) ValueUnion {
}
}

// MarshalJSON marshals value as a JSON object.
func (v ValueUnion) MarshalJSON() ([]byte, error) {
switch v.Type {
case NullType:
return nullBytes, nil
case BoolType:
return json.Marshal(v.BoolVal)
case IntType:
return json.Marshal(v.IntVal)
case DoubleType:
return json.Marshal(v.DoubleVal)
case StringType:
return json.Marshal(v.StringVal)
case TimeType:
return json.Marshal(v.TimeNanosVal)
default:
return nil, fmt.Errorf("unknown value type: %v", v.Type)
}
}

// Equal returns true if two value unions are considered equal.
func (v *ValueUnion) Equal(other *ValueUnion) bool {
if v == nil && other == nil {
Expand Down
Loading

0 comments on commit e5ab829

Please sign in to comment.