github.com/weaviate/weaviate@v1.24.6/adapters/handlers/grpc/v1/batch_parse_request_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 v1
    13  
    14  import (
    15  	"testing"
    16  
    17  	"github.com/stretchr/testify/require"
    18  	"github.com/weaviate/weaviate/entities/models"
    19  	"github.com/weaviate/weaviate/entities/schema"
    20  	pb "github.com/weaviate/weaviate/grpc/generated/protocol/v1"
    21  )
    22  
    23  const (
    24  	UUID3 = "a4de3ca0-6975-464f-b23b-adddd83630d7"
    25  	UUID4 = "7e10ec81-a26d-4ac7-8264-3e3e05397ddc"
    26  )
    27  
    28  func TestGRPCBatchRequest(t *testing.T) {
    29  	collection := "TestClass"
    30  	refClass1 := "OtherClass"
    31  	refClass2 := "AnotherClass"
    32  	multiVecClass := "MultiVec"
    33  	scheme := schema.Schema{
    34  		Objects: &models.Schema{
    35  			Classes: []*models.Class{
    36  				{
    37  					Class: collection,
    38  					Properties: []*models.Property{
    39  						{Name: "name", DataType: schema.DataTypeText.PropString()},
    40  						{Name: "number", DataType: []string{"int"}},
    41  						{Name: "ref", DataType: []string{refClass1}},
    42  						{Name: "multiRef", DataType: []string{refClass1, refClass2}},
    43  					},
    44  				},
    45  				{
    46  					Class: refClass1,
    47  					Properties: []*models.Property{
    48  						{Name: "something", DataType: schema.DataTypeText.PropString()},
    49  						{Name: "ref2", DataType: []string{refClass2}},
    50  					},
    51  				},
    52  				{
    53  					Class: refClass2,
    54  					Properties: []*models.Property{
    55  						{Name: "else", DataType: schema.DataTypeText.PropString()},
    56  						{Name: "ref3", DataType: []string{refClass2}},
    57  					},
    58  				},
    59  				{
    60  					Class: multiVecClass,
    61  					Properties: []*models.Property{
    62  						{Name: "first", DataType: schema.DataTypeText.PropString()},
    63  					},
    64  					VectorConfig: map[string]models.VectorConfig{
    65  						"custom": {
    66  							VectorIndexType: "hnsw",
    67  							Vectorizer:      map[string]interface{}{"none": map[string]interface{}{}},
    68  						},
    69  						"first": {
    70  							VectorIndexType: "flat",
    71  							Vectorizer:      map[string]interface{}{"text2vec-contextionary": map[string]interface{}{}},
    72  						},
    73  					},
    74  				},
    75  			},
    76  		},
    77  	}
    78  
    79  	var nilMap map[string]interface{}
    80  	tests := []struct {
    81  		name      string
    82  		req       []*pb.BatchObject
    83  		out       []*models.Object
    84  		outError  []int
    85  		origIndex map[int]int
    86  	}{
    87  		{
    88  			name: "empty object",
    89  			req:  []*pb.BatchObject{{Collection: collection, Uuid: UUID4}},
    90  			out:  []*models.Object{{Class: collection, Properties: nilMap, ID: UUID4}},
    91  		},
    92  		{
    93  			name:     "no UUID",
    94  			req:      []*pb.BatchObject{{Collection: collection}},
    95  			out:      []*models.Object{},
    96  			outError: []int{0},
    97  		},
    98  		{
    99  			name: "only normal props",
   100  			req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{
   101  				NonRefProperties: newStruct(t, map[string]interface{}{
   102  					"name": "something",
   103  					"age":  45,
   104  				}),
   105  			}}},
   106  			out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{
   107  				"name": "something",
   108  				"age":  float64(45),
   109  			}}},
   110  		},
   111  		{
   112  			name: "only single refs",
   113  			req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{
   114  				SingleTargetRefProps: []*pb.BatchObject_SingleTargetRefProps{
   115  					{PropName: "ref", Uuids: []string{UUID3, UUID4}},
   116  				},
   117  			}}},
   118  			out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{
   119  				"ref": []interface{}{
   120  					map[string]interface{}{"beacon": BEACON_START + refClass1 + "/" + UUID3},
   121  					map[string]interface{}{"beacon": BEACON_START + refClass1 + "/" + UUID4},
   122  				},
   123  			}}},
   124  		},
   125  		{
   126  			name: "Named Vecs",
   127  			req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Vectors: []*pb.Vectors{
   128  				{
   129  					Name:        "custom",
   130  					VectorBytes: byteVector([]float32{0.1, 0.2, 0.3}),
   131  				},
   132  			}}},
   133  			out: []*models.Object{{
   134  				Class: collection, ID: UUID4, Properties: nilMap,
   135  				Vectors: map[string]models.Vector{
   136  					"custom": []float32{0.1, 0.2, 0.3},
   137  				},
   138  			}},
   139  		},
   140  		{
   141  			name: "only mult ref",
   142  			req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{
   143  				MultiTargetRefProps: []*pb.BatchObject_MultiTargetRefProps{
   144  					{PropName: "multiRef", Uuids: []string{UUID3, UUID4}, TargetCollection: refClass2},
   145  				},
   146  			}}},
   147  			out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{
   148  				"multiRef": []interface{}{
   149  					map[string]interface{}{"beacon": BEACON_START + refClass2 + "/" + UUID3},
   150  					map[string]interface{}{"beacon": BEACON_START + refClass2 + "/" + UUID4},
   151  				},
   152  			}}},
   153  		},
   154  		{
   155  			name: "all property types",
   156  			req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{
   157  				MultiTargetRefProps: []*pb.BatchObject_MultiTargetRefProps{
   158  					{PropName: "multiRef", Uuids: []string{UUID4, UUID3}, TargetCollection: refClass2},
   159  				},
   160  				SingleTargetRefProps: []*pb.BatchObject_SingleTargetRefProps{
   161  					{PropName: "ref", Uuids: []string{UUID4, UUID3}},
   162  				},
   163  				NonRefProperties: newStruct(t, map[string]interface{}{
   164  					"name": "else",
   165  					"age":  46,
   166  				}),
   167  			}}},
   168  			out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{
   169  				"multiRef": []interface{}{
   170  					map[string]interface{}{"beacon": BEACON_START + refClass2 + "/" + UUID4},
   171  					map[string]interface{}{"beacon": BEACON_START + refClass2 + "/" + UUID3},
   172  				},
   173  				"ref": []interface{}{
   174  					map[string]interface{}{"beacon": BEACON_START + refClass1 + "/" + UUID4},
   175  					map[string]interface{}{"beacon": BEACON_START + refClass1 + "/" + UUID3},
   176  				},
   177  				"name": "else",
   178  				"age":  float64(46),
   179  			}}},
   180  		},
   181  		{
   182  			name: "mult ref to single target",
   183  			req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{
   184  				MultiTargetRefProps: []*pb.BatchObject_MultiTargetRefProps{
   185  					{PropName: "ref", Uuids: []string{UUID3, UUID4}, TargetCollection: refClass2},
   186  				},
   187  			}}},
   188  			out:      []*models.Object{},
   189  			outError: []int{0},
   190  		},
   191  		{
   192  			name: "single ref to multi target",
   193  			req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{
   194  				SingleTargetRefProps: []*pb.BatchObject_SingleTargetRefProps{
   195  					{PropName: "multiRef", Uuids: []string{UUID3, UUID4}},
   196  				},
   197  			}}},
   198  			out:      []*models.Object{},
   199  			outError: []int{0},
   200  		},
   201  		{
   202  			name: "slice props",
   203  			req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{
   204  				NonRefProperties: newStruct(t, map[string]interface{}{"name": "something"}),
   205  				BooleanArrayProperties: []*pb.BooleanArrayProperties{
   206  					{PropName: "boolArray1", Values: []bool{true, true}},
   207  					{PropName: "boolArray2", Values: []bool{false, true}},
   208  				},
   209  				IntArrayProperties: []*pb.IntArrayProperties{
   210  					{PropName: "int1", Values: []int64{2, 3, 4}}, {PropName: "int2", Values: []int64{7, 8}},
   211  				},
   212  				NumberArrayProperties: []*pb.NumberArrayProperties{
   213  					{PropName: "float1", Values: []float64{1, 2, 3}}, {PropName: "float2", Values: []float64{4, 5}},
   214  				},
   215  				TextArrayProperties: []*pb.TextArrayProperties{
   216  					{PropName: "text1", Values: []string{"first", "second"}}, {PropName: "text2", Values: []string{"third"}},
   217  				},
   218  				EmptyListProps: []string{"text3"},
   219  			}}},
   220  			out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{
   221  				"name":       "something",
   222  				"boolArray1": []interface{}{true, true},
   223  				"boolArray2": []interface{}{false, true},
   224  				"int1":       []interface{}{int64(2), int64(3), int64(4)},
   225  				"int2":       []interface{}{int64(7), int64(8)},
   226  				"float1":     []interface{}{1., 2., 3.},
   227  				"float2":     []interface{}{4., 5.},
   228  				"text1":      []interface{}{"first", "second"},
   229  				"text2":      []interface{}{"third"},
   230  				"text3":      []interface{}{},
   231  			}}},
   232  		},
   233  		{
   234  			name: "object props",
   235  			req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{
   236  				ObjectProperties: []*pb.ObjectProperties{
   237  					{
   238  						PropName: "simpleObj", Value: &pb.ObjectPropertiesValue{
   239  							NonRefProperties: newStruct(t, map[string]interface{}{"name": "something"}),
   240  						},
   241  					},
   242  					{
   243  						PropName: "nestedObj", Value: &pb.ObjectPropertiesValue{
   244  							ObjectProperties: []*pb.ObjectProperties{{
   245  								PropName: "obj", Value: &pb.ObjectPropertiesValue{
   246  									NonRefProperties: newStruct(t, map[string]interface{}{"name": "something"}),
   247  									EmptyListProps:   []string{"empty"},
   248  								},
   249  							}},
   250  						},
   251  					},
   252  				},
   253  			}}},
   254  			out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{
   255  				"simpleObj": map[string]interface{}{"name": "something"},
   256  				"nestedObj": map[string]interface{}{
   257  					"obj": map[string]interface{}{"name": "something", "empty": []interface{}{}},
   258  				},
   259  			}}},
   260  		},
   261  		{
   262  			name: "object array props",
   263  			req: []*pb.BatchObject{{Collection: collection, Uuid: UUID4, Properties: &pb.BatchObject_Properties{
   264  				ObjectArrayProperties: []*pb.ObjectArrayProperties{
   265  					{
   266  						PropName: "simpleObjs", Values: []*pb.ObjectPropertiesValue{
   267  							{
   268  								NonRefProperties: newStruct(t, map[string]interface{}{"name": "something"}),
   269  							},
   270  							{
   271  								NonRefProperties: newStruct(t, map[string]interface{}{"name": "something else"}),
   272  							},
   273  						},
   274  					},
   275  					{
   276  						PropName: "nestedObjs", Values: []*pb.ObjectPropertiesValue{
   277  							{
   278  								ObjectProperties: []*pb.ObjectProperties{{
   279  									PropName: "obj", Value: &pb.ObjectPropertiesValue{
   280  										NonRefProperties: newStruct(t, map[string]interface{}{"name": "something"}),
   281  									},
   282  								}},
   283  							},
   284  							{
   285  								ObjectProperties: []*pb.ObjectProperties{{
   286  									PropName: "obj", Value: &pb.ObjectPropertiesValue{
   287  										NonRefProperties: newStruct(t, map[string]interface{}{"name": "something else"}),
   288  									},
   289  								}},
   290  							},
   291  						},
   292  					},
   293  				},
   294  			}}},
   295  			out: []*models.Object{{Class: collection, ID: UUID4, Properties: map[string]interface{}{
   296  				"simpleObjs": []interface{}{map[string]interface{}{"name": "something"}, map[string]interface{}{"name": "something else"}},
   297  				"nestedObjs": []interface{}{
   298  					map[string]interface{}{"obj": map[string]interface{}{"name": "something"}},
   299  					map[string]interface{}{"obj": map[string]interface{}{"name": "something else"}},
   300  				},
   301  			}}},
   302  		},
   303  		{
   304  			name:      "mix of errors and no errors",
   305  			req:       []*pb.BatchObject{{Collection: collection, Uuid: UUID4}, {Collection: collection}, {Collection: collection}, {Collection: collection, Uuid: UUID3}},
   306  			out:       []*models.Object{{Class: collection, Properties: nilMap, ID: UUID4}, {Class: collection, Properties: nilMap, ID: UUID3}},
   307  			outError:  []int{1, 2},
   308  			origIndex: map[int]int{0: 0, 1: 3},
   309  		},
   310  	}
   311  
   312  	for _, tt := range tests {
   313  		t.Run(tt.name, func(t *testing.T) {
   314  			out, origIndex, batchErrors := batchFromProto(&pb.BatchObjectsRequest{Objects: tt.req}, scheme)
   315  			if len(tt.outError) > 0 {
   316  				require.NotNil(t, batchErrors)
   317  				if len(tt.out) > 0 {
   318  					require.Equal(t, tt.out, out)
   319  					require.Equal(t, tt.origIndex, origIndex)
   320  				}
   321  			} else {
   322  				require.Len(t, batchErrors, 0)
   323  				require.Equal(t, tt.out, out)
   324  			}
   325  		})
   326  	}
   327  }