github.com/weaviate/weaviate@v1.24.6/adapters/handlers/graphql/local/aggregate/resolver_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 aggregate
    13  
    14  import (
    15  	"testing"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/weaviate/weaviate/entities/aggregation"
    19  	"github.com/weaviate/weaviate/entities/filters"
    20  	"github.com/weaviate/weaviate/entities/schema"
    21  	"github.com/weaviate/weaviate/entities/searchparams"
    22  	"github.com/weaviate/weaviate/usecases/config"
    23  )
    24  
    25  type testCase struct {
    26  	name                     string
    27  	query                    string
    28  	expectedProps            []aggregation.ParamProperty
    29  	resolverReturn           interface{}
    30  	expectedResults          []result
    31  	expectedGroupBy          *filters.Path
    32  	expectedWhereFilter      *filters.LocalFilter
    33  	expectedNearObjectFilter *searchparams.NearObject
    34  	expectedNearVectorFilter *searchparams.NearVector
    35  	expectedIncludeMetaCount bool
    36  	expectedLimit            *int
    37  	expectedObjectLimit      *int
    38  }
    39  
    40  type testCases []testCase
    41  
    42  type result struct {
    43  	pathToField   []string
    44  	expectedValue interface{}
    45  }
    46  
    47  func groupCarByMadeByManufacturerName() *filters.Path {
    48  	return &filters.Path{
    49  		Class:    schema.ClassName("Car"),
    50  		Property: schema.PropertyName("madeBy"),
    51  		Child: &filters.Path{
    52  			Class:    schema.ClassName("Manufacturer"),
    53  			Property: schema.PropertyName("name"),
    54  		},
    55  	}
    56  }
    57  
    58  func Test_Resolve(t *testing.T) {
    59  	t.Parallel()
    60  
    61  	tests := testCases{
    62  		testCase{
    63  			name: "for gh-758 (multiple operands)",
    64  			query: `
    65  			{
    66  					Aggregate {
    67  						Car(where:{
    68  							operator:Or
    69  							operands:[{
    70  								valueText:"Fast",
    71  								operator:Equal,
    72  								path:["modelName"]
    73  							}, {
    74  								valueText:"Slow",
    75  								operator:Equal,
    76  								path:["modelName"]
    77  							}]
    78  						}) {
    79  							__typename
    80  							modelName {
    81  								__typename
    82  								count
    83  							}
    84  						}
    85  					}
    86  			}`,
    87  			expectedProps: []aggregation.ParamProperty{
    88  				{
    89  					Name:        "modelName",
    90  					Aggregators: []aggregation.Aggregator{aggregation.CountAggregator},
    91  				},
    92  			},
    93  			resolverReturn: []aggregation.Group{
    94  				{
    95  					Properties: map[string]aggregation.Property{
    96  						"modelName": {
    97  							Type: aggregation.PropertyTypeText,
    98  							TextAggregation: aggregation.Text{
    99  								Count: 20,
   100  							},
   101  						},
   102  					},
   103  				},
   104  			},
   105  			expectedWhereFilter: &filters.LocalFilter{
   106  				Root: &filters.Clause{
   107  					Operator: filters.OperatorOr,
   108  					Operands: []filters.Clause{
   109  						{
   110  							Operator: filters.OperatorEqual,
   111  							On: &filters.Path{
   112  								Class:    schema.ClassName("Car"),
   113  								Property: schema.PropertyName("modelName"),
   114  							},
   115  							Value: &filters.Value{
   116  								Value: "Fast",
   117  								Type:  schema.DataTypeText,
   118  							},
   119  						},
   120  						{
   121  							Operator: filters.OperatorEqual,
   122  							On: &filters.Path{
   123  								Class:    schema.ClassName("Car"),
   124  								Property: schema.PropertyName("modelName"),
   125  							},
   126  							Value: &filters.Value{
   127  								Value: "Slow",
   128  								Type:  schema.DataTypeText,
   129  							},
   130  						},
   131  					},
   132  				},
   133  			},
   134  
   135  			expectedGroupBy: nil,
   136  			expectedResults: []result{{
   137  				pathToField: []string{"Aggregate", "Car"},
   138  				expectedValue: []interface{}{
   139  					map[string]interface{}{
   140  						"__typename": "AggregateCar",
   141  						"modelName": map[string]interface{}{
   142  							"count":      20,
   143  							"__typename": "AggregateCarmodelNameObj",
   144  						},
   145  					},
   146  				},
   147  			}},
   148  		},
   149  		testCase{
   150  			name:  "without grouping prop",
   151  			query: `{ Aggregate { Car { horsepower { mean } } } }`,
   152  			expectedProps: []aggregation.ParamProperty{
   153  				{
   154  					Name:        "horsepower",
   155  					Aggregators: []aggregation.Aggregator{aggregation.MeanAggregator},
   156  				},
   157  			},
   158  			resolverReturn: []aggregation.Group{
   159  				{
   160  					Properties: map[string]aggregation.Property{
   161  						"horsepower": {
   162  							Type: aggregation.PropertyTypeNumerical,
   163  							NumericalAggregations: map[string]interface{}{
   164  								"mean": 275.7773,
   165  							},
   166  						},
   167  					},
   168  				},
   169  			},
   170  
   171  			expectedGroupBy: nil,
   172  			expectedResults: []result{{
   173  				pathToField: []string{"Aggregate", "Car"},
   174  				expectedValue: []interface{}{
   175  					map[string]interface{}{
   176  						"horsepower": map[string]interface{}{"mean": 275.7773},
   177  					},
   178  				},
   179  			}},
   180  		},
   181  		testCase{
   182  			name:  "setting limits overall",
   183  			query: `{ Aggregate { Car(limit:20) { horsepower { mean } } } }`,
   184  			expectedProps: []aggregation.ParamProperty{
   185  				{
   186  					Name:        "horsepower",
   187  					Aggregators: []aggregation.Aggregator{aggregation.MeanAggregator},
   188  				},
   189  			},
   190  			resolverReturn: []aggregation.Group{
   191  				{
   192  					Properties: map[string]aggregation.Property{
   193  						"horsepower": {
   194  							Type: aggregation.PropertyTypeNumerical,
   195  							NumericalAggregations: map[string]interface{}{
   196  								"mean": 275.7773,
   197  							},
   198  						},
   199  					},
   200  				},
   201  			},
   202  
   203  			expectedGroupBy: nil,
   204  			expectedLimit:   ptInt(20),
   205  			expectedResults: []result{{
   206  				pathToField: []string{"Aggregate", "Car"},
   207  				expectedValue: []interface{}{
   208  					map[string]interface{}{
   209  						"horsepower": map[string]interface{}{"mean": 275.7773},
   210  					},
   211  				},
   212  			}},
   213  		},
   214  		testCase{
   215  			name: "with props formerly contained only in Meta",
   216  			query: `{ Aggregate { Car {
   217  				stillInProduction { type count totalTrue percentageTrue totalFalse percentageFalse }
   218  				modelName { type count topOccurrences { value occurs } }
   219  				madeBy { type pointingTo }
   220  				meta { count }
   221  				} } } `,
   222  			expectedIncludeMetaCount: true,
   223  			expectedProps: []aggregation.ParamProperty{
   224  				{
   225  					Name: "stillInProduction",
   226  					Aggregators: []aggregation.Aggregator{
   227  						aggregation.TypeAggregator,
   228  						aggregation.CountAggregator,
   229  						aggregation.TotalTrueAggregator,
   230  						aggregation.PercentageTrueAggregator,
   231  						aggregation.TotalFalseAggregator,
   232  						aggregation.PercentageFalseAggregator,
   233  					},
   234  				},
   235  				{
   236  					Name: "modelName",
   237  					Aggregators: []aggregation.Aggregator{
   238  						aggregation.TypeAggregator,
   239  						aggregation.CountAggregator,
   240  						aggregation.NewTopOccurrencesAggregator(ptInt(5)),
   241  					},
   242  				},
   243  				{
   244  					Name: "madeBy",
   245  					Aggregators: []aggregation.Aggregator{
   246  						aggregation.TypeAggregator,
   247  						aggregation.PointingToAggregator,
   248  					},
   249  				},
   250  			},
   251  			resolverReturn: []aggregation.Group{
   252  				{
   253  					Count: 10,
   254  					Properties: map[string]aggregation.Property{
   255  						"stillInProduction": {
   256  							SchemaType: "boolean",
   257  							Type:       aggregation.PropertyTypeBoolean,
   258  							BooleanAggregation: aggregation.Boolean{
   259  								TotalTrue:       23,
   260  								TotalFalse:      17,
   261  								PercentageTrue:  60,
   262  								PercentageFalse: 40,
   263  								Count:           40,
   264  							},
   265  						},
   266  						"modelName": {
   267  							SchemaType: "string",
   268  							Type:       aggregation.PropertyTypeText,
   269  							TextAggregation: aggregation.Text{
   270  								Count: 40,
   271  								Items: []aggregation.TextOccurrence{
   272  									{
   273  										Value:  "fastcar",
   274  										Occurs: 39,
   275  									},
   276  									{
   277  										Value:  "slowcar",
   278  										Occurs: 1,
   279  									},
   280  								},
   281  							},
   282  						},
   283  						"madeBy": {
   284  							SchemaType: "cref",
   285  							Type:       aggregation.PropertyTypeReference,
   286  							ReferenceAggregation: aggregation.Reference{
   287  								PointingTo: []string{"Manufacturer"},
   288  							},
   289  						},
   290  					},
   291  				},
   292  			},
   293  
   294  			expectedGroupBy: nil,
   295  			expectedResults: []result{{
   296  				pathToField: []string{"Aggregate", "Car"},
   297  				expectedValue: []interface{}{
   298  					map[string]interface{}{
   299  						"stillInProduction": map[string]interface{}{
   300  							"type":            "boolean",
   301  							"totalTrue":       23,
   302  							"totalFalse":      17,
   303  							"percentageTrue":  60.0,
   304  							"percentageFalse": 40.0,
   305  							"count":           40,
   306  						},
   307  						"modelName": map[string]interface{}{
   308  							"count": 40,
   309  							"type":  "string",
   310  							"topOccurrences": []interface{}{
   311  								map[string]interface{}{
   312  									"value":  "fastcar",
   313  									"occurs": 39,
   314  								},
   315  								map[string]interface{}{
   316  									"value":  "slowcar",
   317  									"occurs": 1,
   318  								},
   319  							},
   320  						},
   321  						"madeBy": map[string]interface{}{
   322  							"type":       "cref",
   323  							"pointingTo": []interface{}{"Manufacturer"},
   324  						},
   325  						"meta": map[string]interface{}{
   326  							"count": 10,
   327  						},
   328  					},
   329  				},
   330  			}},
   331  		},
   332  		testCase{
   333  			name: "with custom limit in topOccurrences",
   334  			query: `{ Aggregate { Car {
   335  				modelName { topOccurrences(limit: 7) { value occurs } }
   336  				} } } `,
   337  			expectedProps: []aggregation.ParamProperty{
   338  				{
   339  					Name: "modelName",
   340  					Aggregators: []aggregation.Aggregator{
   341  						aggregation.NewTopOccurrencesAggregator(ptInt(7)),
   342  					},
   343  				},
   344  			},
   345  			resolverReturn: []aggregation.Group{
   346  				{
   347  					Count: 10,
   348  					Properties: map[string]aggregation.Property{
   349  						"modelName": {
   350  							SchemaType: "string",
   351  							Type:       aggregation.PropertyTypeText,
   352  							TextAggregation: aggregation.Text{
   353  								Items: []aggregation.TextOccurrence{
   354  									{
   355  										Value:  "fastcar",
   356  										Occurs: 39,
   357  									},
   358  									{
   359  										Value:  "slowcar",
   360  										Occurs: 1,
   361  									},
   362  								},
   363  							},
   364  						},
   365  					},
   366  				},
   367  			},
   368  
   369  			expectedGroupBy: nil,
   370  			expectedResults: []result{{
   371  				pathToField: []string{"Aggregate", "Car"},
   372  				expectedValue: []interface{}{
   373  					map[string]interface{}{
   374  						"modelName": map[string]interface{}{
   375  							"topOccurrences": []interface{}{
   376  								map[string]interface{}{
   377  									"value":  "fastcar",
   378  									"occurs": 39,
   379  								},
   380  								map[string]interface{}{
   381  									"value":  "slowcar",
   382  									"occurs": 1,
   383  								},
   384  							},
   385  						},
   386  					},
   387  				},
   388  			}},
   389  		},
   390  		testCase{
   391  			name:  "single prop: mean (with type)",
   392  			query: `{ Aggregate { Car(groupBy:["madeBy", "Manufacturer", "name"]) { horsepower { mean type } } } }`,
   393  			expectedProps: []aggregation.ParamProperty{
   394  				{
   395  					Name:        "horsepower",
   396  					Aggregators: []aggregation.Aggregator{aggregation.MeanAggregator, aggregation.TypeAggregator},
   397  				},
   398  			},
   399  			resolverReturn: []aggregation.Group{
   400  				{
   401  					Properties: map[string]aggregation.Property{
   402  						"horsepower": {
   403  							Type:       aggregation.PropertyTypeNumerical,
   404  							SchemaType: "int",
   405  							NumericalAggregations: map[string]interface{}{
   406  								"mean": 275.7773,
   407  							},
   408  						},
   409  					},
   410  				},
   411  			},
   412  
   413  			expectedGroupBy: groupCarByMadeByManufacturerName(),
   414  			expectedResults: []result{{
   415  				pathToField: []string{"Aggregate", "Car"},
   416  				expectedValue: []interface{}{
   417  					map[string]interface{}{
   418  						"horsepower": map[string]interface{}{
   419  							"mean": 275.7773,
   420  							"type": "int",
   421  						},
   422  					},
   423  				},
   424  			}},
   425  		},
   426  
   427  		testCase{
   428  			name:  "single prop: mean with groupedBy path/value",
   429  			query: `{ Aggregate { Car(groupBy:["madeBy", "Manufacturer", "name"]) { horsepower { mean } groupedBy { value path } } } }`,
   430  			expectedProps: []aggregation.ParamProperty{
   431  				{
   432  					Name:        "horsepower",
   433  					Aggregators: []aggregation.Aggregator{aggregation.MeanAggregator},
   434  				},
   435  			},
   436  			resolverReturn: []aggregation.Group{
   437  				{
   438  					GroupedBy: &aggregation.GroupedBy{
   439  						Path:  []string{"madeBy", "Manufacturer", "name"},
   440  						Value: "best-manufacturer",
   441  					},
   442  					Properties: map[string]aggregation.Property{
   443  						"horsepower": {
   444  							Type: aggregation.PropertyTypeNumerical,
   445  							NumericalAggregations: map[string]interface{}{
   446  								"mean": 275.7773,
   447  							},
   448  						},
   449  					},
   450  				},
   451  			},
   452  			expectedGroupBy: groupCarByMadeByManufacturerName(),
   453  			expectedResults: []result{{
   454  				pathToField: []string{"Aggregate", "Car"},
   455  				expectedValue: []interface{}{
   456  					map[string]interface{}{
   457  						"horsepower": map[string]interface{}{"mean": 275.7773},
   458  						"groupedBy": map[string]interface{}{
   459  							"path":  []interface{}{"madeBy", "Manufacturer", "name"},
   460  							"value": "best-manufacturer",
   461  						},
   462  					},
   463  				},
   464  			}},
   465  		},
   466  
   467  		testCase{
   468  			name: "single prop: mean with a where filter",
   469  			query: `{
   470  				Aggregate {
   471  					Car(
   472  						groupBy:["madeBy", "Manufacturer", "name"]
   473  						where: {
   474  							operator: LessThan,
   475  							valueInt: 200,
   476  							path: ["horsepower"],
   477  						}
   478  					) {
   479  						horsepower {
   480  							mean
   481  						}
   482  					}
   483  				}
   484  			}`,
   485  			expectedProps: []aggregation.ParamProperty{
   486  				{
   487  					Name:        "horsepower",
   488  					Aggregators: []aggregation.Aggregator{aggregation.MeanAggregator},
   489  				},
   490  			},
   491  			resolverReturn: []aggregation.Group{
   492  				{
   493  					GroupedBy: &aggregation.GroupedBy{
   494  						Path:  []string{"madeBy", "Manufacturer", "name"},
   495  						Value: "best-manufacturer",
   496  					},
   497  					Properties: map[string]aggregation.Property{
   498  						"horsepower": {
   499  							Type: aggregation.PropertyTypeNumerical,
   500  							NumericalAggregations: map[string]interface{}{
   501  								"mean": 275.7773,
   502  							},
   503  						},
   504  					},
   505  				},
   506  			},
   507  			expectedGroupBy: groupCarByMadeByManufacturerName(),
   508  			expectedResults: []result{{
   509  				pathToField: []string{"Aggregate", "Car"},
   510  				expectedValue: []interface{}{
   511  					map[string]interface{}{
   512  						"horsepower": map[string]interface{}{
   513  							"mean": 275.7773,
   514  						},
   515  					},
   516  				},
   517  			}},
   518  			expectedWhereFilter: &filters.LocalFilter{
   519  				Root: &filters.Clause{
   520  					On: &filters.Path{
   521  						Class:    schema.ClassName("Car"),
   522  						Property: schema.PropertyName("horsepower"),
   523  					},
   524  					Value: &filters.Value{
   525  						Value: 200,
   526  						Type:  schema.DataTypeInt,
   527  					},
   528  					Operator: filters.OperatorLessThan,
   529  				},
   530  			},
   531  		},
   532  
   533  		testCase{
   534  			name:  "all int props",
   535  			query: `{ Aggregate { Car(groupBy:["madeBy", "Manufacturer", "name"]) { horsepower { mean, median, mode, maximum, minimum, count, sum } } } }`,
   536  			expectedProps: []aggregation.ParamProperty{
   537  				{
   538  					Name:        "horsepower",
   539  					Aggregators: []aggregation.Aggregator{aggregation.MeanAggregator, aggregation.MedianAggregator, aggregation.ModeAggregator, aggregation.MaximumAggregator, aggregation.MinimumAggregator, aggregation.CountAggregator, aggregation.SumAggregator},
   540  				},
   541  			},
   542  			resolverReturn: []aggregation.Group{
   543  				{
   544  					GroupedBy: &aggregation.GroupedBy{
   545  						Path:  []string{"madeBy", "Manufacturer", "name"},
   546  						Value: "best-manufacturer",
   547  					},
   548  					Properties: map[string]aggregation.Property{
   549  						"horsepower": {
   550  							Type: aggregation.PropertyTypeNumerical,
   551  							NumericalAggregations: map[string]interface{}{
   552  								"maximum": 610.0,
   553  								"minimum": 89.0,
   554  								"mean":    275.7,
   555  								"median":  289.0,
   556  								"mode":    115.0,
   557  								"count":   23,
   558  								"sum":     6343.0,
   559  							},
   560  						},
   561  					},
   562  				},
   563  			},
   564  			expectedGroupBy: groupCarByMadeByManufacturerName(),
   565  			expectedResults: []result{
   566  				{
   567  					pathToField: []string{"Aggregate", "Car"},
   568  					expectedValue: []interface{}{
   569  						map[string]interface{}{
   570  							"horsepower": map[string]interface{}{
   571  								"maximum": 610.0,
   572  								"minimum": 89.0,
   573  								"mean":    275.7,
   574  								"median":  289.0,
   575  								"mode":    115.0,
   576  								"count":   23,
   577  								"sum":     6343.0,
   578  							},
   579  						},
   580  					},
   581  				},
   582  			},
   583  		},
   584  
   585  		testCase{
   586  			name:  "single prop: string",
   587  			query: `{ Aggregate { Car(groupBy:["madeBy", "Manufacturer", "name"]) { modelName { count } } } }`,
   588  			expectedProps: []aggregation.ParamProperty{
   589  				{
   590  					Name:        "modelName",
   591  					Aggregators: []aggregation.Aggregator{aggregation.CountAggregator},
   592  				},
   593  			},
   594  			resolverReturn: []aggregation.Group{
   595  				{
   596  					GroupedBy: &aggregation.GroupedBy{
   597  						Path:  []string{"madeBy", "Manufacturer", "name"},
   598  						Value: "best-manufacturer",
   599  					},
   600  					Properties: map[string]aggregation.Property{
   601  						"modelName": {
   602  							Type: aggregation.PropertyTypeText,
   603  							TextAggregation: aggregation.Text{
   604  								Count: 7,
   605  							},
   606  						},
   607  					},
   608  				},
   609  			},
   610  			expectedGroupBy: groupCarByMadeByManufacturerName(),
   611  			expectedResults: []result{{
   612  				pathToField: []string{"Aggregate", "Car"},
   613  				expectedValue: []interface{}{
   614  					map[string]interface{}{
   615  						"modelName": map[string]interface{}{
   616  							"count": 7,
   617  						},
   618  					},
   619  				},
   620  			}},
   621  		},
   622  
   623  		testCase{
   624  			name: "with objectLimit + nearObject (distance)",
   625  			query: `
   626  				{
   627  					Aggregate{
   628  						Car(
   629  							objectLimit: 1
   630  							nearObject: {
   631  								id: "123"
   632  								distance: 0.3
   633  							}
   634  						) {
   635  							modelName {
   636  								count
   637  							}
   638  						}
   639  					}
   640  				}
   641  			`,
   642  			expectedProps: []aggregation.ParamProperty{
   643  				{
   644  					Name:        "modelName",
   645  					Aggregators: []aggregation.Aggregator{aggregation.CountAggregator},
   646  				},
   647  			},
   648  			expectedObjectLimit: ptInt(1),
   649  			expectedNearObjectFilter: &searchparams.NearObject{
   650  				ID:           "123",
   651  				Beacon:       "",
   652  				Distance:     0.3,
   653  				WithDistance: true,
   654  			},
   655  			resolverReturn: []aggregation.Group{
   656  				{
   657  					Properties: map[string]aggregation.Property{
   658  						"modelName": {
   659  							Type: aggregation.PropertyTypeText,
   660  							TextAggregation: aggregation.Text{
   661  								Count: 7,
   662  							},
   663  						},
   664  					},
   665  				},
   666  			},
   667  			expectedResults: []result{{
   668  				pathToField: []string{"Aggregate", "Car"},
   669  				expectedValue: []interface{}{
   670  					map[string]interface{}{
   671  						"modelName": map[string]interface{}{
   672  							"count": 7,
   673  						},
   674  					},
   675  				},
   676  			}},
   677  		},
   678  
   679  		testCase{
   680  			name: "with objectLimit + nearObject (certainty)",
   681  			query: `
   682  				{
   683  					Aggregate{
   684  						Car(
   685  							objectLimit: 1
   686  							nearObject: {
   687  								id: "123"
   688  								certainty: 0.7
   689  							}
   690  						) {
   691  							modelName {
   692  								count
   693  							}
   694  						}
   695  					}
   696  				}
   697  			`,
   698  			expectedProps: []aggregation.ParamProperty{
   699  				{
   700  					Name:        "modelName",
   701  					Aggregators: []aggregation.Aggregator{aggregation.CountAggregator},
   702  				},
   703  			},
   704  			expectedObjectLimit: ptInt(1),
   705  			expectedNearObjectFilter: &searchparams.NearObject{
   706  				ID:        "123",
   707  				Beacon:    "",
   708  				Certainty: 0.7,
   709  			},
   710  			resolverReturn: []aggregation.Group{
   711  				{
   712  					Properties: map[string]aggregation.Property{
   713  						"modelName": {
   714  							Type: aggregation.PropertyTypeText,
   715  							TextAggregation: aggregation.Text{
   716  								Count: 7,
   717  							},
   718  						},
   719  					},
   720  				},
   721  			},
   722  			expectedResults: []result{{
   723  				pathToField: []string{"Aggregate", "Car"},
   724  				expectedValue: []interface{}{
   725  					map[string]interface{}{
   726  						"modelName": map[string]interface{}{
   727  							"count": 7,
   728  						},
   729  					},
   730  				},
   731  			}},
   732  		},
   733  
   734  		testCase{
   735  			name: "with objectLimit + nearVector (distance)",
   736  			query: `
   737  				{
   738  					Aggregate{
   739  						Car(
   740  							objectLimit: 1
   741  							nearVector: {
   742  								vector: [1, 2, 3]
   743  								distance: 0.3
   744  							}
   745  						) {
   746  							modelName {
   747  								count
   748  							}
   749  						}
   750  					}
   751  				}
   752  			`,
   753  			expectedProps: []aggregation.ParamProperty{
   754  				{
   755  					Name:        "modelName",
   756  					Aggregators: []aggregation.Aggregator{aggregation.CountAggregator},
   757  				},
   758  			},
   759  			expectedObjectLimit: ptInt(1),
   760  			expectedNearVectorFilter: &searchparams.NearVector{
   761  				Vector:       []float32{1, 2, 3},
   762  				Distance:     0.3,
   763  				WithDistance: true,
   764  			},
   765  			resolverReturn: []aggregation.Group{
   766  				{
   767  					Properties: map[string]aggregation.Property{
   768  						"modelName": {
   769  							Type: aggregation.PropertyTypeText,
   770  							TextAggregation: aggregation.Text{
   771  								Count: 7,
   772  							},
   773  						},
   774  					},
   775  				},
   776  			},
   777  			expectedResults: []result{{
   778  				pathToField: []string{"Aggregate", "Car"},
   779  				expectedValue: []interface{}{
   780  					map[string]interface{}{
   781  						"modelName": map[string]interface{}{
   782  							"count": 7,
   783  						},
   784  					},
   785  				},
   786  			}},
   787  		},
   788  
   789  		testCase{
   790  			name: "with objectLimit + nearVector (certainty)",
   791  			query: `
   792  				{
   793  					Aggregate{
   794  						Car(
   795  							objectLimit: 1
   796  							nearVector: {
   797  								vector: [1, 2, 3]
   798  								certainty: 0.7
   799  							}
   800  						) {
   801  							modelName {
   802  								count
   803  							}
   804  						}
   805  					}
   806  				}
   807  			`,
   808  			expectedProps: []aggregation.ParamProperty{
   809  				{
   810  					Name:        "modelName",
   811  					Aggregators: []aggregation.Aggregator{aggregation.CountAggregator},
   812  				},
   813  			},
   814  			expectedObjectLimit: ptInt(1),
   815  			expectedNearVectorFilter: &searchparams.NearVector{
   816  				Vector:    []float32{1, 2, 3},
   817  				Certainty: 0.7,
   818  			},
   819  			resolverReturn: []aggregation.Group{
   820  				{
   821  					Properties: map[string]aggregation.Property{
   822  						"modelName": {
   823  							Type: aggregation.PropertyTypeText,
   824  							TextAggregation: aggregation.Text{
   825  								Count: 7,
   826  							},
   827  						},
   828  					},
   829  				},
   830  			},
   831  			expectedResults: []result{{
   832  				pathToField: []string{"Aggregate", "Car"},
   833  				expectedValue: []interface{}{
   834  					map[string]interface{}{
   835  						"modelName": map[string]interface{}{
   836  							"count": 7,
   837  						},
   838  					},
   839  				},
   840  			}},
   841  		},
   842  		testCase{
   843  			name: "[deprecated string] for gh-758 (multiple operands)",
   844  			query: `
   845  			{
   846  					Aggregate {
   847  						Car(where:{
   848  							operator:Or
   849  							operands:[{
   850  								valueString:"Fast",
   851  								operator:Equal,
   852  								path:["modelName"]
   853  							}, {
   854  								valueString:"Slow",
   855  								operator:Equal,
   856  								path:["modelName"]
   857  							}]
   858  						}) {
   859  							__typename
   860  							modelName {
   861  								__typename
   862  								count
   863  							}
   864  						}
   865  					}
   866  			}`,
   867  			expectedProps: []aggregation.ParamProperty{
   868  				{
   869  					Name:        "modelName",
   870  					Aggregators: []aggregation.Aggregator{aggregation.CountAggregator},
   871  				},
   872  			},
   873  			resolverReturn: []aggregation.Group{
   874  				{
   875  					Properties: map[string]aggregation.Property{
   876  						"modelName": {
   877  							Type: aggregation.PropertyTypeText,
   878  							TextAggregation: aggregation.Text{
   879  								Count: 20,
   880  							},
   881  						},
   882  					},
   883  				},
   884  			},
   885  			expectedWhereFilter: &filters.LocalFilter{
   886  				Root: &filters.Clause{
   887  					Operator: filters.OperatorOr,
   888  					Operands: []filters.Clause{
   889  						{
   890  							Operator: filters.OperatorEqual,
   891  							On: &filters.Path{
   892  								Class:    schema.ClassName("Car"),
   893  								Property: schema.PropertyName("modelName"),
   894  							},
   895  							Value: &filters.Value{
   896  								Value: "Fast",
   897  								Type:  schema.DataTypeString,
   898  							},
   899  						},
   900  						{
   901  							Operator: filters.OperatorEqual,
   902  							On: &filters.Path{
   903  								Class:    schema.ClassName("Car"),
   904  								Property: schema.PropertyName("modelName"),
   905  							},
   906  							Value: &filters.Value{
   907  								Value: "Slow",
   908  								Type:  schema.DataTypeString,
   909  							},
   910  						},
   911  					},
   912  				},
   913  			},
   914  
   915  			expectedGroupBy: nil,
   916  			expectedResults: []result{{
   917  				pathToField: []string{"Aggregate", "Car"},
   918  				expectedValue: []interface{}{
   919  					map[string]interface{}{
   920  						"__typename": "AggregateCar",
   921  						"modelName": map[string]interface{}{
   922  							"count":      20,
   923  							"__typename": "AggregateCarmodelNameObj",
   924  						},
   925  					},
   926  				},
   927  			}},
   928  		},
   929  	}
   930  
   931  	tests.AssertExtraction(t, "Car")
   932  }
   933  
   934  func (tests testCases) AssertExtraction(t *testing.T, className string) {
   935  	for _, testCase := range tests {
   936  		t.Run(testCase.name, func(t *testing.T) {
   937  			resolver := newMockResolver(config.Config{})
   938  
   939  			expectedParams := &aggregation.Params{
   940  				ClassName:        schema.ClassName(className),
   941  				Properties:       testCase.expectedProps,
   942  				GroupBy:          testCase.expectedGroupBy,
   943  				Filters:          testCase.expectedWhereFilter,
   944  				NearObject:       testCase.expectedNearObjectFilter,
   945  				NearVector:       testCase.expectedNearVectorFilter,
   946  				IncludeMetaCount: testCase.expectedIncludeMetaCount,
   947  				Limit:            testCase.expectedLimit,
   948  				ObjectLimit:      testCase.expectedObjectLimit,
   949  			}
   950  
   951  			resolver.On("Aggregate", expectedParams).
   952  				Return(testCase.resolverReturn, nil).Once()
   953  
   954  			result := resolver.AssertResolve(t, testCase.query)
   955  
   956  			for _, expectedResult := range testCase.expectedResults {
   957  				value := result.Get(expectedResult.pathToField...).Result
   958  
   959  				assert.Equal(t, expectedResult.expectedValue, value)
   960  			}
   961  		})
   962  	}
   963  }
   964  
   965  func ptInt(in int) *int {
   966  	return &in
   967  }