Spaces:
Running
Running
// _ _ | |
// __ _____ __ ___ ___ __ _| |_ ___ | |
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ | |
// \ V V / __/ (_| |\ V /| | (_| | || __/ | |
// \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| | |
// | |
// Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. | |
// | |
// CONTACT: [email protected] | |
// | |
package test | |
import ( | |
"fmt" | |
"testing" | |
"github.com/go-openapi/strfmt" | |
"github.com/weaviate/weaviate/client/objects" | |
clschema "github.com/weaviate/weaviate/client/schema" | |
"github.com/weaviate/weaviate/entities/models" | |
"github.com/weaviate/weaviate/entities/schema" | |
"github.com/weaviate/weaviate/test/helper" | |
testhelper "github.com/weaviate/weaviate/test/helper" | |
) | |
var ( | |
// contextual | |
article1 strfmt.UUID = "dcbe5df8-af01-46f1-b45f-bcc9a7a0773d" // apple macbook | |
article2 strfmt.UUID = "6a8c7b62-fd45-488f-b884-ec87227f6eb3" // ice cream and steak | |
article3 strfmt.UUID = "92f05097-6371-499c-a0fe-3e60ae16fe3d" // president of the us | |
// knn | |
recipeTypeSavory strfmt.UUID = "989d792c-b59e-4430-80a3-cf7f320f31b0" | |
recipeTypeSweet strfmt.UUID = "c9dfda02-6b05-4117-9d95-a188342cca48" | |
unclassifiedSavory strfmt.UUID = "953c03f8-d61e-44c0-bbf1-2afe0dc1ce87" | |
unclassifiedSweet strfmt.UUID = "04603002-cb66-4fce-bf6d-56bdf9b0b5d4" | |
// zeroshot | |
foodTypeMeat strfmt.UUID = "998d792c-b59e-4430-80a3-cf7f320f31b0" | |
foodTypeIceCream strfmt.UUID = "998d792c-b59e-4430-80a3-cf7f320f31b1" | |
unclassifiedSteak strfmt.UUID = "953c03f8-d61e-44c0-bbf1-2afe0dc1ce10" | |
unclassifiedIceCreams strfmt.UUID = "953c03f8-d61e-44c0-bbf1-2afe0dc1ce11" | |
) | |
func Test_Classifications(t *testing.T) { | |
t.Run("article/category setup for contextual classification", setupArticleCategory) | |
t.Run("recipe setup for knn classification", setupRecipe) | |
t.Run("food types and recipes setup for zeroshot classification", setupFoodTypes) | |
// tests | |
t.Run("contextual classification", contextualClassification) | |
t.Run("knn classification", knnClassification) | |
t.Run("zeroshot classification", zeroshotClassification) | |
// tear down | |
deleteObjectClass(t, "Article") | |
deleteObjectClass(t, "Category") | |
deleteObjectClass(t, "Recipe") | |
deleteObjectClass(t, "RecipeType") | |
deleteObjectClass(t, "FoodType") | |
deleteObjectClass(t, "Recipes") | |
} | |
func setupArticleCategory(t *testing.T) { | |
t.Run("schema setup", func(t *testing.T) { | |
createObjectClass(t, &models.Class{ | |
Class: "Category", | |
ModuleConfig: map[string]interface{}{ | |
"text2vec-contextionary": map[string]interface{}{ | |
"vectorizeClassName": true, | |
}, | |
}, | |
Properties: []*models.Property{ | |
{ | |
Name: "name", | |
DataType: schema.DataTypeText.PropString(), | |
Tokenization: models.PropertyTokenizationWhitespace, | |
}, | |
}, | |
}) | |
createObjectClass(t, &models.Class{ | |
Class: "Article", | |
ModuleConfig: map[string]interface{}{ | |
"text2vec-contextionary": map[string]interface{}{ | |
"vectorizeClassName": true, | |
}, | |
}, | |
Properties: []*models.Property{ | |
{ | |
Name: "content", | |
DataType: []string{"text"}, | |
}, | |
{ | |
Name: "OfCategory", | |
DataType: []string{"Category"}, | |
}, | |
}, | |
}) | |
}) | |
t.Run("object setup - categories", func(t *testing.T) { | |
createObject(t, &models.Object{ | |
Class: "Category", | |
Properties: map[string]interface{}{ | |
"name": "Food and Drink", | |
}, | |
}) | |
createObject(t, &models.Object{ | |
Class: "Category", | |
Properties: map[string]interface{}{ | |
"name": "Computers and Technology", | |
}, | |
}) | |
createObject(t, &models.Object{ | |
Class: "Category", | |
Properties: map[string]interface{}{ | |
"name": "Politics", | |
}, | |
}) | |
}) | |
t.Run("object setup - articles", func(t *testing.T) { | |
createObject(t, &models.Object{ | |
ID: article1, | |
Class: "Article", | |
Properties: map[string]interface{}{ | |
"content": "The new Apple Macbook 16 inch provides great performance", | |
}, | |
}) | |
createObject(t, &models.Object{ | |
ID: article2, | |
Class: "Article", | |
Properties: map[string]interface{}{ | |
"content": "I love eating ice cream with my t-bone steak", | |
}, | |
}) | |
createObject(t, &models.Object{ | |
ID: article3, | |
Class: "Article", | |
Properties: map[string]interface{}{ | |
"content": "Barack Obama was the 44th president of the united states", | |
}, | |
}) | |
}) | |
assertGetObjectEventually(t, "92f05097-6371-499c-a0fe-3e60ae16fe3d") | |
} | |
func setupRecipe(t *testing.T) { | |
t.Run("schema setup", func(t *testing.T) { | |
createObjectClass(t, &models.Class{ | |
Class: "RecipeType", | |
ModuleConfig: map[string]interface{}{ | |
"text2vec-contextionary": map[string]interface{}{ | |
"vectorizeClassName": true, | |
}, | |
}, | |
Properties: []*models.Property{ | |
{ | |
Name: "name", | |
DataType: schema.DataTypeText.PropString(), | |
Tokenization: models.PropertyTokenizationWhitespace, | |
}, | |
}, | |
}) | |
createObjectClass(t, &models.Class{ | |
Class: "Recipe", | |
ModuleConfig: map[string]interface{}{ | |
"text2vec-contextionary": map[string]interface{}{ | |
"vectorizeClassName": true, | |
}, | |
}, | |
Properties: []*models.Property{ | |
{ | |
Name: "content", | |
DataType: []string{"text"}, | |
}, | |
{ | |
Name: "OfType", | |
DataType: []string{"RecipeType"}, | |
}, | |
}, | |
}) | |
}) | |
t.Run("object setup - recipe types", func(t *testing.T) { | |
createObject(t, &models.Object{ | |
Class: "RecipeType", | |
ID: recipeTypeSavory, | |
Properties: map[string]interface{}{ | |
"name": "Savory", | |
}, | |
}) | |
createObject(t, &models.Object{ | |
Class: "RecipeType", | |
ID: recipeTypeSweet, | |
Properties: map[string]interface{}{ | |
"name": "Sweet", | |
}, | |
}) | |
}) | |
t.Run("object setup - articles", func(t *testing.T) { | |
createObject(t, &models.Object{ | |
Class: "Recipe", | |
Properties: map[string]interface{}{ | |
"content": "Mix two eggs with milk and 7 grams of sugar, bake in the oven at 200 degrees", | |
"ofType": []interface{}{ | |
map[string]interface{}{ | |
"beacon": fmt.Sprintf("weaviate://localhost/%s", recipeTypeSweet), | |
}, | |
}, | |
}, | |
}) | |
createObject(t, &models.Object{ | |
Class: "Recipe", | |
Properties: map[string]interface{}{ | |
"content": "Sautee the apples with sugar and add a dash of milk.", | |
"ofType": []interface{}{ | |
map[string]interface{}{ | |
"beacon": fmt.Sprintf("weaviate://localhost/%s", recipeTypeSweet), | |
}, | |
}, | |
}, | |
}) | |
createObject(t, &models.Object{ | |
Class: "Recipe", | |
Properties: map[string]interface{}{ | |
"content": "Mix butter, cream and sugar. Make eggwhites fluffy and mix with the batter", | |
"ofType": []interface{}{ | |
map[string]interface{}{ | |
"beacon": fmt.Sprintf("weaviate://localhost/%s", recipeTypeSweet), | |
}, | |
}, | |
}, | |
}) | |
createObject(t, &models.Object{ | |
Class: "Recipe", | |
Properties: map[string]interface{}{ | |
"content": "Fry the steak in the pan, then sautee the onions in the same pan", | |
"ofType": []interface{}{ | |
map[string]interface{}{ | |
"beacon": fmt.Sprintf("weaviate://localhost/%s", recipeTypeSavory), | |
}, | |
}, | |
}, | |
}) | |
createObject(t, &models.Object{ | |
Class: "Recipe", | |
Properties: map[string]interface{}{ | |
"content": "Cut the potatoes in half and add salt and pepper. Serve with the meat.", | |
"ofType": []interface{}{ | |
map[string]interface{}{ | |
"beacon": fmt.Sprintf("weaviate://localhost/%s", recipeTypeSavory), | |
}, | |
}, | |
}, | |
}) | |
createObject(t, &models.Object{ | |
Class: "Recipe", | |
Properties: map[string]interface{}{ | |
"content": "Put the pasta and sauce mix in the oven, top with plenty of cheese", | |
"ofType": []interface{}{ | |
map[string]interface{}{ | |
"beacon": fmt.Sprintf("weaviate://localhost/%s", recipeTypeSavory), | |
}, | |
}, | |
}, | |
}) | |
createObject(t, &models.Object{ | |
ID: unclassifiedSavory, | |
Class: "Recipe", | |
Properties: map[string]interface{}{ | |
"content": "Serve the steak with fries and ketchup.", | |
}, | |
}) | |
createObject(t, &models.Object{ | |
ID: unclassifiedSweet, | |
Class: "Recipe", | |
Properties: map[string]interface{}{ | |
"content": "Whisk the cream, add sugar and serve with strawberries", | |
}, | |
}) | |
}) | |
assertGetObjectEventually(t, unclassifiedSweet) | |
} | |
func setupFoodTypes(t *testing.T) { | |
t.Run("schema setup", func(t *testing.T) { | |
createObjectClass(t, &models.Class{ | |
Class: "FoodType", | |
ModuleConfig: map[string]interface{}{ | |
"text2vec-contextionary": map[string]interface{}{ | |
"vectorizeClassName": true, | |
}, | |
}, | |
Properties: []*models.Property{ | |
{ | |
Name: "text", | |
DataType: schema.DataTypeText.PropString(), | |
Tokenization: models.PropertyTokenizationWhitespace, | |
}, | |
}, | |
}) | |
createObjectClass(t, &models.Class{ | |
Class: "Recipes", | |
ModuleConfig: map[string]interface{}{ | |
"text2vec-contextionary": map[string]interface{}{ | |
"vectorizeClassName": true, | |
}, | |
}, | |
Properties: []*models.Property{ | |
{ | |
Name: "text", | |
DataType: []string{"text"}, | |
}, | |
{ | |
Name: "ofFoodType", | |
DataType: []string{"FoodType"}, | |
}, | |
}, | |
}) | |
}) | |
t.Run("object setup - food types", func(t *testing.T) { | |
createObject(t, &models.Object{ | |
Class: "FoodType", | |
ID: foodTypeIceCream, | |
Properties: map[string]interface{}{ | |
"text": "Ice cream", | |
}, | |
}) | |
createObject(t, &models.Object{ | |
Class: "FoodType", | |
ID: foodTypeMeat, | |
Properties: map[string]interface{}{ | |
"text": "Meat", | |
}, | |
}) | |
}) | |
t.Run("object setup - recipes", func(t *testing.T) { | |
createObject(t, &models.Object{ | |
Class: "Recipes", | |
ID: unclassifiedSteak, | |
Properties: map[string]interface{}{ | |
"text": "Cut the steak in half and put it into pan", | |
}, | |
}) | |
createObject(t, &models.Object{ | |
Class: "Recipes", | |
ID: unclassifiedIceCreams, | |
Properties: map[string]interface{}{ | |
"text": "There are flavors of vanilla, chocolate and strawberry", | |
}, | |
}) | |
}) | |
} | |
func createObjectClass(t *testing.T, class *models.Class) { | |
params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(class) | |
resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil) | |
helper.AssertRequestOk(t, resp, err, nil) | |
} | |
func createObject(t *testing.T, object *models.Object) { | |
params := objects.NewObjectsCreateParams().WithBody(object) | |
resp, err := helper.Client(t).Objects.ObjectsCreate(params, nil) | |
helper.AssertRequestOk(t, resp, err, nil) | |
} | |
func deleteObjectClass(t *testing.T, class string) { | |
delParams := clschema.NewSchemaObjectsDeleteParams().WithClassName(class) | |
delRes, err := helper.Client(t).Schema.SchemaObjectsDelete(delParams, nil) | |
helper.AssertRequestOk(t, delRes, err, nil) | |
} | |
func assertGetObjectEventually(t *testing.T, uuid strfmt.UUID) *models.Object { | |
var ( | |
resp *objects.ObjectsGetOK | |
err error | |
) | |
checkThunk := func() interface{} { | |
resp, err = helper.Client(t).Objects.ObjectsGet(objects.NewObjectsGetParams().WithID(uuid), nil) | |
return err == nil | |
} | |
testhelper.AssertEventuallyEqual(t, true, checkThunk) | |
var object *models.Object | |
helper.AssertRequestOk(t, resp, err, func() { | |
object = resp.Payload | |
}) | |
return object | |
} | |