Repository: weaviate/weaviate (Go)

Issue:
pagination limit not adhered to when using nearVector/Object/Text search
With the addition of unlimited vector search, Weaviate is able to exhaustively search all vectors within a specified certainty if not limit is provided to the query, or with a limit of `-1`.

However, it was determined by a Weaviate user that when running a query with a certainty and a (positive) limit, the provided limit was not adhered to and Weaviate returned all results beyond the specified limit.


Discussion:

Patch:
diff --git a/adapters/handlers/graphql/local/get/class_builder_fields.go b/adapters/handlers/graphql/local/get/class_builder_fields.go
index 42177bad273..78716874978 100644
--- a/adapters/handlers/graphql/local/get/class_builder_fields.go
+++ b/adapters/handlers/graphql/local/get/class_builder_fields.go
@@ -389,7 +389,7 @@ func setLimitBasedOnVectorSearchParams(params *traverser.GetParams) {
 			params.Pagination = &filters.Pagination{
 				Limit: filters.LimitFlagSearchByDist,
 			}
-		} else {
+		} else if params.Pagination.Limit < 0 {
 			params.Pagination.Limit = filters.LimitFlagSearchByDist
 		}
 	}
diff --git a/usecases/objects/auto_schema.go b/usecases/objects/auto_schema.go
index 5719c08c733..9c931ef4207 100644
--- a/usecases/objects/auto_schema.go
+++ b/usecases/objects/auto_schema.go
@@ -58,6 +58,10 @@ func (m *autoSchemaManager) performAutoSchema(ctx context.Context, principal *mo
 	object *models.Object) error {
 	m.mutex.Lock()
 	defer m.mutex.Unlock()
+	if object == nil {
+		return fmt.Errorf(validation.ErrorMissingObject)
+	}
+
 	if len(object.Class) == 0 {
 		// stop performing auto schema
 		return fmt.Errorf(validation.ErrorMissingClass)
diff --git a/usecases/objects/validation/model_validation.go b/usecases/objects/validation/model_validation.go
index 4293a22c12c..d6e0b9d6e79 100644
--- a/usecases/objects/validation/model_validation.go
+++ b/usecases/objects/validation/model_validation.go
@@ -43,6 +43,8 @@ const (
 	ErrorMissingActionObjectsSubjectType string = "no 'type' is found in the subject-thing. Add the 'type' inside the 'subject-thing' part of the JSON"
 	// ErrorInvalidActionObjectsSubjectType message
 	ErrorInvalidActionObjectsSubjectType string = "subject-thing requires one of the following values in 'type': '%s', '%s' or '%s'"
+	// ErrorMisingObject message
+	ErrorMissingObject = "the given object is empty"
 	// ErrorMissingClass message
 	ErrorMissingClass string = "the given class is empty"
 	// ErrorMissingContext message


Test patch:
diff --git a/adapters/handlers/graphql/local/get/get_test.go b/adapters/handlers/graphql/local/get/get_test.go
index 7a145209b60..d393a1f97b5 100644
--- a/adapters/handlers/graphql/local/get/get_test.go
+++ b/adapters/handlers/graphql/local/get/get_test.go
@@ -819,6 +819,88 @@ func TestNearCustomTextRanker(t *testing.T) {
 
 		resolver.AssertResolve(t, query)
 	})
+
+	t.Run("for things with optional certainty and limit set", func(t *testing.T) {
+		query := `{ Get { SomeThing(
+							limit: 6
+							nearCustomText: {
+	            				concepts: ["c1", "c2", "c3"],
+								certainty: 0.4,
+								moveTo: {
+									concepts:["positive"],
+									force: 0.5
+								}
+								moveAwayFrom: {
+									concepts:["epic"]
+									force: 0.25
+								}
+	    			}) { intField } } }`
+
+		expectedParams := traverser.GetParams{
+			ClassName:  "SomeThing",
+			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
+			Pagination: &filters.Pagination{Limit: 6},
+			ModuleParams: map[string]interface{}{
+				"nearCustomText": extractNearTextParam(map[string]interface{}{
+					"concepts":  []interface{}{"c1", "c2", "c3"},
+					"certainty": float64(0.4),
+					"moveTo": map[string]interface{}{
+						"concepts": []interface{}{"positive"},
+						"force":    float64(0.5),
+					},
+					"moveAwayFrom": map[string]interface{}{
+						"concepts": []interface{}{"epic"},
+						"force":    float64(0.25),
+					},
+				}),
+			},
+		}
+		resolver.On("GetClass", expectedParams).
+			Return([]interface{}{}, nil).Once()
+
+		resolver.AssertResolve(t, query)
+	})
+
+	t.Run("for things with optional certainty and negative limit set", func(t *testing.T) {
+		query := `{ Get { SomeThing(
+							limit: -1
+							nearCustomText: {
+	            				concepts: ["c1", "c2", "c3"],
+								certainty: 0.4,
+								moveTo: {
+									concepts:["positive"],
+									force: 0.5
+								}
+								moveAwayFrom: {
+									concepts:["epic"]
+									force: 0.25
+								}
+	    			}) { intField } } }`
+
+		expectedParams := traverser.GetParams{
+			ClassName:  "SomeThing",
+			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
+			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
+			ModuleParams: map[string]interface{}{
+				"nearCustomText": extractNearTextParam(map[string]interface{}{
+					"concepts":  []interface{}{"c1", "c2", "c3"},
+					"certainty": float64(0.4),
+					"moveTo": map[string]interface{}{
+						"concepts": []interface{}{"positive"},
+						"force":    float64(0.5),
+					},
+					"moveAwayFrom": map[string]interface{}{
+						"concepts": []interface{}{"epic"},
+						"force":    float64(0.25),
+					},
+				}),
+			},
+		}
+		resolver.On("GetClass", expectedParams).
+			Return([]interface{}{}, nil).Once()
+
+		resolver.AssertResolve(t, query)
+	})
 }
 
 func TestNearVectorRanker(t *testing.T) {
@@ -865,6 +947,54 @@ func TestNearVectorRanker(t *testing.T) {
 
 		resolver.AssertResolve(t, query)
 	})
+
+	t.Run("for things with optional certainty and limit set", func(t *testing.T) {
+		query := `{ Get { SomeThing(
+					limit: 4  
+    				nearVector: {
+					  vector: [0.123, 0.984] 
+					  certainty: 0.1
+        			}) { intField } } }`
+
+		expectedParams := traverser.GetParams{
+			ClassName:  "SomeThing",
+			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
+			Pagination: &filters.Pagination{Limit: 4},
+			NearVector: &traverser.NearVectorParams{
+				Vector:    []float32{0.123, 0.984},
+				Certainty: 0.1,
+			},
+		}
+
+		resolver.On("GetClass", expectedParams).
+			Return([]interface{}{}, nil).Once()
+
+		resolver.AssertResolve(t, query)
+	})
+
+	t.Run("for things with optional certainty and negative limit set", func(t *testing.T) {
+		query := `{ Get { SomeThing(
+					limit: -1  
+    				nearVector: {
+					  vector: [0.123, 0.984] 
+					  certainty: 0.1
+        			}) { intField } } }`
+
+		expectedParams := traverser.GetParams{
+			ClassName:  "SomeThing",
+			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
+			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
+			NearVector: &traverser.NearVectorParams{
+				Vector:    []float32{0.123, 0.984},
+				Certainty: 0.1,
+			},
+		}
+
+		resolver.On("GetClass", expectedParams).
+			Return([]interface{}{}, nil).Once()
+
+		resolver.AssertResolve(t, query)
+	})
 }
 
 func TestExtractPagination(t *testing.T) {
@@ -1169,6 +1299,52 @@ func TestNearObject(t *testing.T) {
 
 		resolver.AssertResolve(t, query)
 	})
+
+	t.Run("for objects with optional certainty and limit set", func(t *testing.T) {
+		query := `{ Get { SomeThing(
+						limit: 5
+						nearObject: {
+						  id: "some-other-uuid"
+						  certainty: 0.7
+						}) { intField } } }`
+
+		expectedParams := traverser.GetParams{
+			ClassName:  "SomeThing",
+			Pagination: &filters.Pagination{Limit: 5},
+			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
+			NearObject: &traverser.NearObjectParams{
+				ID:        "some-other-uuid",
+				Certainty: 0.7,
+			},
+		}
+		resolver.On("GetClass", expectedParams).
+			Return([]interface{}{}, nil).Once()
+
+		resolver.AssertResolve(t, query)
+	})
+
+	t.Run("for objects with optional certainty and negative limit set", func(t *testing.T) {
+		query := `{ Get { SomeThing(
+						limit: -1
+						nearObject: {
+						  id: "some-other-uuid"
+						  certainty: 0.7
+						}) { intField } } }`
+
+		expectedParams := traverser.GetParams{
+			ClassName:  "SomeThing",
+			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
+			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
+			NearObject: &traverser.NearObjectParams{
+				ID:        "some-other-uuid",
+				Certainty: 0.7,
+			},
+		}
+		resolver.On("GetClass", expectedParams).
+			Return([]interface{}{}, nil).Once()
+
+		resolver.AssertResolve(t, query)
+	})
 }
 
 func TestNearTextNoNoModules(t *testing.T) {
@@ -1226,8 +1402,56 @@ func TestNearObjectNoModules(t *testing.T) {
 		resolver.AssertResolve(t, query)
 	})
 
-	t.Run("for objects with ID", func(t *testing.T) {
+	t.Run("for objects with ID and certainty set", func(t *testing.T) {
+		query := `{ Get { SomeThing(
+								nearObject: {
+									id: "some-uuid"
+									certainty: 0.7
+								}) { intField } } }`
+
+		expectedParams := traverser.GetParams{
+			ClassName:  "SomeThing",
+			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
+			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
+			NearObject: &traverser.NearObjectParams{
+				ID:        "some-uuid",
+				Certainty: 0.7,
+			},
+		}
+
+		resolver.On("GetClass", expectedParams).
+			Return([]interface{}{}, nil).Once()
+
+		resolver.AssertResolve(t, query)
+	})
+
+	t.Run("for objects with certainty and limit set", func(t *testing.T) {
 		query := `{ Get { SomeThing(
+								limit: 12
+								nearObject: {
+									id: "some-uuid"
+									certainty: 0.7
+								}) { intField } } }`
+
+		expectedParams := traverser.GetParams{
+			ClassName:  "SomeThing",
+			Pagination: &filters.Pagination{Limit: 12},
+			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
+			NearObject: &traverser.NearObjectParams{
+				ID:        "some-uuid",
+				Certainty: 0.7,
+			},
+		}
+
+		resolver.On("GetClass", expectedParams).
+			Return([]interface{}{}, nil).Once()
+
+		resolver.AssertResolve(t, query)
+	})
+
+	t.Run("for objects with certainty and negative limit set", func(t *testing.T) {
+		query := `{ Get { SomeThing(
+								limit: -1
 								nearObject: {
 									id: "some-uuid"
 									certainty: 0.7
@@ -1294,6 +1518,52 @@ func TestNearVectorNoModules(t *testing.T) {
 
 		resolver.AssertResolve(t, query)
 	})
+
+	t.Run("for things with optional certainty and limit set", func(t *testing.T) {
+		query := `{ Get { SomeThing(
+						limit: 4
+						nearVector: {
+						  vector: [0.123, 0.984] 
+						  certainty: 0.4
+        				}) { intField } } }`
+
+		expectedParams := traverser.GetParams{
+			ClassName:  "SomeThing",
+			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
+			Pagination: &filters.Pagination{Limit: 4},
+			NearVector: &traverser.NearVectorParams{
+				Vector:    []float32{0.123, 0.984},
+				Certainty: 0.4,
+			},
+		}
+		resolver.On("GetClass", expectedParams).
+			Return([]interface{}{}, nil).Once()
+
+		resolver.AssertResolve(t, query)
+	})
+
+	t.Run("for things with optional certainty and negative limit set", func(t *testing.T) {
+		query := `{ Get { SomeThing(
+						limit: -1
+						nearVector: {
+						  vector: [0.123, 0.984] 
+						  certainty: 0.4
+        				}) { intField } } }`
+
+		expectedParams := traverser.GetParams{
+			ClassName:  "SomeThing",
+			Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}},
+			Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist},
+			NearVector: &traverser.NearVectorParams{
+				Vector:    []float32{0.123, 0.984},
+				Certainty: 0.4,
+			},
+		}
+		resolver.On("GetClass", expectedParams).
+			Return([]interface{}{}, nil).Once()
+
+		resolver.AssertResolve(t, query)
+	})
 }
 
 func ptFloat32(in float32) *float32 {
diff --git a/test/acceptance/graphql_resolvers/local_get_with_unlimited_vector_search.go b/test/acceptance/graphql_resolvers/local_get_with_unlimited_vector_search.go
new file mode 100644
index 00000000000..c1290d9b026
--- /dev/null
+++ b/test/acceptance/graphql_resolvers/local_get_with_unlimited_vector_search.go
@@ -0,0 +1,338 @@
+package test
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/semi-technologies/weaviate/test/acceptance/helper"
+	"github.com/stretchr/testify/require"
+)
+
+func gettingObjectsWithNearFields(t *testing.T) {
+	defaultLimit := 100
+
+	// nearVector
+
+	t.Run("nearVector: with implicit unlimited search - no limit provided", func(t *testing.T) {
+		query := `
+		{
+			Get {
+				RansomNote(
+					nearVector: {
+						certainty: 0.1
+						vector: [-0.07853702, -0.33730024, 0.62998116, 0.08703484, -0.0011832615, 0.23041481, -0.091878965, 0.1184808, 0.060692377, 0.1748896, 0.53659165, 0.12019079, 0.54373807, -0.43369776, 0.1843199, -0.19319294, 0.122559674, -0.09465141, -0.14069664, 0.031092037, -0.1763922, 0.0074394196, -0.2586067, 0.10990611, -0.18623954, -0.038631044, -0.22795723, 0.09296776, -0.31110525, -0.37963995, -0.19045947, 0.48089907, 0.46725857, 0.28541213, 0.08294283, -0.18865398, 0.09647029, 0.2321466, -0.03435125, -0.09602424, -0.3831683, -0.027315892, 0.4215511, -0.35865632, 0.41955224, 0.090477064, 0.29026023, -0.48485047, -0.24656451, -0.06478625, 0.07755969, -0.049564634, 0.026147474, -0.028342195, -0.035627227, 0.49309397, 0.3705331, 0.04615483, 0.14789912, -0.01220134, 0.300666, -0.246646, 0.0038986988, 0.16730541, 0.46581128, -0.04931062, 0.040290095, 0.32867354, -0.18300997, 0.30411696, -0.1969807, 0.4727539, -0.31915516, -0.32722718, 0.12694982, 0.22583495, -0.014532595, -0.14432396, 0.2469766, 0.14872919, -0.06750808, 0.06351308, -0.287756, -0.32118404, 0.25326216, 0.45288888, -0.36307186, 0.05369787, -0.3283361, 0.07754738, 0.38473788, -0.5120014, -0.3344492, -0.1102767, -0.16755687, -0.3575448, -0.2555015, -0.42976367, -0.2695758, 0.04068499, 0.591914, -0.008395256, 0.2613976, -0.51722556, -0.22581989, 0.036452737, 0.42190477, -0.256124, 0.25849363, -0.073515825, -0.08690646, 0.013338611, 0.14928678, 0.16371651, 0.111465625, -0.117571846, -0.44434816, 0.07603647, 0.4188736, -0.16967061, 0.040313836, 0.41924894, -0.36998197, 0.23204626, -0.23309743, -0.18061559, 1.0674918, -0.51468146, -0.37230963, 0.02214618, -0.5616187, -0.07473461, -0.3314831, -0.24691144, -0.34061712, -0.1511554, 0.33711013, 0.1055847, -0.047220375, -0.06317049, -0.22572862, -0.21646689, 0.090705894, 0.018562902, 0.020744732, -0.5342965, -0.23402104, -0.17680043, 0.1363935, -0.17916845, 0.37825805, -0.07233101, -0.28244817, 0.4055966, 0.19701958, 0.6236174, 0.078134544, 0.46439224, -0.60451704, 0.16722181, -0.20011653, 0.36931068, -0.39967215, 0.21178648, 0.47920865, -0.033521328, 0.57077545, -0.8003054, -0.4028354, 0.27799648, -0.23070334, 0.57747835, 0.49984616, -0.12409506, -0.26694623, -0.20168623, -0.19415514, -0.4626071, 0.10374411, 0.24878122, 0.47352287, -0.6494472, -0.26087105, 0.418008, -0.2789803, -0.60986733, -0.54914564, 0.4734504, 0.04347568, -0.13549352, 0.1530609, 0.085039385, -0.014595425, -0.1106091, 0.014441653, 0.14899726, -0.107090004, 0.03979257, 0.20897605, -0.040235993, 0.1928343, -0.048328623, 0.5435577, -0.1704212, -0.016530415, 0.11402996, 0.24666561, -0.62601864, 0.6729872, -0.21594357, -0.3161654, 0.2899072, -0.05281632, 0.026857251, 0.13927892, 0.26362655, 0.37995058, -0.056429606, 0.27310744, -0.34237143, -0.6419976, -0.02513231, -0.18217334, 0.021232722, -0.35155025, 0.055071075, -0.22192729, 0.4597671, 0.09872845, -0.41803727, -0.08897542, -0.63276047, 0.38059604, 0.45347637, 0.52723855, 0.25096536, -0.3165448, 0.43803728, 0.02419832, 0.317004, -0.059602205, 0.15561013, 0.11867607, 0.7157601, 0.08024589, -0.013107148, 0.3127224, -0.08844044, 0.5374578, 0.39421698, -0.054171022, 0.0913302, -0.081881694, 0.24596375, -0.2841653, -0.5482517, -0.5673938, 0.05889957, -0.1146344, 0.39452744, -0.03414711, 0.32027423, 0.2599335, -0.31470263, 0.45967287, -0.5710101, -0.21222454, 0.38154987, -0.21218868, -0.4366558, 0.13715877, 0.23925674, 0.34832072, -0.03769251, 0.25530148, 0.10662722, -0.5269836, 0.32952255, 0.46165445, 0.3794754, -0.061259665, 0.02883365, -0.3199015, 0.40625557, -0.3794913, 0.42420092, 0.4631467, 0.54236996, 0.031472385, 0.2635622, -0.25566247, -0.040713936, 0.48734123, 0.2742017, -0.15524681, 0.025654443, 0.056942068, -0.48883253, 0.60433495, 0.03514151]
+					}
+				) {
+					_additional {
+						vector
+					}
+					contents
+				}
+			}
+		}
+		`
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Greater(t, len(notes), defaultLimit)
+	})
+
+	t.Run("nearVector: with implicit unlimited search - negative limit provided", func(t *testing.T) {
+		query := `
+		{
+			Get {
+				RansomNote(
+					limit: -1
+					nearVector: {
+						certainty: 0.1
+						vector: [-0.07853702, -0.33730024, 0.62998116, 0.08703484, -0.0011832615, 0.23041481, -0.091878965, 0.1184808, 0.060692377, 0.1748896, 0.53659165, 0.12019079, 0.54373807, -0.43369776, 0.1843199, -0.19319294, 0.122559674, -0.09465141, -0.14069664, 0.031092037, -0.1763922, 0.0074394196, -0.2586067, 0.10990611, -0.18623954, -0.038631044, -0.22795723, 0.09296776, -0.31110525, -0.37963995, -0.19045947, 0.48089907, 0.46725857, 0.28541213, 0.08294283, -0.18865398, 0.09647029, 0.2321466, -0.03435125, -0.09602424, -0.3831683, -0.027315892, 0.4215511, -0.35865632, 0.41955224, 0.090477064, 0.29026023, -0.48485047, -0.24656451, -0.06478625, 0.07755969, -0.049564634, 0.026147474, -0.028342195, -0.035627227, 0.49309397, 0.3705331, 0.04615483, 0.14789912, -0.01220134, 0.300666, -0.246646, 0.0038986988, 0.16730541, 0.46581128, -0.04931062, 0.040290095, 0.32867354, -0.18300997, 0.30411696, -0.1969807, 0.4727539, -0.31915516, -0.32722718, 0.12694982, 0.22583495, -0.014532595, -0.14432396, 0.2469766, 0.14872919, -0.06750808, 0.06351308, -0.287756, -0.32118404, 0.25326216, 0.45288888, -0.36307186, 0.05369787, -0.3283361, 0.07754738, 0.38473788, -0.5120014, -0.3344492, -0.1102767, -0.16755687, -0.3575448, -0.2555015, -0.42976367, -0.2695758, 0.04068499, 0.591914, -0.008395256, 0.2613976, -0.51722556, -0.22581989, 0.036452737, 0.42190477, -0.256124, 0.25849363, -0.073515825, -0.08690646, 0.013338611, 0.14928678, 0.16371651, 0.111465625, -0.117571846, -0.44434816, 0.07603647, 0.4188736, -0.16967061, 0.040313836, 0.41924894, -0.36998197, 0.23204626, -0.23309743, -0.18061559, 1.0674918, -0.51468146, -0.37230963, 0.02214618, -0.5616187, -0.07473461, -0.3314831, -0.24691144, -0.34061712, -0.1511554, 0.33711013, 0.1055847, -0.047220375, -0.06317049, -0.22572862, -0.21646689, 0.090705894, 0.018562902, 0.020744732, -0.5342965, -0.23402104, -0.17680043, 0.1363935, -0.17916845, 0.37825805, -0.07233101, -0.28244817, 0.4055966, 0.19701958, 0.6236174, 0.078134544, 0.46439224, -0.60451704, 0.16722181, -0.20011653, 0.36931068, -0.39967215, 0.21178648, 0.47920865, -0.033521328, 0.57077545, -0.8003054, -0.4028354, 0.27799648, -0.23070334, 0.57747835, 0.49984616, -0.12409506, -0.26694623, -0.20168623, -0.19415514, -0.4626071, 0.10374411, 0.24878122, 0.47352287, -0.6494472, -0.26087105, 0.418008, -0.2789803, -0.60986733, -0.54914564, 0.4734504, 0.04347568, -0.13549352, 0.1530609, 0.085039385, -0.014595425, -0.1106091, 0.014441653, 0.14899726, -0.107090004, 0.03979257, 0.20897605, -0.040235993, 0.1928343, -0.048328623, 0.5435577, -0.1704212, -0.016530415, 0.11402996, 0.24666561, -0.62601864, 0.6729872, -0.21594357, -0.3161654, 0.2899072, -0.05281632, 0.026857251, 0.13927892, 0.26362655, 0.37995058, -0.056429606, 0.27310744, -0.34237143, -0.6419976, -0.02513231, -0.18217334, 0.021232722, -0.35155025, 0.055071075, -0.22192729, 0.4597671, 0.09872845, -0.41803727, -0.08897542, -0.63276047, 0.38059604, 0.45347637, 0.52723855, 0.25096536, -0.3165448, 0.43803728, 0.02419832, 0.317004, -0.059602205, 0.15561013, 0.11867607, 0.7157601, 0.08024589, -0.013107148, 0.3127224, -0.08844044, 0.5374578, 0.39421698, -0.054171022, 0.0913302, -0.081881694, 0.24596375, -0.2841653, -0.5482517, -0.5673938, 0.05889957, -0.1146344, 0.39452744, -0.03414711, 0.32027423, 0.2599335, -0.31470263, 0.45967287, -0.5710101, -0.21222454, 0.38154987, -0.21218868, -0.4366558, 0.13715877, 0.23925674, 0.34832072, -0.03769251, 0.25530148, 0.10662722, -0.5269836, 0.32952255, 0.46165445, 0.3794754, -0.061259665, 0.02883365, -0.3199015, 0.40625557, -0.3794913, 0.42420092, 0.4631467, 0.54236996, 0.031472385, 0.2635622, -0.25566247, -0.040713936, 0.48734123, 0.2742017, -0.15524681, 0.025654443, 0.056942068, -0.48883253, 0.60433495, 0.03514151]
+					}
+				) {
+					_additional {
+						vector
+					}
+					contents
+				}
+			}
+		}
+		`
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Greater(t, len(notes), defaultLimit)
+	})
+
+	t.Run("nearVector: with limited search - limit provided", func(t *testing.T) {
+		query := `
+		{
+			Get {
+				RansomNote(
+					limit: 10
+					nearVector: {
+						certainty: 0.1
+						vector: [-0.07853702, -0.33730024, 0.62998116, 0.08703484, -0.0011832615, 0.23041481, -0.091878965, 0.1184808, 0.060692377, 0.1748896, 0.53659165, 0.12019079, 0.54373807, -0.43369776, 0.1843199, -0.19319294, 0.122559674, -0.09465141, -0.14069664, 0.031092037, -0.1763922, 0.0074394196, -0.2586067, 0.10990611, -0.18623954, -0.038631044, -0.22795723, 0.09296776, -0.31110525, -0.37963995, -0.19045947, 0.48089907, 0.46725857, 0.28541213, 0.08294283, -0.18865398, 0.09647029, 0.2321466, -0.03435125, -0.09602424, -0.3831683, -0.027315892, 0.4215511, -0.35865632, 0.41955224, 0.090477064, 0.29026023, -0.48485047, -0.24656451, -0.06478625, 0.07755969, -0.049564634, 0.026147474, -0.028342195, -0.035627227, 0.49309397, 0.3705331, 0.04615483, 0.14789912, -0.01220134, 0.300666, -0.246646, 0.0038986988, 0.16730541, 0.46581128, -0.04931062, 0.040290095, 0.32867354, -0.18300997, 0.30411696, -0.1969807, 0.4727539, -0.31915516, -0.32722718, 0.12694982, 0.22583495, -0.014532595, -0.14432396, 0.2469766, 0.14872919, -0.06750808, 0.06351308, -0.287756, -0.32118404, 0.25326216, 0.45288888, -0.36307186, 0.05369787, -0.3283361, 0.07754738, 0.38473788, -0.5120014, -0.3344492, -0.1102767, -0.16755687, -0.3575448, -0.2555015, -0.42976367, -0.2695758, 0.04068499, 0.591914, -0.008395256, 0.2613976, -0.51722556, -0.22581989, 0.036452737, 0.42190477, -0.256124, 0.25849363, -0.073515825, -0.08690646, 0.013338611, 0.14928678, 0.16371651, 0.111465625, -0.117571846, -0.44434816, 0.07603647, 0.4188736, -0.16967061, 0.040313836, 0.41924894, -0.36998197, 0.23204626, -0.23309743, -0.18061559, 1.0674918, -0.51468146, -0.37230963, 0.02214618, -0.5616187, -0.07473461, -0.3314831, -0.24691144, -0.34061712, -0.1511554, 0.33711013, 0.1055847, -0.047220375, -0.06317049, -0.22572862, -0.21646689, 0.090705894, 0.018562902, 0.020744732, -0.5342965, -0.23402104, -0.17680043, 0.1363935, -0.17916845, 0.37825805, -0.07233101, -0.28244817, 0.4055966, 0.19701958, 0.6236174, 0.078134544, 0.46439224, -0.60451704, 0.16722181, -0.20011653, 0.36931068, -0.39967215, 0.21178648, 0.47920865, -0.033521328, 0.57077545, -0.8003054, -0.4028354, 0.27799648, -0.23070334, 0.57747835, 0.49984616, -0.12409506, -0.26694623, -0.20168623, -0.19415514, -0.4626071, 0.10374411, 0.24878122, 0.47352287, -0.6494472, -0.26087105, 0.418008, -0.2789803, -0.60986733, -0.54914564, 0.4734504, 0.04347568, -0.13549352, 0.1530609, 0.085039385, -0.014595425, -0.1106091, 0.014441653, 0.14899726, -0.107090004, 0.03979257, 0.20897605, -0.040235993, 0.1928343, -0.048328623, 0.5435577, -0.1704212, -0.016530415, 0.11402996, 0.24666561, -0.62601864, 0.6729872, -0.21594357, -0.3161654, 0.2899072, -0.05281632, 0.026857251, 0.13927892, 0.26362655, 0.37995058, -0.056429606, 0.27310744, -0.34237143, -0.6419976, -0.02513231, -0.18217334, 0.021232722, -0.35155025, 0.055071075, -0.22192729, 0.4597671, 0.09872845, -0.41803727, -0.08897542, -0.63276047, 0.38059604, 0.45347637, 0.52723855, 0.25096536, -0.3165448, 0.43803728, 0.02419832, 0.317004, -0.059602205, 0.15561013, 0.11867607, 0.7157601, 0.08024589, -0.013107148, 0.3127224, -0.08844044, 0.5374578, 0.39421698, -0.054171022, 0.0913302, -0.081881694, 0.24596375, -0.2841653, -0.5482517, -0.5673938, 0.05889957, -0.1146344, 0.39452744, -0.03414711, 0.32027423, 0.2599335, -0.31470263, 0.45967287, -0.5710101, -0.21222454, 0.38154987, -0.21218868, -0.4366558, 0.13715877, 0.23925674, 0.34832072, -0.03769251, 0.25530148, 0.10662722, -0.5269836, 0.32952255, 0.46165445, 0.3794754, -0.061259665, 0.02883365, -0.3199015, 0.40625557, -0.3794913, 0.42420092, 0.4631467, 0.54236996, 0.031472385, 0.2635622, -0.25566247, -0.040713936, 0.48734123, 0.2742017, -0.15524681, 0.025654443, 0.056942068, -0.48883253, 0.60433495, 0.03514151]
+					}
+				) {
+					_additional {
+						vector
+					}
+					contents
+				}
+			}
+		}
+		`
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Equal(t, 10, len(notes))
+	})
+
+	t.Run("nearVector: results limited by certainty", func(t *testing.T) {
+		query := `
+		{
+			Get {
+				RansomNote(
+					nearVector: {
+						certainty: 0.99
+						vector: [-0.07853702, -0.33730024, 0.62998116, 0.08703484, -0.0011832615, 0.23041481, -0.091878965, 0.1184808, 0.060692377, 0.1748896, 0.53659165, 0.12019079, 0.54373807, -0.43369776, 0.1843199, -0.19319294, 0.122559674, -0.09465141, -0.14069664, 0.031092037, -0.1763922, 0.0074394196, -0.2586067, 0.10990611, -0.18623954, -0.038631044, -0.22795723, 0.09296776, -0.31110525, -0.37963995, -0.19045947, 0.48089907, 0.46725857, 0.28541213, 0.08294283, -0.18865398, 0.09647029, 0.2321466, -0.03435125, -0.09602424, -0.3831683, -0.027315892, 0.4215511, -0.35865632, 0.41955224, 0.090477064, 0.29026023, -0.48485047, -0.24656451, -0.06478625, 0.07755969, -0.049564634, 0.026147474, -0.028342195, -0.035627227, 0.49309397, 0.3705331, 0.04615483, 0.14789912, -0.01220134, 0.300666, -0.246646, 0.0038986988, 0.16730541, 0.46581128, -0.04931062, 0.040290095, 0.32867354, -0.18300997, 0.30411696, -0.1969807, 0.4727539, -0.31915516, -0.32722718, 0.12694982, 0.22583495, -0.014532595, -0.14432396, 0.2469766, 0.14872919, -0.06750808, 0.06351308, -0.287756, -0.32118404, 0.25326216, 0.45288888, -0.36307186, 0.05369787, -0.3283361, 0.07754738, 0.38473788, -0.5120014, -0.3344492, -0.1102767, -0.16755687, -0.3575448, -0.2555015, -0.42976367, -0.2695758, 0.04068499, 0.591914, -0.008395256, 0.2613976, -0.51722556, -0.22581989, 0.036452737, 0.42190477, -0.256124, 0.25849363, -0.073515825, -0.08690646, 0.013338611, 0.14928678, 0.16371651, 0.111465625, -0.117571846, -0.44434816, 0.07603647, 0.4188736, -0.16967061, 0.040313836, 0.41924894, -0.36998197, 0.23204626, -0.23309743, -0.18061559, 1.0674918, -0.51468146, -0.37230963, 0.02214618, -0.5616187, -0.07473461, -0.3314831, -0.24691144, -0.34061712, -0.1511554, 0.33711013, 0.1055847, -0.047220375, -0.06317049, -0.22572862, -0.21646689, 0.090705894, 0.018562902, 0.020744732, -0.5342965, -0.23402104, -0.17680043, 0.1363935, -0.17916845, 0.37825805, -0.07233101, -0.28244817, 0.4055966, 0.19701958, 0.6236174, 0.078134544, 0.46439224, -0.60451704, 0.16722181, -0.20011653, 0.36931068, -0.39967215, 0.21178648, 0.47920865, -0.033521328, 0.57077545, -0.8003054, -0.4028354, 0.27799648, -0.23070334, 0.57747835, 0.49984616, -0.12409506, -0.26694623, -0.20168623, -0.19415514, -0.4626071, 0.10374411, 0.24878122, 0.47352287, -0.6494472, -0.26087105, 0.418008, -0.2789803, -0.60986733, -0.54914564, 0.4734504, 0.04347568, -0.13549352, 0.1530609, 0.085039385, -0.014595425, -0.1106091, 0.014441653, 0.14899726, -0.107090004, 0.03979257, 0.20897605, -0.040235993, 0.1928343, -0.048328623, 0.5435577, -0.1704212, -0.016530415, 0.11402996, 0.24666561, -0.62601864, 0.6729872, -0.21594357, -0.3161654, 0.2899072, -0.05281632, 0.026857251, 0.13927892, 0.26362655, 0.37995058, -0.056429606, 0.27310744, -0.34237143, -0.6419976, -0.02513231, -0.18217334, 0.021232722, -0.35155025, 0.055071075, -0.22192729, 0.4597671, 0.09872845, -0.41803727, -0.08897542, -0.63276047, 0.38059604, 0.45347637, 0.52723855, 0.25096536, -0.3165448, 0.43803728, 0.02419832, 0.317004, -0.059602205, 0.15561013, 0.11867607, 0.7157601, 0.08024589, -0.013107148, 0.3127224, -0.08844044, 0.5374578, 0.39421698, -0.054171022, 0.0913302, -0.081881694, 0.24596375, -0.2841653, -0.5482517, -0.5673938, 0.05889957, -0.1146344, 0.39452744, -0.03414711, 0.32027423, 0.2599335, -0.31470263, 0.45967287, -0.5710101, -0.21222454, 0.38154987, -0.21218868, -0.4366558, 0.13715877, 0.23925674, 0.34832072, -0.03769251, 0.25530148, 0.10662722, -0.5269836, 0.32952255, 0.46165445, 0.3794754, -0.061259665, 0.02883365, -0.3199015, 0.40625557, -0.3794913, 0.42420092, 0.4631467, 0.54236996, 0.031472385, 0.2635622, -0.25566247, -0.040713936, 0.48734123, 0.2742017, -0.15524681, 0.025654443, 0.056942068, -0.48883253, 0.60433495, 0.03514151]
+					}
+				) {
+					_additional {
+						id
+					}
+					contents
+				}
+			}
+		}
+		`
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Less(t, len(notes), defaultLimit)
+	})
+
+	// nearObject
+
+	nearObjID := getOneExistingID(t, "RansomNote")
+
+	t.Run("nearObject: with implicit unlimited search - no limit provided", func(t *testing.T) {
+		query := fmt.Sprintf(`
+		{
+			Get {
+				RansomNote(
+					nearObject: {
+						certainty: 0.1
+						id: "%s"
+					}
+				) {
+					contents
+				}
+			}
+		}
+		`, nearObjID)
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Greater(t, len(notes), defaultLimit)
+	})
+
+	t.Run("nearObject: with implicit unlimited search - negative limit provided", func(t *testing.T) {
+		query := fmt.Sprintf(`
+		{
+			Get {
+				RansomNote(
+					limit: -1
+					nearObject: {
+						certainty: 0.1
+						id: "%s"
+					}
+				) {
+					contents
+				}
+			}
+		}
+		`, nearObjID)
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Greater(t, len(notes), defaultLimit)
+	})
+
+	t.Run("nearObject: with limited search - limit provided", func(t *testing.T) {
+		query := fmt.Sprintf(`
+		{
+			Get {
+				RansomNote(
+					limit: 13
+					nearObject: {
+						certainty: 0.1
+						id: "%s"
+					}
+				) {
+					contents
+				}
+			}
+		}
+		`, nearObjID)
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Equal(t, 13, len(notes))
+	})
+
+	t.Run("nearObject: results limited by certainty", func(t *testing.T) {
+		query := fmt.Sprintf(`
+		{
+			Get {
+				RansomNote(
+					nearObject: {
+						certainty: 0.99
+						id: "%s"
+					}
+				) {
+					_additional {
+						id
+					}
+					contents
+				}
+			}
+		}
+		`, nearObjID)
+
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Less(t, len(notes), defaultLimit)
+	})
+
+	// nearText
+
+	t.Run("nearText: with implicit unlimited search - no limit provided", func(t *testing.T) {
+		query := `
+		{
+			Get {
+				RansomNote(
+					nearText: {
+						certainty: 0.1
+						concepts: ["abcd"]
+					}
+				) {
+					contents
+				}
+			}
+		}
+		`
+
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Greater(t, len(notes), defaultLimit)
+	})
+
+	t.Run("nearText: with implicit unlimited search - negative limit provided", func(t *testing.T) {
+		query := `
+		{
+			Get {
+				RansomNote(
+					limit: -1
+					nearText: {
+						certainty: 0.1
+						concepts: ["abcd"]
+					}
+				) {
+					contents
+				}
+			}
+		}
+		`
+
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Greater(t, len(notes), defaultLimit)
+	})
+
+	t.Run("nearText: with limited search - limit provided", func(t *testing.T) {
+		query := `
+		{
+			Get {
+				RansomNote(
+					limit: 5
+					nearText: {
+						certainty: 0.1
+						concepts: ["abcd"]
+					}
+				) {
+					contents
+				}
+			}
+		}
+		`
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.NotEmpty(t, notes)
+		require.Equal(t, 5, len(notes))
+	})
+
+	t.Run("nearText: results limited by certainty", func(t *testing.T) {
+		query := `
+		{
+			Get {
+				RansomNote(
+					nearText: {
+						certainty: 0.9
+						concepts: ["abcd"]
+					}
+				) {
+					_additional {
+						id
+					}
+					contents
+				}
+			}
+		}
+		`
+
+		result := AssertGraphQL(t, helper.RootAuth, query)
+		notes := result.Get("Get", "RansomNote").AsSlice()
+		require.Less(t, len(notes), defaultLimit)
+	})
+}
+
+func getOneExistingID(t *testing.T, className string) string {
+	query := fmt.Sprintf("{Get {%s(limit: 1) {_additional {id}}}}", className)
+	result := AssertGraphQL(t, helper.RootAuth, query).Result.(map[string]interface{})
+
+	classes, ok := result["Get"].(map[string]interface{})
+	if !ok {
+		t.Fatalf("Get response is not a map[string]interface{}")
+	}
+
+	class, ok := classes[className]
+	if !ok {
+		t.Fatalf("class %s was not included in response", className)
+	}
+
+	classSlice, ok := class.([]interface{})
+	if !ok {
+		t.Fatalf("%s response is not a []interface{}", className)
+	}
+	require.NotEmpty(t, class, "getOneExistingID empty class response for %s", className)
+
+	firstItem, ok := classSlice[0].(map[string]interface{})
+	if !ok {
+		t.Fatalf("first item is not a map[string]interface{}")
+	}
+
+	additional, ok := firstItem["_additional"].(map[string]interface{})
+	if !ok {
+		t.Fatalf("_additional result is not a map[string]interface{}")
+	}
+
+	id, ok := additional["id"].(string)
+	if !ok {
+		t.Fatalf("id is not a string")
+	}
+
+	return id
+}
diff --git a/test/acceptance/graphql_resolvers/setup_test.go b/test/acceptance/graphql_resolvers/setup_test.go
index 4ff8f2a6061..5c174996b0d 100644
--- a/test/acceptance/graphql_resolvers/setup_test.go
+++ b/test/acceptance/graphql_resolvers/setup_test.go
@@ -12,6 +12,7 @@
 package test
 
 import (
+	"math/rand"
 	"testing"
 	"time"
 
@@ -34,6 +35,7 @@ func Test_GraphQL(t *testing.T) {
 	t.Run("import test data (pizzas)", addTestDataPizzas)
 	t.Run("import test data (custom vector class)", addTestDataCVC)
 	t.Run("import test data (array class)", addTestDataArrayClasses)
+	t.Run("import test data (500 random strings)", addTestDataRansomNotes)
 
 	// tests
 	t.Run("getting objects", gettingObjects)
@@ -41,6 +43,7 @@ func Test_GraphQL(t *testing.T) {
 	t.Run("getting objects with geo filters", gettingObjectsWithGeoFilters)
 	t.Run("getting objects with grouping", gettingObjectsWithGrouping)
 	t.Run("getting objects with additional props", gettingObjectsWithAdditionalProps)
+	t.Run("getting objects with near fields", gettingObjectsWithNearFields)
 	t.Run("aggregates without grouping or filters", aggregatesWithoutGroupingOrFilters)
 	t.Run("aggregates local meta with filters", localMetaWithFilters)
 	t.Run("aggregates local meta string props not set everywhere", localMeta_StringPropsNotSetEverywhere)
@@ -55,6 +58,7 @@ func Test_GraphQL(t *testing.T) {
 	deleteObjectClass(t, "Airport")
 	deleteObjectClass(t, "Company")
 	deleteObjectClass(t, "ArrayClass")
+	deleteObjectClass(t, "RansomNote")
 
 	// only run after everything else is deleted, this way, we can also run an
 	// all-class Explore since all vectors which are now left have the same
@@ -300,6 +304,21 @@ func addTestSchema(t *testing.T) {
 			},
 		},
 	})
+
+	createObjectClass(t, &models.Class{
+		Class: "RansomNote",
+		ModuleConfig: map[string]interface{}{
+			"text2vec-contextionary": map[string]interface{}{
+				"vectorizeClassName": true,
+			},
+		},
+		Properties: []*models.Property{
+			{
+				Name:     "contents",
+				DataType: []string{"string"},
+			},
+		},
+	})
 }
 
 const (
@@ -702,3 +721,36 @@ func addTestDataArrayClasses(t *testing.T) {
 	})
 	assertGetObjectEventually(t, arrayClassID3)
 }
+
+func addTestDataRansomNotes(t *testing.T) {
+	const (
+		charset = "abcdefghijklmnopqrstuvwxyz" +
+			"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
+
+		noteLengthMin = 4
+		noteLengthMax = 1024
+
+		batchSize  = 10
+		numBatches = 50
+	)
+
+	seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+	for i := 0; i < numBatches; i++ {
+		batch := make([]*models.Object, batchSize)
+		for j := 0; j < batchSize; j++ {
+			noteLength := noteLengthMin + seededRand.Intn(noteLengthMax-noteLengthMin+1)
+			note := make([]byte, noteLength)
+			for n := range note {
+				note[n] = charset[seededRand.Intn(len(charset))]
+			}
+
+			batch[j] = &models.Object{
+				Class:      "RansomNote",
+				Properties: map[string]interface{}{"contents": string(note)},
+			}
+		}
+
+		createObjectsBatch(t, batch)
+	}
+}
diff --git a/usecases/objects/auto_schema_test.go b/usecases/objects/auto_schema_test.go
index 7e4ee17c567..2277de2ecd0 100644
--- a/usecases/objects/auto_schema_test.go
+++ b/usecases/objects/auto_schema_test.go
@@ -14,6 +14,7 @@ package objects
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 	"reflect"
 	"testing"
 
@@ -22,6 +23,7 @@ import (
 	"github.com/semi-technologies/weaviate/entities/schema"
 	"github.com/semi-technologies/weaviate/entities/search"
 	"github.com/semi-technologies/weaviate/usecases/config"
+	"github.com/semi-technologies/weaviate/usecases/objects/validation"
 	"github.com/sirupsen/logrus/hooks/test"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
@@ -274,6 +276,31 @@ func Test_autoSchemaManager_determineType(t *testing.T) {
 	}
 }
 
+func Test_autoSchemaManager_autoSchema_emptyRequest(t *testing.T) {
+	// given
+	vectorRepo := &fakeVectorRepo{}
+	vectorRepo.On("ObjectByID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
+		Return(&search.Result{ClassName: "Publication"}, nil).Once()
+	schemaManager := &fakeSchemaManager{}
+	logger, _ := test.NewNullLogger()
+	autoSchemaManager := &autoSchemaManager{
+		schemaManager: schemaManager,
+		vectorRepo:    vectorRepo,
+		config: config.AutoSchema{
+			Enabled:       true,
+			DefaultString: "string",
+			DefaultNumber: "number",
+			DefaultDate:   "date",
+		},
+		logger: logger,
+	}
+
+	var obj *models.Object
+
+	err := autoSchemaManager.autoSchema(context.Background(), &models.Principal{}, obj)
+	assert.EqualError(t, fmt.Errorf(validation.ErrorMissingObject), err.Error())
+}
+
 func Test_autoSchemaManager_autoSchema_create(t *testing.T) {
 	// given
 	vectorRepo := &fakeVectorRepo{}