github.com/weaviate/weaviate@v1.24.6/usecases/objects/references_delete.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  	"errors"
    17  	"fmt"
    18  
    19  	"github.com/go-openapi/strfmt"
    20  	"github.com/weaviate/weaviate/entities/additional"
    21  	"github.com/weaviate/weaviate/entities/models"
    22  	"github.com/weaviate/weaviate/entities/schema/crossref"
    23  )
    24  
    25  // DeleteReferenceInput represents required inputs to delete a reference from an existing object.
    26  type DeleteReferenceInput struct {
    27  	// Class name
    28  	Class string
    29  	// ID of an object
    30  	ID strfmt.UUID
    31  	// Property name
    32  	Property string
    33  	// Reference cross reference
    34  	Reference models.SingleRef
    35  }
    36  
    37  func (m *Manager) DeleteObjectReference(ctx context.Context, principal *models.Principal,
    38  	input *DeleteReferenceInput, repl *additional.ReplicationProperties, tenant string,
    39  ) *Error {
    40  	m.metrics.DeleteReferenceInc()
    41  	defer m.metrics.DeleteReferenceDec()
    42  
    43  	deprecatedEndpoint := input.Class == ""
    44  	beacon, err := crossref.Parse(input.Reference.Beacon.String())
    45  	if err != nil {
    46  		return &Error{"cannot parse beacon", StatusBadRequest, err}
    47  	}
    48  	if input.Class != "" && beacon.Class == "" {
    49  		toClass, toBeacon, replace, err := m.autodetectToClass(ctx, principal, input.Class, input.Property, beacon)
    50  		if err != nil {
    51  			return err
    52  		}
    53  		if replace {
    54  			input.Reference.Class = toClass
    55  			input.Reference.Beacon = toBeacon
    56  		}
    57  	}
    58  
    59  	res, err := m.getObjectFromRepo(ctx, input.Class, input.ID,
    60  		additional.Properties{}, nil, tenant)
    61  	if err != nil {
    62  		errnf := ErrNotFound{}
    63  		if errors.As(err, &errnf) {
    64  			return &Error{"source object", StatusNotFound, err}
    65  		} else if errors.As(err, &ErrMultiTenancy{}) {
    66  			return &Error{"source object", StatusUnprocessableEntity, err}
    67  		}
    68  		return &Error{"source object", StatusInternalServerError, err}
    69  	}
    70  	input.Class = res.ClassName
    71  
    72  	path := fmt.Sprintf("objects/%s/%s", input.Class, input.ID)
    73  	if err := m.authorizer.Authorize(principal, "update", path); err != nil {
    74  		return &Error{path, StatusForbidden, err}
    75  	}
    76  
    77  	unlock, err := m.locks.LockSchema()
    78  	if err != nil {
    79  		return &Error{"cannot lock", StatusInternalServerError, err}
    80  	}
    81  	defer unlock()
    82  
    83  	if err := input.validate(ctx, principal, m.schemaManager); err != nil {
    84  		if deprecatedEndpoint { // for backward comp reasons
    85  			return &Error{"bad inputs deprecated", StatusNotFound, err}
    86  		}
    87  		if errors.As(err, &ErrMultiTenancy{}) {
    88  			return &Error{"bad inputs", StatusUnprocessableEntity, err}
    89  		}
    90  		return &Error{"bad inputs", StatusBadRequest, err}
    91  	}
    92  
    93  	obj := res.Object()
    94  	obj.Tenant = tenant
    95  	ok, errmsg := removeReference(obj, input.Property, &input.Reference)
    96  	if errmsg != "" {
    97  		return &Error{errmsg, StatusInternalServerError, nil}
    98  	}
    99  	if !ok {
   100  		return nil
   101  	}
   102  	obj.LastUpdateTimeUnix = m.timeSource.Now()
   103  
   104  	err = m.vectorRepo.PutObject(ctx, obj, res.Vector, res.Vectors, repl)
   105  	if err != nil {
   106  		return &Error{"repo.putobject", StatusInternalServerError, err}
   107  	}
   108  
   109  	if err := m.updateRefVector(ctx, principal, input.Class, input.ID, tenant); err != nil {
   110  		return &Error{"update ref vector", StatusInternalServerError, err}
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func (req *DeleteReferenceInput) validate(
   117  	ctx context.Context,
   118  	principal *models.Principal,
   119  	sm schemaManager,
   120  ) error {
   121  	if err := validateReferenceName(req.Class, req.Property); err != nil {
   122  		return err
   123  	}
   124  
   125  	schema, err := sm.GetSchema(principal)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	return validateReferenceSchema(req.Class, req.Property, schema)
   130  }
   131  
   132  // removeReference removes ref from object obj with property prop.
   133  // It returns ok (removal took place) and an error message
   134  func removeReference(obj *models.Object, prop string, ref *models.SingleRef) (ok bool, errmsg string) {
   135  	properties := obj.Properties.(map[string]interface{})
   136  	if properties == nil || properties[prop] == nil {
   137  		return false, ""
   138  	}
   139  
   140  	refs, ok := properties[prop].(models.MultipleRef)
   141  	if !ok {
   142  		return false, "source list is not well formed"
   143  	}
   144  
   145  	newrefs := make(models.MultipleRef, 0, len(refs))
   146  	for _, r := range refs {
   147  		if r.Beacon != ref.Beacon {
   148  			newrefs = append(newrefs, r)
   149  		}
   150  	}
   151  	properties[prop] = newrefs
   152  	return len(refs) != len(newrefs), ""
   153  }