github.com/weaviate/weaviate@v1.24.6/adapters/handlers/graphql/local/get/get_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  // These tests verify that the parameters to the resolver are properly extracted from a GraphQL query.
    13  
    14  package get
    15  
    16  import (
    17  	"fmt"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/go-openapi/strfmt"
    22  	"github.com/google/uuid"
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  	"github.com/tailor-inc/graphql/language/ast"
    26  	test_helper "github.com/weaviate/weaviate/adapters/handlers/graphql/test/helper"
    27  	"github.com/weaviate/weaviate/entities/additional"
    28  	"github.com/weaviate/weaviate/entities/dto"
    29  	"github.com/weaviate/weaviate/entities/filters"
    30  	"github.com/weaviate/weaviate/entities/models"
    31  	"github.com/weaviate/weaviate/entities/search"
    32  	"github.com/weaviate/weaviate/entities/searchparams"
    33  	helper "github.com/weaviate/weaviate/test/helper"
    34  )
    35  
    36  func TestSimpleFieldParamsOK(t *testing.T) {
    37  	t.Parallel()
    38  	resolver := newMockResolver()
    39  	expectedParams := dto.GetParams{
    40  		ClassName:  "SomeAction",
    41  		Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
    42  	}
    43  
    44  	resolver.On("GetClass", expectedParams).
    45  		Return(test_helper.EmptyList(), nil).Once()
    46  
    47  	resolver.AssertResolve(t, "{ Get { SomeAction { intField } } }")
    48  }
    49  
    50  func TestExtractIntField(t *testing.T) {
    51  	t.Parallel()
    52  
    53  	resolver := newMockResolver()
    54  
    55  	expectedParams := dto.GetParams{
    56  		ClassName:  "SomeAction",
    57  		Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
    58  	}
    59  
    60  	resolver.On("GetClass", expectedParams).
    61  		Return(test_helper.EmptyList(), nil).Once()
    62  
    63  	query := "{ Get { SomeAction { intField } } }"
    64  	resolver.AssertResolve(t, query)
    65  }
    66  
    67  func TestExtractGeoCoordinatesField(t *testing.T) {
    68  	t.Parallel()
    69  
    70  	resolver := newMockResolver()
    71  
    72  	expectedParams := dto.GetParams{
    73  		ClassName:  "SomeAction",
    74  		Properties: []search.SelectProperty{{Name: "location", IsPrimitive: true}},
    75  	}
    76  
    77  	resolverReturn := []interface{}{
    78  		map[string]interface{}{
    79  			"location": &models.GeoCoordinates{Latitude: ptFloat32(0.5), Longitude: ptFloat32(0.6)},
    80  		},
    81  	}
    82  
    83  	resolver.On("GetClass", expectedParams).
    84  		Return(resolverReturn, nil).Once()
    85  
    86  	query := "{ Get { SomeAction { location { latitude longitude } } } }"
    87  	result := resolver.AssertResolve(t, query)
    88  
    89  	expectedLocation := map[string]interface{}{
    90  		"location": map[string]interface{}{
    91  			"latitude":  float32(0.5),
    92  			"longitude": float32(0.6),
    93  		},
    94  	}
    95  
    96  	assert.Equal(t, expectedLocation, result.Get("Get", "SomeAction").Result.([]interface{})[0])
    97  }
    98  
    99  func TestExtractUUIDField(t *testing.T) {
   100  	t.Parallel()
   101  
   102  	resolver := newMockResolver()
   103  
   104  	expectedParams := dto.GetParams{
   105  		ClassName:  "SomeAction",
   106  		Properties: []search.SelectProperty{{Name: "uuidField", IsPrimitive: true}},
   107  	}
   108  
   109  	id := uuid.New()
   110  
   111  	resolverReturn := []interface{}{
   112  		map[string]interface{}{
   113  			"uuidField": id,
   114  		},
   115  	}
   116  
   117  	resolver.On("GetClass", expectedParams).
   118  		Return(resolverReturn, nil).Once()
   119  
   120  	query := "{ Get { SomeAction { uuidField } } }"
   121  	result := resolver.AssertResolve(t, query)
   122  
   123  	expectedProps := map[string]interface{}{
   124  		"uuidField": id.String(),
   125  	}
   126  
   127  	assert.Equal(t, expectedProps, result.Get("Get", "SomeAction").Result.([]interface{})[0])
   128  }
   129  
   130  func TestExtractUUIDArrayField(t *testing.T) {
   131  	t.Parallel()
   132  
   133  	resolver := newMockResolver()
   134  
   135  	expectedParams := dto.GetParams{
   136  		ClassName:  "SomeAction",
   137  		Properties: []search.SelectProperty{{Name: "uuidArrayField", IsPrimitive: true}},
   138  	}
   139  
   140  	id1 := uuid.New()
   141  	id2 := uuid.New()
   142  
   143  	resolverReturn := []interface{}{
   144  		map[string]interface{}{
   145  			"uuidArrayField": []uuid.UUID{id1, id2},
   146  		},
   147  	}
   148  
   149  	resolver.On("GetClass", expectedParams).
   150  		Return(resolverReturn, nil).Once()
   151  
   152  	query := "{ Get { SomeAction { uuidArrayField } } }"
   153  	result := resolver.AssertResolve(t, query)
   154  
   155  	expectedProps := map[string]interface{}{
   156  		"uuidArrayField": []any{id1.String(), id2.String()},
   157  	}
   158  
   159  	assert.Equal(t, expectedProps, result.Get("Get", "SomeAction").Result.([]interface{})[0])
   160  }
   161  
   162  func TestExtractPhoneNumberField(t *testing.T) {
   163  	// We need to explicitly test all cases of asking for just one sub-property
   164  	// at a time, because the AST-parsing uses known fields of known props to
   165  	// distinguish a complex primitive prop from a reference prop
   166  	//
   167  	// See "isPrimitive()" and "fieldNameIsOfObjectButNonReferenceType" in
   168  	// class_builder_fields.go for more details
   169  
   170  	type test struct {
   171  		name           string
   172  		query          string
   173  		expectedParams dto.GetParams
   174  		resolverReturn interface{}
   175  		expectedResult interface{}
   176  	}
   177  
   178  	tests := []test{
   179  		{
   180  			name:  "with only input requested",
   181  			query: "{ Get { SomeAction { phone { input } } } }",
   182  			expectedParams: dto.GetParams{
   183  				ClassName:  "SomeAction",
   184  				Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}},
   185  			},
   186  			resolverReturn: []interface{}{
   187  				map[string]interface{}{
   188  					"phone": &models.PhoneNumber{Input: "+49 171 1234567"},
   189  				},
   190  			},
   191  			expectedResult: map[string]interface{}{
   192  				"phone": map[string]interface{}{
   193  					"input": "+49 171 1234567",
   194  				},
   195  			},
   196  		},
   197  		{
   198  			name:  "with only internationalFormatted requested",
   199  			query: "{ Get { SomeAction { phone { internationalFormatted } } } }",
   200  			expectedParams: dto.GetParams{
   201  				ClassName:  "SomeAction",
   202  				Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}},
   203  			},
   204  			resolverReturn: []interface{}{
   205  				map[string]interface{}{
   206  					"phone": &models.PhoneNumber{InternationalFormatted: "+49 171 1234567"},
   207  				},
   208  			},
   209  			expectedResult: map[string]interface{}{
   210  				"phone": map[string]interface{}{
   211  					"internationalFormatted": "+49 171 1234567",
   212  				},
   213  			},
   214  		},
   215  		{
   216  			name:  "with only nationalFormatted requested",
   217  			query: "{ Get { SomeAction { phone { nationalFormatted } } } }",
   218  			expectedParams: dto.GetParams{
   219  				ClassName:  "SomeAction",
   220  				Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}},
   221  			},
   222  			resolverReturn: []interface{}{
   223  				map[string]interface{}{
   224  					"phone": &models.PhoneNumber{NationalFormatted: "0171 1234567"},
   225  				},
   226  			},
   227  			expectedResult: map[string]interface{}{
   228  				"phone": map[string]interface{}{
   229  					"nationalFormatted": "0171 1234567",
   230  				},
   231  			},
   232  		},
   233  		{
   234  			name:  "with only national requested",
   235  			query: "{ Get { SomeAction { phone { national } } } }",
   236  			expectedParams: dto.GetParams{
   237  				ClassName:  "SomeAction",
   238  				Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}},
   239  			},
   240  			resolverReturn: []interface{}{
   241  				map[string]interface{}{
   242  					"phone": &models.PhoneNumber{National: 0o1711234567},
   243  				},
   244  			},
   245  			expectedResult: map[string]interface{}{
   246  				"phone": map[string]interface{}{
   247  					"national": 0o1711234567,
   248  				},
   249  			},
   250  		},
   251  		{
   252  			name:  "with only valid requested",
   253  			query: "{ Get { SomeAction { phone { valid } } } }",
   254  			expectedParams: dto.GetParams{
   255  				ClassName:  "SomeAction",
   256  				Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}},
   257  			},
   258  			resolverReturn: []interface{}{
   259  				map[string]interface{}{
   260  					"phone": &models.PhoneNumber{Valid: true},
   261  				},
   262  			},
   263  			expectedResult: map[string]interface{}{
   264  				"phone": map[string]interface{}{
   265  					"valid": true,
   266  				},
   267  			},
   268  		},
   269  		{
   270  			name:  "with only countryCode requested",
   271  			query: "{ Get { SomeAction { phone { countryCode } } } }",
   272  			expectedParams: dto.GetParams{
   273  				ClassName:  "SomeAction",
   274  				Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}},
   275  			},
   276  			resolverReturn: []interface{}{
   277  				map[string]interface{}{
   278  					"phone": &models.PhoneNumber{CountryCode: 49},
   279  				},
   280  			},
   281  			expectedResult: map[string]interface{}{
   282  				"phone": map[string]interface{}{
   283  					"countryCode": 49,
   284  				},
   285  			},
   286  		},
   287  		{
   288  			name:  "with only defaultCountry requested",
   289  			query: "{ Get { SomeAction { phone { defaultCountry } } } }",
   290  			expectedParams: dto.GetParams{
   291  				ClassName:  "SomeAction",
   292  				Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}},
   293  			},
   294  			resolverReturn: []interface{}{
   295  				map[string]interface{}{
   296  					"phone": &models.PhoneNumber{DefaultCountry: "DE"},
   297  				},
   298  			},
   299  			expectedResult: map[string]interface{}{
   300  				"phone": map[string]interface{}{
   301  					"defaultCountry": "DE",
   302  				},
   303  			},
   304  		},
   305  		{
   306  			name: "with multiple fields set",
   307  			query: "{ Get { SomeAction { phone { input internationalFormatted " +
   308  				"nationalFormatted defaultCountry national countryCode valid } } } }",
   309  			expectedParams: dto.GetParams{
   310  				ClassName:  "SomeAction",
   311  				Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}},
   312  			},
   313  			resolverReturn: []interface{}{
   314  				map[string]interface{}{
   315  					"phone": &models.PhoneNumber{
   316  						DefaultCountry:         "DE",
   317  						CountryCode:            49,
   318  						NationalFormatted:      "0171 123456",
   319  						InternationalFormatted: "+49 171 123456",
   320  						National:               171123456,
   321  						Input:                  "0171123456",
   322  						Valid:                  true,
   323  					},
   324  				},
   325  			},
   326  			expectedResult: map[string]interface{}{
   327  				"phone": map[string]interface{}{
   328  					"defaultCountry":         "DE",
   329  					"countryCode":            49,
   330  					"nationalFormatted":      "0171 123456",
   331  					"internationalFormatted": "+49 171 123456",
   332  					"national":               171123456,
   333  					"input":                  "0171123456",
   334  					"valid":                  true,
   335  				},
   336  			},
   337  		},
   338  	}
   339  
   340  	for _, test := range tests {
   341  		t.Run(test.name, func(t *testing.T) {
   342  			resolver := newMockResolver()
   343  
   344  			resolver.On("GetClass", test.expectedParams).
   345  				Return(test.resolverReturn, nil).Once()
   346  			result := resolver.AssertResolve(t, test.query)
   347  			assert.Equal(t, test.expectedResult, result.Get("Get", "SomeAction").Result.([]interface{})[0])
   348  		})
   349  	}
   350  }
   351  
   352  func TestExtractAdditionalFields(t *testing.T) {
   353  	// We don't need to explicitly test every subselection as we did on
   354  	// phoneNumber as these fields have fixed keys. So we can simply check for
   355  	// the prop
   356  
   357  	type test struct {
   358  		name           string
   359  		query          string
   360  		expectedParams dto.GetParams
   361  		resolverReturn interface{}
   362  		expectedResult interface{}
   363  	}
   364  
   365  	// To facilitate testing timestamps
   366  	nowString := fmt.Sprint(time.Now().UnixNano() / int64(time.Millisecond))
   367  
   368  	tests := []test{
   369  		{
   370  			name:  "with _additional distance",
   371  			query: "{ Get { SomeAction { _additional { distance } } } }",
   372  			expectedParams: dto.GetParams{
   373  				ClassName: "SomeAction",
   374  				AdditionalProperties: additional.Properties{
   375  					Distance: true,
   376  				},
   377  			},
   378  			resolverReturn: []interface{}{
   379  				map[string]interface{}{
   380  					"_additional": map[string]interface{}{
   381  						"distance": helper.CertaintyToDist(t, 0.69),
   382  					},
   383  				},
   384  			},
   385  			expectedResult: map[string]interface{}{
   386  				"_additional": map[string]interface{}{
   387  					"distance": helper.CertaintyToDist(t, 0.69),
   388  				},
   389  			},
   390  		},
   391  		{
   392  			name:  "with _additional certainty",
   393  			query: "{ Get { SomeAction { _additional { certainty } } } }",
   394  			expectedParams: dto.GetParams{
   395  				ClassName: "SomeAction",
   396  				AdditionalProperties: additional.Properties{
   397  					Certainty: true,
   398  				},
   399  			},
   400  			resolverReturn: []interface{}{
   401  				map[string]interface{}{
   402  					"_additional": map[string]interface{}{
   403  						"certainty": 0.69,
   404  						"distance":  helper.CertaintyToDist(t, 0.69),
   405  					},
   406  				},
   407  			},
   408  			expectedResult: map[string]interface{}{
   409  				"_additional": map[string]interface{}{
   410  					"certainty": 0.69,
   411  				},
   412  			},
   413  		},
   414  		{
   415  			name:  "with _additional vector",
   416  			query: "{ Get { SomeAction { _additional { vector } } } }",
   417  			expectedParams: dto.GetParams{
   418  				ClassName: "SomeAction",
   419  				AdditionalProperties: additional.Properties{
   420  					Vector: true,
   421  				},
   422  			},
   423  			resolverReturn: []interface{}{
   424  				map[string]interface{}{
   425  					"_additional": map[string]interface{}{
   426  						"vector": []float32{0.1, -0.3},
   427  					},
   428  				},
   429  			},
   430  			expectedResult: map[string]interface{}{
   431  				"_additional": map[string]interface{}{
   432  					"vector": []interface{}{float32(0.1), float32(-0.3)},
   433  				},
   434  			},
   435  		},
   436  		{
   437  			name:  "with _additional creationTimeUnix",
   438  			query: "{ Get { SomeAction { _additional { creationTimeUnix } } } }",
   439  			expectedParams: dto.GetParams{
   440  				ClassName: "SomeAction",
   441  				AdditionalProperties: additional.Properties{
   442  					CreationTimeUnix: true,
   443  				},
   444  			},
   445  			resolverReturn: []interface{}{
   446  				map[string]interface{}{
   447  					"_additional": map[string]interface{}{
   448  						"creationTimeUnix": nowString,
   449  					},
   450  				},
   451  			},
   452  			expectedResult: map[string]interface{}{
   453  				"_additional": map[string]interface{}{
   454  					"creationTimeUnix": nowString,
   455  				},
   456  			},
   457  		},
   458  		{
   459  			name:  "with _additional lastUpdateTimeUnix",
   460  			query: "{ Get { SomeAction { _additional { lastUpdateTimeUnix } } } }",
   461  			expectedParams: dto.GetParams{
   462  				ClassName: "SomeAction",
   463  				AdditionalProperties: additional.Properties{
   464  					LastUpdateTimeUnix: true,
   465  				},
   466  			},
   467  			resolverReturn: []interface{}{
   468  				map[string]interface{}{
   469  					"_additional": map[string]interface{}{
   470  						"lastUpdateTimeUnix": nowString,
   471  					},
   472  				},
   473  			},
   474  			expectedResult: map[string]interface{}{
   475  				"_additional": map[string]interface{}{
   476  					"lastUpdateTimeUnix": nowString,
   477  				},
   478  			},
   479  		},
   480  		{
   481  			name:  "with _additional classification",
   482  			query: "{ Get { SomeAction { _additional { classification { id completed classifiedFields scope basedOn }  } } } }",
   483  			expectedParams: dto.GetParams{
   484  				ClassName: "SomeAction",
   485  				AdditionalProperties: additional.Properties{
   486  					Classification: true,
   487  				},
   488  			},
   489  			resolverReturn: []interface{}{
   490  				map[string]interface{}{
   491  					"_additional": models.AdditionalProperties{
   492  						"classification": &additional.Classification{
   493  							ID:               "12345",
   494  							BasedOn:          []string{"primitiveProp"},
   495  							Scope:            []string{"refprop1", "refprop2", "refprop3"},
   496  							ClassifiedFields: []string{"refprop3"},
   497  							Completed:        timeMust(strfmt.ParseDateTime("2006-01-02T15:04:05.000Z")),
   498  						},
   499  					},
   500  				},
   501  			},
   502  			expectedResult: map[string]interface{}{
   503  				"_additional": map[string]interface{}{
   504  					"classification": map[string]interface{}{
   505  						"id":               "12345",
   506  						"basedOn":          []interface{}{"primitiveProp"},
   507  						"scope":            []interface{}{"refprop1", "refprop2", "refprop3"},
   508  						"classifiedFields": []interface{}{"refprop3"},
   509  						"completed":        "2006-01-02T15:04:05.000Z",
   510  					},
   511  				},
   512  			},
   513  		},
   514  		{
   515  			name:  "with _additional interpretation",
   516  			query: "{ Get { SomeAction { _additional { interpretation { source { concept weight occurrence } }  } } } }",
   517  			expectedParams: dto.GetParams{
   518  				ClassName: "SomeAction",
   519  				AdditionalProperties: additional.Properties{
   520  					ModuleParams: map[string]interface{}{
   521  						"interpretation": true,
   522  					},
   523  				},
   524  			},
   525  			resolverReturn: []interface{}{
   526  				map[string]interface{}{
   527  					"_additional": map[string]interface{}{
   528  						"interpretation": &Interpretation{
   529  							Source: []*InterpretationSource{
   530  								{
   531  									Concept:    "foo",
   532  									Weight:     0.6,
   533  									Occurrence: 1200,
   534  								},
   535  								{
   536  									Concept:    "bar",
   537  									Weight:     0.9,
   538  									Occurrence: 800,
   539  								},
   540  							},
   541  						},
   542  					},
   543  				},
   544  			},
   545  			expectedResult: map[string]interface{}{
   546  				"_additional": map[string]interface{}{
   547  					"interpretation": map[string]interface{}{
   548  						"source": []interface{}{
   549  							map[string]interface{}{
   550  								"concept":    "foo",
   551  								"weight":     0.6,
   552  								"occurrence": 1200,
   553  							},
   554  							map[string]interface{}{
   555  								"concept":    "bar",
   556  								"weight":     0.9,
   557  								"occurrence": 800,
   558  							},
   559  						},
   560  					},
   561  				},
   562  			},
   563  		},
   564  		{
   565  			name:  "with _additional nearestNeighbors",
   566  			query: "{ Get { SomeAction { _additional { nearestNeighbors { neighbors { concept distance } }  } } } }",
   567  			expectedParams: dto.GetParams{
   568  				ClassName: "SomeAction",
   569  				AdditionalProperties: additional.Properties{
   570  					ModuleParams: map[string]interface{}{
   571  						"nearestNeighbors": true,
   572  					},
   573  				},
   574  			},
   575  			resolverReturn: []interface{}{
   576  				map[string]interface{}{
   577  					"_additional": map[string]interface{}{
   578  						"nearestNeighbors": &NearestNeighbors{
   579  							Neighbors: []*NearestNeighbor{
   580  								{
   581  									Concept:  "foo",
   582  									Distance: 0.1,
   583  								},
   584  								{
   585  									Concept:  "bar",
   586  									Distance: 0.2,
   587  								},
   588  							},
   589  						},
   590  					},
   591  				},
   592  			},
   593  			expectedResult: map[string]interface{}{
   594  				"_additional": map[string]interface{}{
   595  					"nearestNeighbors": map[string]interface{}{
   596  						"neighbors": []interface{}{
   597  							map[string]interface{}{
   598  								"concept":  "foo",
   599  								"distance": float32(0.1),
   600  							},
   601  							map[string]interface{}{
   602  								"concept":  "bar",
   603  								"distance": float32(0.2),
   604  							},
   605  						},
   606  					},
   607  				},
   608  			},
   609  		},
   610  		{
   611  			name:  "with _additional featureProjection without any optional parameters",
   612  			query: "{ Get { SomeAction { _additional { featureProjection { vector }  } } } }",
   613  			expectedParams: dto.GetParams{
   614  				ClassName: "SomeAction",
   615  				AdditionalProperties: additional.Properties{
   616  					ModuleParams: map[string]interface{}{
   617  						"featureProjection": extractAdditionalParam("featureProjection", nil),
   618  					},
   619  				},
   620  			},
   621  			resolverReturn: []interface{}{
   622  				map[string]interface{}{
   623  					"_additional": models.AdditionalProperties{
   624  						"featureProjection": &FeatureProjection{
   625  							Vector: []float32{0.0, 1.1, 2.2},
   626  						},
   627  					},
   628  				},
   629  			},
   630  			expectedResult: map[string]interface{}{
   631  				"_additional": map[string]interface{}{
   632  					"featureProjection": map[string]interface{}{
   633  						"vector": []interface{}{float32(0.0), float32(1.1), float32(2.2)},
   634  					},
   635  				},
   636  			},
   637  		},
   638  		{
   639  			name:  "with _additional featureProjection with optional parameters",
   640  			query: `{ Get { SomeAction { _additional { featureProjection(algorithm: "tsne", dimensions: 3, learningRate: 15, iterations: 100, perplexity: 10) { vector }  } } } }`,
   641  			expectedParams: dto.GetParams{
   642  				ClassName: "SomeAction",
   643  				AdditionalProperties: additional.Properties{
   644  					ModuleParams: map[string]interface{}{
   645  						"featureProjection": extractAdditionalParam("featureProjection",
   646  							[]*ast.Argument{
   647  								createArg("algorithm", "tsne"),
   648  								createArg("dimensions", "3"),
   649  								createArg("iterations", "100"),
   650  								createArg("learningRate", "15"),
   651  								createArg("perplexity", "10"),
   652  							},
   653  						),
   654  					},
   655  				},
   656  			},
   657  			resolverReturn: []interface{}{
   658  				map[string]interface{}{
   659  					"_additional": map[string]interface{}{
   660  						"featureProjection": &FeatureProjection{
   661  							Vector: []float32{0.0, 1.1, 2.2},
   662  						},
   663  					},
   664  				},
   665  			},
   666  			expectedResult: map[string]interface{}{
   667  				"_additional": map[string]interface{}{
   668  					"featureProjection": map[string]interface{}{
   669  						"vector": []interface{}{float32(0.0), float32(1.1), float32(2.2)},
   670  					},
   671  				},
   672  			},
   673  		},
   674  		{
   675  			name:  "with _additional semanticPath set",
   676  			query: `{ Get { SomeAction { _additional { semanticPath { path { concept distanceToQuery distanceToResult distanceToPrevious distanceToNext } } } } } }`,
   677  			expectedParams: dto.GetParams{
   678  				ClassName: "SomeAction",
   679  				AdditionalProperties: additional.Properties{
   680  					ModuleParams: map[string]interface{}{
   681  						"semanticPath": extractAdditionalParam("semanticPath", nil),
   682  					},
   683  				},
   684  			},
   685  			resolverReturn: []interface{}{
   686  				map[string]interface{}{
   687  					"_additional": models.AdditionalProperties{
   688  						"semanticPath": &SemanticPath{
   689  							Path: []*SemanticPathElement{
   690  								{
   691  									Concept:            "foo",
   692  									DistanceToNext:     ptFloat32(0.5),
   693  									DistanceToPrevious: nil,
   694  									DistanceToQuery:    0.1,
   695  									DistanceToResult:   0.1,
   696  								},
   697  								{
   698  									Concept:            "bar",
   699  									DistanceToPrevious: ptFloat32(0.5),
   700  									DistanceToNext:     nil,
   701  									DistanceToQuery:    0.1,
   702  									DistanceToResult:   0.1,
   703  								},
   704  							},
   705  						},
   706  					},
   707  				},
   708  			},
   709  			expectedResult: map[string]interface{}{
   710  				"_additional": map[string]interface{}{
   711  					"semanticPath": map[string]interface{}{
   712  						"path": []interface{}{
   713  							map[string]interface{}{
   714  								"concept":            "foo",
   715  								"distanceToNext":     float32(0.5),
   716  								"distanceToPrevious": nil,
   717  								"distanceToQuery":    float32(0.1),
   718  								"distanceToResult":   float32(0.1),
   719  							},
   720  							map[string]interface{}{
   721  								"concept":            "bar",
   722  								"distanceToPrevious": float32(0.5),
   723  								"distanceToNext":     nil,
   724  								"distanceToQuery":    float32(0.1),
   725  								"distanceToResult":   float32(0.1),
   726  							},
   727  						},
   728  					},
   729  				},
   730  			},
   731  		},
   732  	}
   733  
   734  	for _, test := range tests {
   735  		t.Run(test.name, func(t *testing.T) {
   736  			resolver := newMockResolverWithVectorizer("mock-custom-near-text-module")
   737  
   738  			resolver.On("GetClass", test.expectedParams).
   739  				Return(test.resolverReturn, nil).Once()
   740  			result := resolver.AssertResolve(t, test.query)
   741  			assert.Equal(t, test.expectedResult, result.Get("Get", "SomeAction").Result.([]interface{})[0])
   742  		})
   743  	}
   744  }
   745  
   746  func TestNearCustomTextRanker(t *testing.T) {
   747  	t.Parallel()
   748  
   749  	resolver := newMockResolverWithVectorizer("mock-custom-near-text-module")
   750  
   751  	t.Run("for actions", func(t *testing.T) {
   752  		query := `{ Get { SomeAction(nearCustomText: {
   753  								concepts: ["c1", "c2", "c3"],
   754  								moveTo: {
   755  									concepts:["positive"],
   756  									force: 0.5
   757  								}
   758  								moveAwayFrom: {
   759  									concepts:["epic"]
   760  									force: 0.25
   761  								}
   762  							}) { intField } } }`
   763  
   764  		expectedParams := dto.GetParams{
   765  			ClassName:  "SomeAction",
   766  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
   767  			ModuleParams: map[string]interface{}{
   768  				"nearCustomText": extractNearTextParam(map[string]interface{}{
   769  					"concepts": []interface{}{"c1", "c2", "c3"},
   770  					"moveTo": map[string]interface{}{
   771  						"concepts": []interface{}{"positive"},
   772  						"force":    float64(0.5),
   773  					},
   774  					"moveAwayFrom": map[string]interface{}{
   775  						"concepts": []interface{}{"epic"},
   776  						"force":    float64(0.25),
   777  					},
   778  				}),
   779  			},
   780  		}
   781  
   782  		resolver.On("GetClass", expectedParams).
   783  			Return([]interface{}{}, nil).Once()
   784  
   785  		resolver.AssertResolve(t, query)
   786  	})
   787  
   788  	t.Run("for a class that does not have a text2vec module", func(t *testing.T) {
   789  		query := `{ Get { CustomVectorClass(nearCustomText: {
   790  							concepts: ["c1", "c2", "c3"],
   791  								moveTo: {
   792  									concepts:["positive"],
   793  									force: 0.5
   794  								}
   795  								moveAwayFrom: {
   796  									concepts:["epic"]
   797  									force: 0.25
   798  								}
   799  						}) { intField } } }`
   800  
   801  		res := resolver.Resolve(query)
   802  		require.Len(t, res.Errors, 1)
   803  		assert.Contains(t, res.Errors[0].Message, "Unknown argument \"nearCustomText\" on field \"CustomVectorClass\"")
   804  	})
   805  
   806  	t.Run("for things with optional distance set", func(t *testing.T) {
   807  		query := `{ Get { SomeThing(nearCustomText: {
   808  							concepts: ["c1", "c2", "c3"],
   809  								distance: 0.6,
   810  								moveTo: {
   811  									concepts:["positive"],
   812  									force: 0.5
   813  								}
   814  								moveAwayFrom: {
   815  									concepts:["epic"]
   816  									force: 0.25
   817  								}
   818  						}) { intField } } }`
   819  
   820  		expectedParams := dto.GetParams{
   821  			ClassName:  "SomeThing",
   822  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
   823  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
   824  			ModuleParams: map[string]interface{}{
   825  				"nearCustomText": extractNearTextParam(map[string]interface{}{
   826  					"concepts": []interface{}{"c1", "c2", "c3"},
   827  					"distance": float64(0.6),
   828  					"moveTo": map[string]interface{}{
   829  						"concepts": []interface{}{"positive"},
   830  						"force":    float64(0.5),
   831  					},
   832  					"moveAwayFrom": map[string]interface{}{
   833  						"concepts": []interface{}{"epic"},
   834  						"force":    float64(0.25),
   835  					},
   836  				}),
   837  			},
   838  		}
   839  		resolver.On("GetClass", expectedParams).
   840  			Return([]interface{}{}, nil).Once()
   841  
   842  		resolver.AssertResolve(t, query)
   843  	})
   844  
   845  	t.Run("for things with optional certainty set", func(t *testing.T) {
   846  		query := `{ Get { SomeThing(nearCustomText: {
   847  							concepts: ["c1", "c2", "c3"],
   848  								certainty: 0.4,
   849  								moveTo: {
   850  									concepts:["positive"],
   851  									force: 0.5
   852  								}
   853  								moveAwayFrom: {
   854  									concepts:["epic"]
   855  									force: 0.25
   856  								}
   857  						}) { intField } } }`
   858  
   859  		expectedParams := dto.GetParams{
   860  			ClassName:  "SomeThing",
   861  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
   862  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
   863  			ModuleParams: map[string]interface{}{
   864  				"nearCustomText": extractNearTextParam(map[string]interface{}{
   865  					"concepts":  []interface{}{"c1", "c2", "c3"},
   866  					"certainty": float64(0.4),
   867  					"moveTo": map[string]interface{}{
   868  						"concepts": []interface{}{"positive"},
   869  						"force":    float64(0.5),
   870  					},
   871  					"moveAwayFrom": map[string]interface{}{
   872  						"concepts": []interface{}{"epic"},
   873  						"force":    float64(0.25),
   874  					},
   875  				}),
   876  			},
   877  		}
   878  		resolver.On("GetClass", expectedParams).
   879  			Return([]interface{}{}, nil).Once()
   880  
   881  		resolver.AssertResolve(t, query)
   882  	})
   883  
   884  	t.Run("for things with optional distance and objects set", func(t *testing.T) {
   885  		query := `{ Get { SomeThing(nearCustomText: {
   886  								concepts: ["c1", "c2", "c3"],
   887  								distance: 0.4,
   888  								moveTo: {
   889  									concepts:["positive"],
   890  									force: 0.5
   891  									objects: [
   892  										{ id: "moveTo-uuid1" }
   893  										{ beacon: "weaviate://localhost/moveTo-uuid1" },
   894  										{ beacon: "weaviate://localhost/moveTo-uuid2" }
   895  									]
   896  								}
   897  								moveAwayFrom: {
   898  									concepts:["epic"]
   899  									force: 0.25
   900  									objects: [
   901  										{ id: "moveAway-uuid1" }
   902  										{ beacon: "weaviate://localhost/moveAway-uuid2" }
   903  									]
   904  								}
   905  							}) { intField } } }`
   906  
   907  		expectedParams := dto.GetParams{
   908  			ClassName:  "SomeThing",
   909  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
   910  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
   911  			ModuleParams: map[string]interface{}{
   912  				"nearCustomText": extractNearTextParam(map[string]interface{}{
   913  					"concepts": []interface{}{"c1", "c2", "c3"},
   914  					"distance": float64(0.4),
   915  					"moveTo": map[string]interface{}{
   916  						"concepts": []interface{}{"positive"},
   917  						"force":    float64(0.5),
   918  						"objects": []interface{}{
   919  							map[string]interface{}{
   920  								"id": "moveTo-uuid1",
   921  							},
   922  							map[string]interface{}{
   923  								"beacon": "weaviate://localhost/moveTo-uuid1",
   924  							},
   925  							map[string]interface{}{
   926  								"beacon": "weaviate://localhost/moveTo-uuid2",
   927  							},
   928  						},
   929  					},
   930  					"moveAwayFrom": map[string]interface{}{
   931  						"concepts": []interface{}{"epic"},
   932  						"force":    float64(0.25),
   933  						"objects": []interface{}{
   934  							map[string]interface{}{
   935  								"id": "moveAway-uuid1",
   936  							},
   937  							map[string]interface{}{
   938  								"beacon": "weaviate://localhost/moveAway-uuid2",
   939  							},
   940  						},
   941  					},
   942  				}),
   943  			},
   944  		}
   945  		resolver.On("GetClass", expectedParams).
   946  			Return([]interface{}{}, nil).Once()
   947  
   948  		resolver.AssertResolve(t, query)
   949  	})
   950  
   951  	t.Run("for things with optional certainty and objects set", func(t *testing.T) {
   952  		query := `{ Get { SomeThing(nearCustomText: {
   953  								concepts: ["c1", "c2", "c3"],
   954  								certainty: 0.4,
   955  								moveTo: {
   956  									concepts:["positive"],
   957  									force: 0.5
   958  									objects: [
   959  										{ id: "moveTo-uuid1" }
   960  										{ beacon: "weaviate://localhost/moveTo-uuid1" },
   961  										{ beacon: "weaviate://localhost/moveTo-uuid2" }
   962  									]
   963  								}
   964  								moveAwayFrom: {
   965  									concepts:["epic"]
   966  									force: 0.25
   967  									objects: [
   968  										{ id: "moveAway-uuid1" }
   969  										{ beacon: "weaviate://localhost/moveAway-uuid2" }
   970  									]
   971  								}
   972  							}) { intField } } }`
   973  
   974  		expectedParams := dto.GetParams{
   975  			ClassName:  "SomeThing",
   976  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
   977  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
   978  			ModuleParams: map[string]interface{}{
   979  				"nearCustomText": extractNearTextParam(map[string]interface{}{
   980  					"concepts":  []interface{}{"c1", "c2", "c3"},
   981  					"certainty": float64(0.4),
   982  					"moveTo": map[string]interface{}{
   983  						"concepts": []interface{}{"positive"},
   984  						"force":    float64(0.5),
   985  						"objects": []interface{}{
   986  							map[string]interface{}{
   987  								"id": "moveTo-uuid1",
   988  							},
   989  							map[string]interface{}{
   990  								"beacon": "weaviate://localhost/moveTo-uuid1",
   991  							},
   992  							map[string]interface{}{
   993  								"beacon": "weaviate://localhost/moveTo-uuid2",
   994  							},
   995  						},
   996  					},
   997  					"moveAwayFrom": map[string]interface{}{
   998  						"concepts": []interface{}{"epic"},
   999  						"force":    float64(0.25),
  1000  						"objects": []interface{}{
  1001  							map[string]interface{}{
  1002  								"id": "moveAway-uuid1",
  1003  							},
  1004  							map[string]interface{}{
  1005  								"beacon": "weaviate://localhost/moveAway-uuid2",
  1006  							},
  1007  						},
  1008  					},
  1009  				}),
  1010  			},
  1011  		}
  1012  		resolver.On("GetClass", expectedParams).
  1013  			Return([]interface{}{}, nil).Once()
  1014  
  1015  		resolver.AssertResolve(t, query)
  1016  	})
  1017  
  1018  	t.Run("for things with optional distance and limit set", func(t *testing.T) {
  1019  		query := `{ Get { SomeThing(
  1020  							limit: 6
  1021  							nearCustomText: {
  1022  											concepts: ["c1", "c2", "c3"],
  1023  								distance: 0.4,
  1024  								moveTo: {
  1025  									concepts:["positive"],
  1026  									force: 0.5
  1027  								}
  1028  								moveAwayFrom: {
  1029  									concepts:["epic"]
  1030  									force: 0.25
  1031  								}
  1032  						}) { intField } } }`
  1033  
  1034  		expectedParams := dto.GetParams{
  1035  			ClassName:  "SomeThing",
  1036  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1037  			Pagination: &filters.Pagination{Limit: 6},
  1038  			ModuleParams: map[string]interface{}{
  1039  				"nearCustomText": extractNearTextParam(map[string]interface{}{
  1040  					"concepts": []interface{}{"c1", "c2", "c3"},
  1041  					"distance": float64(0.4),
  1042  					"moveTo": map[string]interface{}{
  1043  						"concepts": []interface{}{"positive"},
  1044  						"force":    float64(0.5),
  1045  					},
  1046  					"moveAwayFrom": map[string]interface{}{
  1047  						"concepts": []interface{}{"epic"},
  1048  						"force":    float64(0.25),
  1049  					},
  1050  				}),
  1051  			},
  1052  		}
  1053  		resolver.On("GetClass", expectedParams).
  1054  			Return([]interface{}{}, nil).Once()
  1055  
  1056  		resolver.AssertResolve(t, query)
  1057  	})
  1058  
  1059  	t.Run("for things with optional certainty and limit set", func(t *testing.T) {
  1060  		query := `{ Get { SomeThing(
  1061  							limit: 6
  1062  							nearCustomText: {
  1063  											concepts: ["c1", "c2", "c3"],
  1064  								certainty: 0.4,
  1065  								moveTo: {
  1066  									concepts:["positive"],
  1067  									force: 0.5
  1068  								}
  1069  								moveAwayFrom: {
  1070  									concepts:["epic"]
  1071  									force: 0.25
  1072  								}
  1073  						}) { intField } } }`
  1074  
  1075  		expectedParams := dto.GetParams{
  1076  			ClassName:  "SomeThing",
  1077  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1078  			Pagination: &filters.Pagination{Limit: 6},
  1079  			ModuleParams: map[string]interface{}{
  1080  				"nearCustomText": extractNearTextParam(map[string]interface{}{
  1081  					"concepts":  []interface{}{"c1", "c2", "c3"},
  1082  					"certainty": float64(0.4),
  1083  					"moveTo": map[string]interface{}{
  1084  						"concepts": []interface{}{"positive"},
  1085  						"force":    float64(0.5),
  1086  					},
  1087  					"moveAwayFrom": map[string]interface{}{
  1088  						"concepts": []interface{}{"epic"},
  1089  						"force":    float64(0.25),
  1090  					},
  1091  				}),
  1092  			},
  1093  		}
  1094  		resolver.On("GetClass", expectedParams).
  1095  			Return([]interface{}{}, nil).Once()
  1096  
  1097  		resolver.AssertResolve(t, query)
  1098  	})
  1099  
  1100  	t.Run("for things with optional distance and negative limit set", func(t *testing.T) {
  1101  		query := `{ Get { SomeThing(
  1102  							limit: -1
  1103  							nearCustomText: {
  1104  											concepts: ["c1", "c2", "c3"],
  1105  								distance: 0.4,
  1106  								moveTo: {
  1107  									concepts:["positive"],
  1108  									force: 0.5
  1109  								}
  1110  								moveAwayFrom: {
  1111  									concepts:["epic"]
  1112  									force: 0.25
  1113  								}
  1114  						}) { intField } } }`
  1115  
  1116  		expectedParams := dto.GetParams{
  1117  			ClassName:  "SomeThing",
  1118  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1119  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1120  			ModuleParams: map[string]interface{}{
  1121  				"nearCustomText": extractNearTextParam(map[string]interface{}{
  1122  					"concepts": []interface{}{"c1", "c2", "c3"},
  1123  					"distance": float64(0.4),
  1124  					"moveTo": map[string]interface{}{
  1125  						"concepts": []interface{}{"positive"},
  1126  						"force":    float64(0.5),
  1127  					},
  1128  					"moveAwayFrom": map[string]interface{}{
  1129  						"concepts": []interface{}{"epic"},
  1130  						"force":    float64(0.25),
  1131  					},
  1132  				}),
  1133  			},
  1134  		}
  1135  		resolver.On("GetClass", expectedParams).
  1136  			Return([]interface{}{}, nil).Once()
  1137  
  1138  		resolver.AssertResolve(t, query)
  1139  	})
  1140  
  1141  	t.Run("for things with optional certainty and negative limit set", func(t *testing.T) {
  1142  		query := `{ Get { SomeThing(
  1143  							limit: -1
  1144  							nearCustomText: {
  1145  											concepts: ["c1", "c2", "c3"],
  1146  								certainty: 0.4,
  1147  								moveTo: {
  1148  									concepts:["positive"],
  1149  									force: 0.5
  1150  								}
  1151  								moveAwayFrom: {
  1152  									concepts:["epic"]
  1153  									force: 0.25
  1154  								}
  1155  						}) { intField } } }`
  1156  
  1157  		expectedParams := dto.GetParams{
  1158  			ClassName:  "SomeThing",
  1159  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1160  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1161  			ModuleParams: map[string]interface{}{
  1162  				"nearCustomText": extractNearTextParam(map[string]interface{}{
  1163  					"concepts":  []interface{}{"c1", "c2", "c3"},
  1164  					"certainty": float64(0.4),
  1165  					"moveTo": map[string]interface{}{
  1166  						"concepts": []interface{}{"positive"},
  1167  						"force":    float64(0.5),
  1168  					},
  1169  					"moveAwayFrom": map[string]interface{}{
  1170  						"concepts": []interface{}{"epic"},
  1171  						"force":    float64(0.25),
  1172  					},
  1173  				}),
  1174  			},
  1175  		}
  1176  		resolver.On("GetClass", expectedParams).
  1177  			Return([]interface{}{}, nil).Once()
  1178  
  1179  		resolver.AssertResolve(t, query)
  1180  	})
  1181  }
  1182  
  1183  func TestNearVectorRanker(t *testing.T) {
  1184  	t.Parallel()
  1185  
  1186  	resolver := newMockResolver()
  1187  
  1188  	t.Run("for actions", func(t *testing.T) {
  1189  		query := `{ Get { SomeAction(nearVector: {
  1190  								vector: [0.123, 0.984]
  1191  							}) { intField } } }`
  1192  
  1193  		expectedParams := dto.GetParams{
  1194  			ClassName:  "SomeAction",
  1195  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1196  			NearVector: &searchparams.NearVector{
  1197  				Vector: []float32{0.123, 0.984},
  1198  			},
  1199  		}
  1200  
  1201  		resolver.On("GetClass", expectedParams).
  1202  			Return([]interface{}{}, nil).Once()
  1203  
  1204  		resolver.AssertResolve(t, query)
  1205  	})
  1206  
  1207  	t.Run("for things with optional distance set", func(t *testing.T) {
  1208  		query := `{ Get { SomeThing(nearVector: {
  1209  								vector: [0.123, 0.984]
  1210  								distance: 0.4
  1211  							}) { intField } } }`
  1212  
  1213  		expectedParams := dto.GetParams{
  1214  			ClassName:  "SomeThing",
  1215  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1216  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1217  			NearVector: &searchparams.NearVector{
  1218  				Vector:       []float32{0.123, 0.984},
  1219  				Distance:     0.4,
  1220  				WithDistance: true,
  1221  			},
  1222  		}
  1223  		resolver.On("GetClass", expectedParams).
  1224  			Return([]interface{}{}, nil).Once()
  1225  
  1226  		resolver.AssertResolve(t, query)
  1227  	})
  1228  
  1229  	t.Run("for things with optional certainty set", func(t *testing.T) {
  1230  		query := `{ Get { SomeThing(nearVector: {
  1231  								vector: [0.123, 0.984]
  1232  								certainty: 0.4
  1233  							}) { intField } } }`
  1234  
  1235  		expectedParams := dto.GetParams{
  1236  			ClassName:  "SomeThing",
  1237  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1238  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1239  			NearVector: &searchparams.NearVector{
  1240  				Vector:    []float32{0.123, 0.984},
  1241  				Certainty: 0.4,
  1242  			},
  1243  		}
  1244  		resolver.On("GetClass", expectedParams).
  1245  			Return([]interface{}{}, nil).Once()
  1246  
  1247  		resolver.AssertResolve(t, query)
  1248  	})
  1249  
  1250  	t.Run("for things with optional distance and limit set", func(t *testing.T) {
  1251  		query := `{ Get { SomeThing(
  1252  					limit: 4  
  1253  						nearVector: {
  1254  						vector: [0.123, 0.984]
  1255  						distance: 0.1
  1256  							}) { intField } } }`
  1257  
  1258  		expectedParams := dto.GetParams{
  1259  			ClassName:  "SomeThing",
  1260  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1261  			Pagination: &filters.Pagination{Limit: 4},
  1262  			NearVector: &searchparams.NearVector{
  1263  				Vector:       []float32{0.123, 0.984},
  1264  				Distance:     0.1,
  1265  				WithDistance: true,
  1266  			},
  1267  		}
  1268  
  1269  		resolver.On("GetClass", expectedParams).
  1270  			Return([]interface{}{}, nil).Once()
  1271  
  1272  		resolver.AssertResolve(t, query)
  1273  	})
  1274  
  1275  	t.Run("for things with optional certainty and limit set", func(t *testing.T) {
  1276  		query := `{ Get { SomeThing(
  1277  					limit: 4  
  1278  						nearVector: {
  1279  						vector: [0.123, 0.984]
  1280  						certainty: 0.1
  1281  							}) { intField } } }`
  1282  
  1283  		expectedParams := dto.GetParams{
  1284  			ClassName:  "SomeThing",
  1285  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1286  			Pagination: &filters.Pagination{Limit: 4},
  1287  			NearVector: &searchparams.NearVector{
  1288  				Vector:    []float32{0.123, 0.984},
  1289  				Certainty: 0.1,
  1290  			},
  1291  		}
  1292  
  1293  		resolver.On("GetClass", expectedParams).
  1294  			Return([]interface{}{}, nil).Once()
  1295  
  1296  		resolver.AssertResolve(t, query)
  1297  	})
  1298  
  1299  	t.Run("for things with optional distance and negative limit set", func(t *testing.T) {
  1300  		query := `{ Get { SomeThing(
  1301  					limit: -1  
  1302  						nearVector: {
  1303  						vector: [0.123, 0.984]
  1304  						distance: 0.1
  1305  							}) { intField } } }`
  1306  
  1307  		expectedParams := dto.GetParams{
  1308  			ClassName:  "SomeThing",
  1309  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1310  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1311  			NearVector: &searchparams.NearVector{
  1312  				Vector:       []float32{0.123, 0.984},
  1313  				Distance:     0.1,
  1314  				WithDistance: true,
  1315  			},
  1316  		}
  1317  
  1318  		resolver.On("GetClass", expectedParams).
  1319  			Return([]interface{}{}, nil).Once()
  1320  
  1321  		resolver.AssertResolve(t, query)
  1322  	})
  1323  
  1324  	t.Run("for things with optional certainty and negative limit set", func(t *testing.T) {
  1325  		query := `{ Get { SomeThing(
  1326  					limit: -1  
  1327  						nearVector: {
  1328  						vector: [0.123, 0.984]
  1329  						certainty: 0.1
  1330  							}) { intField } } }`
  1331  
  1332  		expectedParams := dto.GetParams{
  1333  			ClassName:  "SomeThing",
  1334  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1335  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1336  			NearVector: &searchparams.NearVector{
  1337  				Vector:    []float32{0.123, 0.984},
  1338  				Certainty: 0.1,
  1339  			},
  1340  		}
  1341  
  1342  		resolver.On("GetClass", expectedParams).
  1343  			Return([]interface{}{}, nil).Once()
  1344  
  1345  		resolver.AssertResolve(t, query)
  1346  	})
  1347  }
  1348  
  1349  func TestExtractPagination(t *testing.T) {
  1350  	t.Parallel()
  1351  
  1352  	resolver := newMockResolver()
  1353  
  1354  	expectedParams := dto.GetParams{
  1355  		ClassName:  "SomeAction",
  1356  		Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1357  		Pagination: &filters.Pagination{
  1358  			Limit: 10,
  1359  		},
  1360  	}
  1361  
  1362  	resolver.On("GetClass", expectedParams).
  1363  		Return(test_helper.EmptyList(), nil).Once()
  1364  
  1365  	query := "{ Get { SomeAction(limit: 10) { intField } } }"
  1366  	resolver.AssertResolve(t, query)
  1367  }
  1368  
  1369  func TestExtractPaginationWithOffset(t *testing.T) {
  1370  	t.Parallel()
  1371  
  1372  	resolver := newMockResolver()
  1373  
  1374  	expectedParams := dto.GetParams{
  1375  		ClassName:  "SomeAction",
  1376  		Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1377  		Pagination: &filters.Pagination{
  1378  			Offset: 5,
  1379  			Limit:  10,
  1380  		},
  1381  	}
  1382  
  1383  	resolver.On("GetClass", expectedParams).
  1384  		Return(test_helper.EmptyList(), nil).Once()
  1385  
  1386  	query := "{ Get { SomeAction(offset: 5 limit: 10) { intField } } }"
  1387  	resolver.AssertResolve(t, query)
  1388  }
  1389  
  1390  func TestExtractPaginationWithOnlyOffset(t *testing.T) {
  1391  	t.Parallel()
  1392  
  1393  	resolver := newMockResolver()
  1394  
  1395  	expectedParams := dto.GetParams{
  1396  		ClassName:  "SomeAction",
  1397  		Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1398  		Pagination: &filters.Pagination{
  1399  			Offset: 5,
  1400  			Limit:  -1,
  1401  		},
  1402  	}
  1403  
  1404  	resolver.On("GetClass", expectedParams).
  1405  		Return(test_helper.EmptyList(), nil).Once()
  1406  
  1407  	query := "{ Get { SomeAction(offset: 5) { intField } } }"
  1408  	resolver.AssertResolve(t, query)
  1409  }
  1410  
  1411  func TestExtractCursor(t *testing.T) {
  1412  	t.Parallel()
  1413  
  1414  	resolver := newMockResolver()
  1415  
  1416  	expectedParams := dto.GetParams{
  1417  		ClassName:  "SomeAction",
  1418  		Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1419  		Cursor: &filters.Cursor{
  1420  			After: "8ef8d5cc-c101-4fbd-a016-84e766b93ecf",
  1421  			Limit: 2,
  1422  		},
  1423  		Pagination: &filters.Pagination{
  1424  			Offset: 0,
  1425  			Limit:  2,
  1426  		},
  1427  	}
  1428  
  1429  	resolver.On("GetClass", expectedParams).
  1430  		Return(test_helper.EmptyList(), nil).Once()
  1431  
  1432  	query := `{ Get { SomeAction(after: "8ef8d5cc-c101-4fbd-a016-84e766b93ecf" limit: 2) { intField } } }`
  1433  	resolver.AssertResolve(t, query)
  1434  }
  1435  
  1436  func TestExtractGroupParams(t *testing.T) {
  1437  	t.Parallel()
  1438  
  1439  	resolver := newMockResolver()
  1440  
  1441  	expectedParams := dto.GetParams{
  1442  		ClassName:  "SomeAction",
  1443  		Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1444  		Group: &dto.GroupParams{
  1445  			Strategy: "closest",
  1446  			Force:    0.3,
  1447  		},
  1448  	}
  1449  
  1450  	resolver.On("GetClass", expectedParams).
  1451  		Return(test_helper.EmptyList(), nil).Once()
  1452  
  1453  	query := "{ Get { SomeAction(group: {type: closest, force: 0.3}) { intField } } }"
  1454  	resolver.AssertResolve(t, query)
  1455  }
  1456  
  1457  func TestGetRelation(t *testing.T) {
  1458  	t.Parallel()
  1459  
  1460  	t.Run("without using custom fragments", func(t *testing.T) {
  1461  		resolver := newMockResolver()
  1462  
  1463  		expectedParams := dto.GetParams{
  1464  			ClassName: "SomeAction",
  1465  			Properties: []search.SelectProperty{
  1466  				{
  1467  					Name:        "hasAction",
  1468  					IsPrimitive: false,
  1469  					Refs: []search.SelectClass{
  1470  						{
  1471  							ClassName: "SomeAction",
  1472  							RefProperties: []search.SelectProperty{
  1473  								{
  1474  									Name:        "intField",
  1475  									IsPrimitive: true,
  1476  								},
  1477  								{
  1478  									Name:        "hasAction",
  1479  									IsPrimitive: false,
  1480  									Refs: []search.SelectClass{
  1481  										{
  1482  											ClassName: "SomeAction",
  1483  											RefProperties: []search.SelectProperty{
  1484  												{
  1485  													Name:        "intField",
  1486  													IsPrimitive: true,
  1487  												},
  1488  											},
  1489  										},
  1490  									},
  1491  								},
  1492  							},
  1493  						},
  1494  					},
  1495  				},
  1496  			},
  1497  		}
  1498  
  1499  		resolver.On("GetClass", expectedParams).
  1500  			Return(test_helper.EmptyList(), nil).Once()
  1501  
  1502  		query := "{ Get { SomeAction { hasAction { ... on SomeAction { intField, hasAction { ... on SomeAction { intField } } } } } } }"
  1503  		resolver.AssertResolve(t, query)
  1504  	})
  1505  
  1506  	t.Run("with a custom fragment one level deep", func(t *testing.T) {
  1507  		resolver := newMockResolver()
  1508  
  1509  		expectedParams := dto.GetParams{
  1510  			ClassName: "SomeAction",
  1511  			Properties: []search.SelectProperty{
  1512  				{
  1513  					Name:        "hasAction",
  1514  					IsPrimitive: false,
  1515  					Refs: []search.SelectClass{
  1516  						{
  1517  							ClassName: "SomeAction",
  1518  							RefProperties: []search.SelectProperty{
  1519  								{
  1520  									Name:        "intField",
  1521  									IsPrimitive: true,
  1522  								},
  1523  							},
  1524  						},
  1525  					},
  1526  				},
  1527  			},
  1528  		}
  1529  
  1530  		resolver.On("GetClass", expectedParams).
  1531  			Return(test_helper.EmptyList(), nil).Once()
  1532  
  1533  		query := "fragment actionFragment on SomeAction { intField } { Get { SomeAction { hasAction { ...actionFragment } } } }"
  1534  		resolver.AssertResolve(t, query)
  1535  	})
  1536  
  1537  	t.Run("with a custom fragment multiple levels deep", func(t *testing.T) {
  1538  		resolver := newMockResolver()
  1539  
  1540  		expectedParams := dto.GetParams{
  1541  			ClassName: "SomeAction",
  1542  			Properties: []search.SelectProperty{
  1543  				{
  1544  					Name:        "hasAction",
  1545  					IsPrimitive: false,
  1546  					Refs: []search.SelectClass{
  1547  						{
  1548  							ClassName: "SomeAction",
  1549  							RefProperties: []search.SelectProperty{
  1550  								{
  1551  									Name:        "intField",
  1552  									IsPrimitive: true,
  1553  								},
  1554  								{
  1555  									Name:        "hasAction",
  1556  									IsPrimitive: false,
  1557  									Refs: []search.SelectClass{
  1558  										{
  1559  											ClassName: "SomeAction",
  1560  											RefProperties: []search.SelectProperty{
  1561  												{
  1562  													Name:        "intField",
  1563  													IsPrimitive: true,
  1564  												},
  1565  											},
  1566  										},
  1567  									},
  1568  								},
  1569  							},
  1570  						},
  1571  					},
  1572  				},
  1573  			},
  1574  		}
  1575  
  1576  		resolver.On("GetClass", expectedParams).
  1577  			Return(test_helper.EmptyList(), nil).Once()
  1578  
  1579  		query := `
  1580  			fragment innerFragment on SomeAction { intField }
  1581  			fragment actionFragment on SomeAction { intField hasAction { ...innerFragment } } 
  1582  			
  1583  			{ Get { SomeAction { hasAction { ...actionFragment } } } }`
  1584  		resolver.AssertResolve(t, query)
  1585  	})
  1586  }
  1587  
  1588  func TestNearObject(t *testing.T) {
  1589  	t.Parallel()
  1590  
  1591  	resolver := newMockResolver()
  1592  
  1593  	t.Run("for objects with beacon", func(t *testing.T) {
  1594  		query := `{ Get { SomeAction(
  1595  								nearObject: {
  1596  									beacon: "weaviate://localhost/some-uuid"
  1597  								}) { intField } } }`
  1598  
  1599  		expectedParams := dto.GetParams{
  1600  			ClassName:  "SomeAction",
  1601  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1602  			NearObject: &searchparams.NearObject{
  1603  				Beacon: "weaviate://localhost/some-uuid",
  1604  			},
  1605  		}
  1606  
  1607  		resolver.On("GetClass", expectedParams).
  1608  			Return([]interface{}{}, nil).Once()
  1609  
  1610  		resolver.AssertResolve(t, query)
  1611  	})
  1612  
  1613  	t.Run("for objects with beacon and optional distance set", func(t *testing.T) {
  1614  		query := `{ Get { SomeThing(
  1615  								nearObject: {
  1616  									beacon: "weaviate://localhost/some-other-uuid"
  1617  									distance: 0.7
  1618  								}) { intField } } }`
  1619  
  1620  		expectedParams := dto.GetParams{
  1621  			ClassName:  "SomeThing",
  1622  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1623  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1624  			NearObject: &searchparams.NearObject{
  1625  				Beacon:       "weaviate://localhost/some-other-uuid",
  1626  				Distance:     0.7,
  1627  				WithDistance: true,
  1628  			},
  1629  		}
  1630  		resolver.On("GetClass", expectedParams).
  1631  			Return([]interface{}{}, nil).Once()
  1632  
  1633  		resolver.AssertResolve(t, query)
  1634  	})
  1635  
  1636  	t.Run("for objects with beacon and optional certainty set", func(t *testing.T) {
  1637  		query := `{ Get { SomeThing(
  1638  								nearObject: {
  1639  									beacon: "weaviate://localhost/some-other-uuid"
  1640  									certainty: 0.7
  1641  								}) { intField } } }`
  1642  
  1643  		expectedParams := dto.GetParams{
  1644  			ClassName:  "SomeThing",
  1645  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1646  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1647  			NearObject: &searchparams.NearObject{
  1648  				Beacon:    "weaviate://localhost/some-other-uuid",
  1649  				Certainty: 0.7,
  1650  			},
  1651  		}
  1652  		resolver.On("GetClass", expectedParams).
  1653  			Return([]interface{}{}, nil).Once()
  1654  
  1655  		resolver.AssertResolve(t, query)
  1656  	})
  1657  
  1658  	t.Run("for objects with id set", func(t *testing.T) {
  1659  		query := `{ Get { SomeAction(
  1660  								nearObject: {
  1661  									id: "some-uuid"
  1662  								}) { intField } } }`
  1663  
  1664  		expectedParams := dto.GetParams{
  1665  			ClassName:  "SomeAction",
  1666  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1667  			NearObject: &searchparams.NearObject{
  1668  				ID: "some-uuid",
  1669  			},
  1670  		}
  1671  
  1672  		resolver.On("GetClass", expectedParams).
  1673  			Return([]interface{}{}, nil).Once()
  1674  
  1675  		resolver.AssertResolve(t, query)
  1676  	})
  1677  
  1678  	t.Run("for objects with id and optional distance set", func(t *testing.T) {
  1679  		query := `{ Get { SomeThing(
  1680  								nearObject: {
  1681  									id: "some-other-uuid"
  1682  									distance: 0.7
  1683  								}) { intField } } }`
  1684  
  1685  		expectedParams := dto.GetParams{
  1686  			ClassName:  "SomeThing",
  1687  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1688  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1689  			NearObject: &searchparams.NearObject{
  1690  				ID:           "some-other-uuid",
  1691  				Distance:     0.7,
  1692  				WithDistance: true,
  1693  			},
  1694  		}
  1695  		resolver.On("GetClass", expectedParams).
  1696  			Return([]interface{}{}, nil).Once()
  1697  
  1698  		resolver.AssertResolve(t, query)
  1699  	})
  1700  
  1701  	t.Run("for objects with id and optional certainty set", func(t *testing.T) {
  1702  		query := `{ Get { SomeThing(
  1703  								nearObject: {
  1704  									id: "some-other-uuid"
  1705  									certainty: 0.7
  1706  								}) { intField } } }`
  1707  
  1708  		expectedParams := dto.GetParams{
  1709  			ClassName:  "SomeThing",
  1710  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1711  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1712  			NearObject: &searchparams.NearObject{
  1713  				ID:        "some-other-uuid",
  1714  				Certainty: 0.7,
  1715  			},
  1716  		}
  1717  		resolver.On("GetClass", expectedParams).
  1718  			Return([]interface{}{}, nil).Once()
  1719  
  1720  		resolver.AssertResolve(t, query)
  1721  	})
  1722  
  1723  	t.Run("for objects with optional distance and limit set", func(t *testing.T) {
  1724  		query := `{ Get { SomeThing(
  1725  						limit: 5
  1726  						nearObject: {
  1727  							id: "some-other-uuid"
  1728  							distance: 0.7
  1729  						}) { intField } } }`
  1730  
  1731  		expectedParams := dto.GetParams{
  1732  			ClassName:  "SomeThing",
  1733  			Pagination: &filters.Pagination{Limit: 5},
  1734  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1735  			NearObject: &searchparams.NearObject{
  1736  				ID:           "some-other-uuid",
  1737  				Distance:     0.7,
  1738  				WithDistance: true,
  1739  			},
  1740  		}
  1741  		resolver.On("GetClass", expectedParams).
  1742  			Return([]interface{}{}, nil).Once()
  1743  
  1744  		resolver.AssertResolve(t, query)
  1745  	})
  1746  
  1747  	t.Run("for objects with optional certainty and limit set", func(t *testing.T) {
  1748  		query := `{ Get { SomeThing(
  1749  						limit: 5
  1750  						nearObject: {
  1751  							id: "some-other-uuid"
  1752  							certainty: 0.7
  1753  						}) { intField } } }`
  1754  
  1755  		expectedParams := dto.GetParams{
  1756  			ClassName:  "SomeThing",
  1757  			Pagination: &filters.Pagination{Limit: 5},
  1758  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1759  			NearObject: &searchparams.NearObject{
  1760  				ID:        "some-other-uuid",
  1761  				Certainty: 0.7,
  1762  			},
  1763  		}
  1764  		resolver.On("GetClass", expectedParams).
  1765  			Return([]interface{}{}, nil).Once()
  1766  
  1767  		resolver.AssertResolve(t, query)
  1768  	})
  1769  
  1770  	t.Run("for objects with optional distance and negative limit set", func(t *testing.T) {
  1771  		query := `{ Get { SomeThing(
  1772  						limit: -1
  1773  						nearObject: {
  1774  							id: "some-other-uuid"
  1775  							distance: 0.7
  1776  						}) { intField } } }`
  1777  
  1778  		expectedParams := dto.GetParams{
  1779  			ClassName:  "SomeThing",
  1780  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1781  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1782  			NearObject: &searchparams.NearObject{
  1783  				ID:           "some-other-uuid",
  1784  				Distance:     0.7,
  1785  				WithDistance: true,
  1786  			},
  1787  		}
  1788  		resolver.On("GetClass", expectedParams).
  1789  			Return([]interface{}{}, nil).Once()
  1790  
  1791  		resolver.AssertResolve(t, query)
  1792  	})
  1793  
  1794  	t.Run("for objects with optional certainty and negative limit set", func(t *testing.T) {
  1795  		query := `{ Get { SomeThing(
  1796  						limit: -1
  1797  						nearObject: {
  1798  							id: "some-other-uuid"
  1799  							certainty: 0.7
  1800  						}) { intField } } }`
  1801  
  1802  		expectedParams := dto.GetParams{
  1803  			ClassName:  "SomeThing",
  1804  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1805  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1806  			NearObject: &searchparams.NearObject{
  1807  				ID:        "some-other-uuid",
  1808  				Certainty: 0.7,
  1809  			},
  1810  		}
  1811  		resolver.On("GetClass", expectedParams).
  1812  			Return([]interface{}{}, nil).Once()
  1813  
  1814  		resolver.AssertResolve(t, query)
  1815  	})
  1816  }
  1817  
  1818  func TestNearTextNoNoModules(t *testing.T) {
  1819  	t.Parallel()
  1820  
  1821  	resolver := newMockResolverWithNoModules()
  1822  
  1823  	t.Run("for nearText that is not available", func(t *testing.T) {
  1824  		query := `{ Get { SomeAction(nearText: {
  1825  								concepts: ["c1", "c2", "c3"],
  1826  								moveTo: {
  1827  									concepts:["positive"],
  1828  									force: 0.5
  1829  								},
  1830  								moveAwayFrom: {
  1831  									concepts:["epic"],
  1832  									force: 0.25
  1833  								}
  1834  							}) { intField } } }`
  1835  
  1836  		expectedParams := dto.GetParams{
  1837  			ClassName:  "SomeAction",
  1838  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1839  		}
  1840  
  1841  		resolver.On("GetClass", expectedParams).
  1842  			Return(nil, nil).Once()
  1843  
  1844  		resolver.AssertFailToResolve(t, query)
  1845  	})
  1846  }
  1847  
  1848  func TestBM25WithSort(t *testing.T) {
  1849  	t.Parallel()
  1850  	resolver := newMockResolverWithNoModules()
  1851  	query := `{Get{SomeAction(bm25:{query:"apple",properties:["name"]},sort:[{path:["name"],order:desc}]){intField}}}`
  1852  	resolver.AssertFailToResolve(t, query, "bm25 search is not compatible with sort")
  1853  }
  1854  
  1855  func TestHybridWithSort(t *testing.T) {
  1856  	t.Parallel()
  1857  	resolver := newMockResolverWithNoModules()
  1858  	query := `{Get{SomeAction(hybrid:{query:"apple"},sort:[{path:["name"],order:desc}]){intField}}}`
  1859  	resolver.AssertFailToResolve(t, query, "hybrid search is not compatible with sort")
  1860  }
  1861  
  1862  func TestNearObjectNoModules(t *testing.T) {
  1863  	t.Parallel()
  1864  
  1865  	resolver := newMockResolverWithNoModules()
  1866  
  1867  	t.Run("for objects with beacon", func(t *testing.T) {
  1868  		query := `{ Get { SomeAction(
  1869  								nearObject: {
  1870  									beacon: "weaviate://localhost/some-uuid"
  1871  								}) { intField } } }`
  1872  
  1873  		expectedParams := dto.GetParams{
  1874  			ClassName:  "SomeAction",
  1875  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1876  			NearObject: &searchparams.NearObject{
  1877  				Beacon: "weaviate://localhost/some-uuid",
  1878  			},
  1879  		}
  1880  
  1881  		resolver.On("GetClass", expectedParams).
  1882  			Return([]interface{}{}, nil).Once()
  1883  
  1884  		resolver.AssertResolve(t, query)
  1885  	})
  1886  
  1887  	t.Run("for objects with ID and distance set", func(t *testing.T) {
  1888  		query := `{ Get { SomeThing(
  1889  								nearObject: {
  1890  									id: "some-uuid"
  1891  									distance: 0.7
  1892  								}) { intField } } }`
  1893  
  1894  		expectedParams := dto.GetParams{
  1895  			ClassName:  "SomeThing",
  1896  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1897  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1898  			NearObject: &searchparams.NearObject{
  1899  				ID:           "some-uuid",
  1900  				Distance:     0.7,
  1901  				WithDistance: true,
  1902  			},
  1903  		}
  1904  
  1905  		resolver.On("GetClass", expectedParams).
  1906  			Return([]interface{}{}, nil).Once()
  1907  
  1908  		resolver.AssertResolve(t, query)
  1909  	})
  1910  
  1911  	t.Run("for objects with ID and certainty set", func(t *testing.T) {
  1912  		query := `{ Get { SomeThing(
  1913  								nearObject: {
  1914  									id: "some-uuid"
  1915  									certainty: 0.7
  1916  								}) { intField } } }`
  1917  
  1918  		expectedParams := dto.GetParams{
  1919  			ClassName:  "SomeThing",
  1920  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1921  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1922  			NearObject: &searchparams.NearObject{
  1923  				ID:        "some-uuid",
  1924  				Certainty: 0.7,
  1925  			},
  1926  		}
  1927  
  1928  		resolver.On("GetClass", expectedParams).
  1929  			Return([]interface{}{}, nil).Once()
  1930  
  1931  		resolver.AssertResolve(t, query)
  1932  	})
  1933  
  1934  	t.Run("for objects with distance and limit set", func(t *testing.T) {
  1935  		query := `{ Get { SomeThing(
  1936  								limit: 12
  1937  								nearObject: {
  1938  									id: "some-uuid"
  1939  									distance: 0.7
  1940  								}) { intField } } }`
  1941  
  1942  		expectedParams := dto.GetParams{
  1943  			ClassName:  "SomeThing",
  1944  			Pagination: &filters.Pagination{Limit: 12},
  1945  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1946  			NearObject: &searchparams.NearObject{
  1947  				ID:           "some-uuid",
  1948  				Distance:     0.7,
  1949  				WithDistance: true,
  1950  			},
  1951  		}
  1952  
  1953  		resolver.On("GetClass", expectedParams).
  1954  			Return([]interface{}{}, nil).Once()
  1955  
  1956  		resolver.AssertResolve(t, query)
  1957  	})
  1958  
  1959  	t.Run("for objects with certainty and limit set", func(t *testing.T) {
  1960  		query := `{ Get { SomeThing(
  1961  								limit: 12
  1962  								nearObject: {
  1963  									id: "some-uuid"
  1964  									certainty: 0.7
  1965  								}) { intField } } }`
  1966  
  1967  		expectedParams := dto.GetParams{
  1968  			ClassName:  "SomeThing",
  1969  			Pagination: &filters.Pagination{Limit: 12},
  1970  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1971  			NearObject: &searchparams.NearObject{
  1972  				ID:        "some-uuid",
  1973  				Certainty: 0.7,
  1974  			},
  1975  		}
  1976  
  1977  		resolver.On("GetClass", expectedParams).
  1978  			Return([]interface{}{}, nil).Once()
  1979  
  1980  		resolver.AssertResolve(t, query)
  1981  	})
  1982  
  1983  	t.Run("for objects with distance and negative limit set", func(t *testing.T) {
  1984  		query := `{ Get { SomeThing(
  1985  								limit: -1
  1986  								nearObject: {
  1987  									id: "some-uuid"
  1988  									distance: 0.7
  1989  								}) { intField } } }`
  1990  
  1991  		expectedParams := dto.GetParams{
  1992  			ClassName:  "SomeThing",
  1993  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  1994  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  1995  			NearObject: &searchparams.NearObject{
  1996  				ID:           "some-uuid",
  1997  				Distance:     0.7,
  1998  				WithDistance: true,
  1999  			},
  2000  		}
  2001  
  2002  		resolver.On("GetClass", expectedParams).
  2003  			Return([]interface{}{}, nil).Once()
  2004  
  2005  		resolver.AssertResolve(t, query)
  2006  	})
  2007  
  2008  	t.Run("for objects with certainty and negative limit set", func(t *testing.T) {
  2009  		query := `{ Get { SomeThing(
  2010  								limit: -1
  2011  								nearObject: {
  2012  									id: "some-uuid"
  2013  									certainty: 0.7
  2014  								}) { intField } } }`
  2015  
  2016  		expectedParams := dto.GetParams{
  2017  			ClassName:  "SomeThing",
  2018  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  2019  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  2020  			NearObject: &searchparams.NearObject{
  2021  				ID:        "some-uuid",
  2022  				Certainty: 0.7,
  2023  			},
  2024  		}
  2025  
  2026  		resolver.On("GetClass", expectedParams).
  2027  			Return([]interface{}{}, nil).Once()
  2028  
  2029  		resolver.AssertResolve(t, query)
  2030  	})
  2031  }
  2032  
  2033  func TestNearVectorNoModules(t *testing.T) {
  2034  	t.Parallel()
  2035  
  2036  	resolver := newMockResolverWithNoModules()
  2037  
  2038  	t.Run("for actions", func(t *testing.T) {
  2039  		query := `{ Get { SomeAction(nearVector: {
  2040  								vector: [0.123, 0.984]
  2041  							}) { intField } } }`
  2042  
  2043  		expectedParams := dto.GetParams{
  2044  			ClassName:  "SomeAction",
  2045  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  2046  			NearVector: &searchparams.NearVector{
  2047  				Vector: []float32{0.123, 0.984},
  2048  			},
  2049  		}
  2050  
  2051  		resolver.On("GetClass", expectedParams).
  2052  			Return([]interface{}{}, nil).Once()
  2053  
  2054  		resolver.AssertResolve(t, query)
  2055  	})
  2056  
  2057  	t.Run("for things with optional distance set", func(t *testing.T) {
  2058  		query := `{ Get { SomeThing(nearVector: {
  2059  								vector: [0.123, 0.984]
  2060  								distance: 0.4
  2061  							}) { intField } } }`
  2062  
  2063  		expectedParams := dto.GetParams{
  2064  			ClassName:  "SomeThing",
  2065  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  2066  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  2067  			NearVector: &searchparams.NearVector{
  2068  				Vector:       []float32{0.123, 0.984},
  2069  				Distance:     0.4,
  2070  				WithDistance: true,
  2071  			},
  2072  		}
  2073  		resolver.On("GetClass", expectedParams).
  2074  			Return([]interface{}{}, nil).Once()
  2075  
  2076  		resolver.AssertResolve(t, query)
  2077  	})
  2078  
  2079  	t.Run("for things with optional certainty set", func(t *testing.T) {
  2080  		query := `{ Get { SomeThing(nearVector: {
  2081  								vector: [0.123, 0.984]
  2082  								certainty: 0.4
  2083  							}) { intField } } }`
  2084  
  2085  		expectedParams := dto.GetParams{
  2086  			ClassName:  "SomeThing",
  2087  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  2088  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  2089  			NearVector: &searchparams.NearVector{
  2090  				Vector:    []float32{0.123, 0.984},
  2091  				Certainty: 0.4,
  2092  			},
  2093  		}
  2094  		resolver.On("GetClass", expectedParams).
  2095  			Return([]interface{}{}, nil).Once()
  2096  
  2097  		resolver.AssertResolve(t, query)
  2098  	})
  2099  
  2100  	t.Run("for things with optional certainty and limit set", func(t *testing.T) {
  2101  		query := `{ Get { SomeThing(
  2102  						limit: 4
  2103  						nearVector: {
  2104  							vector: [0.123, 0.984]
  2105  							certainty: 0.4
  2106  								}) { intField } } }`
  2107  
  2108  		expectedParams := dto.GetParams{
  2109  			ClassName:  "SomeThing",
  2110  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  2111  			Pagination: &filters.Pagination{Limit: 4},
  2112  			NearVector: &searchparams.NearVector{
  2113  				Vector:    []float32{0.123, 0.984},
  2114  				Certainty: 0.4,
  2115  			},
  2116  		}
  2117  		resolver.On("GetClass", expectedParams).
  2118  			Return([]interface{}{}, nil).Once()
  2119  
  2120  		resolver.AssertResolve(t, query)
  2121  	})
  2122  
  2123  	t.Run("for things with optional distance and negative limit set", func(t *testing.T) {
  2124  		query := `{ Get { SomeThing(
  2125  						limit: -1
  2126  						nearVector: {
  2127  							vector: [0.123, 0.984]
  2128  							distance: 0.4
  2129  								}) { intField } } }`
  2130  
  2131  		expectedParams := dto.GetParams{
  2132  			ClassName:  "SomeThing",
  2133  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  2134  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  2135  			NearVector: &searchparams.NearVector{
  2136  				Vector:       []float32{0.123, 0.984},
  2137  				Distance:     0.4,
  2138  				WithDistance: true,
  2139  			},
  2140  		}
  2141  		resolver.On("GetClass", expectedParams).
  2142  			Return([]interface{}{}, nil).Once()
  2143  
  2144  		resolver.AssertResolve(t, query)
  2145  	})
  2146  
  2147  	t.Run("for things with optional certainty and negative limit set", func(t *testing.T) {
  2148  		query := `{ Get { SomeThing(
  2149  						limit: -1
  2150  						nearVector: {
  2151  							vector: [0.123, 0.984]
  2152  							certainty: 0.4
  2153  								}) { intField } } }`
  2154  
  2155  		expectedParams := dto.GetParams{
  2156  			ClassName:  "SomeThing",
  2157  			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  2158  			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
  2159  			NearVector: &searchparams.NearVector{
  2160  				Vector:    []float32{0.123, 0.984},
  2161  				Certainty: 0.4,
  2162  			},
  2163  		}
  2164  		resolver.On("GetClass", expectedParams).
  2165  			Return([]interface{}{}, nil).Once()
  2166  
  2167  		resolver.AssertResolve(t, query)
  2168  	})
  2169  }
  2170  
  2171  func TestSort(t *testing.T) {
  2172  	t.Parallel()
  2173  
  2174  	tests := []struct {
  2175  		name     string
  2176  		resolver *mockResolver
  2177  	}{
  2178  		{
  2179  			name:     "with modules",
  2180  			resolver: newMockResolver(),
  2181  		},
  2182  		{
  2183  			name:     "with no modules",
  2184  			resolver: newMockResolverWithNoModules(),
  2185  		},
  2186  	}
  2187  	for _, tt := range tests {
  2188  		t.Run(tt.name, func(t *testing.T) {
  2189  			t.Run("simple sort", func(t *testing.T) {
  2190  				query := `{ Get { SomeAction(sort:[{
  2191  										path: ["path"] order: asc
  2192  									}]) { intField } } }`
  2193  
  2194  				expectedParams := dto.GetParams{
  2195  					ClassName:  "SomeAction",
  2196  					Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  2197  					Sort:       []filters.Sort{{Path: []string{"path"}, Order: "asc"}},
  2198  				}
  2199  
  2200  				tt.resolver.On("GetClass", expectedParams).
  2201  					Return([]interface{}{}, nil).Once()
  2202  
  2203  				tt.resolver.AssertResolve(t, query)
  2204  			})
  2205  
  2206  			t.Run("simple sort with two paths", func(t *testing.T) {
  2207  				query := `{ Get { SomeAction(sort:[{
  2208  										path: ["path1", "path2"] order: desc
  2209  									}]) { intField } } }`
  2210  
  2211  				expectedParams := dto.GetParams{
  2212  					ClassName:  "SomeAction",
  2213  					Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  2214  					Sort:       []filters.Sort{{Path: []string{"path1", "path2"}, Order: "desc"}},
  2215  				}
  2216  
  2217  				tt.resolver.On("GetClass", expectedParams).
  2218  					Return([]interface{}{}, nil).Once()
  2219  
  2220  				tt.resolver.AssertResolve(t, query)
  2221  			})
  2222  
  2223  			t.Run("simple sort with two sort filters", func(t *testing.T) {
  2224  				query := `{ Get { SomeAction(sort:[{
  2225  										path: ["first1", "first2", "first3", "first4"] order: asc
  2226  									} {
  2227  										path: ["second1"] order: desc
  2228  									}]) { intField } } }`
  2229  
  2230  				expectedParams := dto.GetParams{
  2231  					ClassName:  "SomeAction",
  2232  					Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
  2233  					Sort: []filters.Sort{
  2234  						{Path: []string{"first1", "first2", "first3", "first4"}, Order: "asc"},
  2235  						{Path: []string{"second1"}, Order: "desc"},
  2236  					},
  2237  				}
  2238  
  2239  				tt.resolver.On("GetClass", expectedParams).
  2240  					Return([]interface{}{}, nil).Once()
  2241  
  2242  				tt.resolver.AssertResolve(t, query)
  2243  			})
  2244  		})
  2245  	}
  2246  }
  2247  
  2248  func TestGroupBy(t *testing.T) {
  2249  	t.Parallel()
  2250  
  2251  	tests := []struct {
  2252  		name     string
  2253  		resolver *mockResolver
  2254  	}{
  2255  		{
  2256  			name:     "with modules",
  2257  			resolver: newMockResolver(),
  2258  		},
  2259  		{
  2260  			name:     "with no modules",
  2261  			resolver: newMockResolverWithNoModules(),
  2262  		},
  2263  	}
  2264  	for _, tt := range tests {
  2265  		t.Run(tt.name, func(t *testing.T) {
  2266  			t.Run("simple groupBy", func(t *testing.T) {
  2267  				query := `{ Get {
  2268  					SomeAction(
  2269  						groupBy:{path: ["path"] groups: 2 objectsPerGroup:3}
  2270  					) {
  2271  						_additional{group{count groupedBy {value path} maxDistance minDistance hits {_additional{distance}}}
  2272  						}
  2273  					} } }`
  2274  
  2275  				expectedParams := dto.GetParams{
  2276  					ClassName:            "SomeAction",
  2277  					GroupBy:              &searchparams.GroupBy{Property: "path", Groups: 2, ObjectsPerGroup: 3},
  2278  					AdditionalProperties: additional.Properties{Group: true},
  2279  				}
  2280  
  2281  				tt.resolver.On("GetClass", expectedParams).
  2282  					Return([]interface{}{}, nil).Once()
  2283  
  2284  				tt.resolver.AssertResolve(t, query)
  2285  			})
  2286  		})
  2287  	}
  2288  }
  2289  
  2290  func ptFloat32(in float32) *float32 {
  2291  	return &in
  2292  }
  2293  
  2294  func timeMust(t strfmt.DateTime, err error) strfmt.DateTime {
  2295  	if err != nil {
  2296  		panic(err)
  2297  	}
  2298  
  2299  	return t
  2300  }