github.com/weaviate/weaviate@v1.24.6/usecases/objects/fakes_for_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 objects
    13  
    14  import (
    15  	"context"
    16  	"fmt"
    17  	"net/http"
    18  
    19  	"github.com/go-openapi/strfmt"
    20  	"github.com/pkg/errors"
    21  	"github.com/sirupsen/logrus"
    22  	"github.com/stretchr/testify/mock"
    23  	"github.com/tailor-inc/graphql"
    24  	"github.com/tailor-inc/graphql/language/ast"
    25  	"github.com/weaviate/weaviate/adapters/handlers/graphql/descriptions"
    26  	"github.com/weaviate/weaviate/entities/additional"
    27  	"github.com/weaviate/weaviate/entities/filters"
    28  	"github.com/weaviate/weaviate/entities/models"
    29  	"github.com/weaviate/weaviate/entities/modulecapabilities"
    30  	"github.com/weaviate/weaviate/entities/moduletools"
    31  	"github.com/weaviate/weaviate/entities/schema"
    32  	"github.com/weaviate/weaviate/entities/schema/crossref"
    33  	"github.com/weaviate/weaviate/entities/search"
    34  	"github.com/weaviate/weaviate/entities/vectorindex/hnsw"
    35  )
    36  
    37  const FindObjectFn = "func(context.Context, string, strfmt.UUID, " +
    38  	"search.SelectProperties, additional.Properties, string) (*search.Result, error)"
    39  
    40  type fakeSchemaManager struct {
    41  	CalledWith struct {
    42  		fromClass string
    43  		property  string
    44  		toClass   string
    45  	}
    46  	GetSchemaResponse schema.Schema
    47  	GetschemaErr      error
    48  }
    49  
    50  func (f *fakeSchemaManager) UpdatePropertyAddDataType(ctx context.Context, principal *models.Principal,
    51  	fromClass, property, toClass string,
    52  ) error {
    53  	f.CalledWith = struct {
    54  		fromClass string
    55  		property  string
    56  		toClass   string
    57  	}{
    58  		fromClass: fromClass,
    59  		property:  property,
    60  		toClass:   toClass,
    61  	}
    62  	return nil
    63  }
    64  
    65  func (f *fakeSchemaManager) GetSchema(principal *models.Principal) (schema.Schema, error) {
    66  	return f.GetSchemaResponse, f.GetschemaErr
    67  }
    68  
    69  func (f *fakeSchemaManager) ShardOwner(class, shard string) (string, error) { return "", nil }
    70  func (f *fakeSchemaManager) TenantShard(class, tenant string) string        { return tenant }
    71  func (f *fakeSchemaManager) ShardFromUUID(class string, uuid []byte) string { return "" }
    72  
    73  func (f *fakeSchemaManager) GetClass(ctx context.Context, principal *models.Principal,
    74  	name string,
    75  ) (*models.Class, error) {
    76  	classes := f.GetSchemaResponse.Objects.Classes
    77  	for _, class := range classes {
    78  		if class.Class == name {
    79  			return class, f.GetschemaErr
    80  		}
    81  	}
    82  	return nil, f.GetschemaErr
    83  }
    84  
    85  func (f *fakeSchemaManager) AddClass(ctx context.Context, principal *models.Principal,
    86  	class *models.Class,
    87  ) error {
    88  	if f.GetSchemaResponse.Objects == nil {
    89  		f.GetSchemaResponse.Objects = schema.Empty().Objects
    90  	}
    91  	class.VectorIndexConfig = hnsw.UserConfig{}
    92  	class.VectorIndexType = "hnsw"
    93  	class.Vectorizer = "none"
    94  	classes := f.GetSchemaResponse.Objects.Classes
    95  	if classes != nil {
    96  		classes = append(classes, class)
    97  	} else {
    98  		classes = []*models.Class{class}
    99  	}
   100  	f.GetSchemaResponse.Objects.Classes = classes
   101  	return nil
   102  }
   103  
   104  func (f *fakeSchemaManager) AddClassProperty(ctx context.Context, principal *models.Principal,
   105  	class string, property *models.Property,
   106  ) error {
   107  	classes := f.GetSchemaResponse.Objects.Classes
   108  	for _, c := range classes {
   109  		if c.Class == class {
   110  			props := c.Properties
   111  			if props != nil {
   112  				props = append(props, property)
   113  			} else {
   114  				props = []*models.Property{property}
   115  			}
   116  			c.Properties = props
   117  			break
   118  		}
   119  	}
   120  	return nil
   121  }
   122  
   123  func (f *fakeSchemaManager) MergeClassObjectProperty(ctx context.Context, principal *models.Principal,
   124  	class string, property *models.Property,
   125  ) error {
   126  	classes := f.GetSchemaResponse.Objects.Classes
   127  	for _, c := range classes {
   128  		if c.Class == class {
   129  			for i, prop := range c.Properties {
   130  				if prop.Name == property.Name {
   131  					c.Properties[i].NestedProperties, _ = schema.MergeRecursivelyNestedProperties(
   132  						c.Properties[i].NestedProperties, property.NestedProperties)
   133  					break
   134  				}
   135  			}
   136  			break
   137  		}
   138  	}
   139  	return nil
   140  }
   141  
   142  type fakeLocks struct {
   143  	Err error
   144  }
   145  
   146  func (f *fakeLocks) LockConnector() (func() error, error) {
   147  	return func() error { return nil }, f.Err
   148  }
   149  
   150  func (f *fakeLocks) LockSchema() (func() error, error) {
   151  	return func() error { return nil }, f.Err
   152  }
   153  
   154  type fakeAuthorizer struct {
   155  	Err error
   156  }
   157  
   158  func (f *fakeAuthorizer) Authorize(principal *models.Principal, verb, resource string) error {
   159  	return f.Err
   160  }
   161  
   162  type fakeVectorRepo struct {
   163  	mock.Mock
   164  }
   165  
   166  func (f *fakeVectorRepo) Exists(ctx context.Context, class string, id strfmt.UUID, repl *additional.ReplicationProperties, tenant string) (bool, error) {
   167  	args := f.Called(class, id)
   168  	return args.Bool(0), args.Error(1)
   169  }
   170  
   171  func (f *fakeVectorRepo) Object(ctx context.Context, cls string, id strfmt.UUID,
   172  	props search.SelectProperties, additional additional.Properties,
   173  	repl *additional.ReplicationProperties, tenant string,
   174  ) (*search.Result, error) {
   175  	args := f.Called(cls, id, props, additional, tenant)
   176  	if args.Get(0) != nil {
   177  		return args.Get(0).(*search.Result), args.Error(1)
   178  	}
   179  	return nil, args.Error(1)
   180  }
   181  
   182  func (f *fakeVectorRepo) ObjectByID(ctx context.Context, id strfmt.UUID,
   183  	props search.SelectProperties, additional additional.Properties,
   184  	tenant string,
   185  ) (*search.Result, error) {
   186  	args := f.Called(id, props, additional)
   187  	if args.Get(0) != nil {
   188  		return args.Get(0).(*search.Result), args.Error(1)
   189  	}
   190  	return nil, args.Error(1)
   191  }
   192  
   193  func (f *fakeVectorRepo) ObjectSearch(ctx context.Context, offset, limit int, filters *filters.LocalFilter,
   194  	sort []filters.Sort, additional additional.Properties, tenant string,
   195  ) (search.Results, error) {
   196  	args := f.Called(offset, limit, sort, filters, additional)
   197  	return args.Get(0).([]search.Result), args.Error(1)
   198  }
   199  
   200  func (f *fakeVectorRepo) Query(ctx context.Context, q *QueryInput) (search.Results, *Error) {
   201  	args := f.Called(q)
   202  	res, err := args.Get(0).([]search.Result), args.Error(1).(*Error)
   203  	return res, err
   204  }
   205  
   206  func (f *fakeVectorRepo) PutObject(ctx context.Context, concept *models.Object, vector []float32,
   207  	vectors models.Vectors, repl *additional.ReplicationProperties,
   208  ) error {
   209  	args := f.Called(concept, vector)
   210  	return args.Error(0)
   211  }
   212  
   213  func (f *fakeVectorRepo) BatchPutObjects(ctx context.Context, batch BatchObjects,
   214  	repl *additional.ReplicationProperties,
   215  ) (BatchObjects, error) {
   216  	args := f.Called(batch)
   217  	return batch, args.Error(0)
   218  }
   219  
   220  func (f *fakeVectorRepo) AddBatchReferences(ctx context.Context, batch BatchReferences,
   221  	repl *additional.ReplicationProperties,
   222  ) (BatchReferences, error) {
   223  	args := f.Called(batch)
   224  	return batch, args.Error(0)
   225  }
   226  
   227  func (f *fakeVectorRepo) BatchDeleteObjects(ctx context.Context, params BatchDeleteParams,
   228  	repl *additional.ReplicationProperties, tenant string,
   229  ) (BatchDeleteResult, error) {
   230  	args := f.Called(params)
   231  	return args.Get(0).(BatchDeleteResult), args.Error(1)
   232  }
   233  
   234  func (f *fakeVectorRepo) Merge(ctx context.Context, merge MergeDocument, repl *additional.ReplicationProperties, tenant string) error {
   235  	args := f.Called(merge)
   236  	return args.Error(0)
   237  }
   238  
   239  func (f *fakeVectorRepo) DeleteObject(ctx context.Context, className string,
   240  	id strfmt.UUID, repl *additional.ReplicationProperties, tenant string,
   241  ) error {
   242  	args := f.Called(className, id)
   243  	return args.Error(0)
   244  }
   245  
   246  func (f *fakeVectorRepo) AddReference(ctx context.Context, source *crossref.RefSource,
   247  	target *crossref.Ref, repl *additional.ReplicationProperties, tenant string,
   248  ) error {
   249  	args := f.Called(source, target)
   250  	return args.Error(0)
   251  }
   252  
   253  func (f *fakeVectorRepo) ReferenceVectorSearch(ctx context.Context,
   254  	obj *models.Object, refProps map[string]struct{},
   255  ) ([][]float32, error) {
   256  	return nil, nil
   257  }
   258  
   259  type fakeExtender struct {
   260  	multi []search.Result
   261  }
   262  
   263  func (f *fakeExtender) AdditionalPropertyFn(ctx context.Context,
   264  	in []search.Result, params interface{}, limit *int,
   265  	argumentModuleParams map[string]interface{}, cfg moduletools.ClassConfig,
   266  ) ([]search.Result, error) {
   267  	return f.multi, nil
   268  }
   269  
   270  func (f *fakeExtender) ExtractAdditionalFn(param []*ast.Argument) interface{} {
   271  	return nil
   272  }
   273  
   274  func (f *fakeExtender) AdditionalPropertyDefaultValue() interface{} {
   275  	return getDefaultParam("nearestNeighbors")
   276  }
   277  
   278  type fakeProjector struct {
   279  	multi []search.Result
   280  }
   281  
   282  func (f *fakeProjector) AdditionalPropertyFn(ctx context.Context,
   283  	in []search.Result, params interface{}, limit *int,
   284  	argumentModuleParams map[string]interface{}, cfg moduletools.ClassConfig,
   285  ) ([]search.Result, error) {
   286  	return f.multi, nil
   287  }
   288  
   289  func (f *fakeProjector) ExtractAdditionalFn(param []*ast.Argument) interface{} {
   290  	return nil
   291  }
   292  
   293  func (f *fakeProjector) AdditionalPropertyDefaultValue() interface{} {
   294  	return getDefaultParam("featureProjection")
   295  }
   296  
   297  type fakePathBuilder struct {
   298  	multi []search.Result
   299  }
   300  
   301  func (f *fakePathBuilder) AdditionalPropertyFn(ctx context.Context,
   302  	in []search.Result, params interface{}, limit *int,
   303  	argumentModuleParams map[string]interface{}, cfg moduletools.ClassConfig,
   304  ) ([]search.Result, error) {
   305  	return f.multi, nil
   306  }
   307  
   308  func (f *fakePathBuilder) ExtractAdditionalFn(param []*ast.Argument) interface{} {
   309  	return nil
   310  }
   311  
   312  func (f *fakePathBuilder) AdditionalPropertyDefaultValue() interface{} {
   313  	return getDefaultParam("semanticPath")
   314  }
   315  
   316  type fakeModulesProvider struct {
   317  	mock.Mock
   318  	customExtender  *fakeExtender
   319  	customProjector *fakeProjector
   320  }
   321  
   322  func (p *fakeModulesProvider) GetObjectAdditionalExtend(ctx context.Context,
   323  	in *search.Result, moduleParams map[string]interface{},
   324  ) (*search.Result, error) {
   325  	res, err := p.additionalExtend(ctx, search.Results{*in}, moduleParams, "ObjectGet")
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	return &res[0], nil
   330  }
   331  
   332  func (p *fakeModulesProvider) ListObjectsAdditionalExtend(ctx context.Context,
   333  	in search.Results, moduleParams map[string]interface{},
   334  ) (search.Results, error) {
   335  	return p.additionalExtend(ctx, in, moduleParams, "ObjectList")
   336  }
   337  
   338  func (p *fakeModulesProvider) UsingRef2Vec(moduleName string) bool {
   339  	args := p.Called(moduleName)
   340  	return args.Bool(0)
   341  }
   342  
   343  func (p *fakeModulesProvider) UpdateVector(ctx context.Context, object *models.Object, class *models.Class,
   344  	compFactory moduletools.PropsComparatorFactory, findObjFn modulecapabilities.FindObjectFn, logger logrus.FieldLogger,
   345  ) error {
   346  	args := p.Called(object, findObjFn)
   347  	switch vec := args.Get(0).(type) {
   348  	case models.C11yVector:
   349  		object.Vector = vec
   350  		return args.Error(1)
   351  	case []float32:
   352  		object.Vector = vec
   353  		return args.Error(1)
   354  	default:
   355  		return args.Error(1)
   356  	}
   357  }
   358  
   359  func (p *fakeModulesProvider) VectorizerName(className string) (string, error) {
   360  	args := p.Called(className)
   361  	return args.String(0), args.Error(1)
   362  }
   363  
   364  func (p *fakeModulesProvider) additionalExtend(ctx context.Context,
   365  	in search.Results, moduleParams map[string]interface{}, capability string,
   366  ) (search.Results, error) {
   367  	txt2vec := newNearCustomTextModule(p.getExtender(), p.getProjector(), &fakePathBuilder{})
   368  	additionalProperties := txt2vec.AdditionalProperties()
   369  	if err := p.checkCapabilities(additionalProperties, moduleParams, capability); err != nil {
   370  		return nil, err
   371  	}
   372  	for name, value := range moduleParams {
   373  		additionalPropertyFn := p.getAdditionalPropertyFn(additionalProperties[name], capability)
   374  		if additionalPropertyFn != nil && value != nil {
   375  			resArray, err := additionalPropertyFn(ctx, in, nil, nil, nil, nil)
   376  			if err != nil {
   377  				return nil, err
   378  			}
   379  			in = resArray
   380  		}
   381  	}
   382  	return in, nil
   383  }
   384  
   385  func (p *fakeModulesProvider) checkCapabilities(additionalProperties map[string]modulecapabilities.AdditionalProperty,
   386  	moduleParams map[string]interface{}, capability string,
   387  ) error {
   388  	for name := range moduleParams {
   389  		additionalPropertyFn := p.getAdditionalPropertyFn(additionalProperties[name], capability)
   390  		if additionalPropertyFn == nil {
   391  			return errors.Errorf("unknown capability: %s", name)
   392  		}
   393  	}
   394  	return nil
   395  }
   396  
   397  func (p *fakeModulesProvider) getAdditionalPropertyFn(additionalProperty modulecapabilities.AdditionalProperty,
   398  	capability string,
   399  ) modulecapabilities.AdditionalPropertyFn {
   400  	switch capability {
   401  	case "ObjectGet":
   402  		return additionalProperty.SearchFunctions.ObjectGet
   403  	case "ObjectList":
   404  		return additionalProperty.SearchFunctions.ObjectList
   405  	case "ExploreGet":
   406  		return additionalProperty.SearchFunctions.ExploreGet
   407  	case "ExploreList":
   408  		return additionalProperty.SearchFunctions.ExploreList
   409  	default:
   410  		return nil
   411  	}
   412  }
   413  
   414  func (p *fakeModulesProvider) getExtender() *fakeExtender {
   415  	if p.customExtender != nil {
   416  		return p.customExtender
   417  	}
   418  	return &fakeExtender{}
   419  }
   420  
   421  func (p *fakeModulesProvider) getProjector() *fakeProjector {
   422  	if p.customProjector != nil {
   423  		return p.customProjector
   424  	}
   425  	return &fakeProjector{}
   426  }
   427  
   428  type nearCustomTextParams struct {
   429  	Values    []string
   430  	MoveTo    nearExploreMove
   431  	Certainty float64
   432  }
   433  
   434  type nearExploreMove struct {
   435  	Values  []string
   436  	Force   float32
   437  	Objects []nearObjectMove
   438  }
   439  
   440  type nearObjectMove struct {
   441  	ID     string
   442  	Beacon string
   443  }
   444  
   445  type nearCustomTextModule struct {
   446  	fakeExtender    *fakeExtender
   447  	fakeProjector   *fakeProjector
   448  	fakePathBuilder *fakePathBuilder
   449  }
   450  
   451  func newNearCustomTextModule(
   452  	fakeExtender *fakeExtender,
   453  	fakeProjector *fakeProjector,
   454  	fakePathBuilder *fakePathBuilder,
   455  ) *nearCustomTextModule {
   456  	return &nearCustomTextModule{fakeExtender, fakeProjector, fakePathBuilder}
   457  }
   458  
   459  func (m *nearCustomTextModule) Name() string {
   460  	return "mock-custom-near-text-module"
   461  }
   462  
   463  func (m *nearCustomTextModule) Init(params moduletools.ModuleInitParams) error {
   464  	return nil
   465  }
   466  
   467  func (m *nearCustomTextModule) RootHandler() http.Handler {
   468  	return nil
   469  }
   470  
   471  func (m *nearCustomTextModule) getNearCustomTextArgument(classname string) *graphql.ArgumentConfig {
   472  	prefix := classname
   473  	return &graphql.ArgumentConfig{
   474  		Type: graphql.NewInputObject(
   475  			graphql.InputObjectConfig{
   476  				Name: fmt.Sprintf("%sNearCustomTextInpObj", prefix),
   477  				Fields: graphql.InputObjectConfigFieldMap{
   478  					"concepts": &graphql.InputObjectFieldConfig{
   479  						Type: graphql.NewNonNull(graphql.NewList(graphql.String)),
   480  					},
   481  					"moveTo": &graphql.InputObjectFieldConfig{
   482  						Description: descriptions.VectorMovement,
   483  						Type: graphql.NewInputObject(
   484  							graphql.InputObjectConfig{
   485  								Name: fmt.Sprintf("%sMoveTo", prefix),
   486  								Fields: graphql.InputObjectConfigFieldMap{
   487  									"concepts": &graphql.InputObjectFieldConfig{
   488  										Description: descriptions.Keywords,
   489  										Type:        graphql.NewList(graphql.String),
   490  									},
   491  									"objects": &graphql.InputObjectFieldConfig{
   492  										Description: "objects",
   493  										Type: graphql.NewList(graphql.NewInputObject(
   494  											graphql.InputObjectConfig{
   495  												Name: fmt.Sprintf("%sMovementObjectsInpObj", prefix),
   496  												Fields: graphql.InputObjectConfigFieldMap{
   497  													"id": &graphql.InputObjectFieldConfig{
   498  														Type:        graphql.String,
   499  														Description: "id of an object",
   500  													},
   501  													"beacon": &graphql.InputObjectFieldConfig{
   502  														Type:        graphql.String,
   503  														Description: descriptions.Beacon,
   504  													},
   505  												},
   506  												Description: "Movement Object",
   507  											},
   508  										)),
   509  									},
   510  									"force": &graphql.InputObjectFieldConfig{
   511  										Description: descriptions.Force,
   512  										Type:        graphql.NewNonNull(graphql.Float),
   513  									},
   514  								},
   515  							}),
   516  					},
   517  					"certainty": &graphql.InputObjectFieldConfig{
   518  						Description: descriptions.Certainty,
   519  						Type:        graphql.Float,
   520  					},
   521  				},
   522  				Description: descriptions.GetWhereInpObj,
   523  			},
   524  		),
   525  	}
   526  }
   527  
   528  func (m *nearCustomTextModule) extractNearCustomTextArgument(source map[string]interface{}) *nearCustomTextParams {
   529  	var args nearCustomTextParams
   530  
   531  	concepts := source["concepts"].([]interface{})
   532  	args.Values = make([]string, len(concepts))
   533  	for i, value := range concepts {
   534  		args.Values[i] = value.(string)
   535  	}
   536  
   537  	certainty, ok := source["certainty"]
   538  	if ok {
   539  		args.Certainty = certainty.(float64)
   540  	}
   541  
   542  	// moveTo is an optional arg, so it could be nil
   543  	moveTo, ok := source["moveTo"]
   544  	if ok {
   545  		moveToMap := moveTo.(map[string]interface{})
   546  		res := nearExploreMove{}
   547  		res.Force = float32(moveToMap["force"].(float64))
   548  
   549  		concepts, ok := moveToMap["concepts"].([]interface{})
   550  		if ok {
   551  			res.Values = make([]string, len(concepts))
   552  			for i, value := range concepts {
   553  				res.Values[i] = value.(string)
   554  			}
   555  		}
   556  
   557  		objects, ok := moveToMap["objects"].([]interface{})
   558  		if ok {
   559  			res.Objects = make([]nearObjectMove, len(objects))
   560  			for i, value := range objects {
   561  				v, ok := value.(map[string]interface{})
   562  				if ok {
   563  					if v["id"] != nil {
   564  						res.Objects[i].ID = v["id"].(string)
   565  					}
   566  					if v["beacon"] != nil {
   567  						res.Objects[i].Beacon = v["beacon"].(string)
   568  					}
   569  				}
   570  			}
   571  		}
   572  
   573  		args.MoveTo = res
   574  	}
   575  
   576  	return &args
   577  }
   578  
   579  func (m *nearCustomTextModule) Arguments() map[string]modulecapabilities.GraphQLArgument {
   580  	arguments := map[string]modulecapabilities.GraphQLArgument{}
   581  	// define nearCustomText argument
   582  	arguments["nearCustomText"] = modulecapabilities.GraphQLArgument{
   583  		GetArgumentsFunction: func(classname string) *graphql.ArgumentConfig {
   584  			return m.getNearCustomTextArgument(classname)
   585  		},
   586  		ExtractFunction: func(source map[string]interface{}) interface{} {
   587  			return m.extractNearCustomTextArgument(source)
   588  		},
   589  		ValidateFunction: func(param interface{}) error {
   590  			// all is valid
   591  			return nil
   592  		},
   593  	}
   594  	return arguments
   595  }
   596  
   597  // additional properties
   598  func (m *nearCustomTextModule) AdditionalProperties() map[string]modulecapabilities.AdditionalProperty {
   599  	additionalProperties := map[string]modulecapabilities.AdditionalProperty{}
   600  	additionalProperties["featureProjection"] = m.getFeatureProjection()
   601  	additionalProperties["nearestNeighbors"] = m.getNearestNeighbors()
   602  	additionalProperties["semanticPath"] = m.getSemanticPath()
   603  	return additionalProperties
   604  }
   605  
   606  func (m *nearCustomTextModule) getFeatureProjection() modulecapabilities.AdditionalProperty {
   607  	return modulecapabilities.AdditionalProperty{
   608  		DefaultValue: m.fakeProjector.AdditionalPropertyDefaultValue(),
   609  		GraphQLNames: []string{"featureProjection"},
   610  		GraphQLFieldFunction: func(classname string) *graphql.Field {
   611  			return &graphql.Field{
   612  				Args: graphql.FieldConfigArgument{
   613  					"algorithm": &graphql.ArgumentConfig{
   614  						Type:         graphql.String,
   615  						DefaultValue: nil,
   616  					},
   617  					"dimensions": &graphql.ArgumentConfig{
   618  						Type:         graphql.Int,
   619  						DefaultValue: nil,
   620  					},
   621  					"learningRate": &graphql.ArgumentConfig{
   622  						Type:         graphql.Int,
   623  						DefaultValue: nil,
   624  					},
   625  					"iterations": &graphql.ArgumentConfig{
   626  						Type:         graphql.Int,
   627  						DefaultValue: nil,
   628  					},
   629  					"perplexity": &graphql.ArgumentConfig{
   630  						Type:         graphql.Int,
   631  						DefaultValue: nil,
   632  					},
   633  				},
   634  				Type: graphql.NewObject(graphql.ObjectConfig{
   635  					Name: fmt.Sprintf("%sAdditionalFeatureProjection", classname),
   636  					Fields: graphql.Fields{
   637  						"vector": &graphql.Field{Type: graphql.NewList(graphql.Float)},
   638  					},
   639  				}),
   640  			}
   641  		},
   642  		GraphQLExtractFunction: m.fakeProjector.ExtractAdditionalFn,
   643  		SearchFunctions: modulecapabilities.AdditionalSearch{
   644  			ObjectList:  m.fakeProjector.AdditionalPropertyFn,
   645  			ExploreGet:  m.fakeProjector.AdditionalPropertyFn,
   646  			ExploreList: m.fakeProjector.AdditionalPropertyFn,
   647  		},
   648  	}
   649  }
   650  
   651  func (m *nearCustomTextModule) getNearestNeighbors() modulecapabilities.AdditionalProperty {
   652  	return modulecapabilities.AdditionalProperty{
   653  		DefaultValue: m.fakeExtender.AdditionalPropertyDefaultValue(),
   654  		GraphQLNames: []string{"nearestNeighbors"},
   655  		GraphQLFieldFunction: func(classname string) *graphql.Field {
   656  			return &graphql.Field{
   657  				Type: graphql.NewObject(graphql.ObjectConfig{
   658  					Name: fmt.Sprintf("%sAdditionalNearestNeighbors", classname),
   659  					Fields: graphql.Fields{
   660  						"neighbors": &graphql.Field{Type: graphql.NewList(graphql.NewObject(graphql.ObjectConfig{
   661  							Name: fmt.Sprintf("%sAdditionalNearestNeighborsNeighbors", classname),
   662  							Fields: graphql.Fields{
   663  								"concept":  &graphql.Field{Type: graphql.String},
   664  								"distance": &graphql.Field{Type: graphql.Float},
   665  							},
   666  						}))},
   667  					},
   668  				}),
   669  			}
   670  		},
   671  		GraphQLExtractFunction: m.fakeExtender.ExtractAdditionalFn,
   672  		SearchFunctions: modulecapabilities.AdditionalSearch{
   673  			ObjectGet:   m.fakeExtender.AdditionalPropertyFn,
   674  			ObjectList:  m.fakeExtender.AdditionalPropertyFn,
   675  			ExploreGet:  m.fakeExtender.AdditionalPropertyFn,
   676  			ExploreList: m.fakeExtender.AdditionalPropertyFn,
   677  		},
   678  	}
   679  }
   680  
   681  func (m *nearCustomTextModule) getSemanticPath() modulecapabilities.AdditionalProperty {
   682  	return modulecapabilities.AdditionalProperty{
   683  		DefaultValue: m.fakePathBuilder.AdditionalPropertyDefaultValue(),
   684  		GraphQLNames: []string{"semanticPath"},
   685  		GraphQLFieldFunction: func(classname string) *graphql.Field {
   686  			return &graphql.Field{
   687  				Type: graphql.NewObject(graphql.ObjectConfig{
   688  					Name: fmt.Sprintf("%sAdditionalSemanticPath", classname),
   689  					Fields: graphql.Fields{
   690  						"path": &graphql.Field{Type: graphql.NewList(graphql.NewObject(graphql.ObjectConfig{
   691  							Name: fmt.Sprintf("%sAdditionalSemanticPathElement", classname),
   692  							Fields: graphql.Fields{
   693  								"concept":            &graphql.Field{Type: graphql.String},
   694  								"distanceToQuery":    &graphql.Field{Type: graphql.Float},
   695  								"distanceToResult":   &graphql.Field{Type: graphql.Float},
   696  								"distanceToNext":     &graphql.Field{Type: graphql.Float},
   697  								"distanceToPrevious": &graphql.Field{Type: graphql.Float},
   698  							},
   699  						}))},
   700  					},
   701  				}),
   702  			}
   703  		},
   704  		GraphQLExtractFunction: m.fakePathBuilder.ExtractAdditionalFn,
   705  		SearchFunctions: modulecapabilities.AdditionalSearch{
   706  			ExploreGet: m.fakePathBuilder.AdditionalPropertyFn,
   707  		},
   708  	}
   709  }
   710  
   711  type fakeParams struct{}
   712  
   713  func getDefaultParam(name string) interface{} {
   714  	switch name {
   715  	case "featureProjection", "semanticPath":
   716  		return &fakeParams{}
   717  	case "nearestNeighbors":
   718  		return true
   719  	default:
   720  		return nil
   721  	}
   722  }
   723  
   724  func getFakeModulesProvider(opts ...func(p *fakeModulesProvider)) *fakeModulesProvider {
   725  	p := &fakeModulesProvider{}
   726  	p.applyOptions(opts...)
   727  	return p
   728  }
   729  
   730  func (p *fakeModulesProvider) applyOptions(opts ...func(provider *fakeModulesProvider)) {
   731  	for _, opt := range opts {
   732  		opt(p)
   733  	}
   734  }
   735  
   736  func getFakeModulesProviderWithCustomExtenders(
   737  	customExtender *fakeExtender,
   738  	customProjector *fakeProjector,
   739  	opts ...func(provider *fakeModulesProvider),
   740  ) *fakeModulesProvider {
   741  	p := &fakeModulesProvider{mock.Mock{}, customExtender, customProjector}
   742  	p.applyOptions(opts...)
   743  	return p
   744  }
   745  
   746  type fakeMetrics struct {
   747  	// Note: only those metric functions that relate to usage-related metrics are
   748  	// covered by this mock, others are empty shells
   749  	mock.Mock
   750  }
   751  
   752  func (f *fakeMetrics) BatchInc() {
   753  }
   754  
   755  func (f *fakeMetrics) BatchDec() {
   756  }
   757  
   758  func (f *fakeMetrics) BatchRefInc() {
   759  }
   760  
   761  func (f *fakeMetrics) BatchRefDec() {
   762  }
   763  
   764  func (f *fakeMetrics) BatchDeleteInc() {
   765  }
   766  
   767  func (f *fakeMetrics) BatchDeleteDec() {
   768  }
   769  
   770  func (f *fakeMetrics) AddObjectInc() {
   771  }
   772  
   773  func (f *fakeMetrics) AddObjectDec() {
   774  }
   775  
   776  func (f *fakeMetrics) UpdateObjectInc() {
   777  }
   778  
   779  func (f *fakeMetrics) UpdateObjectDec() {
   780  }
   781  
   782  func (f *fakeMetrics) MergeObjectInc() {
   783  }
   784  
   785  func (f *fakeMetrics) MergeObjectDec() {
   786  }
   787  
   788  func (f *fakeMetrics) DeleteObjectInc() {
   789  }
   790  
   791  func (f *fakeMetrics) DeleteObjectDec() {
   792  }
   793  
   794  func (f *fakeMetrics) GetObjectInc() {
   795  }
   796  
   797  func (f *fakeMetrics) GetObjectDec() {
   798  }
   799  
   800  func (f *fakeMetrics) HeadObjectInc() {
   801  }
   802  
   803  func (f *fakeMetrics) HeadObjectDec() {
   804  }
   805  
   806  func (f *fakeMetrics) AddReferenceInc() {
   807  }
   808  
   809  func (f *fakeMetrics) AddReferenceDec() {
   810  }
   811  
   812  func (f *fakeMetrics) UpdateReferenceInc() {
   813  }
   814  
   815  func (f *fakeMetrics) UpdateReferenceDec() {
   816  }
   817  
   818  func (f *fakeMetrics) DeleteReferenceInc() {
   819  }
   820  
   821  func (f *fakeMetrics) DeleteReferenceDec() {
   822  }
   823  
   824  func (f *fakeMetrics) AddUsageDimensions(className, queryType, op string, dims int) {
   825  	f.Mock.MethodCalled("AddUsageDimensions", className, queryType, op, dims)
   826  }