github.com/weaviate/weaviate@v1.24.6/test/acceptance/multi_tenancy/gql_get_tenant_objects_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 test
    13  
    14  import (
    15  	"fmt"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/go-openapi/strfmt"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  	"github.com/weaviate/weaviate/entities/models"
    23  	"github.com/weaviate/weaviate/entities/schema"
    24  	"github.com/weaviate/weaviate/test/helper"
    25  	graphqlhelper "github.com/weaviate/weaviate/test/helper/graphql"
    26  )
    27  
    28  func TestGQLGetTenantObjects(t *testing.T) {
    29  	testClass := models.Class{
    30  		Class: "MultiTenantClass",
    31  		MultiTenancyConfig: &models.MultiTenancyConfig{
    32  			Enabled: true,
    33  		},
    34  		Properties: []*models.Property{
    35  			{
    36  				Name:     "name",
    37  				DataType: schema.DataTypeText.PropString(),
    38  			},
    39  			{
    40  				Name:     "text",
    41  				DataType: schema.DataTypeText.PropString(),
    42  			},
    43  		},
    44  		Vectorizer: "text2vec-contextionary",
    45  	}
    46  	tenant := "Tenant1"
    47  	otherTenant := "otherTenant"
    48  	tenantObjects := []*models.Object{
    49  		{
    50  			ID:    "0927a1e0-398e-4e76-91fb-04a7a8f0405c",
    51  			Class: testClass.Class,
    52  			Properties: map[string]interface{}{
    53  				"name": tenant,
    54  				"text": "meat",
    55  			},
    56  			Tenant: tenant,
    57  		},
    58  		{
    59  			ID:    "831ae1d0-f441-44b1-bb2a-46548048e26f",
    60  			Class: testClass.Class,
    61  			Properties: map[string]interface{}{
    62  				"name": tenant,
    63  				"text": "bananas",
    64  			},
    65  			Tenant: tenant,
    66  		},
    67  		{
    68  			ID:    "6f3363e0-c0a0-4618-bf1f-b6cad9cdff59",
    69  			Class: testClass.Class,
    70  			Properties: map[string]interface{}{
    71  				"name": tenant,
    72  				"text": "kiwi",
    73  			},
    74  			Tenant: tenant,
    75  		},
    76  		{
    77  			ID:    "6f3363e0-c0a0-4618-bf1f-b6cad9cdff59",
    78  			Class: testClass.Class,
    79  			Properties: map[string]interface{}{
    80  				"name": tenant,
    81  				"text": "kiwi",
    82  			},
    83  			Tenant: otherTenant,
    84  		},
    85  	}
    86  
    87  	// add more objects for other tenant, won't show up in search
    88  
    89  	defer func() {
    90  		helper.DeleteClass(t, testClass.Class)
    91  	}()
    92  
    93  	helper.CreateClass(t, &testClass)
    94  	helper.CreateTenants(t, testClass.Class, []*models.Tenant{{Name: tenant}, {Name: otherTenant}})
    95  	helper.CreateObjectsBatch(t, tenantObjects)
    96  
    97  	t.Run("Test tenant objects", func(t *testing.T) {
    98  		for _, obj := range tenantObjects {
    99  			resp, err := helper.TenantObject(t, obj.Class, obj.ID, obj.Tenant)
   100  			require.Nil(t, err)
   101  			assert.Equal(t, obj.ID, resp.ID)
   102  			assert.Equal(t, obj.Class, resp.Class)
   103  			assert.Equal(t, obj.Properties, resp.Properties)
   104  		}
   105  	})
   106  
   107  	t.Run("GQL Get tenant objects", func(t *testing.T) {
   108  		expectedIDs := map[strfmt.UUID]bool{}
   109  		for _, obj := range tenantObjects {
   110  			expectedIDs[obj.ID] = false
   111  		}
   112  
   113  		query := fmt.Sprintf(`{Get{%s(tenant:%q){_additional{id}}}}`, testClass.Class, tenant)
   114  		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, query)
   115  		for _, obj := range result.Get("Get", testClass.Class).AsSlice() {
   116  			id := obj.(map[string]any)["_additional"].(map[string]any)["id"].(string)
   117  			if _, ok := expectedIDs[strfmt.UUID(id)]; ok {
   118  				expectedIDs[strfmt.UUID(id)] = true
   119  			} else {
   120  				t.Fatalf("found unexpected id %q", id)
   121  			}
   122  		}
   123  
   124  		for id, found := range expectedIDs {
   125  			if !found {
   126  				t.Fatalf("expected to find id %q, but didn't", id)
   127  			}
   128  		}
   129  	})
   130  
   131  	t.Run("GQL near objects", func(t *testing.T) {
   132  		expectedIDs := map[strfmt.UUID]bool{}
   133  		for _, obj := range tenantObjects {
   134  			expectedIDs[obj.ID] = false
   135  		}
   136  
   137  		query := fmt.Sprintf(`{Get{%s(nearObject:{id: %q}, tenant:%q){_additional{id}}}}`, testClass.Class, tenantObjects[0].ID, tenant)
   138  		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, query)
   139  		res := result.Get("Get", testClass.Class)
   140  		require.NotNil(t, res) // objects have no content, so no result
   141  	})
   142  
   143  	t.Run("GQL near text", func(t *testing.T) {
   144  		expectedIDs := map[strfmt.UUID]bool{}
   145  		for _, obj := range tenantObjects {
   146  			expectedIDs[obj.ID] = false
   147  		}
   148  
   149  		query := fmt.Sprintf(`{Get{%s(nearText:{concepts: "apple", moveTo: {concepts: ["fruit"], force: 0.1}, moveAwayFrom: {objects: [{id:"0927a1e0-398e-4e76-91fb-04a7a8f0405c"}], force: 0.1}}, tenant:%q){_additional{id}}}}`, testClass.Class, tenant)
   150  		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, query)
   151  		res := result.Get("Get", testClass.Class)
   152  		require.NotNil(t, res)
   153  		require.Len(t, res.Result, 3) // don't find object from other tenants
   154  	})
   155  
   156  	t.Run("GQL bm25", func(t *testing.T) {
   157  		expectedIDs := map[strfmt.UUID]bool{}
   158  		for _, obj := range tenantObjects {
   159  			expectedIDs[obj.ID] = false
   160  		}
   161  
   162  		query := fmt.Sprintf(`{Get{%s(bm25:{query: "kiwi"}, tenant:%q){_additional{id}}}}`, testClass.Class, tenant)
   163  		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, query)
   164  		res := result.Get("Get", testClass.Class)
   165  		require.NotNil(t, res)
   166  		require.Len(t, res.Result, 1) // don't find object from other tenants
   167  	})
   168  
   169  	t.Run("GQL hybrid", func(t *testing.T) {
   170  		expectedIDs := map[strfmt.UUID]bool{}
   171  		for _, obj := range tenantObjects {
   172  			expectedIDs[obj.ID] = false
   173  		}
   174  
   175  		query := fmt.Sprintf(`{Get{%s(hybrid:{query: "kiwi", alpha: 0.1}, tenant:%q, autocut:1){text _additional{id}}}}`, testClass.Class, tenant)
   176  		result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, query)
   177  		res := result.Get("Get", testClass.Class)
   178  		require.NotNil(t, res)
   179  		require.Len(t, res.Result, 1) // find only relevant results from tenant
   180  	})
   181  }
   182  
   183  func TestGQLGetTenantObjects_MissingTenant(t *testing.T) {
   184  	testClass := models.Class{
   185  		Class: "MultiTenantClass",
   186  		MultiTenancyConfig: &models.MultiTenancyConfig{
   187  			Enabled: true,
   188  		},
   189  		Properties: []*models.Property{
   190  			{
   191  				Name:     "name",
   192  				DataType: schema.DataTypeText.PropString(),
   193  			},
   194  		},
   195  	}
   196  	tenantName := "Tenant1"
   197  	tenantObjects := []*models.Object{
   198  		{
   199  			ID:    "0927a1e0-398e-4e76-91fb-04a7a8f0405c",
   200  			Class: testClass.Class,
   201  			Properties: map[string]interface{}{
   202  				"name": tenantName,
   203  			},
   204  			Tenant: tenantName,
   205  		},
   206  		{
   207  			ID:    "831ae1d0-f441-44b1-bb2a-46548048e26f",
   208  			Class: testClass.Class,
   209  			Properties: map[string]interface{}{
   210  				"name": tenantName,
   211  			},
   212  			Tenant: tenantName,
   213  		},
   214  		{
   215  			ID:    "6f3363e0-c0a0-4618-bf1f-b6cad9cdff59",
   216  			Class: testClass.Class,
   217  			Properties: map[string]interface{}{
   218  				"name": tenantName,
   219  			},
   220  			Tenant: tenantName,
   221  		},
   222  	}
   223  
   224  	defer func() {
   225  		helper.DeleteClass(t, testClass.Class)
   226  	}()
   227  
   228  	helper.CreateClass(t, &testClass)
   229  	helper.CreateTenants(t, testClass.Class, []*models.Tenant{{Name: tenantName}})
   230  	helper.CreateObjectsBatch(t, tenantObjects)
   231  
   232  	for _, obj := range tenantObjects {
   233  		resp, err := helper.TenantObject(t, obj.Class, obj.ID, tenantName)
   234  		require.Nil(t, err)
   235  		assert.Equal(t, obj.ID, resp.ID)
   236  		assert.Equal(t, obj.Class, resp.Class)
   237  		assert.Equal(t, obj.Properties, resp.Properties)
   238  	}
   239  
   240  	query := fmt.Sprintf(`{Get{%s{_additional{id}}}}`, testClass.Class)
   241  	result, err := graphqlhelper.QueryGraphQL(t, helper.RootAuth, "", query, nil)
   242  	require.Nil(t, err)
   243  	require.Len(t, result.Errors, 1)
   244  	assert.Nil(t, result.Data["Get"].(map[string]interface{})[testClass.Class])
   245  	msg := fmt.Sprintf(`explorer: list class: search: object search at index %s: `,
   246  		strings.ToLower(testClass.Class)) +
   247  		fmt.Sprintf(`class %s has multi-tenancy enabled, but request was without tenant`, testClass.Class)
   248  	assert.Equal(t, result.Errors[0].Message, msg)
   249  }