1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-07-05 00:49:00 +02:00

Feature/#10 values (#128)

* added VALUES function
* updated object.Compare
This commit is contained in:
3timeslazy
2018-10-23 01:24:53 +03:00
committed by Tim Voronov
parent e9e33263f3
commit ebea64da37
6 changed files with 361 additions and 14 deletions

View File

@ -228,7 +228,7 @@ func (t *Array) Sort() *Array {
copy(c, t.value)
sort.SliceStable(c, func(i, j int) bool {
return c[i].Compare(c[j]) == 0
return c[i].Compare(c[j]) == -1
})
res := new(Array)

View File

@ -56,6 +56,9 @@ func (t *Object) String() string {
return string(marshaled)
}
// Compare compares the source object with other core.Value
// The behavior of the Compare is similar
// to the comparison of objects in ArangoDB
func (t *Object) Compare(other core.Value) int {
switch other.Type() {
case core.ObjectType:
@ -73,18 +76,33 @@ func (t *Object) Compare(other core.Value) int {
var res = 0
var val core.Value
var exists bool
sortedT := sort.StringSlice(t.Keys())
sortedT.Sort()
other.ForEach(func(otherVal core.Value, key string) bool {
res = -1
sortedOther := sort.StringSlice(other.Keys())
sortedOther.Sort()
if val, exists = t.value[key]; exists {
res = val.Compare(otherVal)
var tVal, otherVal core.Value
var tKey, otherKey string
for i := 0; i < len(t.value) && res == 0; i++ {
tKey, otherKey = sortedT[i], sortedOther[i]
if tKey == otherKey {
tVal, _ = t.Get(NewString(tKey))
otherVal, _ = other.Get(NewString(tKey))
res = tVal.Compare(otherVal)
continue
}
return res == 0
})
if tKey < otherKey {
res = 1
} else {
res = -1
}
break
}
return res
default:

View File

@ -157,6 +157,68 @@ func TestObject(t *testing.T) {
So(obj1.Compare(obj2), ShouldEqual, -1)
})
Convey("ArangoDB compatibility", func() {
Convey("It should return 1 when {a:1} and {b:2}", func() {
obj1 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1)))
obj2 := values.NewObjectWith(values.NewObjectProperty("b", values.NewInt(2)))
So(obj1.Compare(obj2), ShouldEqual, 1)
})
Convey("It should return 0 when {a:1} and {a:1}", func() {
obj1 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1)))
obj2 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1)))
So(obj1.Compare(obj2), ShouldEqual, 0)
})
Convey("It should return 0 {a:1, c:2} and {c:2, a:1}", func() {
obj1 := values.NewObjectWith(
values.NewObjectProperty("a", values.NewInt(1)),
values.NewObjectProperty("c", values.NewInt(2)),
)
obj2 := values.NewObjectWith(
values.NewObjectProperty("c", values.NewInt(2)),
values.NewObjectProperty("a", values.NewInt(1)),
)
So(obj1.Compare(obj2), ShouldEqual, 0)
})
Convey("It should return -1 when {a:1} and {a:2}", func() {
obj1 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1)))
obj2 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(2)))
So(obj1.Compare(obj2), ShouldEqual, -1)
})
Convey("It should return 1 when {a:1, c:2} and {c:2, b:2}", func() {
obj1 := values.NewObjectWith(
values.NewObjectProperty("a", values.NewInt(1)),
values.NewObjectProperty("c", values.NewInt(2)),
)
obj2 := values.NewObjectWith(
values.NewObjectProperty("c", values.NewInt(2)),
values.NewObjectProperty("b", values.NewInt(2)),
)
So(obj1.Compare(obj2), ShouldEqual, 1)
})
Convey("It should return 1 {a:1, c:3} and {c:2, a:1}", func() {
obj1 := values.NewObjectWith(
values.NewObjectProperty("a", values.NewInt(1)),
values.NewObjectProperty("c", values.NewInt(3)),
)
obj2 := values.NewObjectWith(
values.NewObjectProperty("c", values.NewInt(2)),
values.NewObjectProperty("a", values.NewInt(1)),
)
So(obj1.Compare(obj2), ShouldEqual, 1)
})
})
})
Convey(".Hash", t, func() {

View File

@ -9,5 +9,6 @@ func NewLib() map[string]core.Function {
"KEEP": Keep,
"MERGE": Merge,
"ZIP": Zip,
"VALUES": Values,
}
}

View File

@ -0,0 +1,37 @@
package objects
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
// Values return the attribute values of the object as an array.
// @params obj (Object) - an object.
// @returns (Array of Value) - the values of document returned in any order.
func Values(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 1)
if err != nil {
return values.None, err
}
err = core.ValidateType(args[0], core.ObjectType)
if err != nil {
return values.None, err
}
obj := args[0].(*values.Object)
vals := values.NewArray(0)
obj.ForEach(func(val core.Value, key string) bool {
if values.IsCloneable(val) {
val = val.(core.Cloneable).Clone()
}
vals.Push(val)
return true
})
return vals, nil
}

View File

@ -0,0 +1,229 @@
package objects_test
import (
"context"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/objects"
. "github.com/smartystreets/goconvey/convey"
)
func TestValues(t *testing.T) {
Convey("Invalid arguments", t, func() {
Convey("When there is no arguments", func() {
actual, err := objects.Values(context.Background())
So(err, ShouldBeError)
So(actual.Compare(values.None), ShouldEqual, 0)
})
Convey("When 2 arguments", func() {
obj := values.NewObjectWith(
values.NewObjectProperty("k1", values.NewInt(0)),
values.NewObjectProperty("k2", values.NewInt(1)),
)
actual, err := objects.Values(context.Background(), obj, obj)
So(err, ShouldBeError)
So(actual.Compare(values.None), ShouldEqual, 0)
actual, err = objects.Values(context.Background(), obj, values.NewInt(0))
So(err, ShouldBeError)
So(actual.Compare(values.None), ShouldEqual, 0)
})
Convey("When there is not object argument", func() {
actual, err := objects.Values(context.Background(), values.NewInt(0))
So(err, ShouldBeError)
So(actual.Compare(values.None), ShouldEqual, 0)
})
})
Convey("When simple type attributes (same type)", t, func() {
obj := values.NewObjectWith(
values.NewObjectProperty("k1", values.NewInt(0)),
values.NewObjectProperty("k2", values.NewInt(1)),
)
expected := values.NewArrayWith(
values.NewInt(0), values.NewInt(1),
).Sort()
actual, err := objects.Values(context.Background(), obj)
actualSorted := actual.(*values.Array).Sort()
So(err, ShouldBeNil)
So(actualSorted.Compare(expected), ShouldEqual, 0)
})
Convey("When simple type attributes (different types)", t, func() {
obj := values.NewObjectWith(
values.NewObjectProperty("k1", values.NewInt(0)),
values.NewObjectProperty("k2", values.NewString("v2")),
)
expected := values.NewArrayWith(
values.NewInt(0), values.NewString("v2"),
).Sort()
actual, err := objects.Values(context.Background(), obj)
actualSorted := actual.(*values.Array).Sort()
So(err, ShouldBeNil)
So(actualSorted.Compare(expected), ShouldEqual, 0)
})
Convey("When complex type attributes (array)", t, func() {
arr1 := values.NewArrayWith(
values.NewInt(0), values.NewInt(1),
)
arr2 := values.NewArrayWith(
values.NewInt(2), values.NewInt(3),
)
obj := values.NewObjectWith(
values.NewObjectProperty("k1", arr1),
values.NewObjectProperty("k2", arr2),
)
expected := values.NewArrayWith(arr1, arr2).Sort()
actual, err := objects.Values(context.Background(), obj)
actualSorted := actual.(*values.Array).Sort()
So(err, ShouldBeNil)
So(actualSorted.Compare(expected), ShouldEqual, 0)
})
Convey("When complex type attributes (object)", t, func() {
obj1 := values.NewObjectWith(
values.NewObjectProperty("int0", values.NewInt(0)),
)
obj2 := values.NewObjectWith(
values.NewObjectProperty("int1", values.NewInt(1)),
)
obj := values.NewObjectWith(
values.NewObjectProperty("k1", obj1),
values.NewObjectProperty("k2", obj2),
)
expected := values.NewArrayWith(obj1, obj2).Sort()
actual, err := objects.Values(context.Background(), obj)
actualSorted := actual.(*values.Array).Sort()
So(err, ShouldBeNil)
So(actualSorted.Compare(expected), ShouldEqual, 0)
})
Convey("When complex type attributes (object and array)", t, func() {
obj1 := values.NewObjectWith(
values.NewObjectProperty("k1", values.NewInt(0)),
)
arr1 := values.NewArrayWith(
values.NewInt(0), values.NewInt(1),
)
obj := values.NewObjectWith(
values.NewObjectProperty("obj", obj1),
values.NewObjectProperty("arr", arr1),
)
expected := values.NewArrayWith(obj1, arr1).Sort()
actual, err := objects.Values(context.Background(), obj)
actualSorted := actual.(*values.Array).Sort()
So(err, ShouldBeNil)
So(actualSorted.Compare(expected), ShouldEqual, 0)
})
Convey("When both type attributes", t, func() {
obj1 := values.NewObjectWith(
values.NewObjectProperty("k1", values.NewInt(0)),
)
arr1 := values.NewArrayWith(
values.NewInt(0), values.NewInt(1),
)
int1 := values.NewInt(0)
obj := values.NewObjectWith(
values.NewObjectProperty("obj", obj1),
values.NewObjectProperty("arr", arr1),
values.NewObjectProperty("int", int1),
)
expected := values.NewArrayWith(obj1, arr1, int1).Sort()
actual, err := objects.Values(context.Background(), obj)
actualSorted := actual.(*values.Array).Sort()
So(err, ShouldBeNil)
So(actualSorted.Compare(expected), ShouldEqual, 0)
})
Convey("Result is independent on the source object (array)", t, func() {
arr := values.NewArrayWith(values.NewInt(0))
obj := values.NewObjectWith(
values.NewObjectProperty("arr", arr),
)
expected := values.NewArrayWith(
values.NewArrayWith(
values.NewInt(0),
),
)
actual, err := objects.Values(context.Background(), obj)
actualSorted := actual.(*values.Array).Sort()
So(err, ShouldBeNil)
arr.Push(values.NewInt(1))
So(actualSorted.Compare(expected), ShouldEqual, 0)
})
Convey("Result is independent on the source object (object)", t, func() {
nested := values.NewObjectWith(
values.NewObjectProperty("int", values.NewInt(0)),
)
obj := values.NewObjectWith(
values.NewObjectProperty("nested", nested),
)
expected := values.NewArrayWith(
values.NewObjectWith(
values.NewObjectProperty("int", values.NewInt(0)),
),
)
actual, err := objects.Values(context.Background(), obj)
actualSorted := actual.(*values.Array).Sort()
So(err, ShouldBeNil)
nested.Set("new", values.NewInt(1))
So(actualSorted.Compare(expected), ShouldEqual, 0)
})
}
func TestValuesStress(t *testing.T) {
Convey("Stress", t, func() {
for i := 0; i < 100; i++ {
obj1 := values.NewObjectWith(
values.NewObjectProperty("int0", values.NewInt(0)),
)
obj2 := values.NewObjectWith(
values.NewObjectProperty("int1", values.NewInt(1)),
)
obj := values.NewObjectWith(
values.NewObjectProperty("k1", obj1),
values.NewObjectProperty("k2", obj2),
)
expected := values.NewArrayWith(obj2, obj1).Sort()
actual, err := objects.Values(context.Background(), obj)
actualSorted := actual.(*values.Array).Sort()
So(err, ShouldBeNil)
So(actualSorted.Length(), ShouldEqual, expected.Length())
So(actualSorted.Compare(expected), ShouldEqual, 0)
}
})
}