github.com/weaviate/weaviate@v1.24.6/usecases/modules/module_config_init_and_validate.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 modules
    13  
    14  import (
    15  	"context"
    16  	"fmt"
    17  
    18  	"github.com/pkg/errors"
    19  	"github.com/weaviate/weaviate/entities/models"
    20  	"github.com/weaviate/weaviate/entities/modulecapabilities"
    21  	"github.com/weaviate/weaviate/entities/schema"
    22  )
    23  
    24  // SetClassDefaults sets the module-specific defaults for the class itself, but
    25  // also for each prop
    26  func (p *Provider) SetClassDefaults(class *models.Class) {
    27  	if !hasTargetVectors(class) {
    28  		p.setClassDefaults(class, class.Vectorizer, "", func(vectorizerConfig map[string]interface{}) {
    29  			if class.ModuleConfig == nil {
    30  				class.ModuleConfig = map[string]interface{}{}
    31  			}
    32  			class.ModuleConfig.(map[string]interface{})[class.Vectorizer] = vectorizerConfig
    33  		})
    34  		return
    35  	}
    36  
    37  	for targetVector, vectorConfig := range class.VectorConfig {
    38  		if moduleConfig, ok := vectorConfig.Vectorizer.(map[string]interface{}); ok && len(moduleConfig) == 1 {
    39  			for vectorizer := range moduleConfig {
    40  				p.setClassDefaults(class, vectorizer, targetVector, func(vectorizerConfig map[string]interface{}) {
    41  					moduleConfig[vectorizer] = vectorizerConfig
    42  				})
    43  			}
    44  		}
    45  	}
    46  }
    47  
    48  func (p *Provider) setClassDefaults(class *models.Class, vectorizer string,
    49  	targetVector string, storeFn func(vectorizerConfig map[string]interface{}),
    50  ) {
    51  	if vectorizer == "none" {
    52  		// the class does not use a vectorizer, nothing to do for us
    53  		return
    54  	}
    55  
    56  	mod := p.GetByName(vectorizer)
    57  	cc, ok := mod.(modulecapabilities.ClassConfigurator)
    58  	if !ok {
    59  		// the module exists, but is not a class configurator, nothing to do for us
    60  		return
    61  	}
    62  
    63  	cfg := NewClassBasedModuleConfig(class, vectorizer, "", targetVector)
    64  
    65  	p.setPerClassConfigDefaults(cfg, cc, storeFn)
    66  	for _, prop := range class.Properties {
    67  		p.setSinglePropertyConfigDefaults(prop, vectorizer, cc)
    68  	}
    69  }
    70  
    71  func (p *Provider) setPerClassConfigDefaults(cfg *ClassBasedModuleConfig,
    72  	cc modulecapabilities.ClassConfigurator,
    73  	storeFn func(vectorizerConfig map[string]interface{}),
    74  ) {
    75  	modDefaults := cc.ClassConfigDefaults()
    76  	userSpecified := cfg.Class()
    77  	mergedConfig := map[string]interface{}{}
    78  
    79  	for key, value := range modDefaults {
    80  		mergedConfig[key] = value
    81  	}
    82  	for key, value := range userSpecified {
    83  		mergedConfig[key] = value
    84  	}
    85  
    86  	if len(mergedConfig) > 0 {
    87  		storeFn(mergedConfig)
    88  	}
    89  }
    90  
    91  // SetSinglePropertyDefaults can be used when a property is added later, e.g.
    92  // as part of merging in a ref prop after a class has already been created
    93  func (p *Provider) SetSinglePropertyDefaults(class *models.Class,
    94  	prop *models.Property,
    95  ) {
    96  	if !hasTargetVectors(class) {
    97  		p.setSinglePropertyDefaults(prop, class.Vectorizer)
    98  		return
    99  	}
   100  
   101  	for _, vectorConfig := range class.VectorConfig {
   102  		if moduleConfig, ok := vectorConfig.Vectorizer.(map[string]interface{}); ok && len(moduleConfig) == 1 {
   103  			for vectorizer := range moduleConfig {
   104  				p.setSinglePropertyDefaults(prop, vectorizer)
   105  			}
   106  		}
   107  	}
   108  }
   109  
   110  func (p *Provider) setSinglePropertyDefaults(prop *models.Property, vectorizer string) {
   111  	if vectorizer == "none" {
   112  		// the class does not use a vectorizer, nothing to do for us
   113  		return
   114  	}
   115  
   116  	mod := p.GetByName(vectorizer)
   117  	cc, ok := mod.(modulecapabilities.ClassConfigurator)
   118  	if !ok {
   119  		// the module exists, but is not a class configurator, nothing to do for us
   120  		return
   121  	}
   122  
   123  	p.setSinglePropertyConfigDefaults(prop, vectorizer, cc)
   124  }
   125  
   126  func (p *Provider) setSinglePropertyConfigDefaults(prop *models.Property,
   127  	vectorizer string, cc modulecapabilities.ClassConfigurator,
   128  ) {
   129  	dt, _ := schema.GetValueDataTypeFromString(prop.DataType[0])
   130  	modDefaults := cc.PropertyConfigDefaults(dt)
   131  	userSpecified := map[string]interface{}{}
   132  	mergedConfig := map[string]interface{}{}
   133  
   134  	if prop.ModuleConfig != nil {
   135  		if vectorizerConfig, ok := prop.ModuleConfig.(map[string]interface{})[vectorizer]; ok {
   136  			if mcvm, ok := vectorizerConfig.(map[string]interface{}); ok {
   137  				userSpecified = mcvm
   138  			}
   139  		}
   140  	}
   141  
   142  	for key, value := range modDefaults {
   143  		mergedConfig[key] = value
   144  	}
   145  	for key, value := range userSpecified {
   146  		mergedConfig[key] = value
   147  	}
   148  
   149  	if len(mergedConfig) > 0 {
   150  		if prop.ModuleConfig == nil {
   151  			prop.ModuleConfig = map[string]interface{}{}
   152  		}
   153  		prop.ModuleConfig.(map[string]interface{})[vectorizer] = mergedConfig
   154  	}
   155  }
   156  
   157  func (p *Provider) ValidateClass(ctx context.Context, class *models.Class) error {
   158  	switch len(class.VectorConfig) {
   159  	case 0:
   160  		// legacy configuration
   161  		if class.Vectorizer == "none" {
   162  			// the class does not use a vectorizer, nothing to do for us
   163  			return nil
   164  		}
   165  		if err := p.validateClassesModuleConfig(ctx, class, class.ModuleConfig); err != nil {
   166  			return err
   167  		}
   168  		return nil
   169  	default:
   170  		// named vectors configuration
   171  		for targetVector, vectorConfig := range class.VectorConfig {
   172  			if len(targetVector) > schema.TargetVectorNameMaxLength {
   173  				return errors.Errorf("class.VectorConfig target vector name %q is not valid. "+
   174  					"Target vector name should not be longer than %d characters.",
   175  					targetVector, schema.TargetVectorNameMaxLength)
   176  			}
   177  			if !p.targetVectorNameValidator.MatchString(targetVector) {
   178  				return errors.Errorf("class.VectorConfig target vector name %q is not valid, "+
   179  					"in Weaviate target vector names are restricted to valid GraphQL names, "+
   180  					"which must be “/%s/”.", targetVector, schema.TargetVectorNameRegex)
   181  			}
   182  			vectorizer, ok := vectorConfig.Vectorizer.(map[string]interface{})
   183  			if !ok {
   184  				return errors.Errorf("class.VectorConfig.Vectorizer must be an object, got %T", vectorConfig.Vectorizer)
   185  			}
   186  			if len(vectorizer) != 1 {
   187  				return errors.Errorf("class.VectorConfig.Vectorizer must consist only 1 configuration, got: %v", len(vectorizer))
   188  			}
   189  			for modName := range vectorizer {
   190  				if modName == "none" {
   191  					// the class does not use a vectorizer, nothing to do for us
   192  					continue
   193  				}
   194  				if mod := p.GetByName(modName); mod == nil {
   195  					return errors.Errorf("class.VectorConfig.Vectorizer module with name %s doesn't exist", modName)
   196  				}
   197  				if err := p.validateClassModuleConfig(ctx, class, modName, targetVector); err != nil {
   198  					return err
   199  				}
   200  			}
   201  		}
   202  		// check module config configuration in case that there are other none vectorizer modules defined
   203  		if err := p.validateClassesModuleConfigNoneVectorizers(ctx, class, "", class.ModuleConfig); err != nil {
   204  			return err
   205  		}
   206  		return nil
   207  	}
   208  }
   209  
   210  func (p *Provider) validateClassesModuleConfigNoneVectorizers(ctx context.Context,
   211  	class *models.Class, targetVector string, moduleConfig interface{},
   212  ) error {
   213  	modConfig, ok := moduleConfig.(map[string]interface{})
   214  	if !ok {
   215  		return nil
   216  	}
   217  	for modName := range modConfig {
   218  		mod := p.GetByName(modName)
   219  		if !p.isVectorizerModule(mod.Type()) {
   220  			if err := p.validateClassModuleConfig(ctx, class, modName, ""); err != nil {
   221  				return err
   222  			}
   223  		}
   224  	}
   225  	return nil
   226  }
   227  
   228  func (p *Provider) validateClassesModuleConfig(ctx context.Context,
   229  	class *models.Class, moduleConfig interface{},
   230  ) error {
   231  	modConfig, ok := moduleConfig.(map[string]interface{})
   232  	if !ok {
   233  		return nil
   234  	}
   235  	configuredVectorizers := make([]string, 0, len(modConfig))
   236  	for modName := range modConfig {
   237  		if err := p.validateClassModuleConfig(ctx, class, modName, ""); err != nil {
   238  			return err
   239  		}
   240  		if err := p.ValidateVectorizer(modName); err == nil {
   241  			configuredVectorizers = append(configuredVectorizers, modName)
   242  		}
   243  	}
   244  	if len(configuredVectorizers) > 1 {
   245  		return fmt.Errorf("multiple vectorizers configured in class's moduleConfig: %v. class.vectorizer is set to %q",
   246  			configuredVectorizers, class.Vectorizer)
   247  	}
   248  	return nil
   249  }
   250  
   251  func (p *Provider) validateClassModuleConfig(ctx context.Context,
   252  	class *models.Class, moduleName, targetVector string,
   253  ) error {
   254  	mod := p.GetByName(moduleName)
   255  	cc, ok := mod.(modulecapabilities.ClassConfigurator)
   256  	if !ok {
   257  		// the module exists, but is not a class configurator, nothing to do for us
   258  		return nil
   259  	}
   260  
   261  	cfg := NewClassBasedModuleConfig(class, moduleName, "", targetVector)
   262  	err := cc.ValidateClass(ctx, class, cfg)
   263  	if err != nil {
   264  		return errors.Wrapf(err, "module '%s'", moduleName)
   265  	}
   266  	return nil
   267  }
   268  
   269  func hasTargetVectors(class *models.Class) bool {
   270  	return len(class.VectorConfig) > 0
   271  }