//                           _       _
// __      _____  __ ___   ___  __ _| |_ ___
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
//  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
//   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
//
//  Copyright © 2016 - 2026 Weaviate B.V. All rights reserved.
//
//  CONTACT: hello@weaviate.io
//

package test

import (
	"encoding/json"
	"fmt"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	schemaclient "github.com/weaviate/weaviate/client/schema"
	"github.com/weaviate/weaviate/entities/models"
	"github.com/weaviate/weaviate/entities/schema"
	"github.com/weaviate/weaviate/test/helper"
	graphqlhelper "github.com/weaviate/weaviate/test/helper/graphql"
)

// This test prevents a regression on the fix for
// https://github.com/weaviate/weaviate/issues/824
func localMeta_StringPropsNotSetEverywhere(t *testing.T) {
	graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
		{
			Aggregate {
				City {
					name {
						topOccurrences {
							occurs
							value
						}
					}
				}
			}
		}
	`)
}

func localMetaWithWhereAndNearTextFilters(t *testing.T) {
	t.Run("with distance", func(t *testing.T) {
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
		{
			Aggregate{
				City (where: {
					valueBoolean: true,
					operator: Equal,
					path: ["isCapital"]
				}
				nearText: {
					concepts: ["Amsterdam"]
					distance: 0.2
				}
				){
					meta {
						count
					}
					isCapital {
						count
						percentageFalse
						percentageTrue
						totalFalse
						totalTrue
						type
					}
					population {
						mean
						count
						maximum
						minimum
						sum
						type
					}
					inCountry {
						pointingTo
						type
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
			}
		}
	`)

		t.Run("meta count", func(t *testing.T) {
			meta := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			expected := json.Number("1")
			assert.Equal(t, expected, count)
		})

		t.Run("boolean props", func(t *testing.T) {
			isCapital := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["isCapital"]
			expected := map[string]interface{}{
				"count":           json.Number("1"),
				"percentageTrue":  json.Number("1"),
				"percentageFalse": json.Number("0"),
				"totalTrue":       json.Number("1"),
				"totalFalse":      json.Number("0"),
				"type":            "boolean",
			}
			assert.Equal(t, expected, isCapital)
		})

		t.Run("int/number props", func(t *testing.T) {
			population := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["population"]
			expected := map[string]interface{}{
				"mean":    json.Number("1800000"),
				"count":   json.Number("1"),
				"maximum": json.Number("1800000"),
				"minimum": json.Number("1800000"),
				"sum":     json.Number("1800000"),
				"type":    "int",
			}
			assert.Equal(t, expected, population)
		})

		t.Run("ref prop", func(t *testing.T) {
			inCountry := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["inCountry"]
			expected := map[string]interface{}{
				"pointingTo": []interface{}{"Country"},
				"type":       "cref",
			}
			assert.Equal(t, expected, inCountry)
		})

		t.Run("string prop", func(t *testing.T) {
			name := result.Get("Aggregate", "City").
				AsSlice()[0].(map[string]interface{})["name"].(map[string]interface{})
			typeField := name["type"]
			topOccurrences := name["topOccurrences"]

			assert.Equal(t, schema.DataTypeText.String(), typeField)

			expectedTopOccurrences := []interface{}{
				map[string]interface{}{
					"value":  "Amsterdam",
					"occurs": json.Number("1"),
				},
			}
			assert.ElementsMatch(t, expectedTopOccurrences, topOccurrences)
		})
	})

	t.Run("with certainty", func(t *testing.T) {
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
		{
			Aggregate{
				City (where: {
					valueBoolean: true,
					operator: Equal,
					path: ["isCapital"]
				}
				nearText: {
					concepts: ["Amsterdam"]
					certainty: 0.9
				}
				){
					meta {
						count
					}
					isCapital {
						count
						percentageFalse
						percentageTrue
						totalFalse
						totalTrue
						type
					}
					population {
						mean
						count
						maximum
						minimum
						sum
						type
					}
					inCountry {
						pointingTo
						type
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
			}
		}`)

		t.Run("meta count", func(t *testing.T) {
			meta := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			expected := json.Number("1")
			assert.Equal(t, expected, count)
		})

		t.Run("boolean props", func(t *testing.T) {
			isCapital := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["isCapital"]
			expected := map[string]interface{}{
				"count":           json.Number("1"),
				"percentageTrue":  json.Number("1"),
				"percentageFalse": json.Number("0"),
				"totalTrue":       json.Number("1"),
				"totalFalse":      json.Number("0"),
				"type":            "boolean",
			}
			assert.Equal(t, expected, isCapital)
		})

		t.Run("int/number props", func(t *testing.T) {
			population := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["population"]
			expected := map[string]interface{}{
				"mean":    json.Number("1800000"),
				"count":   json.Number("1"),
				"maximum": json.Number("1800000"),
				"minimum": json.Number("1800000"),
				"sum":     json.Number("1800000"),
				"type":    "int",
			}
			assert.Equal(t, expected, population)
		})

		t.Run("ref prop", func(t *testing.T) {
			inCountry := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["inCountry"]
			expected := map[string]interface{}{
				"pointingTo": []interface{}{"Country"},
				"type":       "cref",
			}
			assert.Equal(t, expected, inCountry)
		})

		t.Run("string prop", func(t *testing.T) {
			name := result.Get("Aggregate", "City").
				AsSlice()[0].(map[string]interface{})["name"].(map[string]interface{})
			typeField := name["type"]
			topOccurrences := name["topOccurrences"]

			assert.Equal(t, schema.DataTypeText.String(), typeField)

			expectedTopOccurrences := []interface{}{
				map[string]interface{}{
					"value":  "Amsterdam",
					"occurs": json.Number("1"),
				},
			}
			assert.ElementsMatch(t, expectedTopOccurrences, topOccurrences)
		})
	})
}

func localMetaWithWhereAndNearObjectFilters(t *testing.T) {
	t.Run("with distance", func(t *testing.T) {
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
		{
			Aggregate{
				City (where: {
					valueBoolean: true,
					operator: Equal,
					path: ["isCapital"]
				}
				nearObject: {
					id: "9b9cbea5-e87e-4cd0-89af-e2f424fd52d6"
					distance: 0.2
				}
				){
					meta {
						count
					}
					isCapital {
						count
						percentageFalse
						percentageTrue
						totalFalse
						totalTrue
						type
					}
					population {
						mean
						count
						maximum
						minimum
						sum
						type
					}
					inCountry {
						pointingTo
						type
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
			}
		}`)

		t.Run("meta count", func(t *testing.T) {
			meta := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			expected := json.Number("1")
			assert.Equal(t, expected, count)
		})

		t.Run("boolean props", func(t *testing.T) {
			isCapital := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["isCapital"]
			expected := map[string]interface{}{
				"count":           json.Number("1"),
				"percentageTrue":  json.Number("1"),
				"percentageFalse": json.Number("0"),
				"totalTrue":       json.Number("1"),
				"totalFalse":      json.Number("0"),
				"type":            "boolean",
			}
			assert.Equal(t, expected, isCapital)
		})

		t.Run("int/number props", func(t *testing.T) {
			population := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["population"]
			expected := map[string]interface{}{
				"mean":    json.Number("3470000"),
				"count":   json.Number("1"),
				"maximum": json.Number("3470000"),
				"minimum": json.Number("3470000"),
				"sum":     json.Number("3470000"),
				"type":    "int",
			}
			assert.Equal(t, expected, population)
		})

		t.Run("ref prop", func(t *testing.T) {
			inCountry := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["inCountry"]
			expected := map[string]interface{}{
				"pointingTo": []interface{}{"Country"},
				"type":       "cref",
			}
			assert.Equal(t, expected, inCountry)
		})

		t.Run("string prop", func(t *testing.T) {
			name := result.Get("Aggregate", "City").
				AsSlice()[0].(map[string]interface{})["name"].(map[string]interface{})
			typeField := name["type"]
			topOccurrences := name["topOccurrences"]

			assert.Equal(t, schema.DataTypeText.String(), typeField)

			expectedTopOccurrences := []interface{}{
				map[string]interface{}{
					"value":  "Berlin",
					"occurs": json.Number("1"),
				},
			}
			assert.ElementsMatch(t, expectedTopOccurrences, topOccurrences)
		})
	})

	t.Run("with certainty", func(t *testing.T) {
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
		{
			Aggregate{
				City (where: {
					valueBoolean: true,
					operator: Equal,
					path: ["isCapital"]
				}
				nearObject: {
					id: "9b9cbea5-e87e-4cd0-89af-e2f424fd52d6"
					certainty: 0.9
				}
				){
					meta {
						count
					}
					isCapital {
						count
						percentageFalse
						percentageTrue
						totalFalse
						totalTrue
						type
					}
					population {
						mean
						count
						maximum
						minimum
						sum
						type
					}
					inCountry {
						pointingTo
						type
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
			}
		}`)

		t.Run("meta count", func(t *testing.T) {
			meta := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			expected := json.Number("1")
			assert.Equal(t, expected, count)
		})

		t.Run("boolean props", func(t *testing.T) {
			isCapital := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["isCapital"]
			expected := map[string]interface{}{
				"count":           json.Number("1"),
				"percentageTrue":  json.Number("1"),
				"percentageFalse": json.Number("0"),
				"totalTrue":       json.Number("1"),
				"totalFalse":      json.Number("0"),
				"type":            "boolean",
			}
			assert.Equal(t, expected, isCapital)
		})

		t.Run("int/number props", func(t *testing.T) {
			population := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["population"]
			expected := map[string]interface{}{
				"mean":    json.Number("3470000"),
				"count":   json.Number("1"),
				"maximum": json.Number("3470000"),
				"minimum": json.Number("3470000"),
				"sum":     json.Number("3470000"),
				"type":    "int",
			}
			assert.Equal(t, expected, population)
		})

		t.Run("ref prop", func(t *testing.T) {
			inCountry := result.Get("Aggregate", "City").AsSlice()[0].(map[string]interface{})["inCountry"]
			expected := map[string]interface{}{
				"pointingTo": []interface{}{"Country"},
				"type":       "cref",
			}
			assert.Equal(t, expected, inCountry)
		})

		t.Run("string prop", func(t *testing.T) {
			name := result.Get("Aggregate", "City").
				AsSlice()[0].(map[string]interface{})["name"].(map[string]interface{})
			typeField := name["type"]
			topOccurrences := name["topOccurrences"]

			assert.Equal(t, schema.DataTypeText.String(), typeField)

			expectedTopOccurrences := []interface{}{
				map[string]interface{}{
					"value":  "Berlin",
					"occurs": json.Number("1"),
				},
			}
			assert.ElementsMatch(t, expectedTopOccurrences, topOccurrences)
		})
	})
}

func localMetaWithNearVectorFilter(t *testing.T) {
	t.Run("with distance", func(t *testing.T) {
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
		{
			Aggregate{
				CustomVectorClass(
					nearVector: {
						vector: [1,0,0]
						distance: 0.0002
					}
				){
					meta {
						count
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
			}
		}`)

		t.Run("meta count", func(t *testing.T) {
			meta := result.Get("Aggregate", "CustomVectorClass").AsSlice()[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			expected := json.Number("1")
			assert.Equal(t, expected, count)
		})

		t.Run("string prop", func(t *testing.T) {
			name := result.Get("Aggregate", "CustomVectorClass").
				AsSlice()[0].(map[string]interface{})["name"].(map[string]interface{})
			typeField := name["type"]
			topOccurrences := name["topOccurrences"]

			assert.Equal(t, schema.DataTypeText.String(), typeField)

			expectedTopOccurrences := []interface{}{
				map[string]interface{}{
					"value":  "Mercedes",
					"occurs": json.Number("1"),
				},
			}
			assert.ElementsMatch(t, expectedTopOccurrences, topOccurrences)
		})
	})

	t.Run("with certainty", func(t *testing.T) {
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
		{
			Aggregate{
				CustomVectorClass(
					nearVector: {
						vector: [1,0,0]
						certainty: 0.9999
					}
				){
					meta {
						count
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
			}
		}`)

		t.Run("meta count", func(t *testing.T) {
			meta := result.Get("Aggregate", "CustomVectorClass").AsSlice()[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			expected := json.Number("1")
			assert.Equal(t, expected, count)
		})

		t.Run("string prop", func(t *testing.T) {
			name := result.Get("Aggregate", "CustomVectorClass").
				AsSlice()[0].(map[string]interface{})["name"].(map[string]interface{})
			typeField := name["type"]
			topOccurrences := name["topOccurrences"]

			assert.Equal(t, schema.DataTypeText.String(), typeField)

			expectedTopOccurrences := []interface{}{
				map[string]interface{}{
					"value":  "Mercedes",
					"occurs": json.Number("1"),
				},
			}
			assert.ElementsMatch(t, expectedTopOccurrences, topOccurrences)
		})
	})
}

func localMetaWithWhereAndNearVectorFilters(t *testing.T) {
	t.Run("with distance", func(t *testing.T) {
		t.Run("with expected results, low certainty", func(t *testing.T) {
			result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate {
					CustomVectorClass(
						where: {
							valueText: "Ford"
							operator: Equal
							path: ["name"]
						}
						nearVector: {
							vector: [1,0,0]
							distance: 0.6
						}
					) {
					meta {
						count
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
				}
			}
		`)

			require.NotNil(t, result)

			agg := result.Result.(map[string]interface{})["Aggregate"].(map[string]interface{})
			cls := agg["CustomVectorClass"].([]interface{})
			require.Len(t, cls, 1)
			name := cls[0].(map[string]interface{})["name"].(map[string]interface{})
			topOcc := name["topOccurrences"].([]interface{})
			require.Len(t, topOcc, 1)
			val := topOcc[0].(map[string]interface{})["value"]
			assert.Equal(t, "Ford", val)
		})

		t.Run("with no expected results, low distance", func(t *testing.T) {
			result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate {
					CustomVectorClass(
						where: {
							valueText: "Ford"
							operator: Equal
							path: ["name"]
						}
						nearVector: {
							vector: [1,0,0]
							distance: 0.2
						}
					) {
					meta {
						count
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
				}
			}
		`)

			require.NotNil(t, result)

			agg := result.Result.(map[string]interface{})["Aggregate"].(map[string]interface{})
			cls := agg["CustomVectorClass"].([]interface{})
			require.Len(t, cls, 1)
			name := cls[0].(map[string]interface{})["name"].(map[string]interface{})
			topOcc := name["topOccurrences"].([]interface{})
			require.Len(t, topOcc, 0)
		})

		t.Run("with expected results, low distance", func(t *testing.T) {
			result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate {
					CustomVectorClass(
						where: {
							valueText: "Mercedes"
							operator: Equal
							path: ["name"]
						}
						nearVector: {
							vector: [1,0,0]
							distance: 0.1
						}
					) {
					meta {
						count
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
				}
			}`)

			require.NotNil(t, result)

			agg := result.Result.(map[string]interface{})["Aggregate"].(map[string]interface{})
			cls := agg["CustomVectorClass"].([]interface{})
			require.Len(t, cls, 1)
			name := cls[0].(map[string]interface{})["name"].(map[string]interface{})
			topOcc := name["topOccurrences"].([]interface{})
			require.Len(t, topOcc, 1)
			val := topOcc[0].(map[string]interface{})["value"]
			assert.Equal(t, "Mercedes", val)
		})
	})

	t.Run("with certainty", func(t *testing.T) {
		t.Run("with expected results, low certainty", func(t *testing.T) {
			result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate {
					CustomVectorClass(
						where: {
							valueText: "Ford"
							operator: Equal
							path: ["name"]
						}
						nearVector: {
							vector: [1,0,0]
							certainty: 0.7
						}
					) {
					meta {
						count
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
				}
			}
		`)

			require.NotNil(t, result)

			agg := result.Result.(map[string]interface{})["Aggregate"].(map[string]interface{})
			cls := agg["CustomVectorClass"].([]interface{})
			require.Len(t, cls, 1)
			name := cls[0].(map[string]interface{})["name"].(map[string]interface{})
			topOcc := name["topOccurrences"].([]interface{})
			require.Len(t, topOcc, 1)
			val := topOcc[0].(map[string]interface{})["value"]
			assert.Equal(t, "Ford", val)
		})

		t.Run("with no expected results, high certainty", func(t *testing.T) {
			result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate {
					CustomVectorClass(
						where: {
							valueText: "Ford"
							operator: Equal
							path: ["name"]
						}
						nearVector: {
							vector: [1,0,0]
							certainty: 0.9
						}
					) {
					meta {
						count
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
				}
			}
		`)

			require.NotNil(t, result)

			agg := result.Result.(map[string]interface{})["Aggregate"].(map[string]interface{})
			cls := agg["CustomVectorClass"].([]interface{})
			require.Len(t, cls, 1)
			name := cls[0].(map[string]interface{})["name"].(map[string]interface{})
			topOcc := name["topOccurrences"].([]interface{})
			require.Len(t, topOcc, 0)
		})

		t.Run("with expected results, high certainty", func(t *testing.T) {
			result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate {
					CustomVectorClass(
						where: {
							valueText: "Mercedes"
							operator: Equal
							path: ["name"]
						}
						nearVector: {
							vector: [1,0,0]
							certainty: 0.9
						}
					) {
					meta {
						count
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
				}
			}
		`)

			require.NotNil(t, result)

			agg := result.Result.(map[string]interface{})["Aggregate"].(map[string]interface{})
			cls := agg["CustomVectorClass"].([]interface{})
			require.Len(t, cls, 1)
			name := cls[0].(map[string]interface{})["name"].(map[string]interface{})
			topOcc := name["topOccurrences"].([]interface{})
			require.Len(t, topOcc, 1)
			val := topOcc[0].(map[string]interface{})["value"]
			assert.Equal(t, "Mercedes", val)
		})
	})
}

func localMetaWithWhereGroupByNearMediaFilters(t *testing.T) {
	t.Run("with nearObject", func(t *testing.T) {
		query := `
			{
				Aggregate {
					Company
					(
						groupBy: "name"
						nearObject: {id: "cfa3b21e-ca4f-4db7-a432-7fc6a23c534d", certainty: 0.99}
					) 
					{
						groupedBy {
							value
					  	}
						meta {
							count
						}
					}
				}
			}`

		expected := map[string]interface{}{
			"Aggregate": map[string]interface{}{
				"Company": []interface{}{
					map[string]interface{}{
						"groupedBy": map[string]interface{}{
							"value": "Microsoft Inc.",
						},
						"meta": map[string]interface{}{
							"count": json.Number("1"),
						},
					},
				},
			},
		}

		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, query).Result
		assert.EqualValues(t, expected, result)
	})

	t.Run("with nearText", func(t *testing.T) {
		t.Run("with distance", func(t *testing.T) {
			result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate{
					City (
					groupBy: "population"
					where: {
						valueBoolean: true,
						operator: Equal,
						path: ["isCapital"]
					}
					nearText: {
						concepts: ["Amsterdam"]
						distance: 0.2
					}
					){
						meta {
							count
						}
						groupedBy {
							value
						}
					}
				}
			}
		`)

			expected := map[string]interface{}{
				"Aggregate": map[string]interface{}{
					"City": []interface{}{
						map[string]interface{}{
							"groupedBy": map[string]interface{}{
								"value": "1.8e+06",
							},
							"meta": map[string]interface{}{
								"count": json.Number("1"),
							},
						},
					},
				},
			}

			assert.EqualValues(t, expected, result.Result)
		})

		t.Run("with certainty", func(t *testing.T) {
			result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate{
					City (
					groupBy: "population"
					where: {
						valueBoolean: true,
						operator: Equal,
						path: ["isCapital"]
					}
					nearText: {
						concepts: ["Amsterdam"]
						certainty: 0.9
					}
					){
						meta {
							count
						}
						groupedBy {
							value
						}
					}
				}
			}
		`)

			expected := map[string]interface{}{
				"Aggregate": map[string]interface{}{
					"City": []interface{}{
						map[string]interface{}{
							"groupedBy": map[string]interface{}{
								"value": "1.8e+06",
							},
							"meta": map[string]interface{}{
								"count": json.Number("1"),
							},
						},
					},
				},
			}

			assert.EqualValues(t, expected, result.Result)
		})
	})

	t.Run("with nearVector", func(t *testing.T) {
		getQuery := `
			{
				Get {
					Company(where: {
						path: ["name"]
						operator: Equal
						valueText: "Google Inc."
					})
					{
						_additional {
							vector
						}
					}
				}
			}`

		vectorResult := graphqlhelper.AssertGraphQL(t, helper.RootAuth, getQuery).
			Get("Get", "Company").
			AsSlice()[0].(map[string]interface{})["_additional"].(map[string]interface{})["vector"].([]interface{})

		vector := make([]float32, len(vectorResult))
		for i, ifc := range vectorResult {
			val, err := strconv.ParseFloat(ifc.(json.Number).String(), 32)
			require.Nil(t, err)
			vector[i] = float32(val)
		}

		aggQuery := fmt.Sprintf(`
			{
				Aggregate {
					Company
					(
						groupBy: "name"
						nearVector: {vector: %+v, certainty: 0.99}
					)
					{
						groupedBy {
							value
						}
						meta {
							count
						}
					}
				}
			}
		`, vector)

		aggResult := graphqlhelper.AssertGraphQL(t, helper.RootAuth, aggQuery).Result

		expected := map[string]interface{}{
			"Aggregate": map[string]interface{}{
				"Company": []interface{}{
					map[string]interface{}{
						"groupedBy": map[string]interface{}{
							"value": "Google Inc.",
						},
						"meta": map[string]interface{}{
							"count": json.Number("1"),
						},
					},
				},
			},
		}

		assert.EqualValues(t, expected, aggResult)
	})
}

func localMetaWithObjectLimit(t *testing.T) {
	t.Run("with nearText and no distance/certainty, where filter and groupBy", func(t *testing.T) {
		objectLimit := 4
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, fmt.Sprintf(`
			{
				Aggregate {
					Company (
						groupBy: ["name"]
						where: {
							valueText: "Apple*",
							operator: Like,
							path: ["name"]
						}
						objectLimit: %d
						nearText: {
							concepts: ["Apple"]
							certainty: 0.5
						}
					){
						meta {
							count
						}
						groupedBy {
        					value
						}
					}
				}
			}
		`, objectLimit))

		expected := []interface{}{
			map[string]interface{}{
				"groupedBy": map[string]interface{}{
					"value": "Apple Incorporated",
				},
				"meta": map[string]interface{}{
					"count": json.Number("1"),
				},
			},
			map[string]interface{}{
				"groupedBy": map[string]interface{}{
					"value": "Apple Inc.",
				},
				"meta": map[string]interface{}{
					"count": json.Number("1"),
				},
			},
			map[string]interface{}{
				"groupedBy": map[string]interface{}{
					"value": "Apple",
				},
				"meta": map[string]interface{}{
					"count": json.Number("1"),
				},
			},
		}

		companies := result.Get("Aggregate", "Company").Result.([]interface{})
		for _, company := range companies {
			assert.Contains(t, expected, company)
		}
	})

	t.Run("with nearObject and distance", func(t *testing.T) {
		objectLimit := 1
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, fmt.Sprintf(`
			{
				Aggregate{
					City (
						objectLimit: %d
						nearObject: {
							id: "9b9cbea5-e87e-4cd0-89af-e2f424fd52d6"
							distance: 0.3
						}
					){
						meta {
							count
						}
					}
				}
			}
		`, objectLimit))

		t.Run("validate objectLimit functions as expected", func(t *testing.T) {
			res := result.Get("Aggregate", "City").AsSlice()
			require.Len(t, res, 1)
			meta := res[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			assert.Equal(t, json.Number(fmt.Sprint(objectLimit)), count)
		})
	})

	t.Run("with nearObject and certainty", func(t *testing.T) {
		objectLimit := 1
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, fmt.Sprintf(`
			{
				Aggregate{
					City (
						objectLimit: %d
						nearObject: {
							id: "9b9cbea5-e87e-4cd0-89af-e2f424fd52d6"
							certainty: 0.7
						}
					){
						meta {
							count
						}
					}
				}
			}
		`, objectLimit))

		t.Run("validate objectLimit functions as expected", func(t *testing.T) {
			res := result.Get("Aggregate", "City").AsSlice()
			require.Len(t, res, 1)
			meta := res[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			assert.Equal(t, json.Number(fmt.Sprint(objectLimit)), count)
		})
	})

	t.Run("with nearObject and no certainty", func(t *testing.T) {
		objectLimit := 2
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, fmt.Sprintf(`
			{
				Aggregate{
					City (
						objectLimit: %d
						nearObject: {
							id: "9b9cbea5-e87e-4cd0-89af-e2f424fd52d6"
						}
					){
						meta {
							count
						}
					}
				}
			}
		`, objectLimit))

		t.Run("validate objectLimit functions as expected", func(t *testing.T) {
			res := result.Get("Aggregate", "City").AsSlice()
			require.Len(t, res, 1)
			meta := res[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			assert.Equal(t, json.Number(fmt.Sprint(objectLimit)), count)
		})
	})

	t.Run("with nearObject and very high distance, no objectLimit", func(t *testing.T) {
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate {
   				RansomNote(
     					nearText: {
							concepts: ["abc"]
							distance: 1.9998
     					}
   				) {
					  meta {
						count
					  }
  					}
 				}
			}
		`)

		t.Run("validate nearMedia runs unlimited without objectLimit", func(t *testing.T) {
			res := result.Get("Aggregate", "RansomNote").AsSlice()
			require.Len(t, res, 1)
			meta := res[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			countInt, err := count.(json.Number).Int64()
			require.Nil(t, err)
			assert.InDelta(t, 500, countInt, 1)
		})
	})

	t.Run("with nearObject and very low certainty, no objectLimit", func(t *testing.T) {
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate {
   				RansomNote(
     					nearText: {
							concepts: ["abc"]
							certainty: 0.0001
     					}
   				) {
					  meta {
						count
					  }
  					}
 				}
			}
		`)

		t.Run("validate nearMedia runs unlimited without objectLimit", func(t *testing.T) {
			res := result.Get("Aggregate", "RansomNote").AsSlice()
			require.Len(t, res, 1)
			meta := res[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			countInt, err := count.(json.Number).Int64()
			require.Nil(t, err)
			assert.InDelta(t, 500, countInt, 1)
		})
	})

	t.Run("with nearObject and low distance (few results), high objectLimit", func(t *testing.T) {
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate {
   				RansomNote(
     					nearText: {
							concepts: ["abc"]
							distance: 0.6 # should return about 6 elements
     					}
						  objectLimit:100,
   				) {
					  meta {
						count
					  }
  					}
 				}
			}
		`)

		t.Run("validate fewer than objectLimit elements are returned", func(t *testing.T) {
			res := result.Get("Aggregate", "RansomNote").AsSlice()
			require.Len(t, res, 1)
			meta := res[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			countParsed, err := count.(json.Number).Int64()
			require.Nil(t, err)
			assert.Less(t, countParsed, int64(100))
		})
	})

	t.Run("with nearObject and high certainty (few results), high objectLimit", func(t *testing.T) {
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
			{
				Aggregate {
   				RansomNote(
     					nearText: {
							concepts: ["abc"]
							certainty: 0.7 # should return about 6 elements
     					}
						  objectLimit:100,
   				) {
					  meta {
						count
					  }
  					}
 				}
			}
		`)

		t.Run("validate fewer than objectLimit elements are returned", func(t *testing.T) {
			res := result.Get("Aggregate", "RansomNote").AsSlice()
			require.Len(t, res, 1)
			meta := res[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			countParsed, err := count.(json.Number).Int64()
			require.Nil(t, err)
			assert.Less(t, countParsed, int64(100))
		})
	})

	t.Run("with nearText and no distance/certainty, where filter and groupBy", func(t *testing.T) {
		objectLimit := 4
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, fmt.Sprintf(`
			{
				Aggregate {
					Company (
						groupBy: ["name"]
						where: {
							valueText: "Apple*",
							operator: Like,
							path: ["name"]
						}
						objectLimit: %d
						nearText: {
							concepts: ["Apple"]
							certainty: 0.5
						}
					){
						meta {
							count
						}
						groupedBy {
        					value
						}
					}
				}
			}
		`, objectLimit))

		expected := []interface{}{
			map[string]interface{}{
				"groupedBy": map[string]interface{}{
					"value": "Apple Incorporated",
				},
				"meta": map[string]interface{}{
					"count": json.Number("1"),
				},
			},
			map[string]interface{}{
				"groupedBy": map[string]interface{}{
					"value": "Apple Inc.",
				},
				"meta": map[string]interface{}{
					"count": json.Number("1"),
				},
			},
			map[string]interface{}{
				"groupedBy": map[string]interface{}{
					"value": "Apple",
				},
				"meta": map[string]interface{}{
					"count": json.Number("1"),
				},
			},
		}

		companies := result.Get("Aggregate", "Company").Result.([]interface{})
		for _, company := range companies {
			assert.Contains(t, expected, company)
		}
	})

	t.Run("with nearObject and certainty, where filter", func(t *testing.T) {
		objectLimit := 1
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, fmt.Sprintf(`
			{
				Aggregate{
					City (
						where: {
							valueBoolean: true,
							operator: Equal,
							path: ["isCapital"]
						}
						objectLimit: %d
						nearObject: {
							id: "9b9cbea5-e87e-4cd0-89af-e2f424fd52d6"
						}
					){
						meta {
							count
						}
					}
				}
			}
		`, objectLimit))

		t.Run("validate objectLimit functions as expected", func(t *testing.T) {
			res := result.Get("Aggregate", "City").AsSlice()
			require.Len(t, res, 1)
			meta := res[0].(map[string]interface{})["meta"]
			count := meta.(map[string]interface{})["count"]
			assert.Equal(t, json.Number(fmt.Sprint(objectLimit)), count)
		})
	})
}

func aggregatesOnDateFields(t *testing.T) {
	t.Run("without grouping", func(t *testing.T) {
		query := `
		{
			Aggregate {
				HasDateField {
					timestamp {
						count
						minimum
						maximum
						median
					}
				}
			}
		}`
		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, query).Get("Aggregate", "HasDateField").AsSlice()
		assert.Len(t, result, 1)

		expected := []interface{}{
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("10"),
					"maximum": "2022-06-16T22:19:11.837473Z",
					"median":  "2022-06-16T22:19:06.1449075Z",
					"minimum": "2022-06-16T22:18:59.640162Z",
				},
			},
		}
		assert.Equal(t, expected, result)
	})

	t.Run("with grouping on a unique field", func(t *testing.T) {
		query := `
		{
			Aggregate {
				HasDateField 
				(
					groupBy: "unique"
				)
				{
					timestamp {
						count
						minimum
						maximum
						median
						mode
					}
				}
			}
		}`

		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, query).Get("Aggregate", "HasDateField").AsSlice()
		assert.Len(t, result, 10)

		expected := []interface{}{
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("1"),
					"maximum": "2022-06-16T22:19:05.894857Z",
					"median":  "2022-06-16T22:19:05.894857Z",
					"minimum": "2022-06-16T22:19:05.894857Z",
					"mode":    "2022-06-16T22:19:05.894857Z",
				},
			},
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("1"),
					"maximum": "2022-06-16T22:19:08.112395Z",
					"median":  "2022-06-16T22:19:08.112395Z",
					"minimum": "2022-06-16T22:19:08.112395Z",
					"mode":    "2022-06-16T22:19:08.112395Z",
				},
			},
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("1"),
					"maximum": "2022-06-16T22:19:03.495596Z",
					"median":  "2022-06-16T22:19:03.495596Z",
					"minimum": "2022-06-16T22:19:03.495596Z",
					"mode":    "2022-06-16T22:19:03.495596Z",
				},
			},
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("1"),
					"maximum": "2022-06-16T22:19:07.589828Z",
					"median":  "2022-06-16T22:19:07.589828Z",
					"minimum": "2022-06-16T22:19:07.589828Z",
					"mode":    "2022-06-16T22:19:07.589828Z",
				},
			},
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("1"),
					"maximum": "2022-06-16T22:19:06.394958Z",
					"median":  "2022-06-16T22:19:06.394958Z",
					"minimum": "2022-06-16T22:19:06.394958Z",
					"mode":    "2022-06-16T22:19:06.394958Z",
				},
			},
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("1"),
					"maximum": "2022-06-16T22:19:11.837473Z",
					"median":  "2022-06-16T22:19:11.837473Z",
					"minimum": "2022-06-16T22:19:11.837473Z",
					"mode":    "2022-06-16T22:19:11.837473Z",
				},
			},
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("1"),
					"maximum": "2022-06-16T22:18:59.640162Z",
					"median":  "2022-06-16T22:18:59.640162Z",
					"minimum": "2022-06-16T22:18:59.640162Z",
					"mode":    "2022-06-16T22:18:59.640162Z",
				},
			},
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("1"),
					"maximum": "2022-06-16T22:19:01.495967Z",
					"median":  "2022-06-16T22:19:01.495967Z",
					"minimum": "2022-06-16T22:19:01.495967Z",
					"mode":    "2022-06-16T22:19:01.495967Z",
				},
			},
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("1"),
					"maximum": "2022-06-16T22:19:10.339493Z",
					"median":  "2022-06-16T22:19:10.339493Z",
					"minimum": "2022-06-16T22:19:10.339493Z",
					"mode":    "2022-06-16T22:19:10.339493Z",
				},
			},
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("1"),
					"maximum": "2022-06-16T22:19:04.3828349Z",
					"median":  "2022-06-16T22:19:04.3828349Z",
					"minimum": "2022-06-16T22:19:04.3828349Z",
					"mode":    "2022-06-16T22:19:04.3828349Z",
				},
			},
		}

		for _, res := range result {
			assert.Contains(t, expected, res)
		}
	})

	t.Run("group on identical field", func(t *testing.T) {
		query := `
		{
			Aggregate {
				HasDateField 
				(
					groupBy: "identical"
				)
				{
					timestamp {
						count
						minimum
						maximum
						median
					}
				}
			}
		}`

		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, query).Get("Aggregate", "HasDateField").AsSlice()

		expected := []interface{}{
			map[string]interface{}{
				"timestamp": map[string]interface{}{
					"count":   json.Number("10"),
					"maximum": "2022-06-16T22:19:11.837473Z",
					"median":  "2022-06-16T22:19:06.1449075Z",
					"minimum": "2022-06-16T22:18:59.640162Z",
				},
			},
		}

		assert.Equal(t, expected, result)
	})
}

func aggregatesUsingAlias(t *testing.T) {
	client := helper.Client(t)
	params := schemaclient.NewAliasesCreateParams().WithBody(&models.Alias{
		Alias: "CustomVectorClassAlias",
		Class: "CustomVectorClass",
	})
	_, err := client.Schema.AliasesCreate(params, nil)
	defer func(t *testing.T) {
		params := schemaclient.NewAliasesDeleteParams().WithAliasName("CustomVectorClassAlias")
		_, err := client.Schema.AliasesDelete(params, nil)
		if err != nil {
			t.Logf("Error deleting aliases: %v", err)
		}
	}(t)
	require.Nil(t, err)

	result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, `
		{
			Aggregate{
				CustomVectorClassAlias(
					nearVector: {
						vector: [1,0,0]
						distance: 0.0002
					}
				){
					meta {
						count
					}
					name {
						topOccurrences {
							occurs
							value
						}
						type
						count
					}
				}
			}
		}`)

	t.Run("meta count", func(t *testing.T) {
		meta := result.Get("Aggregate", "CustomVectorClassAlias").AsSlice()[0].(map[string]interface{})["meta"]
		count := meta.(map[string]interface{})["count"]
		expected := json.Number("1")
		assert.Equal(t, expected, count)
	})

	t.Run("string prop", func(t *testing.T) {
		name := result.Get("Aggregate", "CustomVectorClassAlias").
			AsSlice()[0].(map[string]interface{})["name"].(map[string]interface{})
		typeField := name["type"]
		topOccurrences := name["topOccurrences"]

		assert.Equal(t, schema.DataTypeText.String(), typeField)

		expectedTopOccurrences := []interface{}{
			map[string]interface{}{
				"value":  "Mercedes",
				"occurs": json.Number("1"),
			},
		}
		assert.ElementsMatch(t, expectedTopOccurrences, topOccurrences)
	})

	t.Run("assert alias no longer in schema after deletion", func(t *testing.T) {
		params := schemaclient.NewAliasesDeleteParams().WithAliasName("CustomVectorClassAlias")
		_, err := client.Schema.AliasesDelete(params, nil)
		require.Nil(t, err)

		_ = graphqlhelper.ErrorGraphQL(t, helper.RootAuth, `
			{
				Aggregate{
					CustomVectorClassAlias(
						nearVector: {
							vector: [1,0,0]
							distance: 0.0002
						}
					){
						meta {
							count
						}
						name {
							topOccurrences {
								occurs
								value
							}
							type
							count
						}
					}
				}
			}
		`)
	})
}
