github.com/weaviate/weaviate@v1.24.6/adapters/handlers/grpc/v1/batch_parse_request_test.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 v1 13 14 import ( 15 "testing" 16 17 "github.com/stretchr/testify/require" 18 "github.com/weaviate/weaviate/entities/models" 19 "github.com/weaviate/weaviate/entities/schema" 20 pb "github.com/weaviate/weaviate/grpc/generated/protocol/v1" 21 ) 22 23 const ( 24 UUID3 = "a4de3ca0-6975-464f-b23b-adddd83630d7" 25 UUID4 = "7e10ec81-a26d-4ac7-8264-3e3e05397ddc" 26 ) 27 28 func TestGRPCBatchRequest(t *testing.T) { 29 collection := "TestClass" 30 refClass1 := "OtherClass" 31 refClass2 := "AnotherClass" 32 multiVecClass := "MultiVec" 33 scheme := schema.Schema{ 34 Objects: &models.Schema{ 35 Classes: []*models.Class{ 36 { 37 Class: collection, 38 Properties: []*models.Property{ 39 {Name: "name", DataType: schema.DataTypeText.PropString()}, 40 {Name: "number", DataType: []string{"int"}}, 41 {Name: "ref", DataType: []string{refClass1}}, 42 {Name: "multiRef", DataType: []string{refClass1, refClass2}}, 43 }, 44 }, 45 { 46 Class: refClass1, 47 Properties: []*models.Property{ 48 {Name: "something", DataType: schema.DataTypeText.PropString()}, 49 {Name: "ref2", DataType: []string{refClass2}}, 50 }, 51 }, 52 { 53 Class: refClass2, 54 Properties: []*models.Property{ 55 {Name: "else", DataType: schema.DataTypeText.PropString()}, 56 {Name: "ref3", DataType: []string{refClass2}}, 57 }, 58 }, 59 { 60 Class: multiVecClass, 61 Properties: []*models.Property{ 62 {Name: "first", DataType: schema.DataTypeText.PropString()}, 63 }, 64 VectorConfig: map[string]models.VectorConfig{ 65 "custom": { 66 VectorIndexType: "hnsw", 67 Vectorizer: map[string]interface{}{"none": map[string]interface{}{}}, 68 }, 69 "first": { 70 VectorIndexType: "flat", 71 Vectorizer: map[string]interface{}{"text2vec-contextionary": map[string]interface{}{}}, 72 }, 73 }, 74 }, 75 }, 76 }, 77 } 78 79 var nilMap map[string]interface{} 80 tests := []struct { 81 name string 82 req []*pb.BatchObject 83 out []*models.Object 84 outError []int 85 origIndex map[int]int 86 }{ 87 { 88 name: "empty object", 89 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4}}, 90 out: []*models.Object{{Class: collection, Properties: nilMap, ID: UUID4}}, 91 }, 92 { 93 name: "no UUID", 94 req: []*pb.BatchObject{{Collection: collection}}, 95 out: []*models.Object{}, 96 outError: []int{0}, 97 }, 98 { 99 name: "only normal props", 100 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{ 101 NonRefProperties: newStruct(t, map[string]interface{}{ 102 "name": "something", 103 "age": 45, 104 }), 105 }}}, 106 out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{ 107 "name": "something", 108 "age": float64(45), 109 }}}, 110 }, 111 { 112 name: "only single refs", 113 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{ 114 SingleTargetRefProps: []*pb.BatchObject_SingleTargetRefProps{ 115 {PropName: "ref", Uuids: []string{UUID3, UUID4}}, 116 }, 117 }}}, 118 out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{ 119 "ref": []interface{}{ 120 map[string]interface{}{"beacon": BEACON_START + refClass1 + "/" + UUID3}, 121 map[string]interface{}{"beacon": BEACON_START + refClass1 + "/" + UUID4}, 122 }, 123 }}}, 124 }, 125 { 126 name: "Named Vecs", 127 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Vectors: []*pb.Vectors{ 128 { 129 Name: "custom", 130 VectorBytes: byteVector([]float32{0.1, 0.2, 0.3}), 131 }, 132 }}}, 133 out: []*models.Object{{ 134 Class: collection, ID: UUID4, Properties: nilMap, 135 Vectors: map[string]models.Vector{ 136 "custom": []float32{0.1, 0.2, 0.3}, 137 }, 138 }}, 139 }, 140 { 141 name: "only mult ref", 142 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{ 143 MultiTargetRefProps: []*pb.BatchObject_MultiTargetRefProps{ 144 {PropName: "multiRef", Uuids: []string{UUID3, UUID4}, TargetCollection: refClass2}, 145 }, 146 }}}, 147 out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{ 148 "multiRef": []interface{}{ 149 map[string]interface{}{"beacon": BEACON_START + refClass2 + "/" + UUID3}, 150 map[string]interface{}{"beacon": BEACON_START + refClass2 + "/" + UUID4}, 151 }, 152 }}}, 153 }, 154 { 155 name: "all property types", 156 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{ 157 MultiTargetRefProps: []*pb.BatchObject_MultiTargetRefProps{ 158 {PropName: "multiRef", Uuids: []string{UUID4, UUID3}, TargetCollection: refClass2}, 159 }, 160 SingleTargetRefProps: []*pb.BatchObject_SingleTargetRefProps{ 161 {PropName: "ref", Uuids: []string{UUID4, UUID3}}, 162 }, 163 NonRefProperties: newStruct(t, map[string]interface{}{ 164 "name": "else", 165 "age": 46, 166 }), 167 }}}, 168 out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{ 169 "multiRef": []interface{}{ 170 map[string]interface{}{"beacon": BEACON_START + refClass2 + "/" + UUID4}, 171 map[string]interface{}{"beacon": BEACON_START + refClass2 + "/" + UUID3}, 172 }, 173 "ref": []interface{}{ 174 map[string]interface{}{"beacon": BEACON_START + refClass1 + "/" + UUID4}, 175 map[string]interface{}{"beacon": BEACON_START + refClass1 + "/" + UUID3}, 176 }, 177 "name": "else", 178 "age": float64(46), 179 }}}, 180 }, 181 { 182 name: "mult ref to single target", 183 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{ 184 MultiTargetRefProps: []*pb.BatchObject_MultiTargetRefProps{ 185 {PropName: "ref", Uuids: []string{UUID3, UUID4}, TargetCollection: refClass2}, 186 }, 187 }}}, 188 out: []*models.Object{}, 189 outError: []int{0}, 190 }, 191 { 192 name: "single ref to multi target", 193 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{ 194 SingleTargetRefProps: []*pb.BatchObject_SingleTargetRefProps{ 195 {PropName: "multiRef", Uuids: []string{UUID3, UUID4}}, 196 }, 197 }}}, 198 out: []*models.Object{}, 199 outError: []int{0}, 200 }, 201 { 202 name: "slice props", 203 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{ 204 NonRefProperties: newStruct(t, map[string]interface{}{"name": "something"}), 205 BooleanArrayProperties: []*pb.BooleanArrayProperties{ 206 {PropName: "boolArray1", Values: []bool{true, true}}, 207 {PropName: "boolArray2", Values: []bool{false, true}}, 208 }, 209 IntArrayProperties: []*pb.IntArrayProperties{ 210 {PropName: "int1", Values: []int64{2, 3, 4}}, {PropName: "int2", Values: []int64{7, 8}}, 211 }, 212 NumberArrayProperties: []*pb.NumberArrayProperties{ 213 {PropName: "float1", Values: []float64{1, 2, 3}}, {PropName: "float2", Values: []float64{4, 5}}, 214 }, 215 TextArrayProperties: []*pb.TextArrayProperties{ 216 {PropName: "text1", Values: []string{"first", "second"}}, {PropName: "text2", Values: []string{"third"}}, 217 }, 218 EmptyListProps: []string{"text3"}, 219 }}}, 220 out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{ 221 "name": "something", 222 "boolArray1": []interface{}{true, true}, 223 "boolArray2": []interface{}{false, true}, 224 "int1": []interface{}{int64(2), int64(3), int64(4)}, 225 "int2": []interface{}{int64(7), int64(8)}, 226 "float1": []interface{}{1., 2., 3.}, 227 "float2": []interface{}{4., 5.}, 228 "text1": []interface{}{"first", "second"}, 229 "text2": []interface{}{"third"}, 230 "text3": []interface{}{}, 231 }}}, 232 }, 233 { 234 name: "object props", 235 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{ 236 ObjectProperties: []*pb.ObjectProperties{ 237 { 238 PropName: "simpleObj", Value: &pb.ObjectPropertiesValue{ 239 NonRefProperties: newStruct(t, map[string]interface{}{"name": "something"}), 240 }, 241 }, 242 { 243 PropName: "nestedObj", Value: &pb.ObjectPropertiesValue{ 244 ObjectProperties: []*pb.ObjectProperties{{ 245 PropName: "obj", Value: &pb.ObjectPropertiesValue{ 246 NonRefProperties: newStruct(t, map[string]interface{}{"name": "something"}), 247 EmptyListProps: []string{"empty"}, 248 }, 249 }}, 250 }, 251 }, 252 }, 253 }}}, 254 out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{ 255 "simpleObj": map[string]interface{}{"name": "something"}, 256 "nestedObj": map[string]interface{}{ 257 "obj": map[string]interface{}{"name": "something", "empty": []interface{}{}}, 258 }, 259 }}}, 260 }, 261 { 262 name: "object array props", 263 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{ 264 ObjectArrayProperties: []*pb.ObjectArrayProperties{ 265 { 266 PropName: "simpleObjs", Values: []*pb.ObjectPropertiesValue{ 267 { 268 NonRefProperties: newStruct(t, map[string]interface{}{"name": "something"}), 269 }, 270 { 271 NonRefProperties: newStruct(t, map[string]interface{}{"name": "something else"}), 272 }, 273 }, 274 }, 275 { 276 PropName: "nestedObjs", Values: []*pb.ObjectPropertiesValue{ 277 { 278 ObjectProperties: []*pb.ObjectProperties{{ 279 PropName: "obj", Value: &pb.ObjectPropertiesValue{ 280 NonRefProperties: newStruct(t, map[string]interface{}{"name": "something"}), 281 }, 282 }}, 283 }, 284 { 285 ObjectProperties: []*pb.ObjectProperties{{ 286 PropName: "obj", Value: &pb.ObjectPropertiesValue{ 287 NonRefProperties: newStruct(t, map[string]interface{}{"name": "something else"}), 288 }, 289 }}, 290 }, 291 }, 292 }, 293 }, 294 }}}, 295 out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{ 296 "simpleObjs": []interface{}{map[string]interface{}{"name": "something"}, map[string]interface{}{"name": "something else"}}, 297 "nestedObjs": []interface{}{ 298 map[string]interface{}{"obj": map[string]interface{}{"name": "something"}}, 299 map[string]interface{}{"obj": map[string]interface{}{"name": "something else"}}, 300 }, 301 }}}, 302 }, 303 { 304 name: "mix of errors and no errors", 305 req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4}, {Collection: collection}, {Collection: collection}, {Collection: collection, Uuid: UUID3}}, 306 out: []*models.Object{{Class: collection, Properties: nilMap, ID: UUID4}, {Class: collection, Properties: nilMap, ID: UUID3}}, 307 outError: []int{1, 2}, 308 origIndex: map[int]int{0: 0, 1: 3}, 309 }, 310 } 311 312 for _, tt := range tests { 313 t.Run(tt.name, func(t *testing.T) { 314 out, origIndex, batchErrors := batchFromProto(&pb.BatchObjectsRequest{Objects: tt.req}, scheme) 315 if len(tt.outError) > 0 { 316 require.NotNil(t, batchErrors) 317 if len(tt.out) > 0 { 318 require.Equal(t, tt.out, out) 319 require.Equal(t, tt.origIndex, origIndex) 320 } 321 } else { 322 require.Len(t, batchErrors, 0) 323 require.Equal(t, tt.out, out) 324 } 325 }) 326 } 327 }