github.com/weaviate/weaviate@v1.24.6/usecases/schema/update_property.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 schema
    13  
    14  import (
    15  	"context"
    16  	"encoding/json"
    17  	"fmt"
    18  
    19  	"github.com/pkg/errors"
    20  	"github.com/weaviate/weaviate/entities/models"
    21  	"github.com/weaviate/weaviate/entities/schema"
    22  )
    23  
    24  // MergeClassObjectProperty of an existing Class
    25  // Merges NestedProperties of incoming object/object[] property into existing one
    26  func (m *Manager) MergeClassObjectProperty(ctx context.Context, principal *models.Principal,
    27  	class string, property *models.Property,
    28  ) error {
    29  	err := m.Authorizer.Authorize(principal, "update", "schema/objects")
    30  	if err != nil {
    31  		return err
    32  	}
    33  
    34  	return m.mergeClassObjectProperty(ctx, class, property)
    35  }
    36  
    37  func (m *Manager) mergeClassObjectProperty(ctx context.Context,
    38  	className string, prop *models.Property,
    39  ) error {
    40  	m.Lock()
    41  	defer m.Unlock()
    42  
    43  	class, err := m.schemaCache.readOnlyClass(className)
    44  	if err != nil {
    45  		return err
    46  	}
    47  	prop.Name = schema.LowercaseFirstLetter(prop.Name)
    48  
    49  	// reuse setDefaults/validation/migrate methods coming from add property
    50  	// (empty existing names map, to validate existing updated property)
    51  	// TODO nested - refactor / cleanup setDefaults/validation/migrate methods
    52  	m.setNewPropDefaults(class, prop)
    53  	if err := m.validateProperty(prop, class, map[string]bool{}, false); err != nil {
    54  		return err
    55  	}
    56  	// migrate only after validation in completed
    57  	migratePropertySettings(prop)
    58  
    59  	tx, err := m.cluster.BeginTransaction(ctx, mergeObjectProperty,
    60  		MergeObjectPropertyPayload{className, prop}, DefaultTxTTL)
    61  	if err != nil {
    62  		// possible causes for errors could be nodes down (we expect every node to
    63  		// the up for a schema transaction) or concurrent transactions from other
    64  		// nodes
    65  		return errors.Wrap(err, "open cluster-wide transaction")
    66  	}
    67  
    68  	if err := m.cluster.CommitWriteTransaction(ctx, tx); err != nil {
    69  		// Only log the commit error, but do not abort the changes locally. Once
    70  		// we've told others to commit, we also need to commit ourselves!
    71  		//
    72  		// The idea is that if we abort our changes we are guaranteed to create an
    73  		// inconsistency as soon as any other node honored the commit. This would
    74  		// for example be the case in a 3-node cluster where node 1 is the
    75  		// coordinator, node 2 honored the commit and node 3 died during the commit
    76  		// phase.
    77  		//
    78  		// In this scenario it is far more desirable to make sure that node 1 and
    79  		// node 2 stay in sync, as node 3 - who may or may not have missed the
    80  		// update - can use a local WAL from the first TX phase to replay any
    81  		// missing changes once it's back.
    82  		m.logger.WithError(err).Errorf("not every node was able to commit")
    83  	}
    84  
    85  	return m.mergeClassObjectPropertyApplyChanges(ctx, className, prop)
    86  }
    87  
    88  func (m *Manager) mergeClassObjectPropertyApplyChanges(ctx context.Context,
    89  	className string, prop *models.Property,
    90  ) error {
    91  	class, err := m.schemaCache.mergeObjectProperty(className, prop)
    92  	if err != nil {
    93  		return err
    94  	}
    95  	metadata, err := json.Marshal(&class)
    96  	if err != nil {
    97  		return fmt.Errorf("marshal class %s: %w", className, err)
    98  	}
    99  	m.logger.
   100  		WithField("action", "schema.update_object_property").
   101  		Debug("saving updated schema to configuration store")
   102  	err = m.repo.UpdateClass(ctx, ClassPayload{Name: className, Metadata: metadata})
   103  	if err != nil {
   104  		return err
   105  	}
   106  	m.triggerSchemaUpdateCallbacks()
   107  
   108  	// TODO nested - implement MergeObjectProperty (needed for indexing/filtering)
   109  	// will result in a mismatch between schema and index if function below fails
   110  	// return m.migrator.MergeObjectProperty(ctx, className, prop)
   111  	return nil
   112  }