github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/roaringset/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 roaringset
    13  
    14  import (
    15  	"testing"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  func TestCombinedCursor(t *testing.T) {
    21  	bst1 := createBst(t, []bstIn{
    22  		{
    23  			key:       "aaa",
    24  			additions: []uint64{1},
    25  			deletions: []uint64{},
    26  		},
    27  		{
    28  			key:       "bbb",
    29  			additions: []uint64{22},
    30  			deletions: []uint64{},
    31  		},
    32  		{
    33  			key:       "ccc",
    34  			additions: []uint64{333},
    35  			deletions: []uint64{},
    36  		},
    37  		{
    38  			key:       "ddd",
    39  			additions: []uint64{4444},
    40  			deletions: []uint64{},
    41  		},
    42  	})
    43  
    44  	bst2 := createBst(t, []bstIn{
    45  		{
    46  			key:       "aaa",
    47  			additions: []uint64{2, 3},
    48  			deletions: []uint64{1},
    49  		},
    50  		{
    51  			key:       "bbb",
    52  			additions: []uint64{33},
    53  			deletions: []uint64{22},
    54  		},
    55  		{
    56  			key:       "ggg",
    57  			additions: []uint64{7777777, 8888888},
    58  			deletions: []uint64{},
    59  		},
    60  	})
    61  
    62  	bst3 := createBst(t, []bstIn{
    63  		{
    64  			key:       "bbb",
    65  			additions: []uint64{22},
    66  			deletions: []uint64{},
    67  		},
    68  		{
    69  			key:       "ccc",
    70  			additions: []uint64{},
    71  			deletions: []uint64{333},
    72  		},
    73  		{
    74  			key:       "eee",
    75  			additions: []uint64{55555, 66666},
    76  			deletions: []uint64{},
    77  		},
    78  		{
    79  			key:       "fff",
    80  			additions: []uint64{666666},
    81  			deletions: []uint64{},
    82  		},
    83  		{
    84  			key:       "hhh",
    85  			additions: []uint64{999999999},
    86  			deletions: []uint64{111111111, 222222222, 333333333},
    87  		},
    88  	})
    89  
    90  	expected := []struct {
    91  		key    string
    92  		values []uint64
    93  	}{
    94  		{ // 0
    95  			key:    "aaa",
    96  			values: []uint64{2, 3},
    97  		},
    98  		{ // 1
    99  			key:    "bbb",
   100  			values: []uint64{22, 33},
   101  		},
   102  		{ // 2
   103  			key:    "ddd",
   104  			values: []uint64{4444},
   105  		},
   106  		{ // 3
   107  			key:    "eee",
   108  			values: []uint64{55555, 66666},
   109  		},
   110  		{ // 4
   111  			key:    "fff",
   112  			values: []uint64{666666},
   113  		},
   114  		{ // 5
   115  			key:    "ggg",
   116  			values: []uint64{7777777, 8888888},
   117  		},
   118  		{ // 6
   119  			key:    "hhh",
   120  			values: []uint64{999999999},
   121  		},
   122  	}
   123  
   124  	t.Run("default cursor", func(t *testing.T) {
   125  		t.Run("start from beginning", func(t *testing.T) {
   126  			cursor := createCursor(t, bst1, bst2, bst3)
   127  
   128  			key, bm := cursor.First()
   129  
   130  			assert.Equal(t, []byte(expected[0].key), key)
   131  			assert.Equal(t, len(expected[0].values), bm.GetCardinality())
   132  			for _, v := range expected[0].values {
   133  				assert.True(t, bm.Contains(v))
   134  			}
   135  		})
   136  
   137  		t.Run("start from beginning and go through all", func(t *testing.T) {
   138  			cursor := createCursor(t, bst1, bst2, bst3)
   139  
   140  			i := 0 // 1st match is "aaa"
   141  			for key, bm := cursor.First(); key != nil; key, bm = cursor.Next() {
   142  				assert.Equal(t, []byte(expected[i].key), key)
   143  				assert.Equal(t, len(expected[i].values), bm.GetCardinality())
   144  				for _, v := range expected[i].values {
   145  					assert.True(t, bm.Contains(v))
   146  				}
   147  				i++
   148  			}
   149  		})
   150  
   151  		t.Run("start from beginning using Next and go through all", func(t *testing.T) {
   152  			cursor := createCursor(t, bst1, bst2, bst3)
   153  
   154  			i := 0 // 1st match is "aaa"
   155  			for key, bm := cursor.Next(); key != nil; key, bm = cursor.Next() {
   156  				assert.Equal(t, []byte(expected[i].key), key)
   157  				assert.Equal(t, len(expected[i].values), bm.GetCardinality())
   158  				for _, v := range expected[i].values {
   159  					assert.True(t, bm.Contains(v))
   160  				}
   161  				i++
   162  			}
   163  		})
   164  
   165  		t.Run("seek matching element and go through rest", func(t *testing.T) {
   166  			cursor := createCursor(t, bst1, bst2, bst3)
   167  
   168  			i := 2 // 1st match is "ddd"
   169  			matching := []byte("ddd")
   170  			for key, bm := cursor.Seek(matching); key != nil; key, bm = cursor.Next() {
   171  				assert.Equal(t, []byte(expected[i].key), key)
   172  				assert.Equal(t, len(expected[i].values), bm.GetCardinality())
   173  				for _, v := range expected[i].values {
   174  					assert.True(t, bm.Contains(v))
   175  				}
   176  				i++
   177  			}
   178  		})
   179  
   180  		t.Run("seek non-matching element and go through rest", func(t *testing.T) {
   181  			cursor := createCursor(t, bst1, bst2, bst3)
   182  
   183  			i := 4 // 1st match is "fff"
   184  			nonMatching := []byte("efg")
   185  			for key, bm := cursor.Seek(nonMatching); key != nil; key, bm = cursor.Next() {
   186  				assert.Equal(t, []byte(expected[i].key), key)
   187  				assert.Equal(t, len(expected[i].values), bm.GetCardinality())
   188  				for _, v := range expected[i].values {
   189  					assert.True(t, bm.Contains(v))
   190  				}
   191  				i++
   192  			}
   193  		})
   194  
   195  		t.Run("seek missing element", func(t *testing.T) {
   196  			cursor := createCursor(t, bst1, bst2, bst3)
   197  
   198  			missing := []byte("lll")
   199  			key, bm := cursor.Seek(missing)
   200  
   201  			assert.Nil(t, key)
   202  			assert.NotNil(t, bm)
   203  			assert.Equal(t, 0, bm.GetCardinality())
   204  		})
   205  
   206  		t.Run("next after seek missing element does not change cursor's position", func(t *testing.T) {
   207  			cursor := createCursor(t, bst1, bst2, bst3)
   208  
   209  			key1, _ := cursor.First()
   210  
   211  			missing := []byte("lll")
   212  			cursor.Seek(missing)
   213  
   214  			key2, _ := cursor.Next()
   215  
   216  			assert.Equal(t, []byte("aaa"), key1)
   217  			assert.Equal(t, []byte("bbb"), key2)
   218  		})
   219  
   220  		t.Run("next after last is nil/empty", func(t *testing.T) {
   221  			cursor := createCursor(t, bst1, bst2, bst3)
   222  
   223  			last := []byte("hhh")
   224  			cursor.Seek(last)
   225  			key, bm := cursor.Next()
   226  
   227  			assert.Nil(t, key)
   228  			assert.NotNil(t, bm)
   229  			assert.Equal(t, 0, bm.GetCardinality())
   230  		})
   231  
   232  		t.Run("first after final/empty next", func(t *testing.T) {
   233  			cursor := createCursor(t, bst1, bst2, bst3)
   234  
   235  			last := []byte("hhh")
   236  			cursor.Seek(last)
   237  			cursor.Next()
   238  			key, bm := cursor.First()
   239  
   240  			assert.Equal(t, []byte(expected[0].key), key)
   241  			assert.Equal(t, len(expected[0].values), bm.GetCardinality())
   242  			for _, v := range expected[0].values {
   243  				assert.True(t, bm.Contains(v))
   244  			}
   245  		})
   246  
   247  		t.Run("seek after final/empty next", func(t *testing.T) {
   248  			cursor := createCursor(t, bst1, bst2, bst3)
   249  
   250  			last := []byte("hhh")
   251  			matching := []byte("eee")
   252  			cursor.Seek(last)
   253  			cursor.Next()
   254  			key, bm := cursor.Seek(matching)
   255  
   256  			assert.Equal(t, []byte(expected[3].key), key)
   257  			assert.Equal(t, len(expected[3].values), bm.GetCardinality())
   258  			for _, v := range expected[3].values {
   259  				assert.True(t, bm.Contains(v))
   260  			}
   261  		})
   262  	})
   263  
   264  	t.Run("cursor key only", func(t *testing.T) {
   265  		t.Run("start from beginning", func(t *testing.T) {
   266  			cursor := createCursorKeyOnly(t, bst1, bst2, bst3)
   267  
   268  			key, bm := cursor.First()
   269  
   270  			assert.Equal(t, []byte(expected[0].key), key)
   271  			assert.Nil(t, bm)
   272  		})
   273  
   274  		t.Run("start from beginning and go through all", func(t *testing.T) {
   275  			cursor := createCursorKeyOnly(t, bst1, bst2, bst3)
   276  
   277  			i := 0 // 1st match is "aaa"
   278  			for key, bm := cursor.First(); key != nil; key, bm = cursor.Next() {
   279  				assert.Equal(t, []byte(expected[i].key), key)
   280  				assert.Nil(t, bm)
   281  				i++
   282  			}
   283  		})
   284  
   285  		t.Run("start from beginning using Next and go through all", func(t *testing.T) {
   286  			cursor := createCursorKeyOnly(t, bst1, bst2, bst3)
   287  
   288  			i := 0 // 1st match is "aaa"
   289  			for key, bm := cursor.Next(); key != nil; key, bm = cursor.Next() {
   290  				assert.Equal(t, []byte(expected[i].key), key)
   291  				assert.Nil(t, bm)
   292  				i++
   293  			}
   294  		})
   295  
   296  		t.Run("seek matching element and go through rest", func(t *testing.T) {
   297  			cursor := createCursorKeyOnly(t, bst1, bst2, bst3)
   298  
   299  			i := 2 // 1st match is "ddd"
   300  			matching := []byte("ddd")
   301  			for key, bm := cursor.Seek(matching); key != nil; key, bm = cursor.Next() {
   302  				assert.Equal(t, []byte(expected[i].key), key)
   303  				assert.Nil(t, bm)
   304  				i++
   305  			}
   306  		})
   307  
   308  		t.Run("seek non-matching element and go through rest", func(t *testing.T) {
   309  			cursor := createCursorKeyOnly(t, bst1, bst2, bst3)
   310  
   311  			i := 4 // 1st match is "fff"
   312  			nonMatching := []byte("efg")
   313  			for key, bm := cursor.Seek(nonMatching); key != nil; key, bm = cursor.Next() {
   314  				assert.Equal(t, []byte(expected[i].key), key)
   315  				assert.Nil(t, bm)
   316  				i++
   317  			}
   318  		})
   319  
   320  		t.Run("seek missing element", func(t *testing.T) {
   321  			cursor := createCursorKeyOnly(t, bst1, bst2, bst3)
   322  
   323  			missing := []byte("lll")
   324  			key, bm := cursor.Seek(missing)
   325  
   326  			assert.Nil(t, key)
   327  			assert.Nil(t, bm)
   328  		})
   329  
   330  		t.Run("next after seek missing element does not change cursor's position", func(t *testing.T) {
   331  			cursor := createCursorKeyOnly(t, bst1, bst2, bst3)
   332  
   333  			key1, _ := cursor.First()
   334  
   335  			missing := []byte("lll")
   336  			cursor.Seek(missing)
   337  
   338  			key2, _ := cursor.Next()
   339  
   340  			assert.Equal(t, []byte("aaa"), key1)
   341  			assert.Equal(t, []byte("bbb"), key2)
   342  		})
   343  
   344  		t.Run("next after last is nil/empty", func(t *testing.T) {
   345  			cursor := createCursorKeyOnly(t, bst1, bst2, bst3)
   346  
   347  			last := []byte("hhh")
   348  			cursor.Seek(last)
   349  			key, bm := cursor.Next()
   350  
   351  			assert.Nil(t, key)
   352  			assert.Nil(t, bm)
   353  		})
   354  
   355  		t.Run("first after final/empty next", func(t *testing.T) {
   356  			cursor := createCursorKeyOnly(t, bst1, bst2, bst3)
   357  
   358  			last := []byte("hhh")
   359  			cursor.Seek(last)
   360  			cursor.Next()
   361  			key, bm := cursor.First()
   362  
   363  			assert.Equal(t, []byte(expected[0].key), key)
   364  			assert.Nil(t, bm)
   365  		})
   366  
   367  		t.Run("seek after final/empty next", func(t *testing.T) {
   368  			cursor := createCursorKeyOnly(t, bst1, bst2, bst3)
   369  
   370  			last := []byte("hhh")
   371  			matching := []byte("eee")
   372  			cursor.Seek(last)
   373  			cursor.Next()
   374  			key, bm := cursor.Seek(matching)
   375  
   376  			assert.Equal(t, []byte(expected[3].key), key)
   377  			assert.Nil(t, bm)
   378  		})
   379  	})
   380  }
   381  
   382  type bstIn struct {
   383  	key       string
   384  	additions []uint64
   385  	deletions []uint64
   386  }
   387  
   388  func createBst(t *testing.T, in []bstIn) *BinarySearchTree {
   389  	bst := &BinarySearchTree{}
   390  	for i := range in {
   391  		bst.Insert([]byte(in[i].key), Insert{Additions: in[i].additions, Deletions: in[i].deletions})
   392  	}
   393  	return bst
   394  }
   395  
   396  func createCursor(t *testing.T, bsts ...*BinarySearchTree) *CombinedCursor {
   397  	innerCursors := []InnerCursor{}
   398  	for _, bst := range bsts {
   399  		innerCursors = append(innerCursors, NewBinarySearchTreeCursor(bst))
   400  	}
   401  	return NewCombinedCursor(innerCursors, false)
   402  }
   403  
   404  func createCursorKeyOnly(t *testing.T, bsts ...*BinarySearchTree) *CombinedCursor {
   405  	c := createCursor(t, bsts...)
   406  	c.keyOnly = true
   407  	return c
   408  }