Skip to content

Commit 13e7ce8

Browse files
committed
fix zset
1 parent 244953f commit 13e7ce8

File tree

5 files changed

+176
-13
lines changed

5 files changed

+176
-13
lines changed

cmd/cli.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,14 @@ var supportedCommands = map[string]cmdHandler{
6565
"sunion": sUnion,
6666

6767
// zset commands
68-
"zadd": zAdd,
69-
"zscore": zScore,
70-
"zrem": zRem,
71-
"zcard": zCard,
72-
"zrange": zRange,
68+
"zadd": zAdd,
69+
"zscore": zScore,
70+
"zrem": zRem,
71+
"zcard": zCard,
72+
"zrange": zRange,
73+
"zrevrange": zRevRange,
74+
"zrank": zRank,
75+
"zrevrank": zRevRank,
7376

7477
// generic commands
7578
"type": keyType,

cmd/command.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,3 +738,40 @@ func zRange(cli *Client, args [][]byte) (interface{}, error) {
738738
}
739739
return cli.db.ZRange(args[0], start, stop)
740740
}
741+
742+
func zRevRange(cli *Client, args [][]byte) (interface{}, error) {
743+
if len(args) != 3 {
744+
return nil, newWrongNumOfArgsError("zrevrange")
745+
}
746+
start, err := strconv.Atoi(string(args[1]))
747+
if err != nil {
748+
return nil, err
749+
}
750+
stop, err := strconv.Atoi(string(args[2]))
751+
if err != nil {
752+
return nil, err
753+
}
754+
return cli.db.ZRevRange(args[0], start, stop)
755+
}
756+
757+
func zRank(cli *Client, args [][]byte) (interface{}, error) {
758+
if len(args) != 2 {
759+
return nil, newWrongNumOfArgsError("zrank")
760+
}
761+
ok, rank := cli.db.ZRank(args[0], args[1])
762+
if !ok {
763+
return nil, nil
764+
}
765+
return rank, nil
766+
}
767+
768+
func zRevRank(cli *Client, args [][]byte) (interface{}, error) {
769+
if len(args) != 2 {
770+
return nil, newWrongNumOfArgsError("zrevrank")
771+
}
772+
ok, rank := cli.db.ZRevRank(args[0], args[1])
773+
if !ok {
774+
return nil, nil
775+
}
776+
return rank, nil
777+
}

index.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,27 +133,28 @@ func (db *RoseDB) buildSetsIndex(ent *logfile.LogEntry, pos *valuePos) {
133133
}
134134

135135
func (db *RoseDB) buildZSetIndex(ent *logfile.LogEntry, pos *valuePos) {
136-
idxTree := db.zsetIndex.trees[string(ent.Key)]
137136
if ent.Type == logfile.TypeDelete {
138137
db.zsetIndex.indexes.ZRem(string(ent.Key), string(ent.Value))
139-
if idxTree != nil {
140-
idxTree.Delete(ent.Value)
138+
if db.zsetIndex.trees[string(ent.Key)] != nil {
139+
db.zsetIndex.trees[string(ent.Key)].Delete(ent.Value)
141140
}
142141
return
143142
}
144143

145144
key, scoreBuf := db.decodeKey(ent.Key)
146145
score, _ := util.StrToFloat64(string(scoreBuf))
147-
148-
if idxTree == nil {
149-
idxTree = art.NewART()
150-
}
151146
if err := db.zsetIndex.murhash.Write(ent.Value); err != nil {
152147
logger.Fatalf("fail to write murmur hash: %v", err)
153148
}
154149
sum := db.zsetIndex.murhash.EncodeSum128()
155150
db.zsetIndex.murhash.Reset()
156151

152+
idxTree := db.zsetIndex.trees[string(key)]
153+
if idxTree == nil {
154+
idxTree = art.NewART()
155+
db.zsetIndex.trees[string(key)] = idxTree
156+
}
157+
157158
_, size := logfile.EncodeEntry(ent)
158159
idxNode := &indexNode{fid: pos.fid, offset: pos.offset, entrySize: size}
159160
if db.opts.IndexMode == KeyValueMemMode {

zset.go

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,28 @@ func (db *RoseDB) ZCard(key []byte) int {
103103

104104
// ZRange returns the specified range of elements in the sorted set stored at key.
105105
func (db *RoseDB) ZRange(key []byte, start, stop int) ([][]byte, error) {
106+
return db.zRangeInternal(key, start, stop, false)
107+
}
108+
109+
// ZRevRange returns the specified range of elements in the sorted set stored at key.
110+
// The elements are considered to be ordered from the highest to the lowest score.
111+
func (db *RoseDB) ZRevRange(key []byte, start, stop int) ([][]byte, error) {
112+
return db.zRangeInternal(key, start, stop, true)
113+
}
114+
115+
// ZRank returns the rank of member in the sorted set stored at key, with the scores ordered from low to high.
116+
// The rank (or index) is 0-based, which means that the member with the lowest score has rank 0.
117+
func (db *RoseDB) ZRank(key []byte, member []byte) (ok bool, rank int) {
118+
return db.zRankInternal(key, member, false)
119+
}
120+
121+
// ZRevRank returns the rank of member in the sorted set stored at key, with the scores ordered from high to low.
122+
// The rank (or index) is 0-based, which means that the member with the highest score has rank 0.
123+
func (db *RoseDB) ZRevRank(key []byte, member []byte) (ok bool, rank int) {
124+
return db.zRankInternal(key, member, true)
125+
}
126+
127+
func (db *RoseDB) zRangeInternal(key []byte, start, stop int, rev bool) ([][]byte, error) {
106128
db.zsetIndex.mu.RLock()
107129
defer db.zsetIndex.mu.RUnlock()
108130
if db.zsetIndex.trees[string(key)] == nil {
@@ -111,7 +133,12 @@ func (db *RoseDB) ZRange(key []byte, start, stop int) ([][]byte, error) {
111133
idxTree := db.zsetIndex.trees[string(key)]
112134

113135
var res [][]byte
114-
values := db.zsetIndex.indexes.ZRange(string(key), start, stop)
136+
var values []interface{}
137+
if rev {
138+
values = db.zsetIndex.indexes.ZRevRange(string(key), start, stop)
139+
} else {
140+
values = db.zsetIndex.indexes.ZRange(string(key), start, stop)
141+
}
115142
for _, val := range values {
116143
v, _ := val.(string)
117144
if val, err := db.getVal(idxTree, []byte(v), ZSet); err != nil {
@@ -122,3 +149,29 @@ func (db *RoseDB) ZRange(key []byte, start, stop int) ([][]byte, error) {
122149
}
123150
return res, nil
124151
}
152+
153+
func (db *RoseDB) zRankInternal(key []byte, member []byte, rev bool) (ok bool, rank int) {
154+
db.zsetIndex.mu.RLock()
155+
defer db.zsetIndex.mu.RUnlock()
156+
if db.zsetIndex.trees[string(key)] == nil {
157+
return
158+
}
159+
160+
if err := db.zsetIndex.murhash.Write(member); err != nil {
161+
return
162+
}
163+
sum := db.zsetIndex.murhash.EncodeSum128()
164+
db.zsetIndex.murhash.Reset()
165+
166+
var result int64
167+
if rev {
168+
result = db.zsetIndex.indexes.ZRevRank(string(key), string(sum))
169+
} else {
170+
result = db.zsetIndex.indexes.ZRank(string(key), string(sum))
171+
}
172+
if result != -1 {
173+
ok = true
174+
rank = int(result)
175+
}
176+
return
177+
}

zset_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,75 @@ func testRoseDBZRange(t *testing.T, ioType IOType, mode DataIndexMode) {
188188
assert.Equal(t, 4, len(values))
189189
}
190190

191+
func TestRoseDB_ZRevRange(t *testing.T) {
192+
t.Run("fileio", func(t *testing.T) {
193+
testRoseDBZRevRange(t, FileIO, KeyValueMemMode)
194+
})
195+
t.Run("mmap", func(t *testing.T) {
196+
testRoseDBZRevRange(t, MMap, KeyOnlyMemMode)
197+
})
198+
}
199+
200+
func testRoseDBZRevRange(t *testing.T, ioType IOType, mode DataIndexMode) {
201+
path := filepath.Join("/tmp", "rosedb")
202+
opts := DefaultOptions(path)
203+
opts.IoType = ioType
204+
opts.IndexMode = mode
205+
db, err := Open(opts)
206+
assert.Nil(t, err)
207+
defer destroyDB(db)
208+
209+
zsetKey := []byte("my_zset")
210+
for i := 0; i < 100; i++ {
211+
err := db.ZAdd(zsetKey, float64(i+100), GetKey(i))
212+
assert.Nil(t, err)
213+
}
214+
215+
ok, score := db.ZScore(zsetKey, GetKey(3))
216+
assert.True(t, ok)
217+
assert.Equal(t, float64(103), score)
218+
219+
values, err := db.ZRevRange(zsetKey, 1, 10)
220+
assert.Nil(t, err)
221+
assert.Equal(t, 10, len(values))
222+
}
223+
224+
func TestRoseDB_ZRank(t *testing.T) {
225+
t.Run("fileio", func(t *testing.T) {
226+
testRoseDBZRank(t, FileIO, KeyValueMemMode)
227+
})
228+
t.Run("mmap", func(t *testing.T) {
229+
testRoseDBZRank(t, MMap, KeyOnlyMemMode)
230+
})
231+
}
232+
233+
func testRoseDBZRank(t *testing.T, ioType IOType, mode DataIndexMode) {
234+
path := filepath.Join("/tmp", "rosedb")
235+
opts := DefaultOptions(path)
236+
opts.IoType = ioType
237+
opts.IndexMode = mode
238+
db, err := Open(opts)
239+
assert.Nil(t, err)
240+
defer destroyDB(db)
241+
242+
zsetKey := []byte("my_zset")
243+
for i := 0; i < 100; i++ {
244+
err := db.ZAdd(zsetKey, float64(i+100), GetKey(i))
245+
assert.Nil(t, err)
246+
}
247+
248+
ok, r1 := db.ZRank(zsetKey, GetKey(-1))
249+
assert.False(t, ok)
250+
assert.Equal(t, 0, r1)
251+
252+
ok, r2 := db.ZRank(zsetKey, GetKey(3))
253+
assert.True(t, ok)
254+
assert.Equal(t, 3, r2)
255+
ok, r3 := db.ZRevRank(zsetKey, GetKey(1))
256+
assert.True(t, ok)
257+
assert.Equal(t, 98, r3)
258+
}
259+
191260
func TestRoseDB_ZSetGC(t *testing.T) {
192261
path := filepath.Join("/tmp", "rosedb")
193262
opts := DefaultOptions(path)

0 commit comments

Comments
 (0)