mirror of
https://github.com/redis/go-redis.git
synced 2025-07-28 06:42:00 +03:00
Add support for LCS Command (#2480)
* feat: initial set up for LCSCmd * feat: adding test for LCS with no additional params * feat: adding test for LCS with LEN param * feat: resolving conflicts in command.go * fix: changing reply and query structure * feat: commands test for lcs and lcs with len * fix: using dynamic args length * fix: read * fix: Cmd init * sorting out the LCS commands Signed-off-by: monkey92t <golang@88.com> * fix: Adding error case test * fix: adding correct error message * fix: removed blank lines --------- Signed-off-by: monkey92t <golang@88.com> Co-authored-by: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Co-authored-by: monkey92t <golang@88.com>
This commit is contained in:
committed by
GitHub
parent
38aa0b7792
commit
6790337e5d
177
command.go
177
command.go
@ -3993,9 +3993,178 @@ func (cmd *FunctionListCmd) readFunctions(rd *proto.Reader) ([]Function, error)
|
|||||||
return functions, nil
|
return functions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilterBy struct {
|
//------------------------------------------------------------------------------
|
||||||
Module string
|
|
||||||
ACLCat string
|
// LCSQuery is a parameter used for the LCS command
|
||||||
Pattern string
|
type LCSQuery struct {
|
||||||
|
Key1 string
|
||||||
|
Key2 string
|
||||||
|
Len bool
|
||||||
|
Idx bool
|
||||||
|
MinMatchLen int
|
||||||
|
WithMatchLen bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LCSMatch is the result set of the LCS command.
|
||||||
|
type LCSMatch struct {
|
||||||
|
MatchString string
|
||||||
|
Matches []LCSMatchedPosition
|
||||||
|
Len int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type LCSMatchedPosition struct {
|
||||||
|
Key1 LCSPosition
|
||||||
|
Key2 LCSPosition
|
||||||
|
|
||||||
|
// only for withMatchLen is true
|
||||||
|
MatchLen int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type LCSPosition struct {
|
||||||
|
Start int64
|
||||||
|
End int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type LCSCmd struct {
|
||||||
|
baseCmd
|
||||||
|
|
||||||
|
// 1: match string
|
||||||
|
// 2: match len
|
||||||
|
// 3: match idx LCSMatch
|
||||||
|
readType uint8
|
||||||
|
val *LCSMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
cmd.val = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *LCSCmd) String() string {
|
||||||
|
return cmdString(cmd, cmd.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *LCSCmd) Val() *LCSMatch {
|
||||||
|
return cmd.val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *LCSCmd) Result() (*LCSMatch, error) {
|
||||||
|
return cmd.val, cmd.err
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case "matches":
|
||||||
|
// read array of matched positions
|
||||||
|
if lcs.Matches, err = cmd.readMatchedPositions(rd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case "len":
|
||||||
|
// read match length
|
||||||
|
if lcs.Len, err = rd.ReadInt(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.val = lcs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *LCSCmd) 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++ {
|
||||||
|
pn, err := rd.ReadArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if positions[i].Key1, err = cmd.readPosition(rd); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if positions[i].Key2, err = cmd.readPosition(rd); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// read match length if WithMatchLen is true
|
||||||
|
if pn > 2 {
|
||||||
|
if positions[i].MatchLen, err = rd.ReadInt(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return positions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *LCSCmd) readPosition(rd *proto.Reader) (pos LCSPosition, err error) {
|
||||||
|
if err = rd.ReadFixedArrayLen(2); err != nil {
|
||||||
|
return pos, err
|
||||||
|
}
|
||||||
|
if pos.Start, err = rd.ReadInt(); err != nil {
|
||||||
|
return pos, err
|
||||||
|
}
|
||||||
|
if pos.End, err = rd.ReadInt(); err != nil {
|
||||||
|
return pos, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos, nil
|
||||||
|
}
|
||||||
|
14
commands.go
14
commands.go
@ -227,6 +227,7 @@ type Cmdable interface {
|
|||||||
BLMPop(ctx context.Context, timeout time.Duration, direction string, count int64, keys ...string) *KeyValuesCmd
|
BLMPop(ctx context.Context, timeout time.Duration, direction string, count int64, keys ...string) *KeyValuesCmd
|
||||||
BRPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd
|
BRPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd
|
||||||
BRPopLPush(ctx context.Context, source, destination string, timeout time.Duration) *StringCmd
|
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
|
LIndex(ctx context.Context, key string, index int64) *StringCmd
|
||||||
LInsert(ctx context.Context, key, op string, pivot, value interface{}) *IntCmd
|
LInsert(ctx context.Context, key, op string, pivot, value interface{}) *IntCmd
|
||||||
LInsertBefore(ctx context.Context, key string, pivot, value interface{}) *IntCmd
|
LInsertBefore(ctx context.Context, key string, pivot, value interface{}) *IntCmd
|
||||||
@ -543,6 +544,13 @@ func (c cmdable) Command(ctx context.Context) *CommandsInfoCmd {
|
|||||||
return cmd
|
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 {
|
func (c cmdable) CommandList(ctx context.Context, filter *FilterBy) *StringSliceCmd {
|
||||||
args := make([]interface{}, 0, 5)
|
args := make([]interface{}, 0, 5)
|
||||||
args = append(args, "command", "list")
|
args = append(args, "command", "list")
|
||||||
@ -1524,6 +1532,12 @@ func (c cmdable) BRPopLPush(ctx context.Context, source, destination string, tim
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c cmdable) LCS(ctx context.Context, q *LCSQuery) *LCSCmd {
|
||||||
|
cmd := NewLCSCmd(ctx, q)
|
||||||
|
_ = c(ctx, cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
func (c cmdable) LIndex(ctx context.Context, key string, index int64) *StringCmd {
|
func (c cmdable) LIndex(ctx context.Context, key string, index int64) *StringCmd {
|
||||||
cmd := NewStringCmd(ctx, "lindex", key, index)
|
cmd := NewStringCmd(ctx, "lindex", key, index)
|
||||||
_ = c(ctx, cmd)
|
_ = c(ctx, cmd)
|
||||||
|
@ -2251,6 +2251,86 @@ var _ = Describe("Commands", func() {
|
|||||||
Expect(v).To(Equal("c"))
|
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(err).NotTo(HaveOccurred())
|
||||||
|
Expect(lcs.MatchString).To(Equal(""))
|
||||||
|
|
||||||
|
lcs, err = client.LCS(ctx, &redis.LCSQuery{
|
||||||
|
Key1: "key1",
|
||||||
|
Key2: "key2",
|
||||||
|
Len: true,
|
||||||
|
}).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(lcs.MatchString).To(Equal(""))
|
||||||
|
Expect(lcs.Len).To(Equal(int64(6)))
|
||||||
|
|
||||||
|
lcs, err = client.LCS(ctx, &redis.LCSQuery{
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
_, 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("ERR The specified keys must contain string values"))
|
||||||
|
})
|
||||||
|
|
||||||
It("should LIndex", func() {
|
It("should LIndex", func() {
|
||||||
lPush := client.LPush(ctx, "list", "World")
|
lPush := client.LPush(ctx, "list", "World")
|
||||||
Expect(lPush.Err()).NotTo(HaveOccurred())
|
Expect(lPush.Err()).NotTo(HaveOccurred())
|
||||||
|
Reference in New Issue
Block a user