github.com/weaviate/weaviate@v1.24.6/test/acceptance/graphql_resolvers/local_get_cursor_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  	"testing"
    17  
    18  	"github.com/go-openapi/strfmt"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  	"github.com/weaviate/weaviate/test/helper"
    22  	graphqlhelper "github.com/weaviate/weaviate/test/helper/graphql"
    23  	"github.com/weaviate/weaviate/test/helper/sample-schema/multishard"
    24  )
    25  
    26  func getWithCursorSearch(t *testing.T) {
    27  	t.Run("listing objects using cursor api", func(t *testing.T) {
    28  		tests := []struct {
    29  			name             string
    30  			className        string
    31  			after            string
    32  			limit            int
    33  			filter           string
    34  			expectedIDs      []strfmt.UUID
    35  			expectedErrorMsg string
    36  		}{
    37  			{
    38  				name:      `cursor with after: "" limit: 2`,
    39  				className: "CursorClass",
    40  				after:     "",
    41  				limit:     2,
    42  				expectedIDs: []strfmt.UUID{
    43  					cursorClassID1,
    44  					cursorClassID2,
    45  					cursorClassID3,
    46  					cursorClassID4,
    47  					cursorClassID5,
    48  					cursorClassID6,
    49  					cursorClassID7,
    50  				},
    51  			},
    52  			{
    53  				name:      fmt.Sprintf("cursor with after: \"%s\" limit: 1", cursorClassID4),
    54  				className: "CursorClass",
    55  				after:     cursorClassID4.String(),
    56  				limit:     1,
    57  				expectedIDs: []strfmt.UUID{
    58  					cursorClassID5,
    59  					cursorClassID6,
    60  					cursorClassID7,
    61  				},
    62  			},
    63  			{
    64  				name:             "error with offset",
    65  				className:        "CursorClass",
    66  				filter:           `limit: 1 after: "" offset: 1`,
    67  				expectedErrorMsg: "cursor api: invalid 'after' parameter: offset cannot be set with after and limit parameters",
    68  			},
    69  			{
    70  				name:             "error with nearObject",
    71  				className:        "CursorClass",
    72  				filter:           fmt.Sprintf("limit: 1 after: \"\" nearObject:{id:\"%s\"}", cursorClassID1),
    73  				expectedErrorMsg: "cursor api: invalid 'after' parameter: other params cannot be set with after and limit parameters",
    74  			},
    75  			{
    76  				name:             "error with nearVector",
    77  				className:        "CursorClass",
    78  				filter:           `limit: 1 after: "" nearVector:{vector:[0.1, 0.2]}`,
    79  				expectedErrorMsg: "cursor api: invalid 'after' parameter: other params cannot be set with after and limit parameters",
    80  			},
    81  			{
    82  				name:             "error with hybrid",
    83  				className:        "CursorClass",
    84  				filter:           `limit: 1 after: "" hybrid:{query:"cursor api"}`,
    85  				expectedErrorMsg: "cursor api: invalid 'after' parameter: other params cannot be set with after and limit parameters",
    86  			},
    87  			{
    88  				name:             "error with bm25",
    89  				className:        "CursorClass",
    90  				filter:           `limit: 1 after: "" bm25:{query:"cursor api"}`,
    91  				expectedErrorMsg: "cursor api: invalid 'after' parameter: other params cannot be set with after and limit parameters",
    92  			},
    93  			{
    94  				name:             "error with sort",
    95  				className:        "CursorClass",
    96  				filter:           `limit: 1 after: "" sort:{path:"name"}`,
    97  				expectedErrorMsg: "cursor api: invalid 'after' parameter: sort cannot be set with after and limit parameters",
    98  			},
    99  			{
   100  				name:             "error with where",
   101  				className:        "CursorClass",
   102  				filter:           `limit: 1 after: "" where:{path:"id" operator:Like valueText:"*"}`,
   103  				expectedErrorMsg: "cursor api: invalid 'after' parameter: where cannot be set with after and limit parameters",
   104  			},
   105  			{
   106  				name:             "error with bm25, hybrid and offset",
   107  				className:        "CursorClass",
   108  				filter:           `limit: 1 after: "" bm25:{query:"cursor api"} hybrid:{query:"cursor api"} offset:1`,
   109  				expectedErrorMsg: "cursor api: invalid 'after' parameter: other params cannot be set with after and limit parameters",
   110  			},
   111  			{
   112  				name:             "error with no limit set",
   113  				className:        "CursorClass",
   114  				filter:           `after:"00000000-0000-0000-0000-000000000000"`,
   115  				expectedErrorMsg: "cursor api: invalid 'after' parameter: limit parameter must be set",
   116  			},
   117  			// multi shard
   118  			{
   119  				name:      `multi shard cursor with after: "" limit: 1`,
   120  				className: "MultiShard",
   121  				after:     "",
   122  				limit:     1,
   123  				expectedIDs: []strfmt.UUID{
   124  					multishard.MultiShardID1,
   125  					multishard.MultiShardID2,
   126  					multishard.MultiShardID3,
   127  				},
   128  			},
   129  		}
   130  		for _, tt := range tests {
   131  			t.Run(tt.name, func(t *testing.T) {
   132  				query := "{ Get { " + tt.className + " %s { _additional { id } } } }"
   133  				if len(tt.expectedErrorMsg) > 0 {
   134  					errQuery := fmt.Sprintf(query, fmt.Sprintf("(%s)", tt.filter))
   135  					result := graphqlhelper.ErrorGraphQL(t, helper.RootAuth, errQuery)
   136  					assert.Len(t, result, 1)
   137  
   138  					errMsg := result[0].Message
   139  					assert.Equal(t, tt.expectedErrorMsg, errMsg)
   140  				} else {
   141  					parseResults := func(t *testing.T, cities []interface{}) []strfmt.UUID {
   142  						var ids []strfmt.UUID
   143  						for _, city := range cities {
   144  							id, ok := city.(map[string]interface{})["_additional"].(map[string]interface{})["id"]
   145  							require.True(t, ok)
   146  
   147  							idString, ok := id.(string)
   148  							require.True(t, ok)
   149  
   150  							ids = append(ids, strfmt.UUID(idString))
   151  						}
   152  						return ids
   153  					}
   154  					// use cursor api
   155  					cursorSearch := func(t *testing.T, className, after string, limit int) []strfmt.UUID {
   156  						cursor := fmt.Sprintf(`(limit: %v after: "%s")`, limit, after)
   157  						result := graphqlhelper.AssertGraphQL(t, helper.RootAuth, fmt.Sprintf(query, cursor))
   158  						cities := result.Get("Get", className).AsSlice()
   159  						return parseResults(t, cities)
   160  					}
   161  
   162  					var cursorIDs []strfmt.UUID
   163  					after, limit := tt.after, tt.limit
   164  					for {
   165  						result := cursorSearch(t, tt.className, after, limit)
   166  						cursorIDs = append(cursorIDs, result...)
   167  						if len(result) == 0 {
   168  							break
   169  						}
   170  						after = result[len(result)-1].String()
   171  					}
   172  
   173  					assert.ElementsMatch(t, tt.expectedIDs, cursorIDs)
   174  					require.Equal(t, len(tt.expectedIDs), len(cursorIDs))
   175  					for i := range tt.expectedIDs {
   176  						assert.Equal(t, tt.expectedIDs[i], cursorIDs[i])
   177  					}
   178  				}
   179  			})
   180  		}
   181  	})
   182  }