166 lines
3.4 KiB
Go
166 lines
3.4 KiB
Go
package coperator
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func isImplementObject(v interface{}) (Object, bool) {
|
|
switch a := v.(type) {
|
|
case Document:
|
|
return &a, true
|
|
case Array:
|
|
return &a, true
|
|
default:
|
|
return nil, false
|
|
}
|
|
}
|
|
|
|
type Object interface {
|
|
Get(path string) interface{}
|
|
GetPathArray(path []string) interface{}
|
|
}
|
|
type Array []interface{}
|
|
|
|
func (a *Array) Get(path string) interface{} {
|
|
paths := strings.Split(path, ".")
|
|
return a.GetPathArray(paths)
|
|
}
|
|
|
|
func (a *Array) GetPathArray(path []string) interface{} {
|
|
if len(path) == 0 {
|
|
return *a
|
|
}
|
|
index, err := strconv.Atoi(path[0])
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if len(path) > 1 {
|
|
remaining := path[1:]
|
|
v := (*a)[index]
|
|
if pv, ok := isImplementObject(v); ok {
|
|
return pv.GetPathArray(remaining)
|
|
}
|
|
return nil
|
|
}
|
|
return (*a)[index]
|
|
}
|
|
|
|
type Document map[string]interface{}
|
|
|
|
func (d *Document) Get(path string) interface{} {
|
|
paths := strings.Split(path, ".")
|
|
return d.GetPathArray(paths)
|
|
}
|
|
|
|
func (d *Document) GetPathArray(path []string) interface{} {
|
|
if len(path) == 0 {
|
|
return *d
|
|
}
|
|
if len(path) == 1 {
|
|
return (*d)[path[0]]
|
|
}
|
|
xv, xok := (*d)[path[0]]
|
|
if !xok {
|
|
return nil
|
|
}
|
|
if v, ok := isImplementObject(xv); ok {
|
|
return v.GetPathArray(path[1:])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type Collection []Document
|
|
type Filter interface{}
|
|
|
|
type Operator func(doc *Document, filter Filter, key string, value interface{}) (bool, error)
|
|
|
|
var operatorMap = map[string]Operator{}
|
|
|
|
func DocumentOperator(doc *Document, vfilter Filter, key string, value interface{}) (ret bool, err error) {
|
|
filter, ok := vfilter.(map[string]interface{})
|
|
if !ok {
|
|
return false, errors.New("filter must be a map")
|
|
}
|
|
for k, v := range filter {
|
|
|
|
if strings.HasPrefix(k, "$") {
|
|
// top level operator, commonly be $and,$or
|
|
opFn, ok := operatorMap[k]
|
|
if !ok {
|
|
return false, errors.New("operator not supported")
|
|
}
|
|
ret, err = opFn(doc, v.(Filter), "", nil)
|
|
if !ret {
|
|
return ret, err
|
|
}
|
|
continue
|
|
}
|
|
if f, ok := v.(map[string]interface{}); ok {
|
|
ret, err = FieldOperator(doc, f, k, nil)
|
|
if !ret {
|
|
return ret, err
|
|
}
|
|
continue
|
|
}
|
|
ret, err = ValueOperator(doc, filter, k, v)
|
|
if !ret {
|
|
return ret, err
|
|
}
|
|
continue
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func ValueOperator(doc *Document, filter Filter, key string, value interface{}) (bool, error) {
|
|
if doc.Get(key) != value {
|
|
return false, nil
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func FieldOperator(doc *Document, vfilter Filter, key string, value interface{}) (ret bool, err error) {
|
|
filter, ok := vfilter.(map[string]interface{})
|
|
if !ok {
|
|
return false, errors.New("filter must be a map")
|
|
}
|
|
for k, v := range filter {
|
|
if !strings.HasPrefix(k, "$") {
|
|
return false, errors.New("uncorrect grammar")
|
|
}
|
|
opFn, ok := operatorMap[k]
|
|
if !ok {
|
|
return false, errors.New("unsupport operator")
|
|
}
|
|
if fv, ok := v.(map[string]interface{}); ok {
|
|
ret, err = opFn(doc, fv, key, nil)
|
|
if !ret {
|
|
return ret, err
|
|
}
|
|
continue
|
|
}
|
|
ret, err = opFn(doc, nil, key, v)
|
|
if !ret {
|
|
return ret, err
|
|
}
|
|
continue
|
|
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func init() {
|
|
operatorMap["$gt"] = operatorGt
|
|
operatorMap["$ge"] = operatorGe
|
|
operatorMap["$lt"] = operatorLt
|
|
operatorMap["$le"] = operatorLe
|
|
operatorMap["$eq"] = operatorEq
|
|
operatorMap["$ne"] = operatorNe
|
|
operatorMap["$in"] = operatorIn
|
|
operatorMap["$nin"] = operatorNotIn
|
|
operatorMap["$and"] = operatorAnd
|
|
operatorMap["$or"] = operatorOr
|
|
operatorMap["$nor"] = operatorNor
|
|
}
|