Skip to content

Commit bd43d1a

Browse files
committed
Add Support for Count-Min Sketch Commands
1 parent b4e2818 commit bd43d1a

File tree

2 files changed

+141
-0
lines changed

2 files changed

+141
-0
lines changed

client.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,71 @@ func (client *Client) BfExistsMulti(key string, items []string) ([]int64, error)
131131
result, err := conn.Do("BF.MEXISTS", args...)
132132
return redis.Int64s(result, err)
133133
}
134+
135+
// Initializes a Count-Min Sketch to dimensions specified by user.
136+
func (client *Client) CmsInitByDim(key string, width int64, depth int64) (string, error) {
137+
conn := client.Pool.Get()
138+
defer conn.Close()
139+
result, err := conn.Do("CMS.INITBYDIM", key, width, depth)
140+
return redis.String(result, err)
141+
}
142+
143+
// Initializes a Count-Min Sketch to accommodate requested capacity.
144+
func (client *Client) CmsInitByProb(key string, error float64, probability float64) (string, error) {
145+
conn := client.Pool.Get()
146+
defer conn.Close()
147+
result, err := conn.Do("CMS.INITBYPROB", key, error, probability)
148+
return redis.String(result, err)
149+
}
150+
151+
// Increases the count of item by increment. Multiple items can be increased with one call.
152+
func (client *Client) CmsIncrBy(key string, itemIncrements map[string]int64) ([]int64, error) {
153+
conn := client.Pool.Get()
154+
defer conn.Close()
155+
args := redis.Args{key}
156+
for k, v := range itemIncrements {
157+
args = args.Add(k, v)
158+
}
159+
result, err := conn.Do("CMS.INCRBY", args...)
160+
return redis.Int64s(result, err)
161+
}
162+
163+
// Returns count for item.
164+
func (client *Client) CmsQuery(key string, items []string) ([]int64, error) {
165+
conn := client.Pool.Get()
166+
defer conn.Close()
167+
args := redis.Args{key}.AddFlat(items)
168+
result, err := conn.Do("CMS.QUERY", args...)
169+
return redis.Int64s(result, err)
170+
}
171+
172+
// Merges several sketches into one sketch.
173+
func (client *Client) CmsMerge(key string, srcs []string, weights []string) (string, error) {
174+
conn := client.Pool.Get()
175+
defer conn.Close()
176+
args := redis.Args{key}.Add(len(srcs)).AddFlat(srcs)
177+
if weights != nil && len(weights) > 0 {
178+
args = args.Add("WEIGHTS").AddFlat(weights)
179+
}
180+
return redis.String(conn.Do("CMS.MERGE", args...))
181+
}
182+
183+
// Returns width, depth and total count of the sketch.
184+
func (client *Client) CmsInfo(key string) (map[string]int64, error) {
185+
conn := client.Pool.Get()
186+
defer conn.Close()
187+
reply, err := conn.Do("CMS.INFO", key)
188+
189+
values, err := redis.Values(reply, err)
190+
if err != nil {
191+
return nil, err
192+
}
193+
if len(values)%2 != 0 {
194+
return nil, errors.New("expects even number of values result")
195+
}
196+
m := make(map[string]int64, len(values)/2)
197+
for i := 0; i < len(values); i += 2 {
198+
m[values[i].(string)] = values[i+1].(int64)
199+
}
200+
return m, err
201+
}

client_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,76 @@ func TestClient_BfExistsMulti(t *testing.T) {
129129
assert.Equal(t, int64(1), existsResult[1])
130130
assert.Equal(t, int64(0), existsResult[2])
131131
}
132+
133+
func TestClient_CmsInitByDim(t *testing.T) {
134+
client.FlushAll()
135+
ret, err := client.CmsInitByDim("test_cms_initbydim", 1000, 5)
136+
assert.Nil(t, err)
137+
assert.Equal(t, "OK", ret)
138+
}
139+
140+
func TestClient_CmsInitByProb(t *testing.T) {
141+
client.FlushAll()
142+
ret, err := client.CmsInitByProb("test_cms_initbyprob", 0.01, 0.01)
143+
assert.Nil(t, err)
144+
assert.Equal(t, "OK", ret)
145+
}
146+
147+
func TestClient_CmsIncrBy(t *testing.T) {
148+
client.FlushAll()
149+
key := "test_cms_incrby"
150+
ret, err := client.CmsInitByDim(key, 1000, 5)
151+
assert.Nil(t, err)
152+
assert.Equal(t, "OK", ret)
153+
results, err := client.CmsIncrBy(key, map[string]int64{"foo": 5})
154+
assert.Nil(t, err)
155+
assert.NotNil(t, results)
156+
assert.Equal(t, int64(5), results[0])
157+
}
158+
159+
func TestClient_CmsQuery(t *testing.T) {
160+
client.FlushAll()
161+
key := "test_cms_query"
162+
ret, err := client.CmsInitByDim(key, 1000, 5)
163+
assert.Nil(t, err)
164+
assert.Equal(t, "OK", ret)
165+
results, err := client.CmsQuery(key, []string{"notexist"})
166+
assert.Nil(t, err)
167+
assert.NotNil(t, 0, results[0])
168+
_, err = client.CmsIncrBy(key, map[string]int64{"foo": 5})
169+
assert.Nil(t, err)
170+
results, err = client.CmsQuery(key, []string{"foo"})
171+
assert.Nil(t, err)
172+
assert.Equal(t, int64(5), results[0])
173+
}
174+
175+
func TestClient_CmsMerge(t *testing.T) {
176+
client.FlushAll()
177+
ret, err := client.CmsInitByDim("A", 1000, 5)
178+
assert.Nil(t, err)
179+
assert.Equal(t, "OK", ret)
180+
ret, err = client.CmsInitByDim("B", 1000, 5)
181+
assert.Nil(t, err)
182+
assert.Equal(t, "OK", ret)
183+
ret, err = client.CmsInitByDim("C", 1000, 5)
184+
assert.Nil(t, err)
185+
assert.Equal(t, "OK", ret)
186+
client.CmsIncrBy("A", map[string]int64{"foo": 5, "bar": 3, "baz": 9})
187+
client.CmsIncrBy("B", map[string]int64{"foo": 2, "bar": 3, "baz": 1})
188+
client.CmsMerge("C", []string{"A", "B"}, nil)
189+
results, err := client.CmsQuery("C", []string{"foo", "bar", "baz"})
190+
assert.Equal(t, []int64{7, 6, 10}, results)
191+
}
192+
193+
func TestClient_CmsInfo(t *testing.T) {
194+
client.FlushAll()
195+
key := "test_cms_info"
196+
ret, err := client.CmsInitByDim(key, 1000, 5)
197+
assert.Nil(t, err)
198+
assert.Equal(t, "OK", ret)
199+
info, err := client.CmsInfo(key)
200+
assert.Nil(t, err)
201+
assert.Equal(t, int64(1000), info["width"])
202+
assert.Equal(t, int64(5), info["depth"])
203+
assert.Equal(t, int64(0), info["count"])
204+
}

0 commit comments

Comments
 (0)