683 changes: 342 additions & 341 deletions protos/pb/pb.pb.go

Large diffs are not rendered by default.

85 changes: 79 additions & 6 deletions query/aggregator.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package query
import (
"bytes"
"math"
"math/big"
"time"

"github.com/pkg/errors"
Expand Down Expand Up @@ -119,12 +120,10 @@ func applyAdd(a, b, c *types.Val) error {
switch vBase {
case INT:
aVal, bVal := a.Value.(int64), b.Value.(int64)

if (aVal > 0 && bVal > math.MaxInt64-aVal) ||
(aVal < 0 && bVal < math.MinInt64-aVal) {
return ErrorIntOverflow
}

c.Value = aVal + bVal

case FLOAT:
Expand All @@ -148,6 +147,11 @@ func applyAdd(a, b, c *types.Val) error {
}
c.Value = cVal

case BIGFLOAT:
aVal, bVal := a.Value.(big.Float), b.Value.(big.Float)
aVal.Add(&aVal, &bVal)
c.Value = aVal

case DEFAULT:
return errors.Errorf("Wrong type %v encountered for func +", a.Tid)
}
Expand All @@ -159,12 +163,10 @@ func applySub(a, b, c *types.Val) error {
switch vBase {
case INT:
aVal, bVal := a.Value.(int64), b.Value.(int64)

if (bVal < 0 && aVal > math.MaxInt64+bVal) ||
(bVal > 0 && aVal < math.MinInt64+bVal) {
return ErrorIntOverflow
}

c.Value = aVal - bVal

case FLOAT:
Expand All @@ -188,6 +190,11 @@ func applySub(a, b, c *types.Val) error {
}
c.Value = cVal

case BIGFLOAT:
aVal, bVal := a.Value.(big.Float), b.Value.(big.Float)
aVal.Sub(&aVal, &bVal)
c.Value = aVal

case DEFAULT:
return errors.Errorf("Wrong type %v encountered for func -", a.Tid)
}
Expand All @@ -211,7 +218,6 @@ func applyMul(a, b, c *types.Val) error {
aVal, bVal := a.Value.(int64), b.Value.(int64)
c.Tid = types.IntID
c.Value = aVal * bVal

if aVal == 0 || bVal == 0 {
return nil
} else if c.Value.(int64)/bVal != aVal {
Expand Down Expand Up @@ -254,6 +260,11 @@ func applyMul(a, b, c *types.Val) error {
}
c.Tid = types.VFloatID

case BIGFLOAT:
aVal, bVal := a.Value.(big.Float), b.Value.(big.Float)
aVal.Mul(&aVal, &bVal)
c.Value = aVal

case DEFAULT:
return invalidTypeError(lValType, rValType, "*")
}
Expand Down Expand Up @@ -298,6 +309,14 @@ func applyDiv(a, b, c *types.Val) error {
}
c.Value = cVal
c.Tid = types.VFloatID
case BIGFLOAT:
aVal, bVal := a.Value.(big.Float), b.Value.(big.Float)
var zero big.Float
if bVal.Cmp(&zero) == 0 {
return ErrorDivisionByZero
}
aVal.Quo(&aVal, &bVal)
c.Value = aVal
case DEFAULT:
return invalidTypeError(numeratorType, getValType(b), "/")
}
Expand Down Expand Up @@ -477,6 +496,11 @@ func applyNeg(a, res *types.Val) error {
res.Value = resVal
res.Tid = types.VFloatID

case BIGFLOAT:
value := a.Value.(big.Float)
neg := big.NewFloat(0).SetPrec(types.BigFloatPrecision).Neg(&value)
res.Value = *neg

case DEFAULT:
return errors.Errorf("Wrong type %v encountered for func u-", a.Tid)
}
Expand All @@ -499,6 +523,11 @@ func applySqrt(a, res *types.Val) error {
}
res.Value = math.Sqrt(a.Value.(float64))

case BIGFLOAT:
value := a.Value.(big.Float)
rt := big.NewFloat(0).SetPrec(types.BigFloatPrecision).Sqrt(&value)
res.Value = *rt

case DEFAULT:
return errors.Errorf("Wrong type %v encountered for func sqrt", a.Tid)
}
Expand All @@ -514,6 +543,11 @@ func applyFloor(a, res *types.Val) error {
case FLOAT:
res.Value = math.Floor(a.Value.(float64))

case BIGFLOAT:
value := a.Value.(big.Float)
f, _ := value.Float64()
res.Value = *big.NewFloat(math.Floor(f)).SetPrec(types.BigFloatPrecision)

case DEFAULT:
return errors.Errorf("Wrong type %v encountered for func floor", a.Tid)
}
Expand All @@ -529,6 +563,11 @@ func applyCeil(a, res *types.Val) error {
case FLOAT:
res.Value = math.Ceil(a.Value.(float64))

case BIGFLOAT:
value := a.Value.(big.Float)
f, _ := value.Float64()
res.Value = *big.NewFloat(math.Ceil(f)).SetPrec(types.BigFloatPrecision)

case DEFAULT:
return errors.Errorf("Wrong type %v encountered for fun ceil", a.Tid)
}
Expand Down Expand Up @@ -595,6 +634,7 @@ const (
FLOAT
VFLOAT
DEFAULT
BIGFLOAT
)

func getValType(v *types.Val) valType {
Expand All @@ -606,6 +646,8 @@ func getValType(v *types.Val) valType {
vBase = FLOAT
case types.VFloatID:
vBase = VFLOAT
case types.BigFloatID:
vBase = BIGFLOAT
default:
vBase = DEFAULT
}
Expand Down Expand Up @@ -655,6 +697,10 @@ func (ag *aggregator) matchType(left, right *types.Val) error {
if _, found := mixedScalarVectOps[ag.name]; !found {
return invalidTypeError(leftType, rightType, ag.name)
}
} else if rightType == BIGFLOAT {
var bf big.Float
left.Value = bf.SetPrec(types.BigFloatPrecision).SetInt64(left.Value.(int64))
left.Tid = types.BigFloatID
}
// rightType must be either FLOAT or VFLOAT. In either case, we
// must cast the left type to a FLOAT.
Expand All @@ -665,6 +711,10 @@ func (ag *aggregator) matchType(left, right *types.Val) error {
if _, found := mixedScalarVectOps[ag.name]; !found {
return invalidTypeError(leftType, rightType, ag.name)
}
} else if rightType == BIGFLOAT {
var bf big.Float
left.Value = bf.SetPrec(types.BigFloatPrecision).SetFloat64(left.Value.(float64))
left.Tid = types.BigFloatID
} else {
// We can assume here: rightType == INT
right.Tid = types.FloatID
Expand All @@ -675,6 +725,17 @@ func (ag *aggregator) matchType(left, right *types.Val) error {
right.Tid = types.FloatID
right.Value = float64(right.Value.(int64))
}
case BIGFLOAT:
if rightType == FLOAT {
var bf big.Float
right.Value = *bf.SetPrec(types.BigFloatPrecision).SetFloat64(right.Value.(float64))
right.Tid = types.BigFloatID
} else if rightType == INT {
var bf big.Float
right.Value = *bf.SetPrec(types.BigFloatPrecision).SetInt64(right.Value.(int64))
right.Tid = types.BigFloatID
}

// We can assume that if rightType is not INT then it must
// be FLOAT, in which case, there is no further step needed.
default:
Expand Down Expand Up @@ -789,8 +850,11 @@ func (ag *aggregator) Apply(val types.Val) error {
for i := 0; i < len(bVal); i++ {
accumVal[i] += bVal[i]
}
case va.Tid == types.BigFloatID && vb.Tid == types.BigFloatID:
lhs := va.Value.(big.Float)
rhs := vb.Value.(big.Float)
va.Value = *new(big.Float).SetPrec(types.BigFloatPrecision).Add(&lhs, &rhs)
}
// Skipping the else case since that means the pair cannot be summed.
res = va
default:
x.Fatalf("Unhandled aggregator function %v", ag.name)
Expand Down Expand Up @@ -820,6 +884,15 @@ func (ag *aggregator) divideByCount() {
if ag.name != "avg" || ag.count == 0 || ag.result.Value == nil {
return
}

if ag.result.Tid == types.BigFloatID {
val := ag.result.Value.(big.Float)
val.Quo(&val, new(big.Float).SetPrec(types.BigFloatPrecision).SetInt64(int64(ag.count)))
ag.result.Tid = types.BigFloatID
ag.result.Value = val
return
}

var v float64
switch ag.result.Tid {
case types.IntID:
Expand Down
173 changes: 173 additions & 0 deletions query/math_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package query

import (
"math"
"math/big"
"testing"

"github.com/pkg/errors"
Expand Down Expand Up @@ -616,7 +617,74 @@ func TestProcessBinary(t *testing.T) {
}
}

func TestProcessBinaryBigFloat(t *testing.T) {
tests := []struct {
in *mathTree
out types.Val
}{
{in: &mathTree{
Fn: "+",
Child: []*mathTree{
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(2.15)}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(1.15)}},
}},
out: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(3.3)},
},
{in: &mathTree{
Fn: "-",
Child: []*mathTree{
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(100)}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(1)}},
}},
out: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(99)},
},
{in: &mathTree{
Fn: "*",
Child: []*mathTree{
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(3)}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(3)}},
}},
out: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(9)},
},
{in: &mathTree{
Fn: "/",
Child: []*mathTree{
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(12)}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(4)}},
}},
out: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(3)},
},
{in: &mathTree{
Fn: "max",
Child: []*mathTree{
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(1)}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(100)}},
}},
out: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(100)},
},
{in: &mathTree{
Fn: "min",
Child: []*mathTree{
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(1)}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(100)}},
}},
out: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(1)},
},
}
for _, tc := range tests {
t.Logf("Test %s", tc.in.Fn)
err := processBinary(tc.in)
require.NoError(t, err)
require.EqualValues(t, tc.out, tc.in.Const)
}
}

func TestProcessUnary(t *testing.T) {
float3 := *new(big.Float).SetPrec(200)
float3.SetFloat64(3.1)
sqrt3 := *new(big.Float).SetPrec(200)
sqrt3.Sqrt(&float3)

tests := []struct {
in *mathTree
out types.Val
Expand All @@ -628,6 +696,13 @@ func TestProcessUnary(t *testing.T) {
}},
out: types.Val{Tid: types.IntID, Value: int64(-2.0)},
},
{in: &mathTree{
Fn: "u-",
Child: []*mathTree{
{Const: types.Val{Tid: types.BigFloatID, Value: float3}},
}},
out: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(-3.1).SetPrec(200)},
},
{in: &mathTree{
Fn: "ln",
Child: []*mathTree{
Expand All @@ -649,20 +724,41 @@ func TestProcessUnary(t *testing.T) {
}},
out: types.Val{Tid: types.FloatID, Value: 3.0},
},
{in: &mathTree{
Fn: "sqrt",
Child: []*mathTree{
{Const: types.Val{Tid: types.BigFloatID, Value: float3}},
}},
out: types.Val{Tid: types.BigFloatID, Value: sqrt3},
},
{in: &mathTree{
Fn: "floor",
Child: []*mathTree{
{Const: types.Val{Tid: types.FloatID, Value: 2.5}},
}},
out: types.Val{Tid: types.FloatID, Value: 2.0},
},
{in: &mathTree{
Fn: "floor",
Child: []*mathTree{
{Const: types.Val{Tid: types.BigFloatID, Value: sqrt3}},
}},
out: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(1).SetPrec(200)},
},
{in: &mathTree{
Fn: "ceil",
Child: []*mathTree{
{Const: types.Val{Tid: types.FloatID, Value: 2.5}},
}},
out: types.Val{Tid: types.FloatID, Value: 3.0},
},
{in: &mathTree{
Fn: "ceil",
Child: []*mathTree{
{Const: types.Val{Tid: types.BigFloatID, Value: sqrt3}},
}},
out: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(2).SetPrec(200)},
},
{in: &mathTree{
Fn: "u-",
Child: []*mathTree{
Expand Down Expand Up @@ -802,6 +898,74 @@ func TestProcessBinaryBoolean(t *testing.T) {
}
}

func TestBigFloatMathsBoolean(t *testing.T) {
tests := []struct {
in *mathTree
out types.Val
}{
{in: &mathTree{
Fn: "==",
Child: []*mathTree{
{Val: map[uint64]types.Val{
0: {Tid: types.BigFloatID, Value: *big.NewFloat(2.123)}}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(2.123)}},
}},
out: types.Val{Tid: types.BoolID, Value: true},
},
{in: &mathTree{
Fn: "!=",
Child: []*mathTree{
{Val: map[uint64]types.Val{
0: {Tid: types.BigFloatID, Value: *big.NewFloat(2.4623)}}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(3.623)}},
}},
out: types.Val{Tid: types.BoolID, Value: true},
},
{in: &mathTree{
Fn: ">=",
Child: []*mathTree{
{Val: map[uint64]types.Val{
0: {Tid: types.BigFloatID, Value: *big.NewFloat(2.123)}}},
{Val: map[uint64]types.Val{
0: {Tid: types.BigFloatID, Value: *big.NewFloat(4.123)}}},
}},
out: types.Val{Tid: types.BoolID, Value: false},
},
{in: &mathTree{
Fn: "<=",
Child: []*mathTree{
{Val: map[uint64]types.Val{
0: {Tid: types.BigFloatID, Value: *big.NewFloat(2.123)}}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(3.992)}},
}},
out: types.Val{Tid: types.BoolID, Value: true},
},
{in: &mathTree{
Fn: ">",
Child: []*mathTree{
{Val: map[uint64]types.Val{
0: {Tid: types.BigFloatID, Value: *big.NewFloat(2.45)}}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(3.43)}},
}},
out: types.Val{Tid: types.BoolID, Value: false},
},
{in: &mathTree{
Fn: "<",
Child: []*mathTree{
{Val: map[uint64]types.Val{
0: {Tid: types.BigFloatID, Value: *big.NewFloat(2.1213)}}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(2.1232)}},
}},
out: types.Val{Tid: types.BoolID, Value: true},
},
}
for _, tc := range tests {
t.Logf("Test %s", tc.in.Fn)
require.NoError(t, processBinaryBoolean(tc.in))
require.EqualValues(t, tc.out, tc.in.Val[0])
}
}

func TestProcessTernary(t *testing.T) {
tests := []struct {
in *mathTree
Expand All @@ -825,6 +989,15 @@ func TestProcessTernary(t *testing.T) {
}},
out: types.Val{Tid: types.FloatID, Value: 2.0},
},
{in: &mathTree{
Fn: "cond",
Child: []*mathTree{
{Val: map[uint64]types.Val{0: {Tid: types.BoolID, Value: false}}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(1.456)}},
{Const: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(2.123)}},
}},
out: types.Val{Tid: types.BigFloatID, Value: *big.NewFloat(2.123)},
},
}
for _, tc := range tests {
t.Logf("Test: %s", tc.in.Fn)
Expand Down
4 changes: 4 additions & 0 deletions query/outputnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"encoding/json"
"fmt"
"math"
"math/big"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -691,6 +692,9 @@ func valToBytes(v types.Val) ([]byte, error) {
return marshalTimeJson(t)
case types.GeoID:
return geojson.Marshal(v.Value.(geom.T))
case types.BigFloatID:
b := v.Value.(big.Float)
return b.MarshalText()
case types.UidID:
return []byte(fmt.Sprintf("\"%#x\"", v.Value)), nil
case types.PasswordID:
Expand Down
415 changes: 347 additions & 68 deletions query/query4_test.go

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions schema/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ age:int .
name: string .
address: string .
<http://scalar.com/helloworld/> : string .
amount: bigfloat .
coordinates: float32vector .
indexvector: float32vector @index(hnsw(metric:"euclidian")) .
`
Expand All @@ -75,6 +76,10 @@ func TestSchema(t *testing.T) {
Predicate: x.GalaxyAttr("age"),
ValueType: pb.Posting_INT,
}},
{x.GalaxyAttr("amount"), &pb.SchemaUpdate{
Predicate: x.GalaxyAttr("amount"),
ValueType: pb.Posting_BIGFLOAT,
}},
{x.GalaxyAttr("coordinates"), &pb.SchemaUpdate{
Predicate: x.GalaxyAttr("coordinates"),
ValueType: pb.Posting_VFLOAT,
Expand Down Expand Up @@ -102,6 +107,10 @@ func TestSchema(t *testing.T) {
require.NoError(t, err)
require.Equal(t, types.IntID, typ)

typ, err = State().TypeOf(x.GalaxyAttr("amount"))
require.NoError(t, err)
require.Equal(t, types.BigFloatID, typ)

typ, err = State().TypeOf(x.GalaxyAttr("coordinates"))
require.NoError(t, err)
require.Equal(t, types.VFloatID, typ)
Expand Down
18 changes: 17 additions & 1 deletion tok/tok.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package tok

import (
"encoding/binary"
"math/big"
"plugin"
"strings"
"time"
Expand Down Expand Up @@ -56,7 +57,7 @@ const (
IdentTrigram = 0xA
IdentHash = 0xB
IdentSha = 0xC
// Reserving 0xD for IdentBigFloat
IdentBigFloat = 0xD
IdentVFloat = 0xE
IdentCustom = 0x80
IdentDelimiter = 0x1f // ASCII 31 - Unit separator
Expand Down Expand Up @@ -93,6 +94,7 @@ var tokenizers = make(map[string]Tokenizer)
var indexFactories = make(map[string]IndexFactory)

func init() {
registerTokenizer(BigFloatTokenizer{})
registerIndexFactory(createIndexFactory(hnsw.CreateFactory[float32](32)))
registerTokenizer(GeoTokenizer{})
registerTokenizer(IntTokenizer{})
Expand Down Expand Up @@ -239,6 +241,20 @@ func registerTokenizer(t Tokenizer) {
tokenizers[t.Name()] = t
}

// BigFloatTokenizer generates tokens from big float data.
type BigFloatTokenizer struct{}

func (t BigFloatTokenizer) Name() string { return "bigfloat" }
func (t BigFloatTokenizer) Type() string { return "bigfloat" }
func (t BigFloatTokenizer) Tokens(v interface{}) ([]string, error) {
value := v.(big.Float)
roundOff, _ := value.Int64()
return []string{encodeInt(roundOff)}, nil
}
func (t BigFloatTokenizer) Identifier() byte { return IdentBigFloat }
func (t BigFloatTokenizer) IsSortable() bool { return true }
func (t BigFloatTokenizer) IsLossy() bool { return true }

// GeoTokenizer generates tokens from geo data.
type GeoTokenizer struct{}

Expand Down
93 changes: 92 additions & 1 deletion types/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/binary"
"encoding/json"
"math"
"math/big"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -137,6 +138,13 @@ func Convert(from Val, toID TypeID) (Val, error) {
}
i := binary.LittleEndian.Uint64(data)
*res = math.Float64frombits(i)
case BigFloatID:
var b big.Float
b.SetPrec(BigFloatPrecision)
if err := b.UnmarshalText(data); err != nil {
return to, err
}
*res = b
case BoolID:
if len(data) == 0 || data[0] == 0 {
*res = false
Expand Down Expand Up @@ -190,6 +198,13 @@ func Convert(from Val, toID TypeID) (Val, error) {
return to, errors.Errorf("Got invalid value: NaN")
}
*res = val
case BigFloatID:
var b big.Float
b.SetPrec(BigFloatPrecision)
if err := b.UnmarshalText(data); err != nil {
return to, err
}
*res = b
case StringID, DefaultID:
*res = vc
case BoolID:
Expand Down Expand Up @@ -243,6 +258,10 @@ func Convert(from Val, toID TypeID) (Val, error) {
*res = bs[:]
case FloatID:
*res = float64(vc)
case BigFloatID:
var b big.Float
b.SetPrec(BigFloatPrecision).SetInt64(vc)
*res = b
case BoolID:
*res = vc != 0
case StringID, DefaultID:
Expand All @@ -255,6 +274,42 @@ func Convert(from Val, toID TypeID) (Val, error) {
return to, cantConvert(fromID, toID)
}
}
case BigFloatID:
{
Copy link
Member

Choose a reason for hiding this comment

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

why is this in a block?

var t big.Float
t.SetPrec(BigFloatPrecision)
if err := t.UnmarshalText(data); err != nil {
return to, err
}

switch toID {
case BigFloatID:
*res = t
case FloatID:
*res, _ = t.Float64()
case BinaryID:
b, err := t.MarshalText()
if err != nil {
return to, errors.Errorf("Error while conversion %s", err.Error())
}
*res = b
case IntID:
*res, _ = t.Int64()
case BoolID:
*res = t.Cmp(new(big.Float).SetFloat64(0)) != 0
case StringID, DefaultID:
*res = t.String()
case DateTimeID:
secs, _ := t.Int64()
floatSecs, _ := t.Float64()
fracSecs := floatSecs - float64(secs)
nsecs := int64(fracSecs * nanoSecondsInSec)
*res = time.Unix(secs, nsecs).UTC()

default:
return to, cantConvert(fromID, toID)
}
}
case FloatID:
{
if len(data) < 8 {
Expand All @@ -265,6 +320,10 @@ func Convert(from Val, toID TypeID) (Val, error) {
switch toID {
case FloatID:
*res = vc
case BigFloatID:
var b big.Float
b.SetPrec(BigFloatPrecision).SetFloat64(vc)
*res = b
case BinaryID:
var bs [8]byte
u := math.Float64bits(vc)
Expand Down Expand Up @@ -322,6 +381,12 @@ func Convert(from Val, toID TypeID) (Val, error) {
asFloat = float32(1)
}
*res = []float32{asFloat}
case BigFloatID:
if vc {
*res = big.NewFloat(1).SetPrec(BigFloatPrecision)
} else {
*res = big.NewFloat(0).SetPrec(BigFloatPrecision)
}
case StringID, DefaultID:
*res = strconv.FormatBool(vc)
default:
Expand Down Expand Up @@ -353,6 +418,9 @@ func Convert(from Val, toID TypeID) (Val, error) {
*res = t.Unix()
case FloatID:
*res = float64(t.UnixNano()) / float64(nanoSecondsInSec)
case BigFloatID:
x, y := big.NewFloat(nanoSecondsInSec), big.NewFloat(float64(t.UnixNano()))
*res = new(big.Float).Quo(y, x)
default:
return to, cantConvert(fromID, toID)
}
Expand Down Expand Up @@ -484,6 +552,20 @@ func Marshal(from Val, to *Val) error {
default:
return cantConvert(fromID, toID)
}
case BigFloatID:
vc := val.(big.Float)
switch toID {
case StringID, DefaultID:
*res = vc.String()
case BinaryID:
val, err := vc.MarshalText()
if err != nil {
return errors.Errorf("Error while conversion %s", err.Error())
}
*res = val
default:
return cantConvert(fromID, toID)
}
case BoolID:
vc := val.(bool)
switch toID {
Expand Down Expand Up @@ -604,8 +686,14 @@ func ObjectValue(id TypeID, value interface{}) (*api.Value, error) {
return def, errors.Errorf("Expected value of type []byte. Got : %v", value)
}
return &api.Value{Val: &api.Value_BytesVal{BytesVal: v}}, nil
// Geo and datetime are stored in binary format in the N-Quad, so lets
// Geo, datetime, and BigFloat are stored in binary format in the N-Quad, so lets
// convert them here.
case BigFloatID:
b, err := toBinary(id, value)
if err != nil {
return def, err
}
return &api.Value{Val: &api.Value_BigfloatVal{BigfloatVal: b}}, nil
case GeoID:
b, err := toBinary(id, value)
if err != nil {
Expand Down Expand Up @@ -660,6 +748,9 @@ func (v Val) MarshalJSON() ([]byte, error) {
return json.Marshal(v.Value.(time.Time))
case GeoID:
return geojson.Marshal(v.Value.(geom.T))
case BigFloatID:
value := v.Value.(big.Float)
return value.MarshalText()
case StringID, DefaultID:
return json.Marshal(v.Safe().(string))
case PasswordID:
Expand Down
29 changes: 23 additions & 6 deletions types/scalar_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,26 @@ package types

import (
"errors"
"math/big"
"time"

"github.com/twpayne/go-geom"

"github.com/dgraph-io/dgraph/protos/pb"
)

const nanoSecondsInSec = 1000000000
const dateFormatY = "2006" // time.longYear
const dateFormatYM = "2006-01"
const dateFormatYMD = "2006-01-02"
const dateFormatYMDZone = "2006-01-02 15:04:05 -0700 MST"
const dateTimeFormat = "2006-01-02T15:04:05"
const (
dateFormatY = "2006" // time.longYear
dateFormatYM = "2006-01"
dateFormatYMD = "2006-01-02"
dateFormatYMDZone = "2006-01-02 15:04:05 -0700 MST"
dateTimeFormat = "2006-01-02T15:04:05"
)

const (
nanoSecondsInSec = 1000000000
BigFloatPrecision = 200
)

// Note: These ids are stored in the posting lists to indicate the type
// of the data. The order *cannot* be changed without breaking existing
Expand Down Expand Up @@ -65,6 +72,8 @@ const (
VFloatID = TypeID(pb.Posting_VFLOAT)
// UndefinedID represents the undefined type.
UndefinedID = TypeID(100)
// BigFloatID represents the arbitrary precision type.
BigFloatID = TypeID(pb.Posting_BIGFLOAT)
)

var typeNameMap = map[string]TypeID{
Expand All @@ -78,6 +87,7 @@ var typeNameMap = map[string]TypeID{
"uid": UidID,
"string": StringID,
"password": PasswordID,
"bigfloat": BigFloatID,
"float32vector": VFloatID,
}

Expand Down Expand Up @@ -112,6 +122,8 @@ func (t TypeID) Name() string {
return "string"
case PasswordID:
return "password"
case BigFloatID:
return "bigfloat"
case VFloatID:
return "float32vector"
}
Expand Down Expand Up @@ -178,6 +190,11 @@ func ValueForType(id TypeID) Val {
var t time.Time
return Val{DateTimeID, &t}

case BigFloatID:
var b big.Float
b.SetPrec(BigFloatPrecision)
return Val{BigFloatID, &b}

case StringID:
var s string
return Val{StringID, s}
Expand Down
16 changes: 13 additions & 3 deletions types/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package types

import (
"math/big"
"sort"
"time"

Expand Down Expand Up @@ -94,7 +95,7 @@ func (s byValue) Less(i, j int) bool {
// IsSortable returns true, if tid is sortable. Otherwise it returns false.
func IsSortable(tid TypeID) bool {
switch tid {
case DateTimeID, IntID, FloatID, StringID, DefaultID:
case DateTimeID, IntID, FloatID, StringID, DefaultID, BigFloatID:
return true
default:
return false
Expand Down Expand Up @@ -187,7 +188,7 @@ func Less(a, b Val) (bool, error) {
}
typ := a.Tid
switch typ {
case DateTimeID, UidID, IntID, FloatID, StringID, DefaultID:
case DateTimeID, UidID, IntID, FloatID, StringID, DefaultID, BigFloatID:
// Don't do anything, we can sort values of this type.
default:
return false, errors.Errorf("Compare not supported for type: %v", a.Tid)
Expand All @@ -214,6 +215,11 @@ func less(a, b Val, cl *collate.Collator) bool {
return cl.CompareString(a.Safe().(string), b.Safe().(string)) < 0
}
return (a.Safe().(string)) < (b.Safe().(string))
case BigFloatID:
var lValue, rValue big.Float
lValue = a.Value.(big.Float)
rValue = b.Value.(big.Float)
return lValue.Cmp(&rValue) == -1
}
return false
}
Expand Down Expand Up @@ -243,7 +249,7 @@ func Equal(a, b Val) (bool, error) {
}
typ := a.Tid
switch typ {
case DateTimeID, IntID, FloatID, StringID, DefaultID, BoolID:
case DateTimeID, IntID, FloatID, StringID, DefaultID, BoolID, BigFloatID:
// Don't do anything, we can sort values of this type.
default:
return false, errors.Errorf("Equal not supported for type: %v", a.Tid)
Expand Down Expand Up @@ -276,6 +282,10 @@ func equal(a, b Val) bool {
aVal, aOk := a.Value.(bool)
bVal, bOk := b.Value.(bool)
return aOk && bOk && aVal == bVal
case BigFloatID:
aVal := a.Value.(big.Float)
bVal := b.Value.(big.Float)
return aVal.Cmp(&bVal) == 0
}
return false
}
1 change: 1 addition & 0 deletions worker/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ var rdfTypeMap = map[types.TypeID]string{
types.GeoID: "geo:geojson",
types.BinaryID: "xs:base64Binary",
types.PasswordID: "xs:password",
types.BigFloatID: "xs:decimal",
types.VFloatID: "xs:[]float32",
}

Expand Down