func reduceCount(x interface{}) (ast.Value, error) { switch x := x.(type) { case []interface{}: return ast.IntNumberTerm(len(x)).Value, nil case map[string]interface{}: return ast.IntNumberTerm(len(x)).Value, nil case string: return ast.IntNumberTerm(len(x)).Value, nil default: return nil, fmt.Errorf("count: source must be array, object, or string") } }
// Ref returns a ref that represents p rooted at head. func (p Path) Ref(head *ast.Term) (ref ast.Ref) { ref = make(ast.Ref, len(p)+1) ref[0] = head for i := range p { idx, err := strconv.ParseInt(p[i], 10, 64) if err == nil { ref[i+1] = ast.IntNumberTerm(int(idx)) } else { ref[i+1] = ast.StringTerm(p[i]) } } return ref }
func stringPathToRef(s string) (r ast.Ref) { if len(s) == 0 { return r } p := strings.Split(s, "/") for _, x := range p { if x == "" { continue } i, err := strconv.Atoi(x) if err != nil { r = append(r, ast.StringTerm(x)) } else { r = append(r, ast.IntNumberTerm(i)) } } return r }
func evalIndexOf(t *Topdown, expr *ast.Expr, iter Iterator) error { ops := expr.Terms.([]*ast.Term) base, err := ValueToString(ops[1].Value, t) if err != nil { return errors.Wrapf(err, "%v: base value must be a string", ast.IndexOf.Name) } search, err := ValueToString(ops[2].Value, t) if err != nil { return errors.Wrapf(err, "%v: search value must be a string", ast.IndexOf.Name) } index := ast.IntNumberTerm(strings.Index(base, search)) undo, err := evalEqUnify(t, index.Value, ops[3].Value, nil, iter) t.Unbind(undo) return err }
func evalEqUnifyArrayRef(t *Topdown, a ast.Array, b ast.Ref, prev *Undo, iter Iterator) (*Undo, error) { r, err := t.Resolve(b) if err != nil { return prev, err } slice, ok := r.([]interface{}) if !ok { return prev, nil } if len(a) != len(slice) { return prev, nil } for i := range a { var tmp *Topdown child := make(ast.Ref, len(b), len(b)+1) copy(child, b) child = append(child, ast.IntNumberTerm(i)) p, err := evalEqUnify(t, a[i].Value, child, prev, func(t *Topdown) error { tmp = t return nil }) prev = p if err != nil { return nil, err } if tmp == nil { return nil, nil } t = tmp } return prev, iter(t) }
func iterStorage(ctx context.Context, store Store, txn Transaction, nonGround, ground ast.Ref, bindings *ast.ValueMap, iter func(*ast.ValueMap, interface{})) error { if len(nonGround) == 0 { path, err := NewPathForRef(ground) if err != nil { return err } node, err := store.Read(ctx, txn, path) if err != nil { if IsNotFound(err) { return nil } return err } iter(bindings, node) return nil } head := nonGround[0] tail := nonGround[1:] headVar, isVar := head.Value.(ast.Var) if !isVar || len(ground) == 0 { ground = append(ground, head) return iterStorage(ctx, store, txn, tail, ground, bindings, iter) } path, err := NewPathForRef(ground) if err != nil { return err } node, err := store.Read(ctx, txn, path) if err != nil { if IsNotFound(err) { return nil } return err } switch node := node.(type) { case map[string]interface{}: for key := range node { ground = append(ground, ast.StringTerm(key)) cpy := bindings.Copy() cpy.Put(headVar, ast.String(key)) err := iterStorage(ctx, store, txn, tail, ground, cpy, iter) if err != nil { return err } ground = ground[:len(ground)-1] } case []interface{}: for i := range node { idx := ast.IntNumberTerm(i) ground = append(ground, idx) cpy := bindings.Copy() cpy.Put(headVar, idx.Value) err := iterStorage(ctx, store, txn, tail, ground, cpy, iter) if err != nil { return err } ground = ground[:len(ground)-1] } } return nil }