github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/crud.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 db 13 14 import ( 15 "context" 16 "fmt" 17 "time" 18 19 "github.com/go-openapi/strfmt" 20 "github.com/pkg/errors" 21 "github.com/weaviate/weaviate/adapters/repos/db/refcache" 22 "github.com/weaviate/weaviate/entities/additional" 23 "github.com/weaviate/weaviate/entities/models" 24 "github.com/weaviate/weaviate/entities/multi" 25 "github.com/weaviate/weaviate/entities/schema" 26 "github.com/weaviate/weaviate/entities/schema/crossref" 27 "github.com/weaviate/weaviate/entities/search" 28 "github.com/weaviate/weaviate/entities/storobj" 29 "github.com/weaviate/weaviate/usecases/objects" 30 ) 31 32 func (db *DB) PutObject(ctx context.Context, obj *models.Object, 33 vector []float32, vectors models.Vectors, repl *additional.ReplicationProperties, 34 ) error { 35 object := storobj.FromObject(obj, vector, vectors) 36 idx := db.GetIndex(object.Class()) 37 if idx == nil { 38 return fmt.Errorf("import into non-existing index for %s", object.Class()) 39 } 40 41 if err := idx.putObject(ctx, object, repl); err != nil { 42 return fmt.Errorf("import into index %s: %w", idx.ID(), err) 43 } 44 45 return nil 46 } 47 48 // DeleteObject from of a specific class giving its ID 49 func (db *DB) DeleteObject(ctx context.Context, class string, id strfmt.UUID, 50 repl *additional.ReplicationProperties, tenant string, 51 ) error { 52 idx := db.GetIndex(schema.ClassName(class)) 53 if idx == nil { 54 return fmt.Errorf("delete from non-existing index for %s", class) 55 } 56 57 err := idx.deleteObject(ctx, id, repl, tenant) 58 if err != nil { 59 return fmt.Errorf("delete from index %q: %w", idx.ID(), err) 60 } 61 62 return nil 63 } 64 65 func (db *DB) MultiGet(ctx context.Context, query []multi.Identifier, 66 additional additional.Properties, tenant string, 67 ) ([]search.Result, error) { 68 byIndex := map[string][]multi.Identifier{} 69 db.indexLock.RLock() 70 defer db.indexLock.RUnlock() 71 72 for i, q := range query { 73 // store original position to make assembly easier later 74 q.OriginalPosition = i 75 76 for _, index := range db.indices { 77 if index.Config.ClassName != schema.ClassName(q.ClassName) { 78 continue 79 } 80 81 queue := byIndex[index.ID()] 82 queue = append(queue, q) 83 byIndex[index.ID()] = queue 84 } 85 } 86 87 out := make(search.Results, len(query)) 88 for indexID, queries := range byIndex { 89 indexRes, err := db.indices[indexID].multiObjectByID(ctx, queries, tenant) 90 if err != nil { 91 return nil, errors.Wrapf(err, "index %q", indexID) 92 } 93 94 for i, obj := range indexRes { 95 if obj == nil { 96 continue 97 } 98 res := obj.SearchResult(additional, tenant) 99 out[queries[i].OriginalPosition] = *res 100 } 101 } 102 103 return out, nil 104 } 105 106 // ObjectByID checks every index of the particular kind for the ID 107 // 108 // @warning: this function is deprecated by Object() 109 func (db *DB) ObjectByID(ctx context.Context, id strfmt.UUID, 110 props search.SelectProperties, additional additional.Properties, 111 tenant string, 112 ) (*search.Result, error) { 113 results, err := db.ObjectsByID(ctx, id, props, additional, tenant) 114 if err != nil { 115 return nil, err 116 } 117 if len(results) == 0 { 118 return nil, nil 119 } 120 return &results[0], nil 121 } 122 123 // ObjectsByID checks every index of the particular kind for the ID 124 // this method is only used for Explore queries where we don't have 125 // a class context 126 func (db *DB) ObjectsByID(ctx context.Context, id strfmt.UUID, 127 props search.SelectProperties, additional additional.Properties, 128 tenant string, 129 ) (search.Results, error) { 130 var result []*storobj.Object 131 // TODO: Search in parallel, rather than sequentially or this will be 132 // painfully slow on large schemas 133 db.indexLock.RLock() 134 135 for _, index := range db.indices { 136 res, err := index.objectByID(ctx, id, props, additional, nil, tenant) 137 if err != nil { 138 db.indexLock.RUnlock() 139 switch err.(type) { 140 case objects.ErrMultiTenancy: 141 return nil, objects.NewErrMultiTenancy(fmt.Errorf("search index %s: %w", index.ID(), err)) 142 default: 143 return nil, errors.Wrapf(err, "search index %s", index.ID()) 144 } 145 } 146 147 if res != nil { 148 result = append(result, res) 149 } 150 } 151 db.indexLock.RUnlock() 152 153 if result == nil { 154 return nil, nil 155 } 156 157 return db.ResolveReferences(ctx, 158 storobj.SearchResults(result, additional, tenant), props, nil, additional, tenant) 159 } 160 161 // Object gets object with id from index of specified class. 162 func (db *DB) Object(ctx context.Context, class string, id strfmt.UUID, 163 props search.SelectProperties, addl additional.Properties, 164 repl *additional.ReplicationProperties, tenant string, 165 ) (*search.Result, error) { 166 idx := db.GetIndex(schema.ClassName(class)) 167 if idx == nil { 168 return nil, nil 169 } 170 171 obj, err := idx.objectByID(ctx, id, props, addl, repl, tenant) 172 if err != nil { 173 switch err.(type) { 174 case objects.ErrMultiTenancy: 175 return nil, objects.NewErrMultiTenancy(fmt.Errorf("search index %s: %w", idx.ID(), err)) 176 default: 177 return nil, errors.Wrapf(err, "search index %s", idx.ID()) 178 } 179 } 180 var r *search.Result 181 if obj != nil { 182 r = obj.SearchResult(addl, tenant) 183 } 184 if r == nil { 185 return nil, nil 186 } 187 return db.enrichRefsForSingle(ctx, r, props, addl, tenant) 188 } 189 190 func (db *DB) enrichRefsForSingle(ctx context.Context, obj *search.Result, 191 props search.SelectProperties, additional additional.Properties, tenant string, 192 ) (*search.Result, error) { 193 res, err := refcache.NewResolver(refcache.NewCacher(db, db.logger, tenant)). 194 Do(ctx, []search.Result{*obj}, props, additional) 195 if err != nil { 196 return nil, errors.Wrap(err, "resolve cross-refs") 197 } 198 199 return &res[0], nil 200 } 201 202 func (db *DB) Exists(ctx context.Context, class string, id strfmt.UUID, 203 repl *additional.ReplicationProperties, tenant string, 204 ) (bool, error) { 205 if class == "" { 206 return db.anyExists(ctx, id, repl) 207 } 208 index := db.GetIndex(schema.ClassName(class)) 209 if index == nil { 210 return false, nil 211 } 212 return index.exists(ctx, id, repl, tenant) 213 } 214 215 func (db *DB) anyExists(ctx context.Context, id strfmt.UUID, 216 repl *additional.ReplicationProperties, 217 ) (bool, error) { 218 // TODO: Search in parallel, rather than sequentially or this will be 219 // painfully slow on large schemas 220 db.indexLock.RLock() 221 defer db.indexLock.RUnlock() 222 223 for _, index := range db.indices { 224 ok, err := index.exists(ctx, id, repl, "") 225 if err != nil { 226 switch err.(type) { 227 case objects.ErrMultiTenancy: 228 return false, objects.NewErrMultiTenancy(fmt.Errorf("search index %s: %w", index.ID(), err)) 229 default: 230 return false, errors.Wrapf(err, "search index %s", index.ID()) 231 } 232 } 233 if ok { 234 return true, nil 235 } 236 } 237 238 return false, nil 239 } 240 241 func (db *DB) AddReference(ctx context.Context, source *crossref.RefSource, target *crossref.Ref, 242 repl *additional.ReplicationProperties, tenant string, 243 ) error { 244 return db.Merge(ctx, objects.MergeDocument{ 245 Class: source.Class.String(), 246 ID: source.TargetID, 247 UpdateTime: time.Now().UnixMilli(), 248 References: objects.BatchReferences{ 249 objects.BatchReference{ 250 From: source, 251 To: target, 252 }, 253 }, 254 }, repl, tenant) 255 } 256 257 func (db *DB) Merge(ctx context.Context, merge objects.MergeDocument, 258 repl *additional.ReplicationProperties, tenant string, 259 ) error { 260 idx := db.GetIndex(schema.ClassName(merge.Class)) 261 if idx == nil { 262 return fmt.Errorf("merge from non-existing index for %s", merge.Class) 263 } 264 265 err := idx.mergeObject(ctx, merge, repl, tenant) 266 if err != nil { 267 return errors.Wrapf(err, "merge into index %s", idx.ID()) 268 } 269 270 return nil 271 }