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  }