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 map[string]interface{} type Operator func(doc *Document, filter Filter, key string, value interface{}) (bool, error) var operatorMap = map[string]Operator{} func DocumentOperator(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) { 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.(Filter); 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, filter Filter, key string, value interface{}) (ret bool, err error) { 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.(Filter); 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 operatorGt(doc *Document, filter Filter, key string, value interface{}) (ret bool, err error) { return false, nil }