From 55e5caed14454e1aabcdf5669680bc69bef4ab97 Mon Sep 17 00:00:00 2001 From: Harmen Date: Sat, 20 Sep 2014 11:22:55 +0200 Subject: [PATCH] SREM --- README.md | 1 + cmd_set.go | 30 +++++++++++++++++++++++- cmd_set_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ db.go | 18 +++++++++++++++ direct.go | 18 +++++++++++++++ 5 files changed, 127 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2af6abd8..7785d9ee 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ Implemented commands: - SCARD - SISMEMBER - SMEMBERS + - SREM Since this is intended to be used in unittests timeouts are not implemented. diff --git a/cmd_set.go b/cmd_set.go index 333519c9..599683a0 100644 --- a/cmd_set.go +++ b/cmd_set.go @@ -19,7 +19,7 @@ func commandsSet(m *Miniredis, srv *redeo.Server) { // SMOVE source destination member // SPOP key // SRANDMEMBER key [count] - // SREM key member [member ...] + srv.HandleFunc("SREM", m.cmdSrem) // SUNION key [key ...] // SUNIONSTORE destination key [key ...] // SSCAN key cursor [MATCH pattern] [COUNT count] @@ -140,3 +140,31 @@ func (m *Miniredis) cmdSmembers(out *redeo.Responder, r *redeo.Request) error { } }) } + +// SREM +func (m *Miniredis) cmdSrem(out *redeo.Responder, r *redeo.Request) error { + if len(r.Args) < 2 { + setDirty(r.Client()) + out.WriteErrorString("ERR wrong number of arguments for 'srem' command") + return nil + } + + key := r.Args[0] + fields := r.Args[1:] + + return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + if !db.exists(key) { + out.WriteInt(0) + return + } + + if db.t(key) != "set" { + out.WriteErrorString(ErrWrongType.Error()) + return + } + + out.WriteInt(db.setrem(key, fields...)) + }) +} diff --git a/cmd_set_test.go b/cmd_set_test.go index f3624246..94012c1a 100644 --- a/cmd_set_test.go +++ b/cmd_set_test.go @@ -133,3 +133,64 @@ func TestSismember(t *testing.T) { } } + +// Test SREM +func TestSrem(t *testing.T) { + s, err := Run() + ok(t, err) + defer s.Close() + c, err := redis.Dial("tcp", s.Addr()) + ok(t, err) + + s.SetAdd("s", "aap", "noot", "mies", "vuur") + + { + b, err := redis.Int(c.Do("SREM", "s", "aap", "noot")) + ok(t, err) + equals(t, 2, b) + + members, err := s.Members("s") + ok(t, err) + equals(t, []string{"mies", "vuur"}, members) + } + + // a nonexisting field + { + b, err := redis.Int(c.Do("SREM", "s", "nosuch")) + ok(t, err) + equals(t, 0, b) + } + + // a nonexisting key + { + b, err := redis.Int(c.Do("SREM", "nosuch", "nosuch")) + ok(t, err) + equals(t, 0, b) + } + + // Direct usage + { + b, err := s.SRem("s", "mies") + ok(t, err) + equals(t, 1, b) + + members, err := s.Members("s") + ok(t, err) + equals(t, []string{"vuur"}, members) + } + + // Wrong type of key + { + _, err := redis.String(c.Do("SET", "str", "value")) + ok(t, err) + _, err = redis.Int(c.Do("SREM", "str", "value")) + assert(t, err != nil, "SREM error") + // Wrong argument counts + _, err = redis.String(c.Do("SREM")) + assert(t, err != nil, "SREM error") + _, err = redis.String(c.Do("SREM", "set")) + assert(t, err != nil, "SREM error") + _, err = redis.String(c.Do("SREM", "set", "spurious", "args")) + assert(t, err != nil, "SREM error") + } +} diff --git a/db.go b/db.go index c7574539..44d077ad 100644 --- a/db.go +++ b/db.go @@ -200,6 +200,24 @@ func (db *RedisDB) setadd(k string, elems ...string) int { return added } +// setrem removes members from a set. Returns nr of deleted keys. +func (db *RedisDB) setrem(k string, fields ...string) int { + s, ok := db.setKeys[k] + if !ok { + return 0 + } + removed := 0 + for _, f := range fields { + if _, ok := s[f]; ok { + removed++ + delete(s, f) + } + } + db.setKeys[k] = s + db.keyVersion[k]++ + return removed +} + // All members of a set. func (db *RedisDB) members(k string) []string { set := db.setKeys[k] diff --git a/direct.go b/direct.go index 3d274710..d6a275b0 100644 --- a/direct.go +++ b/direct.go @@ -370,3 +370,21 @@ func (db *RedisDB) HIncrfloat(k, f string, delta float64) (float64, error) { defer db.master.Unlock() return db.hincrfloat(k, f, delta) } + +// SRem removes fields from a set. Returns number of deleted fields. +func (m *Miniredis) SRem(k string, fields ...string) (int, error) { + return m.DB(m.selectedDB).SRem(k, fields...) +} + +// SRem removes fields from a set. Returns number of deleted fields. +func (db *RedisDB) SRem(k string, fields ...string) (int, error) { + db.master.Lock() + defer db.master.Unlock() + if !db.exists(k) { + return 0, ErrKeyNotFound + } + if db.t(k) != "set" { + return 0, ErrWrongType + } + return db.setrem(k, fields...), nil +}