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:
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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() {
|
||||
|
@ -4,10 +4,11 @@ import "github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
|
||||
func NewLib() map[string]core.Function {
|
||||
return map[string]core.Function{
|
||||
"HAS": Has,
|
||||
"KEYS": Keys,
|
||||
"KEEP": Keep,
|
||||
"MERGE": Merge,
|
||||
"ZIP": Zip,
|
||||
"HAS": Has,
|
||||
"KEYS": Keys,
|
||||
"KEEP": Keep,
|
||||
"MERGE": Merge,
|
||||
"ZIP": Zip,
|
||||
"VALUES": Values,
|
||||
}
|
||||
}
|
||||
|
37
pkg/stdlib/objects/values.go
Normal file
37
pkg/stdlib/objects/values.go
Normal 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
|
||||
}
|
229
pkg/stdlib/objects/values_test.go
Normal file
229
pkg/stdlib/objects/values_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user