Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -7647,6 +7647,208 @@ func (cmd *VectorScoreSliceCmd) Clone() Cmder {
}
}

func readVectorAttribStringOrNil(rd *proto.Reader) (*string, error) {
v, err := rd.ReadReply()
if err != nil {
if err == proto.Nil {
return nil, nil
}
return nil, err
}
s, ok := v.(string)
if !ok {
return nil, fmt.Errorf("redis: can't parse reply=%T reading string", v)
}
return &s, nil
}

type VectorAttribSliceCmd struct {
baseCmd

val []VectorAttrib
}

var _ Cmder = (*VectorAttribSliceCmd)(nil)

func NewVectorAttribSliceCmd(ctx context.Context, args ...any) *VectorAttribSliceCmd {
return &VectorAttribSliceCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}

func (cmd *VectorAttribSliceCmd) SetVal(val []VectorAttrib) {
cmd.val = val
}

func (cmd *VectorAttribSliceCmd) Val() []VectorAttrib {
return cmd.val
}

func (cmd *VectorAttribSliceCmd) Result() ([]VectorAttrib, error) {
return cmd.val, cmd.err
}

func (cmd *VectorAttribSliceCmd) String() string {
return cmdString(cmd, cmd.val)
}

func (cmd *VectorAttribSliceCmd) readReply(rd *proto.Reader) error {
replyType, err := rd.PeekReplyType()
if err != nil {
return err
}

if replyType == proto.RespMap {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
cmd.val = make([]VectorAttrib, n)
for i := 0; i < n; i++ {
name, err := rd.ReadString()
if err != nil {
return err
}
attrib, err := readVectorAttribStringOrNil(rd)
if err != nil {
return err
}
cmd.val[i] = VectorAttrib{Name: name, Attribs: attrib}
}
return nil
}

n, err := rd.ReadArrayLen()
if err != nil {
return err
}
if n%2 != 0 {
return fmt.Errorf("redis: got %d elements in the VSIM array, wanted a multiple of 2", n)
}
cmd.val = make([]VectorAttrib, n/2)
for i := range cmd.val {
name, err := rd.ReadString()
if err != nil {
return err
}
attrib, err := readVectorAttribStringOrNil(rd)
if err != nil {
return err
}
cmd.val[i] = VectorAttrib{Name: name, Attribs: attrib}
}
return nil
}

func (cmd *VectorAttribSliceCmd) Clone() Cmder {
return &VectorAttribSliceCmd{
baseCmd: cmd.cloneBaseCmd(),
val: cmd.val,
}
}

type VectorScoreAttribSliceCmd struct {
baseCmd

val []VectorScoreAttrib
}

var _ Cmder = (*VectorScoreAttribSliceCmd)(nil)

func NewVectorScoreAttribSliceCmd(ctx context.Context, args ...any) *VectorScoreAttribSliceCmd {
return &VectorScoreAttribSliceCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}

func (cmd *VectorScoreAttribSliceCmd) SetVal(val []VectorScoreAttrib) {
cmd.val = val
}

func (cmd *VectorScoreAttribSliceCmd) Val() []VectorScoreAttrib {
return cmd.val
}

func (cmd *VectorScoreAttribSliceCmd) Result() ([]VectorScoreAttrib, error) {
return cmd.val, cmd.err
}

func (cmd *VectorScoreAttribSliceCmd) String() string {
return cmdString(cmd, cmd.val)
}

func (cmd *VectorScoreAttribSliceCmd) readReply(rd *proto.Reader) error {
Comment thread
ndyakov marked this conversation as resolved.
replyType, err := rd.PeekReplyType()
if err != nil {
return err
}

if replyType == proto.RespMap {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
cmd.val = make([]VectorScoreAttrib, n)
for i := 0; i < n; i++ {
name, err := rd.ReadString()
if err != nil {
return err
}
if err := rd.ReadFixedArrayLen(2); err != nil {
return err
}
score, err := rd.ReadFloat()
if err != nil {
return err
}
attrib, err := readVectorAttribStringOrNil(rd)
if err != nil {
return err
}
cmd.val[i] = VectorScoreAttrib{Name: name, Score: score, Attribs: attrib}
}
return nil
}

n, err := rd.ReadArrayLen()
if err != nil {
return err
}
if n%3 != 0 {
return fmt.Errorf("redis: got %d elements in the VSIM array, wanted a multiple of 3", n)
}
cmd.val = make([]VectorScoreAttrib, n/3)
for i := range cmd.val {
name, err := rd.ReadString()
if err != nil {
return err
}
score, err := rd.ReadFloat()
if err != nil {
return err
}
attrib, err := readVectorAttribStringOrNil(rd)
if err != nil {
return err
}
cmd.val[i] = VectorScoreAttrib{Name: name, Score: score, Attribs: attrib}
}
return nil
}

func (cmd *VectorScoreAttribSliceCmd) Clone() Cmder {
return &VectorScoreAttribSliceCmd{
baseCmd: cmd.cloneBaseCmd(),
val: cmd.val,
}
}

func (cmd *MonitorCmd) Clone() Cmder {
// MonitorCmd cannot be safely cloned due to channels and goroutines
// Return a new MonitorCmd with the same channel
Expand Down
42 changes: 42 additions & 0 deletions doctests/vec_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,44 @@ func ExampleClient_vectorset() {
}

fmt.Println(res30) // >>> [pt:C pt:B]

res31, err := rdb.VIsMember(ctx, "points", "pt:A").Result()

if err != nil {
panic(err)
}

fmt.Println(res31) // >>> true

res32, err := rdb.VIsMember(ctx, "points", "pt:W").Result()

if err != nil {
panic(err)
}

fmt.Println(res32) // >>> false

res33, err := rdb.VSimWithArgsWithAttribs(ctx, "points",
&redis.VectorRef{Name: "pt:A"},
&redis.VSimArgs{Count: 1, Truth: true},
).Result()

if err != nil {
panic(err)
}

fmt.Println(res33[0].Name, *res33[0].Attribs) // >>> pt:A {"price":18.99,"size":"large"}

res34, err := rdb.VSimWithArgsWithScoresWithAttribs(ctx, "points",
&redis.VectorRef{Name: "pt:A"},
&redis.VSimArgs{Count: 1, Truth: true},
).Result()

if err != nil {
panic(err)
}

fmt.Println(res34[0].Name, res34[0].Score, *res34[0].Attribs) // >>> pt:A 1 {"price":18.99,"size":"large"}
// STEP_END

// Output:
Expand Down Expand Up @@ -378,6 +416,10 @@ func ExampleClient_vectorset() {
// true
// [pt:A pt:C pt:B]
// [pt:C pt:B]
// true
// false
// pt:A {"price":18.99,"size":"large"}
// pt:A 1 {"price":18.99,"size":"large"}
}

func ExampleClient_vectorset_quantization() {
Expand Down
59 changes: 58 additions & 1 deletion vectorset_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ type VectorSetCmdable interface {
VSimWithScores(ctx context.Context, key string, val Vector) *VectorScoreSliceCmd
VSimWithArgs(ctx context.Context, key string, val Vector, args *VSimArgs) *StringSliceCmd
VSimWithArgsWithScores(ctx context.Context, key string, val Vector, args *VSimArgs) *VectorScoreSliceCmd
VSimWithArgsWithAttribs(ctx context.Context, key string, val Vector, args *VSimArgs) *VectorAttribSliceCmd
VSimWithArgsWithScoresWithAttribs(ctx context.Context, key string, val Vector, args *VSimArgs) *VectorScoreAttribSliceCmd
VRange(ctx context.Context, key, start, end string, count int64) *StringSliceCmd
VIsMember(ctx context.Context, key, element string) *BoolCmd
}

type Vector interface {
Expand Down Expand Up @@ -79,6 +82,17 @@ type VectorScore struct {
Score float64
}

type VectorAttrib struct {
Name string
Attribs *string
}

type VectorScoreAttrib struct {
Name string
Score float64
Attribs *string
Comment thread
ndyakov marked this conversation as resolved.
}

// `VADD key (FP32 | VALUES num) vector element`
// note: the API is experimental and may be subject to change.
func (c cmdable) VAdd(ctx context.Context, key, element string, val Vector) *BoolCmd {
Expand Down Expand Up @@ -311,7 +325,7 @@ func (v VSimArgs) appendArgs(args []any) []any {
args = append(args, "nothread")
}
if v.Epsilon > 0 {
args = append(args, "Epsilon", v.Epsilon)
args = append(args, "epsilon", v.Epsilon)
}
return args
}
Expand Down Expand Up @@ -347,6 +361,40 @@ func (c cmdable) VSimWithArgsWithScores(ctx context.Context, key string, val Vec
return cmd
}

// `VSIM key (ELE | FP32 | VALUES num) (vector | element) [WITHATTRIBS] [COUNT num] [EPSILON delta]
Comment thread
cursor[bot] marked this conversation as resolved.
// [EF search-exploration-factor] [FILTER expression] [FILTER-EF max-filtering-effort] [TRUTH] [NOTHREAD]`
// WITHATTRIBS is only available in Redis v8.2.0+
// note: the API is experimental and may be subject to change.
func (c cmdable) VSimWithArgsWithAttribs(ctx context.Context, key string, val Vector, simArgs *VSimArgs) *VectorAttribSliceCmd {
if simArgs == nil {
simArgs = &VSimArgs{}
}
args := []any{"vsim", key}
args = append(args, val.Value()...)
args = append(args, "withattribs")
args = simArgs.appendArgs(args)
cmd := NewVectorAttribSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}

// `VSIM key (ELE | FP32 | VALUES num) (vector | element) [WITHSCORES] [WITHATTRIBS] [COUNT num] [EPSILON delta]
// [EF search-exploration-factor] [FILTER expression] [FILTER-EF max-filtering-effort] [TRUTH] [NOTHREAD]`
// WITHATTRIBS is only available in Redis v8.2.0+
// note: the API is experimental and may be subject to change.
func (c cmdable) VSimWithArgsWithScoresWithAttribs(ctx context.Context, key string, val Vector, simArgs *VSimArgs) *VectorScoreAttribSliceCmd {
if simArgs == nil {
simArgs = &VSimArgs{}
}
args := []any{"vsim", key}
args = append(args, val.Value()...)
args = append(args, "withscores", "withattribs")
args = simArgs.appendArgs(args)
cmd := NewVectorScoreAttribSliceCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}

// `VRANGE key start end count`
// a negative count means to return all the elements in the vector set.
// note: the API is experimental and may be subject to change.
Expand All @@ -356,3 +404,12 @@ func (c cmdable) VRange(ctx context.Context, key, start, end string, count int64
_ = c(ctx, cmd)
return cmd
}

// `VISMEMBER key element`
// Check if an element exists in a vector set.
// note: the API is experimental and may be subject to change.
func (c cmdable) VIsMember(ctx context.Context, key, element string) *BoolCmd {
cmd := NewBoolCmd(ctx, "vismember", key, element)
_ = c(ctx, cmd)
return cmd
}
Loading
Loading