coperator/coperator.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
}