From 198845ac308f7bc40a4fb5b6042ff337ad32b6a1 Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Fri, 10 Mar 2023 19:13:47 +0530 Subject: [PATCH 01/13] feat: initial set up for LCSCmd --- command.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ commands.go | 19 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/command.go b/command.go index da1c01c03..77dd85104 100644 --- a/command.go +++ b/command.go @@ -3840,3 +3840,54 @@ func (cmd *ZSliceWithKeyCmd) readReply(rd *proto.Reader) (err error) { return nil } + +//------------------------------------------------------------------------------ + +type LCSMatch struct { + Positions1 []int + Positions2 []int +} + +type LCSMatchLen struct { + Match LCSMatch + Len int64 +} + +type LCSCmd struct { + baseCmd + + Val LCSMatchLen +} + +var _ Cmder = (*LCSCmd)(nil) + +func NewLCSCmd(ctx context.Context,args ...interface{}) *LCSCmd { + return &LCSCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *LCSCmd) Result() (LCSMatchLen, error) { + return cmd.Val, cmd.err +} + +func (cmd *LCSCmd) String() string { + return cmdString(cmd, cmd.Val) +} + +func (cmd *LCSCmd) readReply(rd *proto.Reader) error { + + length, err := rd.ReadArrayLen() + if err != nil { + return err + } + + if length != 4 { + return fmt.Errorf("Unexpected number of items in array reply: %d", length) + } + + return nil +} diff --git a/commands.go b/commands.go index 6a7bae538..99fb4ed4a 100644 --- a/commands.go +++ b/commands.go @@ -1476,6 +1476,25 @@ func (c cmdable) BRPopLPush(ctx context.Context, source, destination string, tim return cmd } +func (c cmdable) LCS(ctx context.Context, key1 string, key2 string) *StringCmd { + cmd := NewStringCmd(ctx, "lcs", key1, key2) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LCSLen(ctx context.Context, key1 string, key2 string) *IntCmd { + cmd := NewIntCmd(ctx, "lcs", key1, key2, "len") + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) LCSIdx(ctx context.Context, key1 string, key2 string, idx bool) *LCSCmd { + cmd := NewLCSCmd(ctx, "lcs", key1, key2, true) + _ = c(ctx,cmd) + return cmd +} + + func (c cmdable) LIndex(ctx context.Context, key string, index int64) *StringCmd { cmd := NewStringCmd(ctx, "lindex", key, index) _ = c(ctx, cmd) From cb93a2cd80956418f7a80f0fc2b5d4bfa815e6f2 Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Fri, 10 Mar 2023 19:23:58 +0530 Subject: [PATCH 02/13] feat: adding test for LCS with no additional params --- commands_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/commands_test.go b/commands_test.go index 86bde09a0..9e3f5b084 100644 --- a/commands_test.go +++ b/commands_test.go @@ -2238,6 +2238,20 @@ var _ = Describe("Commands", func() { Expect(v).To(Equal("c")) }) + It("should LCS", func() { + mSet := client.MSet(ctx, "key1", "ohmytext", "key2", "mynewtext") + Expect(mSet.Err()).NotTo(HaveOccurred()) + Expect(mSet.Val()).To(Equal("OK")) + + lcs := client.LCS(ctx, "key1", "key2") + Expect(lcs.Err()).NotTo(HaveOccurred()) + Expect(lcs.Val()).To(Equal("mytext")) + + lcs = client.LCS(ctx, "nonexistent_key1", "nonexistent_key2") + Expect(lcs.Err()).To(Equal(redis.Nil)) + }) + + It("should LIndex", func() { lPush := client.LPush(ctx, "list", "World") Expect(lPush.Err()).NotTo(HaveOccurred()) From 66f56b2a83101a29c683849bf4131b6cff8fdd9f Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Fri, 10 Mar 2023 19:41:07 +0530 Subject: [PATCH 03/13] feat: adding test for LCS with LEN param --- commands_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/commands_test.go b/commands_test.go index 9e3f5b084..c187a8806 100644 --- a/commands_test.go +++ b/commands_test.go @@ -2251,6 +2251,20 @@ var _ = Describe("Commands", func() { Expect(lcs.Err()).To(Equal(redis.Nil)) }) + It("should LCSLen", func() { + mSet := client.MSet(ctx, "key1", "ohmytext", "key2", "mynewtext") + Expect(mSet.Err()).NotTo(HaveOccurred()) + Expect(mSet.Val()).To(Equal("OK")) + + lcsLen := client.LCSLen(ctx, "key1", "key2") + Expect(lcsLen.Err()).NotTo(HaveOccurred()) + Expect(lcsLen.Val()).To(Equal(6)) + + lcsLen = client.LCSLen(ctx, "nonexistent_key1", "nonexistent_key2") + Expect(lcsLen.Err()).To(Equal(redis.Nil)) + + }) + It("should LIndex", func() { lPush := client.LPush(ctx, "list", "World") From 2ca945e6d5084a76c6535c401f5feee4802f6a17 Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Fri, 10 Mar 2023 20:02:30 +0530 Subject: [PATCH 04/13] feat: resolving conflicts in command.go --- command.go | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index 77dd85104..6dfbfee6c 100644 --- a/command.go +++ b/command.go @@ -3841,6 +3841,158 @@ func (cmd *ZSliceWithKeyCmd) readReply(rd *proto.Reader) (err error) { return nil } +type Function struct { + Name string + Description string + Flags []string +} + +type Library struct { + Name string + Engine string + Functions []Function + Code string +} + +type FunctionListCmd struct { + baseCmd + + val []Library +} + +var _ Cmder = (*FunctionListCmd)(nil) + +func NewFunctionListCmd(ctx context.Context, args ...interface{}) *FunctionListCmd { + return &FunctionListCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *FunctionListCmd) SetVal(val []Library) { + cmd.val = val +} + +func (cmd *FunctionListCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *FunctionListCmd) Val() []Library { + return cmd.val +} + +func (cmd *FunctionListCmd) Result() ([]Library, error) { + return cmd.val, cmd.err +} + +func (cmd *FunctionListCmd) First() (*Library, error) { + if cmd.err != nil { + return nil, cmd.err + } + if len(cmd.val) > 0 { + return &cmd.val[0], nil + } + return nil, Nil +} + +func (cmd *FunctionListCmd) readReply(rd *proto.Reader) (err error) { + n, err := rd.ReadArrayLen() + if err != nil { + return err + } + + libraries := make([]Library, n) + for i := 0; i < n; i++ { + nn, err := rd.ReadMapLen() + if err != nil { + return err + } + + library := Library{} + for f := 0; f < nn; f++ { + key, err := rd.ReadString() + if err != nil { + return err + } + + switch key { + case "library_name": + library.Name, err = rd.ReadString() + case "engine": + library.Engine, err = rd.ReadString() + case "functions": + library.Functions, err = cmd.readFunctions(rd) + case "library_code": + library.Code, err = rd.ReadString() + default: + return fmt.Errorf("redis: function list unexpected key %s", key) + } + + if err != nil { + return err + } + } + + libraries[i] = library + } + cmd.val = libraries + return nil +} + +func (cmd *FunctionListCmd) readFunctions(rd *proto.Reader) ([]Function, error) { + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + + functions := make([]Function, n) + for i := 0; i < n; i++ { + nn, err := rd.ReadMapLen() + if err != nil { + return nil, err + } + + function := Function{} + for f := 0; f < nn; f++ { + key, err := rd.ReadString() + if err != nil { + return nil, err + } + + switch key { + case "name": + if function.Name, err = rd.ReadString(); err != nil { + return nil, err + } + case "description": + if function.Description, err = rd.ReadString(); err != nil && err != Nil { + return nil, err + } + case "flags": + // resp set + nx, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + + function.Flags = make([]string, nx) + for j := 0; j < nx; j++ { + if function.Flags[j], err = rd.ReadString(); err != nil { + return nil, err + } + } + default: + return nil, fmt.Errorf("redis: function list unexpected key %s", key) + } + } + + functions[i] = function + } + return functions, nil +} + //------------------------------------------------------------------------------ type LCSMatch struct { @@ -3886,7 +4038,7 @@ func (cmd *LCSCmd) readReply(rd *proto.Reader) error { } if length != 4 { - return fmt.Errorf("Unexpected number of items in array reply: %d", length) + return fmt.Errorf("unexpected number of items in array reply: %d", length) } return nil From bbb648550e7cfe10c192d351e8c312ec2ff008a3 Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Sat, 11 Mar 2023 13:41:06 +0530 Subject: [PATCH 05/13] fix: changing reply and query structure --- command.go | 164 +++++++++++++++++++++++++++++++++++++++++------ commands.go | 35 +++++----- commands_test.go | 28 -------- 3 files changed, 164 insertions(+), 63 deletions(-) diff --git a/command.go b/command.go index 6dfbfee6c..0642eb68c 100644 --- a/command.go +++ b/command.go @@ -3995,26 +3995,47 @@ func (cmd *FunctionListCmd) readFunctions(rd *proto.Reader) ([]Function, error) //------------------------------------------------------------------------------ +type LCSQuery struct { + Key1 string + Key2 string + Len bool + Idx bool + MinMatchLen int + WithMatchLen bool +} + type LCSMatch struct { - Positions1 []int - Positions2 []int + MatchString string + Matches []LCSMatchedPosition + Len int64 +} + +type LCSMatchedPosition struct { + // or A/B ? + Key1 LCSPosition + Key2 LCSPosition + + // only for withMatchLen is true + MatchLen int64 } -type LCSMatchLen struct { - Match LCSMatch - Len int64 +type LCSPosition struct { + Start int64 + End int64 } type LCSCmd struct { baseCmd - Val LCSMatchLen + // 1: match string + // 2: match len + // 3: match idx LCSMatch + readType uint8 + val *LCSMatch } -var _ Cmder = (*LCSCmd)(nil) - -func NewLCSCmd(ctx context.Context,args ...interface{}) *LCSCmd { - return &LCSCmd{ +func NewLCSCmd(ctx context.Context, args ...interface{}) *LCSCmd { + return &LCSCmd{ baseCmd: baseCmd{ ctx: ctx, args: args, @@ -4022,24 +4043,127 @@ func NewLCSCmd(ctx context.Context,args ...interface{}) *LCSCmd { } } -func (cmd *LCSCmd) Result() (LCSMatchLen, error) { - return cmd.Val, cmd.err +func (cmd *LCSCmd) String() string { + return cmdString(cmd, cmd.val) } -func (cmd *LCSCmd) String() string { - return cmdString(cmd, cmd.Val) +func (cmd *LCSCmd) Val() *LCSMatch { + return cmd.val } -func (cmd *LCSCmd) readReply(rd *proto.Reader) error { +func (cmd *LCSCmd) Result() (*LCSMatch, error) { + return cmd.val, cmd.err +} - length, err := rd.ReadArrayLen() - if err != nil { +func (cmd *LCSCmd) readReply(rd *proto.Reader) (err error) { + lcs := &LCSMatch{} + switch cmd.readType { + case 1: + // match string + if lcs.MatchString, err = rd.ReadString(); err != nil { return err } + case 2: + // match len + if lcs.Len, err = rd.ReadInt(); err != nil { + return err + } + case 3: + // read LCSMatch + if err = rd.ReadFixedMapLen(2); err != nil { + return err + } + + // read matches or len field + for i := 0; i < 2; i++ { + key, err := rd.ReadString() + if err != nil { + return err + } - if length != 4 { - return fmt.Errorf("unexpected number of items in array reply: %d", length) + switch key { + case "matches": + // read array of matched positions + if lcs.Matches, err = readMatchedPositions(rd); err != nil { + return err + } + case "len": + // read match length + if lcs.Len, err = rd.ReadInt(); err != nil { + return err + } + } } + } - return nil + cmd.val = lcs + return nil +} + +func readMatchedPositions(rd *proto.Reader) ([]LCSMatchedPosition, error) { + + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + + positions := make([]LCSMatchedPosition, n) + for i := 0; i < n; i++ { + if err := rd.ReadFixedArrayLen(2); err != nil { + return nil, err + } + + pos1, err := readPosition(rd) + if err != nil { + return nil, err + } + + pos2, err := readPosition(rd) + if err != nil { + return nil, err + } + + // read match length if WithMatchLen is true + matchLen := int64(0) + if pos, _ := rd.PeekReplyType(); pos == proto.RespString { + if lenField, err := rd.ReadString(); err != nil { + return nil, err + } else if lenField == "MatchLen" { + matchLen, err = rd.ReadInt() + if err != nil { + return nil, err + } + } + } + + positions[i] = LCSMatchedPosition{ + Key1: pos1, + Key2: pos2, + MatchLen: matchLen, + } + } + + return positions, nil +} + +func readPosition(rd *proto.Reader) (LCSPosition, error) { + var pos LCSPosition + if err := rd.ReadFixedArrayLen(2); err != nil { + return pos, err + } + + start, err := rd.ReadInt() + if err != nil { + return pos, err + } + + end, err := rd.ReadInt() + if err != nil { + return pos, err + } + + return LCSPosition{ + Start: start, + End: end, + }, nil } diff --git a/commands.go b/commands.go index 99fb4ed4a..3561563cb 100644 --- a/commands.go +++ b/commands.go @@ -221,6 +221,7 @@ type Cmdable interface { BLMPop(ctx context.Context, timeout time.Duration, direction string, count int64, keys ...string) *KeyValuesCmd BRPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd BRPopLPush(ctx context.Context, source, destination string, timeout time.Duration) *StringCmd + LCS(ctx context.Context,q *LCSQuery) *LCSCmd LIndex(ctx context.Context, key string, index int64) *StringCmd LInsert(ctx context.Context, key, op string, pivot, value interface{}) *IntCmd LInsertBefore(ctx context.Context, key string, pivot, value interface{}) *IntCmd @@ -1476,25 +1477,29 @@ func (c cmdable) BRPopLPush(ctx context.Context, source, destination string, tim return cmd } -func (c cmdable) LCS(ctx context.Context, key1 string, key2 string) *StringCmd { - cmd := NewStringCmd(ctx, "lcs", key1, key2) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) LCSLen(ctx context.Context, key1 string, key2 string) *IntCmd { - cmd := NewIntCmd(ctx, "lcs", key1, key2, "len") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) LCSIdx(ctx context.Context, key1 string, key2 string, idx bool) *LCSCmd { - cmd := NewLCSCmd(ctx, "lcs", key1, key2, true) +func (c cmdable) LCS(ctx context.Context, q *LCSQuery) *LCSCmd { + args:= make([]interface{},3,6) + args[0] = "lcs" + args[1] = q.Key1 + args[2] = q.Key2 + if q.Len { + args[3] = "len" + }else if q.Idx { + args[3] = "idx" + if q.MinMatchLen != 0 { + args[4] = "minmatchlen" + args[5] = q.MinMatchLen + } + if q.WithMatchLen { + args[6] = "withmatchlen" + } + + } + cmd := NewLCSCmd(ctx,args) _ = c(ctx,cmd) return cmd } - func (c cmdable) LIndex(ctx context.Context, key string, index int64) *StringCmd { cmd := NewStringCmd(ctx, "lindex", key, index) _ = c(ctx, cmd) diff --git a/commands_test.go b/commands_test.go index c187a8806..86bde09a0 100644 --- a/commands_test.go +++ b/commands_test.go @@ -2238,34 +2238,6 @@ var _ = Describe("Commands", func() { Expect(v).To(Equal("c")) }) - It("should LCS", func() { - mSet := client.MSet(ctx, "key1", "ohmytext", "key2", "mynewtext") - Expect(mSet.Err()).NotTo(HaveOccurred()) - Expect(mSet.Val()).To(Equal("OK")) - - lcs := client.LCS(ctx, "key1", "key2") - Expect(lcs.Err()).NotTo(HaveOccurred()) - Expect(lcs.Val()).To(Equal("mytext")) - - lcs = client.LCS(ctx, "nonexistent_key1", "nonexistent_key2") - Expect(lcs.Err()).To(Equal(redis.Nil)) - }) - - It("should LCSLen", func() { - mSet := client.MSet(ctx, "key1", "ohmytext", "key2", "mynewtext") - Expect(mSet.Err()).NotTo(HaveOccurred()) - Expect(mSet.Val()).To(Equal("OK")) - - lcsLen := client.LCSLen(ctx, "key1", "key2") - Expect(lcsLen.Err()).NotTo(HaveOccurred()) - Expect(lcsLen.Val()).To(Equal(6)) - - lcsLen = client.LCSLen(ctx, "nonexistent_key1", "nonexistent_key2") - Expect(lcsLen.Err()).To(Equal(redis.Nil)) - - }) - - It("should LIndex", func() { lPush := client.LPush(ctx, "list", "World") Expect(lPush.Err()).NotTo(HaveOccurred()) From 6e72de9b609d72f6eb3462dfeab4dd54b042e0d1 Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Sat, 11 Mar 2023 15:54:05 +0530 Subject: [PATCH 06/13] feat: commands test for lcs and lcs with len --- command.go | 6 +++++- commands_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index 0642eb68c..c9ef74350 100644 --- a/command.go +++ b/command.go @@ -4043,6 +4043,10 @@ func NewLCSCmd(ctx context.Context, args ...interface{}) *LCSCmd { } } +func (cmd *LCSCmd) SetVal(val *LCSMatch) { + cmd.val = val +} + func (cmd *LCSCmd) String() string { return cmdString(cmd, cmd.val) } @@ -4101,7 +4105,7 @@ func (cmd *LCSCmd) readReply(rd *proto.Reader) (err error) { } func readMatchedPositions(rd *proto.Reader) ([]LCSMatchedPosition, error) { - + n, err := rd.ReadArrayLen() if err != nil { return nil, err diff --git a/commands_test.go b/commands_test.go index f806dc8f5..ffdd749da 100644 --- a/commands_test.go +++ b/commands_test.go @@ -2238,6 +2238,58 @@ var _ = Describe("Commands", func() { Expect(v).To(Equal("c")) }) + It("should LCS", func() { + err := client.MSet(ctx, "key1", "ohmytext", "key2", "mynewtext").Err() + Expect(err).NotTo(HaveOccurred()) + + lcs, err := client.LCS(ctx, &redis.LCSQuery{ + Key1: "key1", + Key2: "key2", + }).Result() + + Expect(err).NotTo(HaveOccurred()) + Expect(lcs.MatchString).To(Equal("mytext")) + + lcs, err = client.LCS(ctx, &redis.LCSQuery{ + Key1: "nonexistent_key1", + Key2: "key2", + }).Result() + + // Expect an error to be returned + Expect(err).To(HaveOccurred()) + // Expect the LCS result to be an empty string + Expect(lcs.MatchString).To(BeEmpty()) + + }) + + It("should LCS with Len", func() { + err := client.MSet(ctx, "key1", "ohmytext", "key2", "mynewtext").Err() + Expect(err).NotTo(HaveOccurred()) + + lcs, err := client.LCS(ctx, &redis.LCSQuery{ + Key1: "key1", + Key2: "key2", + Len: true, + }).Result() + + Expect(err).NotTo(HaveOccurred()) + Expect(lcs.Len).To(Equal(6)) + + // Call LCS with both keys as non-existent + lcs, err = client.LCS(ctx, &redis.LCSQuery{ + Key1: "nonexistent_key1", + Key2: "nonexistent_key2", + Len: true, + }).Result() + + // Expect an error to be returned + Expect(err).To(HaveOccurred()) + // Expect the LCS length result to be 0 + Expect(lcs.Len).To(BeZero()) + }) + + + It("should LIndex", func() { lPush := client.LPush(ctx, "list", "World") Expect(lPush.Err()).NotTo(HaveOccurred()) From 881e109a6b34e3cf22d8b4684c43babd5c822bae Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Wed, 22 Mar 2023 18:29:29 +0530 Subject: [PATCH 07/13] fix: using dynamic args length --- commands.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/commands.go b/commands.go index 858fa9d6a..331390f78 100644 --- a/commands.go +++ b/commands.go @@ -1494,15 +1494,14 @@ func (c cmdable) LCS(ctx context.Context, q *LCSQuery) *LCSCmd { args[1] = q.Key1 args[2] = q.Key2 if q.Len { - args[3] = "len" + args = append(args, "len") }else if q.Idx { - args[3] = "idx" + args = append(args, "idx") if q.MinMatchLen != 0 { - args[4] = "minmatchlen" - args[5] = q.MinMatchLen + args = append(args,"minmatchlen",q.MinMatchLen) } if q.WithMatchLen { - args[6] = "withmatchlen" + args = append(args,"withmatchlen") } } From 350115d82ff5146695c40265d9b0d0e54e2ebeca Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Wed, 22 Mar 2023 18:32:08 +0530 Subject: [PATCH 08/13] fix: read --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index c9ef74350..dbe4b1c9a 100644 --- a/command.go +++ b/command.go @@ -4132,7 +4132,7 @@ func readMatchedPositions(rd *proto.Reader) ([]LCSMatchedPosition, error) { if pos, _ := rd.PeekReplyType(); pos == proto.RespString { if lenField, err := rd.ReadString(); err != nil { return nil, err - } else if lenField == "MatchLen" { + } else if lenField == "matchlen" { matchLen, err = rd.ReadInt() if err != nil { return nil, err From e69c70c7b6e63da86eb5853ddd493bea7dda2a1d Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Wed, 22 Mar 2023 19:51:59 +0530 Subject: [PATCH 09/13] fix: Cmd init --- command.go | 7 ++++--- commands.go | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/command.go b/command.go index 20d9965f6..516721f1f 100644 --- a/command.go +++ b/command.go @@ -3993,6 +3993,7 @@ func (cmd *FunctionListCmd) readFunctions(rd *proto.Reader) ([]Function, error) return functions, nil } +//------------------------------------------------------------------------------ type LCSQuery struct { Key1 string @@ -4033,12 +4034,13 @@ type LCSCmd struct { val *LCSMatch } -func NewLCSCmd(ctx context.Context, args ...interface{}) *LCSCmd { +func NewLCSCmd(ctx context.Context, readType uint8, args ...interface{}) *LCSCmd { return &LCSCmd{ baseCmd: baseCmd{ ctx: ctx, args: args, }, + readType: readType, } } @@ -4170,11 +4172,10 @@ func readPosition(rd *proto.Reader) (LCSPosition, error) { End: end, }, nil } + // ======= type FilterBy struct { Module string ACLCat string Pattern string } - - diff --git a/commands.go b/commands.go index e6738af19..4e93efa3e 100644 --- a/commands.go +++ b/commands.go @@ -222,7 +222,7 @@ type Cmdable interface { BLMPop(ctx context.Context, timeout time.Duration, direction string, count int64, keys ...string) *KeyValuesCmd BRPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd BRPopLPush(ctx context.Context, source, destination string, timeout time.Duration) *StringCmd - LCS(ctx context.Context,q *LCSQuery) *LCSCmd + LCS(ctx context.Context, q *LCSQuery) *LCSCmd LIndex(ctx context.Context, key string, index int64) *StringCmd LInsert(ctx context.Context, key, op string, pivot, value interface{}) *IntCmd LInsertBefore(ctx context.Context, key string, pivot, value interface{}) *IntCmd @@ -1507,24 +1507,27 @@ func (c cmdable) BRPopLPush(ctx context.Context, source, destination string, tim } func (c cmdable) LCS(ctx context.Context, q *LCSQuery) *LCSCmd { - args:= make([]interface{},3,6) + args := make([]interface{}, 3, 6) args[0] = "lcs" args[1] = q.Key1 args[2] = q.Key2 + readType := uint8(1) if q.Len { args = append(args, "len") - }else if q.Idx { + readType = 2 + } else if q.Idx { args = append(args, "idx") + readType = 3 if q.MinMatchLen != 0 { - args = append(args,"minmatchlen",q.MinMatchLen) + args = append(args, "minmatchlen", q.MinMatchLen) } if q.WithMatchLen { - args = append(args,"withmatchlen") + args = append(args, "withmatchlen") } - + } - cmd := NewLCSCmd(ctx,args) - _ = c(ctx,cmd) + cmd := NewLCSCmd(ctx, readType, args) + _ = c(ctx, cmd) return cmd } From 97c5bad8b9d8ae1a81efe579664a15aba1ca2fcd Mon Sep 17 00:00:00 2001 From: monkey92t Date: Thu, 23 Mar 2023 00:07:57 +0800 Subject: [PATCH 10/13] sorting out the LCS commands Signed-off-by: monkey92t --- command.go | 91 +++++++++++++++++++++--------------------------- commands.go | 28 +++++---------- commands_test.go | 61 ++++++++++++++++++++------------ 3 files changed, 87 insertions(+), 93 deletions(-) diff --git a/command.go b/command.go index 516721f1f..e05f0ed37 100644 --- a/command.go +++ b/command.go @@ -3995,6 +3995,7 @@ func (cmd *FunctionListCmd) readFunctions(rd *proto.Reader) ([]Function, error) //------------------------------------------------------------------------------ +// LCSQuery is a parameter used for the LCS command type LCSQuery struct { Key1 string Key2 string @@ -4004,6 +4005,7 @@ type LCSQuery struct { WithMatchLen bool } +// LCSMatch is the result set of the LCS command. type LCSMatch struct { MatchString string Matches []LCSMatchedPosition @@ -4011,7 +4013,6 @@ type LCSMatch struct { } type LCSMatchedPosition struct { - // or A/B ? Key1 LCSPosition Key2 LCSPosition @@ -4034,14 +4035,32 @@ type LCSCmd struct { val *LCSMatch } -func NewLCSCmd(ctx context.Context, readType uint8, args ...interface{}) *LCSCmd { - return &LCSCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - readType: readType, +func NewLCSCmd(ctx context.Context, q *LCSQuery) *LCSCmd { + args := make([]interface{}, 3, 7) + args[0] = "lcs" + args[1] = q.Key1 + args[2] = q.Key2 + + cmd := &LCSCmd{readType: 1} + if q.Len { + cmd.readType = 2 + args = append(args, "len") + } else if q.Idx { + cmd.readType = 3 + args = append(args, "idx") + if q.MinMatchLen != 0 { + args = append(args, "minmatchlen", q.MinMatchLen) + } + if q.WithMatchLen { + args = append(args, "withmatchlen") + } } + cmd.baseCmd = baseCmd{ + ctx: ctx, + args: args, + } + + return cmd } func (cmd *LCSCmd) SetVal(val *LCSMatch) { @@ -4089,7 +4108,7 @@ func (cmd *LCSCmd) readReply(rd *proto.Reader) (err error) { switch key { case "matches": // read array of matched positions - if lcs.Matches, err = readMatchedPositions(rd); err != nil { + if lcs.Matches, err = cmd.readMatchedPositions(rd); err != nil { return err } case "len": @@ -4105,8 +4124,7 @@ func (cmd *LCSCmd) readReply(rd *proto.Reader) (err error) { return nil } -func readMatchedPositions(rd *proto.Reader) ([]LCSMatchedPosition, error) { - +func (cmd *LCSCmd) readMatchedPositions(rd *proto.Reader) ([]LCSMatchedPosition, error) { n, err := rd.ReadArrayLen() if err != nil { return nil, err @@ -4114,68 +4132,39 @@ func readMatchedPositions(rd *proto.Reader) ([]LCSMatchedPosition, error) { positions := make([]LCSMatchedPosition, n) for i := 0; i < n; i++ { - if err := rd.ReadFixedArrayLen(2); err != nil { + pn, err := rd.ReadArrayLen() + if err != nil { return nil, err } - pos1, err := readPosition(rd) - if err != nil { + if positions[i].Key1, err = cmd.readPosition(rd); err != nil { return nil, err } - - pos2, err := readPosition(rd) - if err != nil { + if positions[i].Key2, err = cmd.readPosition(rd); err != nil { return nil, err } // read match length if WithMatchLen is true - matchLen := int64(0) - if pos, _ := rd.PeekReplyType(); pos == proto.RespString { - if lenField, err := rd.ReadString(); err != nil { + if pn > 2 { + if positions[i].MatchLen, err = rd.ReadInt(); err != nil { return nil, err - } else if lenField == "matchlen" { - matchLen, err = rd.ReadInt() - if err != nil { - return nil, err - } } } - - positions[i] = LCSMatchedPosition{ - Key1: pos1, - Key2: pos2, - MatchLen: matchLen, - } } return positions, nil } -func readPosition(rd *proto.Reader) (LCSPosition, error) { - var pos LCSPosition - if err := rd.ReadFixedArrayLen(2); err != nil { +func (cmd *LCSCmd) readPosition(rd *proto.Reader) (pos LCSPosition, err error) { + if err = rd.ReadFixedArrayLen(2); err != nil { return pos, err } - - start, err := rd.ReadInt() - if err != nil { + if pos.Start, err = rd.ReadInt(); err != nil { return pos, err } - - end, err := rd.ReadInt() - if err != nil { + if pos.End, err = rd.ReadInt(); err != nil { return pos, err } - return LCSPosition{ - Start: start, - End: end, - }, nil -} - -// ======= -type FilterBy struct { - Module string - ACLCat string - Pattern string + return pos, nil } diff --git a/commands.go b/commands.go index 4e93efa3e..770a29f0d 100644 --- a/commands.go +++ b/commands.go @@ -539,6 +539,13 @@ func (c cmdable) Command(ctx context.Context) *CommandsInfoCmd { return cmd } +// FilterBy is used for the `CommandList` command parameter. +type FilterBy struct { + Module string + ACLCat string + Pattern string +} + func (c cmdable) CommandList(ctx context.Context, filter *FilterBy) *StringSliceCmd { args := make([]interface{}, 0, 5) args = append(args, "command", "list") @@ -1507,26 +1514,7 @@ func (c cmdable) BRPopLPush(ctx context.Context, source, destination string, tim } func (c cmdable) LCS(ctx context.Context, q *LCSQuery) *LCSCmd { - args := make([]interface{}, 3, 6) - args[0] = "lcs" - args[1] = q.Key1 - args[2] = q.Key2 - readType := uint8(1) - if q.Len { - args = append(args, "len") - readType = 2 - } else if q.Idx { - args = append(args, "idx") - readType = 3 - if q.MinMatchLen != 0 { - args = append(args, "minmatchlen", q.MinMatchLen) - } - if q.WithMatchLen { - args = append(args, "withmatchlen") - } - - } - cmd := NewLCSCmd(ctx, readType, args) + cmd := NewLCSCmd(ctx, q) _ = c(ctx, cmd) return cmd } diff --git a/commands_test.go b/commands_test.go index 8c417155e..4631dc485 100644 --- a/commands_test.go +++ b/commands_test.go @@ -2255,41 +2255,58 @@ var _ = Describe("Commands", func() { Key2: "key2", }).Result() - // Expect an error to be returned - Expect(err).To(HaveOccurred()) - // Expect the LCS result to be an empty string - Expect(lcs.MatchString).To(BeEmpty()) - - }) - - It("should LCS with Len", func() { - err := client.MSet(ctx, "key1", "ohmytext", "key2", "mynewtext").Err() Expect(err).NotTo(HaveOccurred()) + Expect(lcs.MatchString).To(Equal("")) - lcs, err := client.LCS(ctx, &redis.LCSQuery{ + lcs, err = client.LCS(ctx, &redis.LCSQuery{ Key1: "key1", Key2: "key2", Len: true, }).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(lcs.Len).To(Equal(6)) + Expect(lcs.MatchString).To(Equal("")) + Expect(lcs.Len).To(Equal(int64(6))) - // Call LCS with both keys as non-existent lcs, err = client.LCS(ctx, &redis.LCSQuery{ - Key1: "nonexistent_key1", - Key2: "nonexistent_key2", - Len: true, + Key1: "key1", + Key2: "key2", + Idx: true, }).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(lcs.MatchString).To(Equal("")) + Expect(lcs.Len).To(Equal(int64(6))) + Expect(lcs.Matches).To(Equal([]redis.LCSMatchedPosition{ + { + Key1: redis.LCSPosition{Start: 4, End: 7}, + Key2: redis.LCSPosition{Start: 5, End: 8}, + MatchLen: 0, + }, + { + Key1: redis.LCSPosition{Start: 2, End: 3}, + Key2: redis.LCSPosition{Start: 0, End: 1}, + MatchLen: 0, + }, + })) - // Expect an error to be returned - Expect(err).To(HaveOccurred()) - // Expect the LCS length result to be 0 - Expect(lcs.Len).To(BeZero()) + lcs, err = client.LCS(ctx, &redis.LCSQuery{ + Key1: "key1", + Key2: "key2", + Idx: true, + MinMatchLen: 3, + WithMatchLen: true, + }).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(lcs.MatchString).To(Equal("")) + Expect(lcs.Len).To(Equal(int64(6))) + Expect(lcs.Matches).To(Equal([]redis.LCSMatchedPosition{ + { + Key1: redis.LCSPosition{Start: 4, End: 7}, + Key2: redis.LCSPosition{Start: 5, End: 8}, + MatchLen: 4, + }, + })) }) - - It("should LIndex", func() { lPush := client.LPush(ctx, "list", "World") Expect(lPush.Err()).NotTo(HaveOccurred()) From 7642667d2f93fff8d99db03be169a2cf6ec60c40 Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Fri, 24 Mar 2023 09:47:15 +0530 Subject: [PATCH 11/13] fix: Adding error case test --- commands_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/commands_test.go b/commands_test.go index 47616f670..018569d75 100644 --- a/commands_test.go +++ b/commands_test.go @@ -2318,6 +2318,21 @@ var _ = Describe("Commands", func() { MatchLen: 4, }, })) + + _, err = client.Set(ctx, "keywithstringvalue", "golang",0).Result() + Expect(err).NotTo(HaveOccurred()) + + _, err = client.LPush(ctx, "keywithnonstringvalue", "somevalue").Result() + Expect(err).NotTo(HaveOccurred()) + + _, err = client.LCS(ctx, &redis.LCSQuery{ + Key1: "keywithstringvalue", + Key2: "keywithnonstringvalue", + }).Result() + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("WRONGTYPE Operation against a key holding the wrong kind of value")) + + }) It("should LIndex", func() { From cbccb55a1aa1633e43c8995aef0b690447078114 Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Fri, 24 Mar 2023 12:11:43 +0530 Subject: [PATCH 12/13] fix: adding correct error message --- commands_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands_test.go b/commands_test.go index 018569d75..00434bae1 100644 --- a/commands_test.go +++ b/commands_test.go @@ -2330,7 +2330,7 @@ var _ = Describe("Commands", func() { Key2: "keywithnonstringvalue", }).Result() Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("WRONGTYPE Operation against a key holding the wrong kind of value")) + Expect(err.Error()).To(Equal("ERR The specified keys must contain string values")) }) From 48d685c6b455588c75c6f09b0c2446133837f411 Mon Sep 17 00:00:00 2001 From: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Date: Fri, 24 Mar 2023 12:20:36 +0530 Subject: [PATCH 13/13] fix: removed blank lines --- commands_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/commands_test.go b/commands_test.go index 00434bae1..05509f552 100644 --- a/commands_test.go +++ b/commands_test.go @@ -2319,20 +2319,16 @@ var _ = Describe("Commands", func() { }, })) - _, err = client.Set(ctx, "keywithstringvalue", "golang",0).Result() + _, err = client.Set(ctx, "keywithstringvalue", "golang", 0).Result() Expect(err).NotTo(HaveOccurred()) - - _, err = client.LPush(ctx, "keywithnonstringvalue", "somevalue").Result() + _, err = client.LPush(ctx, "keywithnonstringvalue", "somevalue").Result() Expect(err).NotTo(HaveOccurred()) - _, err = client.LCS(ctx, &redis.LCSQuery{ Key1: "keywithstringvalue", Key2: "keywithnonstringvalue", }).Result() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("ERR The specified keys must contain string values")) - - }) It("should LIndex", func() {