diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f81e1c77d..9a95b8bafd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ #### Changes * Java: bump `netty` version ([#2795](https://github.com/valkey-io/valkey-glide/pull/2795)) * Java: Bump protobuf (protoc) version ([#2796](https://github.com/valkey-io/valkey-glide/pull/2796), [#2800](https://github.com/valkey-io/valkey-glide/pull/2800)) +* Go: Add `SInterStore` ([#2779](https://github.com/valkey-io/valkey-glide/issues/2779)) #### Breaking Changes diff --git a/go/api/base_client.go b/go/api/base_client.go index 26b0a5262b..ab7e788d3c 100644 --- a/go/api/base_client.go +++ b/go/api/base_client.go @@ -588,6 +588,15 @@ func (client *baseClient) SInter(keys []string) (map[Result[string]]struct{}, er return handleStringSetResponse(result) } +func (client *baseClient) SInterStore(destination string, keys []string) (Result[int64], error) { + result, err := client.executeCommand(C.SInterStore, append([]string{destination}, keys...)) + if err != nil { + return CreateNilInt64Result(), err + } + + return handleLongResponse(result) +} + func (client *baseClient) SInterCard(keys []string) (Result[int64], error) { result, err := client.executeCommand(C.SInterCard, append([]string{strconv.Itoa(len(keys))}, keys...)) if err != nil { diff --git a/go/api/set_commands.go b/go/api/set_commands.go index 0992f1708d..18a68f90fe 100644 --- a/go/api/set_commands.go +++ b/go/api/set_commands.go @@ -184,6 +184,29 @@ type SetCommands interface { // [valkey.io]: https://valkey.io/commands/sinter/ SInter(keys []string) (map[Result[string]]struct{}, error) + // Stores the members of the intersection of all given sets specified by `keys` into a new set at `destination` + // + // Note: When in cluster mode, `destination` and all `keys` must map to the same hash slot. + // + // See [valkey.io] for details. + // + // Parameters: + // destination - The key of the destination set. + // keys - The keys from which to retrieve the set members. + // + // Return value: + // The number of elements in the resulting set. + // + // Example: + // result, err := client.SInterStore("my_set", []string{"set1", "set2"}) + // if err != nil { + // fmt.Println(result) + // } + // // Output: 2 - Two elements were stored at "my_set", and those elements are the intersection of "set1" and "set2". + // + // [valkey.io]: https://valkey.io/commands/sinterstore/ + SInterStore(destination string, keys []string) (Result[int64], error) + // SInterCard gets the cardinality of the intersection of all the given sets. // // Since: diff --git a/go/integTest/shared_commands_test.go b/go/integTest/shared_commands_test.go index 11f14d5c93..630e24e994 100644 --- a/go/integTest/shared_commands_test.go +++ b/go/integTest/shared_commands_test.go @@ -1571,6 +1571,101 @@ func (suite *GlideTestSuite) TestSinter_WithNotExistingKeys() { }) } +func (suite *GlideTestSuite) TestSinterStore() { + suite.runWithDefaultClients(func(client api.BaseClient) { + key1 := "{key}-1-" + uuid.NewString() + key2 := "{key}-2-" + uuid.NewString() + key3 := "{key}-3-" + uuid.NewString() + stringKey := "{key}-4-" + uuid.NewString() + nonExistingKey := "{key}-5-" + uuid.NewString() + memberArray1 := []string{"a", "b", "c"} + memberArray2 := []string{"c", "d", "e"} + t := suite.T() + + res1, err := client.SAdd(key1, memberArray1) + assert.NoError(t, err) + assert.Equal(t, int64(3), res1.Value()) + + res2, err := client.SAdd(key2, memberArray2) + assert.NoError(t, err) + assert.Equal(t, int64(3), res2.Value()) + + // store in new key + res3, err := client.SInterStore(key3, []string{key1, key2}) + assert.NoError(t, err) + assert.Equal(t, int64(1), res3.Value()) + + res4, err := client.SMembers(key3) + assert.NoError(t, err) + assert.Len(t, res4, 1) + for key := range res4 { + assert.Equal(t, key.Value(), "c") + } + + // overwrite existing set, which is also a source set + res5, err := client.SInterStore(key2, []string{key1, key2}) + assert.NoError(t, err) + assert.Equal(t, int64(1), res5.Value()) + + res6, err := client.SMembers(key2) + assert.NoError(t, err) + assert.Len(t, res6, 1) + for key := range res6 { + assert.Equal(t, key.Value(), "c") + } + + // source set is the same as the existing set + res7, err := client.SInterStore(key1, []string{key2}) + assert.NoError(t, err) + assert.Equal(t, int64(1), res7.Value()) + + res8, err := client.SMembers(key2) + assert.NoError(t, err) + assert.Len(t, res8, 1) + for key := range res8 { + assert.Equal(t, key.Value(), "c") + } + + // intersection with non-existing key + res9, err := client.SInterStore(key1, []string{key2, nonExistingKey}) + assert.NoError(t, err) + assert.Equal(t, int64(0), res9.Value()) + + // check that the key is now empty + members1, err := client.SMembers(key1) + assert.NoError(t, err) + assert.Empty(t, members1) + + // invalid argument - key list must not be empty + res10, err := client.SInterStore(key3, []string{}) + assert.Equal(suite.T(), int64(0), res10.Value()) + assert.NotNil(suite.T(), err) + assert.IsType(suite.T(), &api.RequestError{}, err) + + // non-set key + _, err = client.Set(stringKey, "value") + assert.NoError(t, err) + + res11, err := client.SInterStore(key3, []string{stringKey}) + assert.Equal(suite.T(), int64(0), res11.Value()) + assert.NotNil(suite.T(), err) + assert.IsType(suite.T(), &api.RequestError{}, err) + + // overwrite the non-set key + res12, err := client.SInterStore(stringKey, []string{key2}) + assert.NoError(t, err) + assert.Equal(t, int64(1), res12.Value()) + + // check that the key is now empty + res13, err := client.SMembers(stringKey) + assert.NoError(t, err) + assert.Len(t, res13, 1) + for key := range res13 { + assert.Equal(t, key.Value(), "c") + } + }) +} + func (suite *GlideTestSuite) TestSInterCard() { suite.SkipIfServerVersionLowerThanBy("7.0.0")