github.com/weaviate/weaviate@v1.24.6/usecases/objects/add.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 objects 13 14 import ( 15 "context" 16 "fmt" 17 "strings" 18 19 "github.com/go-openapi/strfmt" 20 "github.com/google/uuid" 21 "github.com/weaviate/weaviate/entities/additional" 22 "github.com/weaviate/weaviate/entities/models" 23 "github.com/weaviate/weaviate/entities/moduletools" 24 "github.com/weaviate/weaviate/entities/schema" 25 "github.com/weaviate/weaviate/usecases/objects/validation" 26 ) 27 28 type schemaManager interface { 29 GetSchema(principal *models.Principal) (schema.Schema, error) 30 AddClass(ctx context.Context, principal *models.Principal, 31 class *models.Class) error 32 GetClass(ctx context.Context, principal *models.Principal, 33 name string, 34 ) (*models.Class, error) 35 AddClassProperty(ctx context.Context, principal *models.Principal, 36 class string, property *models.Property) error 37 MergeClassObjectProperty(ctx context.Context, principal *models.Principal, 38 class string, property *models.Property) error 39 } 40 41 // AddObject Class Instance to the connected DB. 42 func (m *Manager) AddObject(ctx context.Context, principal *models.Principal, object *models.Object, 43 repl *additional.ReplicationProperties, 44 ) (*models.Object, error) { 45 err := m.authorizer.Authorize(principal, "create", "objects") 46 if err != nil { 47 return nil, err 48 } 49 50 unlock, err := m.locks.LockSchema() 51 if err != nil { 52 return nil, NewErrInternal("could not acquire lock: %v", err) 53 } 54 defer unlock() 55 56 m.metrics.AddObjectInc() 57 defer m.metrics.AddObjectDec() 58 59 return m.addObjectToConnectorAndSchema(ctx, principal, object, repl) 60 } 61 62 func (m *Manager) checkIDOrAssignNew(ctx context.Context, class string, id strfmt.UUID, 63 repl *additional.ReplicationProperties, tenant string, 64 ) (strfmt.UUID, error) { 65 if id == "" { 66 newID, err := generateUUID() 67 if err != nil { 68 return "", NewErrInternal("could not generate id: %v", err) 69 } 70 return newID, nil 71 } else { 72 // IDs are always returned lowercase, but they are written 73 // to disk as uppercase, when provided that way. Here we 74 // ensure they are lowercase on disk as well, so things 75 // like filtering are not affected. 76 // See: https://github.com/weaviate/weaviate/issues/2647 77 id = strfmt.UUID(strings.ToLower(id.String())) 78 } 79 80 // only validate ID uniqueness if explicitly set 81 if ok, err := m.vectorRepo.Exists(ctx, class, id, repl, tenant); ok { 82 return "", NewErrInvalidUserInput("id '%s' already exists", id) 83 } else if err != nil { 84 switch err.(type) { 85 case ErrInvalidUserInput: 86 return "", err 87 case ErrMultiTenancy: 88 return "", err 89 default: 90 return "", NewErrInternal(err.Error()) 91 } 92 } 93 return id, nil 94 } 95 96 func (m *Manager) addObjectToConnectorAndSchema(ctx context.Context, principal *models.Principal, 97 object *models.Object, repl *additional.ReplicationProperties, 98 ) (*models.Object, error) { 99 id, err := m.checkIDOrAssignNew(ctx, object.Class, object.ID, repl, object.Tenant) 100 if err != nil { 101 return nil, err 102 } 103 object.ID = id 104 105 err = m.autoSchemaManager.autoSchema(ctx, principal, object, true) 106 if err != nil { 107 return nil, NewErrInvalidUserInput("invalid object: %v", err) 108 } 109 110 err = m.validateObjectAndNormalizeNames(ctx, principal, repl, object, nil) 111 if err != nil { 112 return nil, NewErrInvalidUserInput("invalid object: %v", err) 113 } 114 115 now := m.timeSource.Now() 116 object.CreationTimeUnix = now 117 object.LastUpdateTimeUnix = now 118 if object.Properties == nil { 119 object.Properties = map[string]interface{}{} 120 } 121 class, err := m.schemaManager.GetClass(ctx, principal, object.Class) 122 if err != nil { 123 return nil, err 124 } 125 compFactory := func() (moduletools.VectorizablePropsComparator, error) { 126 return moduletools.NewVectorizablePropsComparatorDummy(class.Properties, object.Properties), nil 127 } 128 err = m.modulesProvider.UpdateVector(ctx, object, class, compFactory, m.findObject, m.logger) 129 if err != nil { 130 return nil, err 131 } 132 133 err = m.vectorRepo.PutObject(ctx, object, object.Vector, object.Vectors, repl) 134 if err != nil { 135 return nil, fmt.Errorf("put object: %w", err) 136 } 137 138 return object, nil 139 } 140 141 func (m *Manager) validateObjectAndNormalizeNames(ctx context.Context, 142 principal *models.Principal, repl *additional.ReplicationProperties, 143 incoming *models.Object, existing *models.Object, 144 ) error { 145 class, err := m.validateSchema(ctx, principal, incoming) 146 if err != nil { 147 return err 148 } 149 150 return validation.New(m.vectorRepo.Exists, m.config, repl). 151 Object(ctx, class, incoming, existing) 152 } 153 154 func (m *Manager) validateSchema(ctx context.Context, 155 principal *models.Principal, obj *models.Object, 156 ) (*models.Class, error) { 157 // Validate schema given in body with the weaviate schema 158 if _, err := uuid.Parse(obj.ID.String()); err != nil { 159 return nil, err 160 } 161 162 class, err := m.schemaManager.GetClass(ctx, principal, obj.Class) 163 if err != nil { 164 return nil, err 165 } 166 167 if class == nil { 168 return nil, fmt.Errorf("class %q not found in schema", obj.Class) 169 } 170 171 return class, nil 172 }