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