storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/s3select/sql/jsonpath.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2019 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package sql 18 19 import ( 20 "errors" 21 22 "github.com/bcicen/jstream" 23 "github.com/minio/simdjson-go" 24 ) 25 26 var ( 27 errKeyLookup = errors.New("Cannot look up key in non-object value") 28 errIndexLookup = errors.New("Cannot look up array index in non-array value") 29 errWildcardObjectLookup = errors.New("Object wildcard used on non-object value") 30 errWildcardArrayLookup = errors.New("Array wildcard used on non-array value") 31 errWilcardObjectUsageInvalid = errors.New("Invalid usage of object wildcard") 32 ) 33 34 // jsonpathEval evaluates a JSON path and returns the value at the path. 35 // If the value should be considered flat (from wildcards) any array returned should be considered individual values. 36 func jsonpathEval(p []*JSONPathElement, v interface{}) (r interface{}, flat bool, err error) { 37 // fmt.Printf("JPATHexpr: %v jsonobj: %v\n\n", p, v) 38 if len(p) == 0 || v == nil { 39 return v, false, nil 40 } 41 42 switch { 43 case p[0].Key != nil: 44 key := p[0].Key.keyString() 45 46 switch kvs := v.(type) { 47 case jstream.KVS: 48 for _, kv := range kvs { 49 if kv.Key == key { 50 return jsonpathEval(p[1:], kv.Value) 51 } 52 } 53 // Key not found - return nil result 54 return nil, false, nil 55 case simdjson.Object: 56 elem := kvs.FindKey(key, nil) 57 if elem == nil { 58 // Key not found - return nil result 59 return nil, false, nil 60 } 61 val, err := IterToValue(elem.Iter) 62 if err != nil { 63 return nil, false, err 64 } 65 return jsonpathEval(p[1:], val) 66 default: 67 return nil, false, errKeyLookup 68 } 69 70 case p[0].Index != nil: 71 idx := *p[0].Index 72 73 arr, ok := v.([]interface{}) 74 if !ok { 75 return nil, false, errIndexLookup 76 } 77 78 if idx >= len(arr) { 79 return nil, false, nil 80 } 81 return jsonpathEval(p[1:], arr[idx]) 82 83 case p[0].ObjectWildcard: 84 switch kvs := v.(type) { 85 case jstream.KVS: 86 if len(p[1:]) > 0 { 87 return nil, false, errWilcardObjectUsageInvalid 88 } 89 90 return kvs, false, nil 91 case simdjson.Object: 92 if len(p[1:]) > 0 { 93 return nil, false, errWilcardObjectUsageInvalid 94 } 95 96 return kvs, false, nil 97 default: 98 return nil, false, errWildcardObjectLookup 99 } 100 101 case p[0].ArrayWildcard: 102 arr, ok := v.([]interface{}) 103 if !ok { 104 return nil, false, errWildcardArrayLookup 105 } 106 107 // Lookup remainder of path in each array element and 108 // make result array. 109 var result []interface{} 110 for _, a := range arr { 111 rval, flatten, err := jsonpathEval(p[1:], a) 112 if err != nil { 113 return nil, false, err 114 } 115 116 if flatten { 117 // Flatten if array. 118 if arr, ok := rval.([]interface{}); ok { 119 result = append(result, arr...) 120 continue 121 } 122 } 123 result = append(result, rval) 124 } 125 return result, true, nil 126 } 127 panic("cannot reach here") 128 }