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  }