github.com/weaviate/weaviate@v1.24.6/entities/storobj/parse_single_object.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package storobj
    13  
    14  import (
    15  	"bytes"
    16  	"encoding/binary"
    17  	"strconv"
    18  
    19  	"github.com/buger/jsonparser"
    20  	"github.com/google/uuid"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  func ParseAndExtractProperty(data []byte, propName string) ([]string, bool, error) {
    25  	if propName == "id" || propName == "_id" {
    26  		return extractID(data)
    27  	}
    28  	if propName == "_creationTimeUnix" {
    29  		return extractCreationTimeUnix(data)
    30  	}
    31  	if propName == "_lastUpdateTimeUnix" {
    32  		return extractLastUpdateTimeUnix(data)
    33  	}
    34  	return ParseAndExtractTextProp(data, propName)
    35  }
    36  
    37  func ParseAndExtractTextProp(data []byte, propName string) ([]string, bool, error) {
    38  	vals := []string{}
    39  	err := parseAndExtractValueProp(data, propName, func(value []byte) {
    40  		vals = append(vals, string(value))
    41  	})
    42  	if err != nil {
    43  		return nil, false, err
    44  	}
    45  	return vals, true, nil
    46  }
    47  
    48  func ParseAndExtractNumberArrayProp(data []byte, propName string) ([]float64, bool, error) {
    49  	vals := []float64{}
    50  	err := parseAndExtractValueProp(data, propName, func(value []byte) {
    51  		vals = append(vals, mustExtractNumber(value))
    52  	})
    53  	if err != nil {
    54  		return nil, false, err
    55  	}
    56  	return vals, true, nil
    57  }
    58  
    59  func ParseAndExtractBoolArrayProp(data []byte, propName string) ([]bool, bool, error) {
    60  	vals := []bool{}
    61  	err := parseAndExtractValueProp(data, propName, func(value []byte) {
    62  		vals = append(vals, mustExtractBool(value))
    63  	})
    64  	if err != nil {
    65  		return nil, false, err
    66  	}
    67  	return vals, true, nil
    68  }
    69  
    70  func parseAndExtractValueProp(data []byte, propName string, valueFn func(value []byte)) error {
    71  	propsBytes, err := extractPropsBytes(data)
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	val, t, _, err := jsonparser.Get(propsBytes, propName)
    77  	// Some objects can have nil as value for the property, in this case skip the object
    78  	if err != nil {
    79  		if err.Error() == "Key path not found" {
    80  			return nil
    81  		}
    82  		return err
    83  	}
    84  
    85  	if t == jsonparser.Array {
    86  		jsonparser.ArrayEach(val, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
    87  			valueFn(value)
    88  		})
    89  	} else {
    90  		valueFn(val)
    91  	}
    92  
    93  	return nil
    94  }
    95  
    96  func mustExtractNumber(value []byte) float64 {
    97  	number, err := strconv.ParseFloat(string(value), 64)
    98  	if err != nil {
    99  		panic("not a float64")
   100  	}
   101  	return number
   102  }
   103  
   104  func mustExtractBool(value []byte) bool {
   105  	boolVal, err := strconv.ParseBool(string(value))
   106  	if err != nil {
   107  		panic("not a bool")
   108  	}
   109  	return boolVal
   110  }
   111  
   112  func extractID(data []byte) ([]string, bool, error) {
   113  	start := 1 + 8 + 1
   114  	end := start + 16
   115  	if len(data) > end {
   116  		uuidParsed, err := uuid.FromBytes(data[start:end])
   117  		if err != nil {
   118  			return nil, false, errors.New("cannot parse id property")
   119  		}
   120  		return []string{uuidParsed.String()}, true, nil
   121  	}
   122  	return nil, false, errors.New("id property not found")
   123  }
   124  
   125  func extractCreationTimeUnix(data []byte) ([]string, bool, error) {
   126  	start := 1 + 8 + 1 + 16
   127  	end := start + 8
   128  	if len(data) > end {
   129  		return extractTimeUnix(data[start:end], "_creationTimeUnix")
   130  	}
   131  	return nil, false, errors.New("_creationTimeUnix property not found")
   132  }
   133  
   134  func extractLastUpdateTimeUnix(data []byte) ([]string, bool, error) {
   135  	start := 1 + 8 + 1 + 16 + 8
   136  	end := start + 8
   137  	if len(data) > end {
   138  		return extractTimeUnix(data[start:end], "_lastUpdateTimeUnix")
   139  	}
   140  	return nil, false, errors.New("_lastUpdateTimeUnix property not found")
   141  }
   142  
   143  func extractTimeUnix(data []byte, propertyName string) ([]string, bool, error) {
   144  	var timeUnix int64
   145  	r := bytes.NewReader(data)
   146  	if err := binary.Read(r, binary.LittleEndian, &timeUnix); err != nil {
   147  		return nil, false, errors.Errorf("cannot parse %s property", propertyName)
   148  	}
   149  	return []string{strconv.FormatInt(timeUnix, 10)}, true, nil
   150  }
   151  
   152  func extractPropsBytes(data []byte) ([]byte, error) {
   153  	version := uint8(data[0])
   154  	if version != 1 {
   155  		return nil, errors.Errorf("unsupported binary marshaller version %d", version)
   156  	}
   157  
   158  	vecLen := binary.LittleEndian.Uint16(data[discardBytesPreVector : discardBytesPreVector+2])
   159  
   160  	classNameStart := int64(discardBytesPreVector) + 2 + int64(vecLen)*4
   161  
   162  	classNameLen := binary.LittleEndian.Uint16(data[classNameStart : classNameStart+2])
   163  
   164  	propsLenStart := classNameStart + 2 + int64(classNameLen)
   165  	propsLen := binary.LittleEndian.Uint32(data[propsLenStart : propsLenStart+4])
   166  
   167  	start := int64(propsLenStart + 4)
   168  	end := start + int64(propsLen)
   169  
   170  	return data[start:end], nil
   171  }
   172  
   173  const discardBytesPreVector = 1 + 8 + 1 + 16 + 8 + 8