github.com/weaviate/weaviate@v1.24.6/usecases/schema/update_test.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  	"testing"
    18  
    19  	"github.com/pkg/errors"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  	"github.com/weaviate/weaviate/entities/models"
    23  	"github.com/weaviate/weaviate/entities/schema"
    24  	"github.com/weaviate/weaviate/usecases/config"
    25  )
    26  
    27  // As of now, most class settings are immutable, but we need to allow some
    28  // specific updates, such as the vector index config
    29  func TestClassUpdates(t *testing.T) {
    30  	t.Run("a class which doesn't exist", func(t *testing.T) {
    31  		err := newSchemaManager().UpdateClass(context.Background(),
    32  			nil, "WrongClass", &models.Class{})
    33  		require.NotNil(t, err)
    34  		assert.Equal(t, ErrNotFound, err)
    35  	})
    36  
    37  	t.Run("various immutable and mutable fields", func(t *testing.T) {
    38  		type test struct {
    39  			name          string
    40  			initial       *models.Class
    41  			update        *models.Class
    42  			expectedError error
    43  		}
    44  
    45  		tests := []test{
    46  			{
    47  				name:    "attempting a name change",
    48  				initial: &models.Class{Class: "InitialName"},
    49  				update:  &models.Class{Class: "UpdatedName"},
    50  				expectedError: errors.Errorf(
    51  					"class name is immutable: " +
    52  						"attempted change from \"InitialName\" to \"UpdatedName\""),
    53  			},
    54  			{
    55  				name:    "attempting to modify the vectorizer",
    56  				initial: &models.Class{Class: "InitialName", Vectorizer: "model1"},
    57  				update:  &models.Class{Class: "InitialName", Vectorizer: "model2"},
    58  				expectedError: errors.Errorf(
    59  					"vectorizer is immutable: " +
    60  						"attempted change from \"model1\" to \"model2\""),
    61  			},
    62  			{
    63  				name:    "attempting to modify the vector index type",
    64  				initial: &models.Class{Class: "InitialName", VectorIndexType: "hnsw"},
    65  				update:  &models.Class{Class: "InitialName", VectorIndexType: "lsh"},
    66  				expectedError: errors.Errorf(
    67  					"vector index type is immutable: " +
    68  						"attempted change from \"hnsw\" to \"lsh\""),
    69  			},
    70  			{
    71  				name:    "attempting to add a property",
    72  				initial: &models.Class{Class: "InitialName"},
    73  				update: &models.Class{
    74  					Class: "InitialName",
    75  					Properties: []*models.Property{
    76  						{
    77  							Name: "newProp",
    78  						},
    79  					},
    80  				},
    81  				expectedError: errors.Errorf(
    82  					"properties cannot be updated through updating the class. Use the add " +
    83  						"property feature (e.g. \"POST /v1/schema/{className}/properties\") " +
    84  						"to add additional properties"),
    85  			},
    86  			{
    87  				name: "leaving properties unchanged",
    88  				initial: &models.Class{
    89  					Class: "InitialName",
    90  					Properties: []*models.Property{
    91  						{
    92  							Name:     "aProp",
    93  							DataType: schema.DataTypeText.PropString(),
    94  						},
    95  					},
    96  				},
    97  				update: &models.Class{
    98  					Class: "InitialName",
    99  					Properties: []*models.Property{
   100  						{
   101  							Name:     "aProp",
   102  							DataType: schema.DataTypeText.PropString(),
   103  						},
   104  					},
   105  				},
   106  				expectedError: nil,
   107  			},
   108  			{
   109  				name: "attempting to rename a property",
   110  				initial: &models.Class{
   111  					Class: "InitialName",
   112  					Properties: []*models.Property{
   113  						{
   114  							Name:     "aProp",
   115  							DataType: schema.DataTypeText.PropString(),
   116  						},
   117  					},
   118  				},
   119  				update: &models.Class{
   120  					Class: "InitialName",
   121  					Properties: []*models.Property{
   122  						{
   123  							Name:     "changedProp",
   124  							DataType: schema.DataTypeText.PropString(),
   125  						},
   126  					},
   127  				},
   128  				expectedError: errors.Errorf(
   129  					"properties cannot be updated through updating the class. Use the add " +
   130  						"property feature (e.g. \"POST /v1/schema/{className}/properties\") " +
   131  						"to add additional properties"),
   132  			},
   133  			{
   134  				name: "attempting to update the inverted index cleanup interval",
   135  				initial: &models.Class{
   136  					Class: "InitialName",
   137  					InvertedIndexConfig: &models.InvertedIndexConfig{
   138  						CleanupIntervalSeconds: 17,
   139  					},
   140  				},
   141  				update: &models.Class{
   142  					Class: "InitialName",
   143  					InvertedIndexConfig: &models.InvertedIndexConfig{
   144  						CleanupIntervalSeconds: 18,
   145  						Bm25: &models.BM25Config{
   146  							K1: config.DefaultBM25k1,
   147  							B:  config.DefaultBM25b,
   148  						},
   149  					},
   150  				},
   151  			},
   152  			{
   153  				name: "attempting to update the inverted index BM25 config",
   154  				initial: &models.Class{
   155  					Class: "InitialName",
   156  					InvertedIndexConfig: &models.InvertedIndexConfig{
   157  						CleanupIntervalSeconds: 18,
   158  						Bm25: &models.BM25Config{
   159  							K1: 1.012,
   160  							B:  0.125,
   161  						},
   162  					},
   163  				},
   164  				update: &models.Class{
   165  					Class: "InitialName",
   166  					InvertedIndexConfig: &models.InvertedIndexConfig{
   167  						CleanupIntervalSeconds: 18,
   168  						Bm25: &models.BM25Config{
   169  							K1: 1.012,
   170  							B:  0.125,
   171  						},
   172  					},
   173  				},
   174  			},
   175  			{
   176  				name: "attempting to update the inverted index Stopwords config",
   177  				initial: &models.Class{
   178  					Class: "InitialName",
   179  					InvertedIndexConfig: &models.InvertedIndexConfig{
   180  						CleanupIntervalSeconds: 18,
   181  						Stopwords: &models.StopwordConfig{
   182  							Preset: "en",
   183  						},
   184  					},
   185  				},
   186  				update: &models.Class{
   187  					Class: "InitialName",
   188  					InvertedIndexConfig: &models.InvertedIndexConfig{
   189  						CleanupIntervalSeconds: 18,
   190  						Stopwords: &models.StopwordConfig{
   191  							Preset:    "none",
   192  							Additions: []string{"banana", "passionfruit", "kiwi"},
   193  							Removals:  []string{"a", "the"},
   194  						},
   195  					},
   196  				},
   197  			},
   198  			{
   199  				name: "attempting to update module config",
   200  				initial: &models.Class{
   201  					Class: "InitialName",
   202  					ModuleConfig: map[string]interface{}{
   203  						"my-module1": map[string]interface{}{
   204  							"my-setting": "some-value",
   205  						},
   206  					},
   207  				},
   208  				update: &models.Class{
   209  					Class: "InitialName",
   210  					ModuleConfig: map[string]interface{}{
   211  						"my-module1": map[string]interface{}{
   212  							"my-setting": "updated-value",
   213  						},
   214  					},
   215  				},
   216  				expectedError: errors.Errorf("module config is immutable"),
   217  			},
   218  			{
   219  				name: "updating vector index config",
   220  				initial: &models.Class{
   221  					Class: "InitialName",
   222  					VectorIndexConfig: map[string]interface{}{
   223  						"some-setting": "old-value",
   224  					},
   225  				},
   226  				update: &models.Class{
   227  					Class: "InitialName",
   228  					VectorIndexConfig: map[string]interface{}{
   229  						"some-setting": "new-value",
   230  					},
   231  				},
   232  				expectedError: nil,
   233  			},
   234  		}
   235  
   236  		for _, test := range tests {
   237  			t.Run(test.name, func(t *testing.T) {
   238  				sm := newSchemaManager()
   239  				assert.Nil(t, sm.AddClass(context.Background(), nil, test.initial))
   240  				err := sm.UpdateClass(context.Background(), nil, test.initial.Class, test.update)
   241  				if test.expectedError == nil {
   242  					assert.Nil(t, err)
   243  				} else {
   244  					require.NotNil(t, err, "update must error")
   245  					assert.Equal(t, test.expectedError.Error(), err.Error())
   246  				}
   247  			})
   248  		}
   249  	})
   250  
   251  	t.Run("update vector index config", func(t *testing.T) {
   252  		t.Run("with a validation error", func(t *testing.T) {
   253  			sm := newSchemaManager()
   254  			migrator := &configMigrator{
   255  				vectorConfigValidationError: errors.Errorf("don't think so!"),
   256  			}
   257  			sm.migrator = migrator
   258  
   259  			t.Run("create an initial class", func(t *testing.T) {
   260  				err := sm.AddClass(context.Background(), nil, &models.Class{
   261  					Class: "ClassWithVectorIndexConfig",
   262  					VectorIndexConfig: map[string]interface{}{
   263  						"setting-1": "value-1",
   264  					},
   265  				})
   266  
   267  				assert.Nil(t, err)
   268  			})
   269  
   270  			t.Run("attempt an update of the vector index config", func(t *testing.T) {
   271  				err := sm.UpdateClass(context.Background(), nil,
   272  					"ClassWithVectorIndexConfig", &models.Class{
   273  						Class: "ClassWithVectorIndexConfig",
   274  						VectorIndexConfig: map[string]interface{}{
   275  							"setting-1": "updated-value",
   276  						},
   277  					})
   278  				expectedErrMsg := "vector index config: don't think so!"
   279  				expectedValidateCalledWith := fakeVectorConfig{
   280  					raw: map[string]interface{}{
   281  						"distance":  "cosine",
   282  						"setting-1": "updated-value",
   283  					},
   284  				}
   285  				expectedUpdateCalled := false
   286  
   287  				require.NotNil(t, err)
   288  				assert.Equal(t, expectedErrMsg, err.Error())
   289  				assert.Equal(t, expectedValidateCalledWith, migrator.vectorConfigValidateCalledWith)
   290  				assert.Equal(t, expectedUpdateCalled, migrator.vectorConfigUpdateCalled)
   291  			})
   292  		})
   293  
   294  		t.Run("with a valid update", func(t *testing.T) {
   295  			sm := newSchemaManager()
   296  			migrator := &configMigrator{}
   297  			sm.migrator = migrator
   298  
   299  			t.Run("create an initial class", func(t *testing.T) {
   300  				err := sm.AddClass(context.Background(), nil, &models.Class{
   301  					Class: "ClassWithVectorIndexConfig",
   302  					VectorIndexConfig: map[string]interface{}{
   303  						"setting-1": "value-1",
   304  					},
   305  				})
   306  
   307  				assert.Nil(t, err)
   308  			})
   309  
   310  			t.Run("update the vector index config", func(t *testing.T) {
   311  				err := sm.UpdateClass(context.Background(), nil,
   312  					"ClassWithVectorIndexConfig", &models.Class{
   313  						Class: "ClassWithVectorIndexConfig",
   314  						VectorIndexConfig: map[string]interface{}{
   315  							"setting-1": "updated-value",
   316  						},
   317  					})
   318  				expectedValidateCalledWith := fakeVectorConfig{
   319  					raw: map[string]interface{}{
   320  						"distance":  "cosine",
   321  						"setting-1": "updated-value",
   322  					},
   323  				}
   324  				expectedUpdateCalledWith := fakeVectorConfig{
   325  					raw: map[string]interface{}{
   326  						"distance":  "cosine",
   327  						"setting-1": "updated-value",
   328  					},
   329  				}
   330  				expectedUpdateCalled := true
   331  
   332  				require.Nil(t, err)
   333  				assert.Equal(t, expectedValidateCalledWith, migrator.vectorConfigValidateCalledWith)
   334  				assert.Equal(t, expectedUpdateCalledWith, migrator.vectorConfigUpdateCalledWith)
   335  				assert.Equal(t, expectedUpdateCalled, migrator.vectorConfigUpdateCalled)
   336  			})
   337  
   338  			t.Run("the update is reflected", func(t *testing.T) {
   339  				class := sm.getClassByName("ClassWithVectorIndexConfig")
   340  				require.NotNil(t, class)
   341  				expectedVectorIndexConfig := fakeVectorConfig{
   342  					raw: map[string]interface{}{
   343  						"distance":  "cosine",
   344  						"setting-1": "updated-value",
   345  					},
   346  				}
   347  
   348  				assert.Equal(t, expectedVectorIndexConfig, class.VectorIndexConfig)
   349  			})
   350  		})
   351  	})
   352  
   353  	t.Run("update sharding config", func(t *testing.T) {
   354  		t.Run("with a validation error (immutable field)", func(t *testing.T) {
   355  			sm := newSchemaManager()
   356  			migrator := &NilMigrator{}
   357  			sm.migrator = migrator
   358  
   359  			t.Run("create an initial class", func(t *testing.T) {
   360  				err := sm.AddClass(context.Background(), nil, &models.Class{
   361  					Class: "ClassWithShardingConfig",
   362  				})
   363  
   364  				assert.Nil(t, err)
   365  			})
   366  
   367  			t.Run("attempt an update of the vector index config", func(t *testing.T) {
   368  				err := sm.UpdateClass(context.Background(), nil,
   369  					"ClassWithShardingConfig", &models.Class{
   370  						Class: "ClassWithShardingConfig",
   371  						ShardingConfig: map[string]interface{}{
   372  							"desiredCount": json.Number("7"),
   373  						},
   374  					})
   375  				expectedErrMsg := "re-sharding not supported yet: shard count is immutable: attempted change from \"1\" to \"7\""
   376  				require.NotNil(t, err)
   377  				assert.Contains(t, err.Error(), expectedErrMsg)
   378  			})
   379  		})
   380  	})
   381  }
   382  
   383  func TestClassUpdate_ValidateVectorIndexConfigs(t *testing.T) {
   384  	type testCase struct {
   385  		name           string
   386  		initial        *models.Class
   387  		updated        *models.Class
   388  		expectedErrMsg string
   389  	}
   390  
   391  	createClass := func(cfg map[string]models.VectorConfig) *models.Class {
   392  		return &models.Class{
   393  			Class:        "TargetVectors",
   394  			VectorConfig: cfg,
   395  		}
   396  	}
   397  
   398  	vcFlatContextionary := models.VectorConfig{
   399  		VectorIndexType: "flat",
   400  		Vectorizer: map[string]interface{}{
   401  			"text2vec-contextionary": "some-settings",
   402  		},
   403  		VectorIndexConfig: map[string]interface{}{
   404  			"setting-flat": "value-flat",
   405  		},
   406  	}
   407  	vcHnswContextionary := models.VectorConfig{
   408  		VectorIndexType: "hnsw",
   409  		Vectorizer: map[string]interface{}{
   410  			"text2vec-contextionary": "some-settings",
   411  		},
   412  		VectorIndexConfig: map[string]interface{}{
   413  			"setting-hnsw": "value-hnsw",
   414  		},
   415  	}
   416  
   417  	_ = vcFlatContextionary
   418  	_ = vcHnswContextionary
   419  
   420  	testCases := []testCase{
   421  		{
   422  			name:           "same settings with nil vectors config",
   423  			initial:        createClass(nil),
   424  			updated:        createClass(nil),
   425  			expectedErrMsg: "",
   426  		},
   427  		{
   428  			name:           "same settings with nil+empty vectors config",
   429  			initial:        createClass(nil),
   430  			updated:        createClass(map[string]models.VectorConfig{}),
   431  			expectedErrMsg: "",
   432  		},
   433  		{
   434  			name:           "same settings with empty+nil vectors config",
   435  			initial:        createClass(map[string]models.VectorConfig{}),
   436  			updated:        createClass(nil),
   437  			expectedErrMsg: "",
   438  		},
   439  		{
   440  			name:           "same settings with empty vectors config",
   441  			initial:        createClass(map[string]models.VectorConfig{}),
   442  			updated:        createClass(map[string]models.VectorConfig{}),
   443  			expectedErrMsg: "",
   444  		},
   445  		{
   446  			name: "same settings with single vector",
   447  			initial: createClass(map[string]models.VectorConfig{
   448  				"vector": vcFlatContextionary,
   449  			}),
   450  			updated: createClass(map[string]models.VectorConfig{
   451  				"vector": vcFlatContextionary,
   452  			}),
   453  			expectedErrMsg: "",
   454  		},
   455  		{
   456  			name: "same settings with multi vectors",
   457  			initial: createClass(map[string]models.VectorConfig{
   458  				"vector1": vcFlatContextionary,
   459  				"vector2": vcHnswContextionary,
   460  			}),
   461  			updated: createClass(map[string]models.VectorConfig{
   462  				"vector2": vcHnswContextionary,
   463  				"vector1": vcFlatContextionary,
   464  			}),
   465  			expectedErrMsg: "",
   466  		},
   467  		{
   468  			name:    "no initial vectors",
   469  			initial: createClass(nil),
   470  			updated: createClass(map[string]models.VectorConfig{
   471  				"vector2": vcHnswContextionary,
   472  				"vector1": vcFlatContextionary,
   473  			}),
   474  			expectedErrMsg: "additional configs for vectors",
   475  		},
   476  		{
   477  			name: "no updated vectors",
   478  			initial: createClass(map[string]models.VectorConfig{
   479  				"vector1": vcFlatContextionary,
   480  				"vector2": vcHnswContextionary,
   481  			}),
   482  			updated:        createClass(nil),
   483  			expectedErrMsg: "missing configs for vectors",
   484  		},
   485  		{
   486  			name: "more updated vectors",
   487  			initial: createClass(map[string]models.VectorConfig{
   488  				"vector1": vcFlatContextionary,
   489  			}),
   490  			updated: createClass(map[string]models.VectorConfig{
   491  				"vector2": vcHnswContextionary,
   492  				"vector1": vcFlatContextionary,
   493  			}),
   494  			expectedErrMsg: "additional config for vector \"vector2\"",
   495  		},
   496  		{
   497  			name: "more initial vectors",
   498  			initial: createClass(map[string]models.VectorConfig{
   499  				"vector1": vcFlatContextionary,
   500  				"vector2": vcHnswContextionary,
   501  			}),
   502  			updated: createClass(map[string]models.VectorConfig{
   503  				"vector1": vcFlatContextionary,
   504  			}),
   505  			expectedErrMsg: "missing config for vector \"vector2\"",
   506  		},
   507  		{
   508  			name: "index type changed",
   509  			initial: createClass(map[string]models.VectorConfig{
   510  				"vector1": vcFlatContextionary,
   511  				"vector2": vcHnswContextionary,
   512  			}),
   513  			updated: createClass(map[string]models.VectorConfig{
   514  				"vector1": vcFlatContextionary,
   515  				"vector2": vcFlatContextionary,
   516  			}),
   517  			expectedErrMsg: "vector index type of vector \"vector2\" is immutable: attempted change from \"hnsw\" to \"flat\"",
   518  		},
   519  		{
   520  			name: "vectorizer changed",
   521  			initial: createClass(map[string]models.VectorConfig{
   522  				"vector1": vcFlatContextionary,
   523  			}),
   524  			updated: createClass(map[string]models.VectorConfig{
   525  				"vector1": {
   526  					VectorIndexType: "flat",
   527  					Vectorizer: map[string]interface{}{
   528  						"not-contextionary": "some-settings",
   529  					},
   530  				},
   531  			}),
   532  			expectedErrMsg: "vectorizer of vector \"vector1\" is immutable: attempted change from \"text2vec-contextionary\" to \"not-contextionary\"",
   533  		},
   534  		{
   535  			name: "vectorizer config not map",
   536  			initial: createClass(map[string]models.VectorConfig{
   537  				"vector1": vcFlatContextionary,
   538  			}),
   539  			updated: createClass(map[string]models.VectorConfig{
   540  				"vector1": {
   541  					VectorIndexType: "flat",
   542  					Vectorizer:      "not-map",
   543  				},
   544  			}),
   545  			expectedErrMsg: "invalid vectorizer config for vector \"vector1\"",
   546  		},
   547  		{
   548  			name: "vectorizer config multiple keys",
   549  			initial: createClass(map[string]models.VectorConfig{
   550  				"vector1": vcFlatContextionary,
   551  			}),
   552  			updated: createClass(map[string]models.VectorConfig{
   553  				"vector1": {
   554  					VectorIndexType: "flat",
   555  					Vectorizer: map[string]interface{}{
   556  						"text2vec-contextionary": "some-settings",
   557  						"additional-key":         "value",
   558  					},
   559  				},
   560  			}),
   561  			expectedErrMsg: "invalid vectorizer config for vector \"vector1\"",
   562  		},
   563  	}
   564  
   565  	t.Run("validation only", func(t *testing.T) {
   566  		for _, tc := range testCases {
   567  			t.Run(tc.name, func(t *testing.T) {
   568  				err := validateVectorConfigsParityAndImmutables(tc.initial, tc.updated)
   569  
   570  				if tc.expectedErrMsg == "" {
   571  					assert.NoError(t, err)
   572  				} else {
   573  					assert.ErrorContains(t, err, tc.expectedErrMsg)
   574  				}
   575  			})
   576  		}
   577  	})
   578  
   579  	t.Run("full update", func(t *testing.T) {
   580  		ctx := context.Background()
   581  
   582  		for _, tc := range testCases {
   583  			t.Run(tc.name, func(t *testing.T) {
   584  				sm := newSchemaManager()
   585  				m := &configMigrator{}
   586  				sm.migrator = m
   587  
   588  				err := sm.AddClass(ctx, nil, tc.initial)
   589  				require.NoError(t, err)
   590  
   591  				err = sm.UpdateClass(ctx, nil, tc.updated.Class, tc.updated)
   592  
   593  				if tc.expectedErrMsg == "" {
   594  					assert.NoError(t, err)
   595  				} else {
   596  					assert.ErrorContains(t, err, tc.expectedErrMsg)
   597  				}
   598  
   599  				// migrator's validation and update are called only for configured target vectors
   600  				if tc.expectedErrMsg == "" && len(tc.updated.VectorConfig) > 0 {
   601  					cfgs := map[string]schema.VectorIndexConfig{}
   602  					for vecName, vecCfg := range tc.updated.VectorConfig {
   603  						cfgs[vecName] = vecCfg.VectorIndexConfig.(schema.VectorIndexConfig)
   604  					}
   605  
   606  					assert.True(t, m.vectorConfigsUpdateCalled)
   607  					assert.Equal(t, cfgs, m.vectorConfigsValidateCalledWith)
   608  					assert.Equal(t, cfgs, m.vectorConfigsUpdateCalledWith)
   609  				} else {
   610  					assert.False(t, m.vectorConfigsUpdateCalled)
   611  					assert.Nil(t, m.vectorConfigsValidateCalledWith)
   612  					assert.Nil(t, m.vectorConfigsUpdateCalledWith)
   613  				}
   614  			})
   615  		}
   616  	})
   617  }
   618  
   619  type configMigrator struct {
   620  	NilMigrator
   621  	vectorConfigValidationError     error
   622  	vectorConfigValidateCalledWith  schema.VectorIndexConfig
   623  	vectorConfigUpdateCalled        bool
   624  	vectorConfigUpdateCalledWith    schema.VectorIndexConfig
   625  	vectorConfigsValidationError    error
   626  	vectorConfigsValidateCalledWith map[string]schema.VectorIndexConfig
   627  	vectorConfigsUpdateCalled       bool
   628  	vectorConfigsUpdateCalledWith   map[string]schema.VectorIndexConfig
   629  }
   630  
   631  func (m *configMigrator) ValidateVectorIndexConfigUpdate(ctx context.Context,
   632  	old, updated schema.VectorIndexConfig,
   633  ) error {
   634  	m.vectorConfigValidateCalledWith = updated
   635  	return m.vectorConfigValidationError
   636  }
   637  
   638  func (m *configMigrator) UpdateVectorIndexConfig(ctx context.Context,
   639  	className string, updated schema.VectorIndexConfig,
   640  ) error {
   641  	m.vectorConfigUpdateCalledWith = updated
   642  	m.vectorConfigUpdateCalled = true
   643  	return nil
   644  }
   645  
   646  func (m *configMigrator) ValidateVectorIndexConfigsUpdate(ctx context.Context,
   647  	old, updated map[string]schema.VectorIndexConfig,
   648  ) error {
   649  	m.vectorConfigsValidateCalledWith = updated
   650  	return m.vectorConfigsValidationError
   651  }
   652  
   653  func (m *configMigrator) UpdateVectorIndexConfigs(ctx context.Context,
   654  	className string, updated map[string]schema.VectorIndexConfig,
   655  ) error {
   656  	m.vectorConfigsUpdateCalledWith = updated
   657  	m.vectorConfigsUpdateCalled = true
   658  	return nil
   659  }