github.com/weaviate/weaviate@v1.24.6/adapters/handlers/grpc/v1/batch_parse_request.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  	"fmt"
    16  
    17  	"github.com/go-openapi/strfmt"
    18  	"github.com/google/uuid"
    19  	"github.com/weaviate/weaviate/usecases/byteops"
    20  
    21  	"github.com/weaviate/weaviate/entities/models"
    22  	"github.com/weaviate/weaviate/entities/schema"
    23  	pb "github.com/weaviate/weaviate/grpc/generated/protocol/v1"
    24  )
    25  
    26  const BEACON_START = "weaviate://localhost/"
    27  
    28  func sliceToInterface[T any](values []T) []interface{} {
    29  	tmpArray := make([]interface{}, len(values))
    30  	for k := range values {
    31  		tmpArray[k] = values[k]
    32  	}
    33  	return tmpArray
    34  }
    35  
    36  func batchFromProto(req *pb.BatchObjectsRequest, scheme schema.Schema) ([]*models.Object, map[int]int, map[int]error) {
    37  	objectsBatch := req.Objects
    38  	objs := make([]*models.Object, 0, len(objectsBatch))
    39  	objOriginalIndex := make(map[int]int)
    40  	objectErrors := make(map[int]error, len(objectsBatch))
    41  
    42  	insertCounter := 0
    43  	for i, obj := range objectsBatch {
    44  		class := scheme.GetClass(schema.ClassName(obj.Collection))
    45  		var props map[string]interface{}
    46  		if obj.Properties != nil {
    47  			props = extractPrimitiveProperties(&pb.ObjectPropertiesValue{
    48  				NonRefProperties:       obj.Properties.NonRefProperties,
    49  				BooleanArrayProperties: obj.Properties.BooleanArrayProperties,
    50  				NumberArrayProperties:  obj.Properties.NumberArrayProperties,
    51  				TextArrayProperties:    obj.Properties.TextArrayProperties,
    52  				IntArrayProperties:     obj.Properties.IntArrayProperties,
    53  				ObjectProperties:       obj.Properties.ObjectProperties,
    54  				ObjectArrayProperties:  obj.Properties.ObjectArrayProperties,
    55  				EmptyListProps:         obj.Properties.EmptyListProps,
    56  			})
    57  			if err := extractSingleRefTarget(class, obj.Properties.SingleTargetRefProps, props); err != nil {
    58  				objectErrors[i] = err
    59  				continue
    60  			}
    61  			if err := extractMultiRefTarget(class, obj.Properties.MultiTargetRefProps, props); err != nil {
    62  				objectErrors[i] = err
    63  				continue
    64  			}
    65  		}
    66  
    67  		_, err := uuid.Parse(obj.Uuid)
    68  		if err != nil {
    69  			objectErrors[i] = err
    70  			continue
    71  		}
    72  
    73  		var vector []float32 = nil
    74  		// bytes vector has precedent for being more efficient
    75  		if len(obj.VectorBytes) > 0 {
    76  			vector = byteops.Float32FromByteVector(obj.VectorBytes)
    77  		} else if len(obj.Vector) > 0 {
    78  			vector = obj.Vector
    79  		}
    80  
    81  		var vectors models.Vectors = nil
    82  		if len(obj.Vectors) > 0 {
    83  			vectors = make(models.Vectors, len(obj.Vectors))
    84  			for _, vec := range obj.Vectors {
    85  				vectors[vec.Name] = byteops.Float32FromByteVector(vec.VectorBytes)
    86  			}
    87  		}
    88  
    89  		objOriginalIndex[insertCounter] = i
    90  		objs = append(objs, &models.Object{
    91  			Class:      obj.Collection,
    92  			Tenant:     obj.Tenant,
    93  			Vector:     vector,
    94  			Properties: props,
    95  			ID:         strfmt.UUID(obj.Uuid),
    96  			Vectors:    vectors,
    97  		})
    98  		insertCounter += 1
    99  	}
   100  	return objs[:insertCounter], objOriginalIndex, objectErrors
   101  }
   102  
   103  func extractSingleRefTarget(class *models.Class, properties []*pb.BatchObject_SingleTargetRefProps, props map[string]interface{}) error {
   104  	for _, refSingle := range properties {
   105  		propName := refSingle.GetPropName()
   106  		prop, err := schema.GetPropertyByName(class, propName)
   107  		if err != nil {
   108  			return err
   109  		}
   110  		if len(prop.DataType) > 1 {
   111  			return fmt.Errorf("target is a multi-target reference, need single target %v", prop.DataType)
   112  		}
   113  		toClass := prop.DataType[0]
   114  		beacons := make([]interface{}, len(refSingle.Uuids))
   115  		for j, uuid := range refSingle.Uuids {
   116  			beacons[j] = map[string]interface{}{"beacon": BEACON_START + toClass + "/" + uuid}
   117  		}
   118  		props[propName] = beacons
   119  	}
   120  	return nil
   121  }
   122  
   123  func extractMultiRefTarget(class *models.Class, properties []*pb.BatchObject_MultiTargetRefProps, props map[string]interface{}) error {
   124  	for _, refMulti := range properties {
   125  		propName := refMulti.GetPropName()
   126  		prop, err := schema.GetPropertyByName(class, propName)
   127  		if err != nil {
   128  			return err
   129  		}
   130  		if len(prop.DataType) < 2 {
   131  			return fmt.Errorf("target is a single-target reference, need multi-target %v", prop.DataType)
   132  		}
   133  		beacons := make([]interface{}, len(refMulti.Uuids))
   134  		for j, uid := range refMulti.Uuids {
   135  			beacons[j] = map[string]interface{}{"beacon": BEACON_START + refMulti.TargetCollection + "/" + uid}
   136  		}
   137  		props[propName] = beacons
   138  	}
   139  	return nil
   140  }
   141  
   142  func extractPrimitiveProperties(properties *pb.ObjectPropertiesValue) map[string]interface{} {
   143  	var props map[string]interface{}
   144  	if properties.NonRefProperties != nil {
   145  		props = properties.NonRefProperties.AsMap()
   146  	} else {
   147  		props = make(map[string]interface{})
   148  	}
   149  
   150  	// arrays cannot be part of a GRPC map, so we need to handle each type separately
   151  	for j := range properties.BooleanArrayProperties {
   152  		props[properties.BooleanArrayProperties[j].PropName] = sliceToInterface(properties.BooleanArrayProperties[j].Values)
   153  	}
   154  
   155  	for j := range properties.NumberArrayProperties {
   156  		inputValuesBytes := properties.NumberArrayProperties[j].ValuesBytes
   157  		var values []float64
   158  
   159  		if len(inputValuesBytes) > 0 {
   160  			values = byteops.Float64FromByteVector(inputValuesBytes)
   161  		} else {
   162  			values = properties.NumberArrayProperties[j].Values
   163  		}
   164  
   165  		props[properties.NumberArrayProperties[j].PropName] = sliceToInterface(values)
   166  	}
   167  
   168  	for j := range properties.TextArrayProperties {
   169  		props[properties.TextArrayProperties[j].PropName] = sliceToInterface(properties.TextArrayProperties[j].Values)
   170  	}
   171  
   172  	for j := range properties.IntArrayProperties {
   173  		props[properties.IntArrayProperties[j].PropName] = sliceToInterface(properties.IntArrayProperties[j].Values)
   174  	}
   175  
   176  	for j := range properties.ObjectProperties {
   177  		props[properties.ObjectProperties[j].PropName] = extractPrimitiveProperties(properties.ObjectProperties[j].Value)
   178  	}
   179  
   180  	for _, prop := range properties.ObjectArrayProperties {
   181  		nested := make([]interface{}, len(prop.Values))
   182  		for k := range prop.Values {
   183  			nested[k] = extractPrimitiveProperties(prop.Values[k])
   184  		}
   185  		props[prop.PropName] = nested
   186  	}
   187  
   188  	for _, propName := range properties.EmptyListProps {
   189  		props[propName] = []interface{}{}
   190  	}
   191  
   192  	return props
   193  }