redis_recipes 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +28 -0
- data/README.md +26 -0
- data/Rakefile +8 -0
- data/lib/range_lookup/README.md +62 -0
- data/lib/range_lookup/add.lua +66 -0
- data/lib/range_lookup/lookup.lua +27 -0
- data/lib/range_lookup/remove.lua +62 -0
- data/lib/range_lookup_x/README.md +59 -0
- data/lib/range_lookup_x/add.lua +59 -0
- data/lib/range_lookup_x/lookup.lua +19 -0
- data/lib/range_lookup_x/remove.lua +51 -0
- data/redis_recipes.gemspec +23 -0
- data/spec/range_lookup/add_spec.rb +146 -0
- data/spec/range_lookup/lookup_spec.rb +64 -0
- data/spec/range_lookup/remove_spec.rb +189 -0
- data/spec/range_lookup_x/add_spec.rb +149 -0
- data/spec/range_lookup_x/lookup_spec.rb +65 -0
- data/spec/range_lookup_x/remove_spec.rb +186 -0
- data/spec/spec_helper.rb +64 -0
- metadata +121 -0
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
redis_recipes (0.3.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.3)
|
10
|
+
rake (0.9.2.2)
|
11
|
+
redis (3.0.2)
|
12
|
+
rspec (2.11.0)
|
13
|
+
rspec-core (~> 2.11.0)
|
14
|
+
rspec-expectations (~> 2.11.0)
|
15
|
+
rspec-mocks (~> 2.11.0)
|
16
|
+
rspec-core (2.11.1)
|
17
|
+
rspec-expectations (2.11.3)
|
18
|
+
diff-lcs (~> 1.1.3)
|
19
|
+
rspec-mocks (2.11.3)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
rake
|
26
|
+
redis (~> 3.0.0)
|
27
|
+
redis_recipes!
|
28
|
+
rspec
|
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Redis Recipes
|
2
|
+
|
3
|
+
Redis LUA recipes. Require Redis 2.6.0 or higher.
|
4
|
+
|
5
|
+
## License
|
6
|
+
|
7
|
+
Copyright (c) 2012 Black Square Media Ltd
|
8
|
+
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
a copy of this software and associated documentation files (the
|
11
|
+
"Software"), to deal in the Software without restriction, including
|
12
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be
|
18
|
+
included in all copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Redis Recipes: Range Lookup
|
2
|
+
|
3
|
+
Data structure, optimised for fast lookups of (possibly overlapping) ranges
|
4
|
+
containing a given value. Works with any numericly representable ranges, e.g.
|
5
|
+
dates, times, IP addresses, etc. Inspired by: http://stackoverflow.com/questions/8622816/redis-or-mongo-for-determining-if-a-number-falls-within-ranges/8624231#8624231
|
6
|
+
|
7
|
+
### Example / Internals:
|
8
|
+
|
9
|
+
Consider the following use case: "Holiday System"
|
10
|
+
|
11
|
+
* [A]lice is on vacation from the 4th until the 11th
|
12
|
+
* [B]ob leaves an 7th and returns on the 21st
|
13
|
+
* [C]olin is also away from 4th, but returns only on the 18th
|
14
|
+
* [D]eborah's holidays are from 16th to the 23th
|
15
|
+
|
16
|
+
Our numeric ranges would therefore be:
|
17
|
+
|
18
|
+
A 4-11
|
19
|
+
B 7-21
|
20
|
+
C 4-18
|
21
|
+
D 16-23
|
22
|
+
|
23
|
+
To identify the people away for any specific date quickly, we create a
|
24
|
+
flattened index:
|
25
|
+
|
26
|
+
4 [A C]
|
27
|
+
7 [A B C]
|
28
|
+
11 [B C]
|
29
|
+
16 [B C D]
|
30
|
+
18 [B D]
|
31
|
+
21 [D]
|
32
|
+
23 []
|
33
|
+
|
34
|
+
For any given day, we can match the list (set) of members that are on vacation,
|
35
|
+
by:
|
36
|
+
|
37
|
+
1. Just reading the members for exact hits. For example: on the 18th [B C D]
|
38
|
+
match.
|
39
|
+
2. Creating an intersection between the preceeding and the following set. For
|
40
|
+
example: on the 19th INTER([B C D], [B D]) -> [B D] match.
|
41
|
+
|
42
|
+
### Usage:
|
43
|
+
|
44
|
+
Add ranges:
|
45
|
+
|
46
|
+
redis-cli --eval <path/to/add.lua> holidays , A 4 11
|
47
|
+
redis-cli --eval <path/to/add.lua> holidays , B 7 21
|
48
|
+
redis-cli --eval <path/to/add.lua> holidays , C 4 18
|
49
|
+
redis-cli --eval <path/to/add.lua> holidays , D 16 23
|
50
|
+
redis-cli --eval <path/to/add.lua> holidays , X 5 15
|
51
|
+
|
52
|
+
Remove a range:
|
53
|
+
|
54
|
+
redis-cli --eval <path/to/remove.lua> holidays , X 5 15
|
55
|
+
|
56
|
+
Lookup range:
|
57
|
+
|
58
|
+
redis-cli --eval <path/to/lookup.lua> holidays , 18 # => 1) "B"
|
59
|
+
# 2) "C"
|
60
|
+
# 3) "D"
|
61
|
+
redis-cli --eval <path/to/lookup.lua> holidays , 19 # => 1) "B"
|
62
|
+
# 2) "D"
|
@@ -0,0 +1,66 @@
|
|
1
|
+
if #KEYS ~= 1 or #ARGV ~= 3 then
|
2
|
+
return redis.error_reply("wrong number of arguments")
|
3
|
+
end
|
4
|
+
|
5
|
+
local prefix = KEYS[1]
|
6
|
+
local member = ARGV[1]
|
7
|
+
local min = tonumber(ARGV[2])
|
8
|
+
local max = tonumber(ARGV[3])
|
9
|
+
|
10
|
+
if min == nil or max == nil or min > max then
|
11
|
+
return redis.error_reply("min/max are not numeric or out of range")
|
12
|
+
end
|
13
|
+
|
14
|
+
local index = prefix .. ":~"
|
15
|
+
local minscr = redis.call('zscore', index, min)
|
16
|
+
local maxscr = redis.call('zscore', index, max)
|
17
|
+
local minwrp = {}
|
18
|
+
local maxwrp = {}
|
19
|
+
local window = redis.call('zrangebyscore', index, "(" .. min, "(" .. max)
|
20
|
+
|
21
|
+
-- Find existing members to be included in the new min
|
22
|
+
if not minscr then
|
23
|
+
local before = redis.call('zrevrangebyscore', index, "(" .. min, "-inf", "limit", 0, 1)[1]
|
24
|
+
if before then
|
25
|
+
local after = redis.call('zrangebyscore', index, "(" .. min, "inf", "limit", 0, 1)[1]
|
26
|
+
if after then
|
27
|
+
minwrp = redis.call('sinter', prefix .. ":" .. before, prefix .. ":" .. after)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
-- Find existing members to be included in the new max
|
33
|
+
if not maxscr then
|
34
|
+
local after = redis.call('zrangebyscore', index, "(" .. max, "inf", "limit", 0, 1)[1]
|
35
|
+
if after then
|
36
|
+
local before = redis.call('zrevrangebyscore', index, "(" .. max, "-inf", "limit", 0, 1)[1]
|
37
|
+
if before then
|
38
|
+
maxwrp = redis.call('sinter', prefix .. ":" .. before, prefix .. ":" .. after)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
-- Store members in min & max sets
|
44
|
+
redis.call('sadd', prefix .. ":" .. min, member)
|
45
|
+
redis.call('sadd', prefix .. ":" .. max, member)
|
46
|
+
|
47
|
+
-- Store new min & max indices
|
48
|
+
if not minscr then redis.call('zadd', index, min, min) end
|
49
|
+
if not maxscr then redis.call('zadd', index, max, max) end
|
50
|
+
|
51
|
+
-- Store member in all existing sets between min & max
|
52
|
+
for _,key in pairs(window) do
|
53
|
+
redis.call('sadd', prefix .. ":" .. key, member)
|
54
|
+
end
|
55
|
+
|
56
|
+
-- Merge existing members into min
|
57
|
+
if #minwrp > 0 then
|
58
|
+
redis.call('sadd', prefix .. ":" .. min, unpack(minwrp))
|
59
|
+
end
|
60
|
+
|
61
|
+
-- Merge existing members into max
|
62
|
+
if #maxwrp > 0 then
|
63
|
+
redis.call('sadd', prefix .. ":" .. max, unpack(maxwrp))
|
64
|
+
end
|
65
|
+
|
66
|
+
return redis.status_reply("OK")
|
@@ -0,0 +1,27 @@
|
|
1
|
+
if #KEYS ~= 1 or #ARGV ~= 1 then
|
2
|
+
return redis.error_reply("wrong number of arguments")
|
3
|
+
end
|
4
|
+
|
5
|
+
local prefix = KEYS[1]
|
6
|
+
local value = tonumber(ARGV[1])
|
7
|
+
|
8
|
+
if value == nil then
|
9
|
+
return redis.error_reply("value is not numeric or out of range")
|
10
|
+
end
|
11
|
+
|
12
|
+
local members = {}
|
13
|
+
local score = redis.call('zscore', prefix .. ":~", value)
|
14
|
+
|
15
|
+
if score then -- Do we have an exact match?
|
16
|
+
members = redis.call('smembers', prefix .. ":" .. score)
|
17
|
+
else
|
18
|
+
local before = redis.call('zrevrangebyscore', prefix .. ":~", value, "-inf", "limit", 0, 1)[1]
|
19
|
+
if before then
|
20
|
+
local after = redis.call('zrangebyscore', prefix .. ":~", value, "inf", "limit", 0, 1)[1]
|
21
|
+
if after then
|
22
|
+
members = redis.call('sinter', prefix .. ":" .. before, prefix .. ":" .. after)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
return members
|
@@ -0,0 +1,62 @@
|
|
1
|
+
if #KEYS ~= 1 or #ARGV ~= 3 then
|
2
|
+
return redis.error_reply("wrong number of arguments")
|
3
|
+
end
|
4
|
+
|
5
|
+
local prefix = KEYS[1]
|
6
|
+
local member = ARGV[1]
|
7
|
+
local min = tonumber(ARGV[2])
|
8
|
+
local max = tonumber(ARGV[3])
|
9
|
+
|
10
|
+
if min == nil or max == nil or min > max then
|
11
|
+
return redis.error_reply("min/max are not numeric or out of range")
|
12
|
+
end
|
13
|
+
|
14
|
+
local index = prefix .. ":~"
|
15
|
+
local window = redis.call('zrangebyscore', index, min, max)
|
16
|
+
local before = nil
|
17
|
+
local after = nil
|
18
|
+
local minwrp = {}
|
19
|
+
local maxwrp = {}
|
20
|
+
|
21
|
+
-- Remove the member from all sets between min & max
|
22
|
+
for _, val in pairs(window) do
|
23
|
+
redis.call('srem', prefix .. ":" .. val, member)
|
24
|
+
end
|
25
|
+
|
26
|
+
-- Identify members wrapped in min
|
27
|
+
before = redis.call('zrevrangebyscore', index, "(" .. min, "-inf", "limit", 0, 1)[1]
|
28
|
+
if before then
|
29
|
+
after = redis.call('zrangebyscore', index, "(" .. min, "inf", "limit", 0, 1)[1]
|
30
|
+
if after then
|
31
|
+
minwrp = redis.call('sinter', prefix .. ":" .. before, prefix .. ":" .. after)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
-- Identify members wrapped in max
|
36
|
+
after = redis.call('zrangebyscore', index, "(" .. max, "inf", "limit", 0, 1)[1]
|
37
|
+
if after then
|
38
|
+
before = redis.call('zrevrangebyscore', index, "(" .. max, "-inf", "limit", 0, 1)[1]
|
39
|
+
if before then
|
40
|
+
maxwrp = redis.call('sinter', prefix .. ":" .. before, prefix .. ":" .. after)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
-- Remove existing wrapped members from min
|
45
|
+
if #minwrp > 0 then
|
46
|
+
redis.call('srem', prefix .. ":" .. min, unpack(minwrp))
|
47
|
+
end
|
48
|
+
|
49
|
+
-- Remove existing wrapped members from max
|
50
|
+
if #maxwrp > 0 then
|
51
|
+
redis.call('srem', prefix .. ":" .. max, unpack(maxwrp))
|
52
|
+
end
|
53
|
+
|
54
|
+
-- Remove the min index, if no more items are left in the min set
|
55
|
+
local minlen = redis.call('scard', prefix .. ":" .. min)
|
56
|
+
if minlen == 0 then redis.call('zrem', index, min) end
|
57
|
+
|
58
|
+
-- Remove the max index, if no more items are left in the max set
|
59
|
+
local maxlen = redis.call('scard', prefix .. ":" .. max)
|
60
|
+
if maxlen == 0 then redis.call('zrem', index, max) end
|
61
|
+
|
62
|
+
return redis.status_reply("OK")
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Redis Recipes: Range Lookup X
|
2
|
+
|
3
|
+
A data structure, very similar to the "normal" Range Lookup, except that it
|
4
|
+
stores ranges that exclude the end value.
|
5
|
+
|
6
|
+
### Example / Internals:
|
7
|
+
|
8
|
+
Consider the following use case: "Holiday System"
|
9
|
+
|
10
|
+
* [A]lice is on vacation from the 4th until the 11th
|
11
|
+
* [B]ob leaves an 7th and returns on the 21st
|
12
|
+
* [C]olin is also away from 4th, but returns only on the 18th
|
13
|
+
* [D]eborah's holidays are from 16th to the 23th
|
14
|
+
|
15
|
+
Our numeric ranges would therefore be:
|
16
|
+
|
17
|
+
A 4...12
|
18
|
+
B 7...22
|
19
|
+
C 4...19
|
20
|
+
D 16...24
|
21
|
+
|
22
|
+
To identify the people away for any specific date quickly, we create a
|
23
|
+
flattened index:
|
24
|
+
|
25
|
+
4 [A C]
|
26
|
+
7 [A B C]
|
27
|
+
12 [B C]
|
28
|
+
16 [B C D]
|
29
|
+
19 [B D]
|
30
|
+
22 [D]
|
31
|
+
24 []
|
32
|
+
|
33
|
+
For any given day, we can match the list (set) of members that are on vacation,
|
34
|
+
by:
|
35
|
+
|
36
|
+
1. Finding the index <= the given value. For example: a search for the 18th would return 16.
|
37
|
+
2. Reading the members on the index. For example: for the 16th, [B C D] would be returned.
|
38
|
+
|
39
|
+
### Usage:
|
40
|
+
|
41
|
+
Add ranges:
|
42
|
+
|
43
|
+
redis-cli --eval <path/to/add.lua> holidays , A 4 12
|
44
|
+
redis-cli --eval <path/to/add.lua> holidays , B 7 22
|
45
|
+
redis-cli --eval <path/to/add.lua> holidays , C 4 19
|
46
|
+
redis-cli --eval <path/to/add.lua> holidays , D 16 24
|
47
|
+
redis-cli --eval <path/to/add.lua> holidays , X 5 16
|
48
|
+
|
49
|
+
Remove a range:
|
50
|
+
|
51
|
+
redis-cli --eval <path/to/remove.lua> holidays , X 5 16
|
52
|
+
|
53
|
+
Lookup range:
|
54
|
+
|
55
|
+
redis-cli --eval <path/to/lookup.lua> holidays , 18 # => 1) "B"
|
56
|
+
# 2) "C"
|
57
|
+
# 3) "D"
|
58
|
+
redis-cli --eval <path/to/lookup.lua> holidays , 19 # => 1) "B"
|
59
|
+
# 2) "D"
|
@@ -0,0 +1,59 @@
|
|
1
|
+
if #KEYS ~= 1 or #ARGV ~= 3 then
|
2
|
+
return redis.error_reply("wrong number of arguments")
|
3
|
+
end
|
4
|
+
|
5
|
+
local prefix = KEYS[1]
|
6
|
+
local member = ARGV[1]
|
7
|
+
local min = tonumber(ARGV[2])
|
8
|
+
local max = tonumber(ARGV[3])
|
9
|
+
|
10
|
+
if min == nil or max == nil or min >= max then
|
11
|
+
return redis.error_reply("min/max are not numeric or out of range")
|
12
|
+
end
|
13
|
+
|
14
|
+
local index = prefix .. ":~"
|
15
|
+
local minscr = redis.call('zscore', index, min)
|
16
|
+
local maxscr = redis.call('zscore', index, max)
|
17
|
+
local minwrp = {}
|
18
|
+
local maxwrp = {}
|
19
|
+
local window = redis.call('zrangebyscore', index, "(" .. min, "(" .. max)
|
20
|
+
|
21
|
+
-- Find existing members to be included in the new min
|
22
|
+
if not minscr then
|
23
|
+
local before = redis.call('zrevrangebyscore', index, "(" .. min, "-inf", "limit", 0, 1)[1]
|
24
|
+
if before then
|
25
|
+
minwrp = redis.call('smembers', prefix .. ":" .. before)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
-- Find existing members to be included in the new max
|
30
|
+
if not maxscr then
|
31
|
+
local before = redis.call('zrevrangebyscore', index, "(" .. max, "-inf", "limit", 0, 1)[1]
|
32
|
+
if before then
|
33
|
+
maxwrp = redis.call('smembers', prefix .. ":" .. before)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
-- Store members in min set
|
38
|
+
redis.call('sadd', prefix .. ":" .. min, member)
|
39
|
+
|
40
|
+
-- Store new min & max indices
|
41
|
+
if not minscr then redis.call('zadd', index, min, min) end
|
42
|
+
if not maxscr then redis.call('zadd', index, max, max) end
|
43
|
+
|
44
|
+
-- Store member in all existing sets between min & max
|
45
|
+
for _,key in pairs(window) do
|
46
|
+
redis.call('sadd', prefix .. ":" .. key, member)
|
47
|
+
end
|
48
|
+
|
49
|
+
-- Merge existing members into min
|
50
|
+
if #minwrp > 0 then
|
51
|
+
redis.call('sadd', prefix .. ":" .. min, unpack(minwrp))
|
52
|
+
end
|
53
|
+
|
54
|
+
-- Merge existing members into max
|
55
|
+
if #maxwrp > 0 then
|
56
|
+
redis.call('sadd', prefix .. ":" .. max, unpack(maxwrp))
|
57
|
+
end
|
58
|
+
|
59
|
+
return redis.status_reply("OK")
|
@@ -0,0 +1,19 @@
|
|
1
|
+
if #KEYS ~= 1 or #ARGV ~= 1 then
|
2
|
+
return redis.error_reply("wrong number of arguments")
|
3
|
+
end
|
4
|
+
|
5
|
+
local prefix = KEYS[1]
|
6
|
+
local value = tonumber(ARGV[1])
|
7
|
+
|
8
|
+
if value == nil then
|
9
|
+
return redis.error_reply("value is not numeric or out of range")
|
10
|
+
end
|
11
|
+
|
12
|
+
local members = {}
|
13
|
+
local pos = redis.call('zrevrangebyscore', prefix .. ":~", value, "-inf", "limit", 0, 1)[1]
|
14
|
+
|
15
|
+
if pos then
|
16
|
+
members = redis.call('smembers', prefix .. ":" .. pos)
|
17
|
+
end
|
18
|
+
|
19
|
+
return members
|
@@ -0,0 +1,51 @@
|
|
1
|
+
if #KEYS ~= 1 or #ARGV ~= 3 then
|
2
|
+
return redis.error_reply("wrong number of arguments")
|
3
|
+
end
|
4
|
+
|
5
|
+
local prefix = KEYS[1]
|
6
|
+
local member = ARGV[1]
|
7
|
+
local min = tonumber(ARGV[2])
|
8
|
+
local max = tonumber(ARGV[3])
|
9
|
+
|
10
|
+
if min == nil or max == nil or min >= max then
|
11
|
+
return redis.error_reply("min/max are not numeric or out of range")
|
12
|
+
end
|
13
|
+
|
14
|
+
local index = prefix .. ":~"
|
15
|
+
local window = redis.call('zrangebyscore', index, min, "(" .. max)
|
16
|
+
|
17
|
+
local minlen = redis.call('scard', prefix .. ":" .. min)
|
18
|
+
local maxlen = redis.call('scard', prefix .. ":" .. max)
|
19
|
+
|
20
|
+
-- Calculate cardinality of the set before min
|
21
|
+
local befmin = redis.call('zrevrangebyscore', index, "(" .. min, "-inf", "limit", 0, 1)[1]
|
22
|
+
local bminlen = 0
|
23
|
+
if befmin then
|
24
|
+
bminlen = redis.call('scard', prefix .. ":" .. befmin)
|
25
|
+
end
|
26
|
+
|
27
|
+
-- Calculate cardinality of the set before max
|
28
|
+
local befmax = redis.call('zrevrangebyscore', index, "(" .. max, "-inf", "limit", 0, 1)[1]
|
29
|
+
local bmaxlen = 0
|
30
|
+
if befmax then
|
31
|
+
bmaxlen = redis.call('scard', prefix .. ":" .. befmax)
|
32
|
+
end
|
33
|
+
|
34
|
+
-- Remove min if the cardinality between min and the set before min differs by 1
|
35
|
+
if minlen - bminlen == 1 then
|
36
|
+
redis.call('del', prefix .. ":" .. min)
|
37
|
+
redis.call('zrem', index, min)
|
38
|
+
end
|
39
|
+
|
40
|
+
-- Remove max if the cardinality between max and the set before max differs by -1
|
41
|
+
if bmaxlen - maxlen == 1 then
|
42
|
+
redis.call('del', prefix .. ":" .. max)
|
43
|
+
redis.call('zrem', index, max)
|
44
|
+
end
|
45
|
+
|
46
|
+
-- Remove the member from all sets between min & max
|
47
|
+
for _, val in pairs(window) do
|
48
|
+
redis.call('srem', prefix .. ":" .. val, member)
|
49
|
+
end
|
50
|
+
|
51
|
+
return redis.status_reply("OK")
|