-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: update readme * feat: remove github.com/tkeel-io/core/pkg/tql * feat: update gomod * fix: number parse * feat: embed collectjs & fix test * feat: fix parse bug * fix: unit test (GroupBy\MergeBy\KeyBy) * feat: refactor Node * feat: clean code * feat: update gomod * feat: clean code
- Loading branch information
Showing
82 changed files
with
1,567 additions
and
6,803 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package tdtl | ||
|
||
import "sort" | ||
|
||
// A couple of type definitions to make the units clear. | ||
type earthMass float64 | ||
type au float64 | ||
|
||
// By is the type of a "less" function that defines the ordering of its Planet arguments. | ||
type By func(p1, p2 *Collect) bool | ||
|
||
// Sort is a method on the function type, By, that sorts the argument slice according to the function. | ||
func (by By) Sort(collects []*Collect) { | ||
ps := &collectSorter{ | ||
collects: collects, | ||
by: by, // The Sort method's receiver is the function (closure) that defines the sort order. | ||
} | ||
sort.Sort(ps) | ||
} | ||
|
||
// planetSorter joins a By function and a slice of Planets to be sorted. | ||
type collectSorter struct { | ||
collects []*Collect | ||
by func(p1, p2 *Collect) bool // Closure used in the Less method. | ||
} | ||
|
||
// Len is part of sort.Interface. | ||
func (s *collectSorter) Len() int { | ||
return len(s.collects) | ||
} | ||
|
||
// Swap is part of sort.Interface. | ||
func (s *collectSorter) Swap(i, j int) { | ||
s.collects[i], s.collects[j] = s.collects[j], s.collects[i] | ||
} | ||
|
||
// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. | ||
func (s *collectSorter) Less(i, j int) bool { | ||
return s.by(s.collects[i], s.collects[j]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package tdtl | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
) | ||
|
||
var planets = []*Collect{ | ||
New(`["Venus", 0.815, 0.7]`), | ||
New(`["Earth", 1.0, 1.0]`), | ||
New(`["Mars", 0.107, 1.5]`), | ||
} | ||
|
||
func TestSort(t *testing.T) { | ||
|
||
// Closures that order the Planet structure. | ||
name := func(p1, p2 *Collect) bool { | ||
return bytes.Compare(p1.Get("[0]").Raw(), p2.Get("[0]").Raw()) > 0 | ||
} | ||
mass := func(p1, p2 *Collect) bool { | ||
return bytes.Compare(p1.Get("[1]").Raw(), p2.Get("[1]").Raw()) > 0 | ||
} | ||
distance := func(p1, p2 *Collect) bool { | ||
return bytes.Compare(p1.Get("[2]").Raw(), p2.Get("[2]").Raw()) > 0 | ||
} | ||
|
||
// Sort the planets by the various criteria. | ||
By(name).Sort(planets) | ||
t.Log("By name:", string(planets[0].Raw()), string(planets[1].Raw()), string(planets[2].Raw())) | ||
|
||
By(mass).Sort(planets) | ||
t.Log("By mass:", string(planets[0].Raw()), string(planets[1].Raw()), string(planets[2].Raw())) | ||
|
||
By(distance).Sort(planets) | ||
t.Log("By distance:", string(planets[0].Raw()), string(planets[1].Raw()), string(planets[2].Raw())) | ||
|
||
// Output: | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package tdtl | ||
|
||
import ( | ||
"strings" | ||
) | ||
|
||
func Byte(raw string) []byte { | ||
return []byte(raw) | ||
} | ||
|
||
func path2JSONPARSER(path string) []string { | ||
keys := []string{} | ||
if len(path) > 0 { | ||
if path[0] == '"' && path[len(path)-1] == '"' { | ||
return []string{path[1 : len(path)-1]} | ||
} | ||
path = strings.Replace(path, "[", ".[", -1) | ||
keys = strings.Split(path, ".") | ||
} | ||
if len(keys) > 0 && keys[0] == "" { | ||
return keys[1:] | ||
} | ||
return keys | ||
} | ||
|
||
func path2GJSON(path string) string { | ||
path = strings.Replace(path, "[", ".", -1) | ||
path = strings.Replace(path, "]", "", -1) | ||
if len(path) > 0 && path[0] == '.' { | ||
return path[1:] | ||
} | ||
return path | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,253 @@ | ||
package tdtl | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/tkeel-io/tdtl/pkg/json/jsonparser" | ||
) | ||
|
||
var EmptyBytes = []byte("") | ||
|
||
type Collect = JSONNode | ||
|
||
func New(raw interface{}) *Collect { | ||
switch raw := raw.(type) { | ||
case string: | ||
return newCollect(Byte(raw)) | ||
case []byte: | ||
return newCollect(raw) | ||
case Result: | ||
return newCollectFromJsonResult(raw) | ||
} | ||
return UNDEFINED_RESULT | ||
} | ||
|
||
func newCollect(data []byte) *Collect { | ||
collect := &Collect{} | ||
value := make([]byte, len(data)) | ||
copy(value, data) | ||
collect.path = "" | ||
collect.value = value | ||
if _, jtype, _, err := jsonparser.Get(data); err == nil { | ||
collect.datatype = datetype(jtype) | ||
} else { | ||
collect.err = err | ||
} | ||
return collect | ||
} | ||
|
||
func newCollectFromJsonResult(ret Result) *Collect { | ||
collect := &Collect{} | ||
collect.path = "" | ||
collect.datatype = datetype(ret) | ||
if collect.datatype == String { | ||
collect.value = []byte(ret.Str) | ||
} else { | ||
collect.value = []byte(ret.Raw) | ||
} | ||
|
||
return collect | ||
} | ||
|
||
func newCollectFromJsonparserResult(dataType jsonparser.ValueType, value []byte) *Collect { | ||
collect := &Collect{} | ||
collect.path = "" | ||
collect.value = []byte(value) | ||
collect.datatype = datetype(dataType) | ||
return collect | ||
} | ||
|
||
// GetError returns collect error. | ||
func (cc *Collect) GetError() error { | ||
return cc.err | ||
} | ||
|
||
func (cc *Collect) Node() Node { | ||
return cc.To(cc.datatype) | ||
} | ||
|
||
func (cc *Collect) Get(path ...string) *Collect { | ||
absPath := strings.Join(path, ".") | ||
if absPath == "" { | ||
return cc | ||
} | ||
ret := get(cc.value, absPath) | ||
return ret | ||
} | ||
|
||
func (cc *Collect) Set(path string, value Node) { | ||
cc.value, cc.err = set(cc.value, path, value.Raw()) | ||
} | ||
|
||
func (cc *Collect) Append(path string, value Node) { | ||
cc.value, cc.err = add(cc.value, path, value.Raw()) | ||
} | ||
|
||
func (cc *Collect) Del(path ...string) { | ||
cc.value = del(cc.value, path...) | ||
} | ||
|
||
func (cc *Collect) Copy() *JSONNode { | ||
return newCollect(cc.value) | ||
} | ||
|
||
func (cc *Collect) Foreach(fn ForeachHandle) { | ||
cc.value = forEach(cc.value, cc.datatype, fn) | ||
} | ||
|
||
func (cc *Collect) Map(handle MapHandle) { | ||
ret := cc.Copy() | ||
cc.Foreach(func(key []byte, value *Collect) { | ||
newValue := handle(key, value) | ||
ret.Set(string(key), newValue) | ||
}) | ||
cc.value, cc.datatype = ret.value, ret.datatype | ||
} | ||
|
||
func (cc *Collect) GroupBy(path string) *Collect { | ||
if cc.datatype != Array { | ||
cc.err = fmt.Errorf("datatype is not array") | ||
return cc | ||
} | ||
|
||
ret := New("{}") | ||
cc.Foreach(func(key []byte, value *Collect) { | ||
keyValue := get(value.Raw(), path).String() | ||
if len(keyValue) == 0 { | ||
return | ||
} | ||
keyValue = strings.Replace(keyValue, ".", "_", -1) | ||
ret.Append(keyValue, value) | ||
}) | ||
return ret | ||
} | ||
|
||
func (c *Collect) MergeBy(paths ...string) *Collect { | ||
if c.datatype != Array { | ||
c.err = fmt.Errorf("MergeBy: datatype is not array") | ||
return c | ||
} | ||
|
||
var err error | ||
ret := New("{}") | ||
c.Foreach(func(key []byte, value *Collect) { | ||
keys := make([]string, 0, len(paths)) | ||
for _, path := range paths { | ||
keyValueRaw := value.Get(path).String() | ||
if len(keyValueRaw) == 0 { | ||
break | ||
} | ||
keys = append(keys, keyValueRaw) | ||
} | ||
|
||
if len(keys) == 0 { | ||
return | ||
} | ||
|
||
keyValue := strings.Join(keys, "+") | ||
keyValue = strings.Replace(keyValue, ".", "_", -1) | ||
|
||
nv := ret.Get(keyValue) | ||
nv = nv.Merge(value) | ||
if nv.err != nil { | ||
ret.err = err | ||
} | ||
ret.Set(keyValue, nv) | ||
}) | ||
return ret | ||
} | ||
|
||
func (cc *Collect) SortBy(fn func(p1 *Collect, p2 *Collect) bool) { | ||
if cc.datatype != Array && cc.datatype != Object { | ||
cc.err = errors.New("SortBy:datatype is not array or object") | ||
return | ||
} | ||
carr := make([]*Collect, 0) | ||
cc.Foreach(func(key []byte, value *Collect) { | ||
carr = append(carr, value) | ||
}) | ||
By(fn).Sort(carr) | ||
|
||
ret := New("[]") | ||
for _, c := range carr { | ||
ret.Append("", c) | ||
} | ||
cc.value = ret.value | ||
cc.datatype = ret.datatype | ||
} | ||
|
||
func (c *Collect) KeyBy(path string) *Collect { | ||
if c.datatype != Array { | ||
c.err = errors.New("KeyBy:datatype is not array") | ||
} | ||
|
||
ret := New("{}") | ||
c.Foreach(func(key []byte, value *Collect) { | ||
keyValue := value.Get(path) | ||
ret.Set(keyValue.String(), value) | ||
}) | ||
|
||
return ret | ||
} | ||
|
||
func (cc *Collect) Merge(mc *Collect) *Collect { | ||
if cc.datatype != Object && mc.datatype != Object { | ||
cc.err = errors.New("datatype is not object") | ||
return cc | ||
} | ||
if cc.datatype == Null { | ||
return mc | ||
} | ||
|
||
mc.Foreach(func(key []byte, value *Collect) { | ||
cc.Set(string(key), value) | ||
}) | ||
|
||
return cc | ||
} | ||
|
||
// | ||
//func (c *Collect) Sort( path string) ([]byte, error) { | ||
// if c.datatype != Array { | ||
// c.err = errors.New("SortBy:datatype is not array") | ||
// return nil, errors.New("datatype is not array") | ||
// } | ||
// | ||
// var err error | ||
// ret := []byte("[]") | ||
// c.Foreach(func(key []byte, value *Collect) { | ||
// keyValue := get(value.Raw(), path).Raw() | ||
// if keyValue[0] == '"' && keyValue[len(keyValue)-1] == '"' { | ||
// keyValue = keyValue[1 : len(keyValue)-1] | ||
// } | ||
// if ret, err = jsonparser.Append(ret, value.Raw(), string(keyValue)); nil != err { | ||
// c.err = err | ||
// } | ||
// }) | ||
// | ||
// return ret, c.err | ||
//} | ||
|
||
func Combine(cKey *Collect, cValue *Collect) ([]byte, error) { | ||
if cKey.datatype != Array { | ||
return nil, errors.New("datatype is not array") | ||
} else if cValue.datatype != Array { | ||
return nil, errors.New("datatype is not array") | ||
} | ||
|
||
var ( | ||
idx int | ||
err error | ||
ret = []byte("{}") | ||
) | ||
|
||
cKey.Foreach(func(key []byte, value *Collect) { | ||
if ret, err = jsonparser.Set(ret, get(cValue.value, fmt.Sprintf("[%d]", idx)).Raw(), value.String()); nil != err { | ||
cKey.err = err | ||
} | ||
idx++ | ||
}) | ||
return ret, cKey.err | ||
} |
Oops, something went wrong.