redis-bloomfilter 0.0.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YWU5ZWE2Mjg1OTVmMGVlZWQ0ZTZmNzkwNTE3MDJiNjI1OTI0MzMzYQ==
5
- data.tar.gz: !binary |-
6
- Zjg5NDkwNDdmOWZiOThlMWYxZTkyOWI1OGE2ODcxZmU1ZjZkMTZiYQ==
2
+ SHA256:
3
+ metadata.gz: 03a3466f74223e60731f3054278118e7ce0d3745826469b3f752e0b916e664e1
4
+ data.tar.gz: 840df14ff183236d0c1dd78e954b43635c39469e82c845787b7741526edb4890
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- OTQ2YmM1Zjk2NTg4NjE4N2MwMjVlZDEyMmE3ZGE2MTlmMmE2NWM3YzdmYjJh
10
- YjdjODBkM2QzN2NiNTgzNmUyMmY4OWNiNTBiNjhiYTU5OGRkNTI1NDNlOWNh
11
- ZDJhYWNmMmZjOGQ5M2QxNjYyYzdmMmI4YmEzZjM4NzU0YjIwMzA=
12
- data.tar.gz: !binary |-
13
- YzkzMjg5ZTRhZDNlNGI1YTBlMzMwMjY4NDQ0N2RhMTM4MmE2ZmZmMTY1ZjQ3
14
- NTg0MzM3NmM2MGE0N2JkYWMzMDI0NDdjZTFlMzMyMDMzNmU2ZmYzYTZkMjhl
15
- ZjMxMTllZTVmZGY4YmMxZGUzYzExZGYyNWNmZmNlZTRiZTlmZGQ=
6
+ metadata.gz: 82d34182fd1599a6c88f87b6d04cfc0d1e01fe001920b8e7e6371e7bfb824549c21697853516b8f34b90c238616ed88a4b3d1e9c5e9e109430d87b20e0400f5e
7
+ data.tar.gz: 6595a1a50381052cd7bfdc4d4c73cf2710deab362168fd08aff1d1c14abb58a0a7ccfa219a04067b7b5f4175e351180d33f0d69f3477d591fad66a2cc168facf
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1.1
3
+ - 2.6
4
+ - 2.5
5
+ - 2.4
6
+ - 2.3
6
7
 
7
8
  services:
8
9
  - redis
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
- source "http://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'http://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in redis-bloomfilter.gemspec
4
6
  gemspec
data/Rakefile CHANGED
@@ -1,9 +1,11 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec) do |spec|
5
7
  spec.pattern = 'spec/*_spec.rb'
6
8
  end
7
9
 
8
- task :default => :spec
9
- task :test => :spec
10
+ task default: :spec
11
+ task test: :spec
@@ -1,23 +1,25 @@
1
- $:.push File.expand_path("../lib", __FILE__)
2
- require "redis-bloomfilter"
3
- require "benchmark"
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
4
+ require 'redis-bloomfilter'
5
+ require 'benchmark'
4
6
  items = 100_000
5
7
  error_rate = 0.01
6
- %w(lua ruby).each do |driver|
8
+ %w[lua ruby].each do |driver|
7
9
  bf = Redis::Bloomfilter.new(
8
10
  {
9
- :size => items,
10
- :error_rate => error_rate,
11
- :key_name => "bloom-filter-bench-flat-#{driver}",
12
- :driver => driver
11
+ size: items,
12
+ error_rate: error_rate,
13
+ key_name: "bloom-filter-bench-flat-#{driver}",
14
+ driver: driver
13
15
  }
14
16
  )
15
17
  bf.clear
16
- puts "---------------------------------------------"
18
+ puts '---------------------------------------------'
17
19
  puts "Benchmarking #{driver} driver with #{items} items"
18
20
  Benchmark.bm(7) do |x|
19
- x.report("insert: ") {items.times { |i| bf.insert(rand(items)) }}
20
- x.report("include?:") {items.times { |i| bf.include?(rand(items)) }}
21
+ x.report('insert: ') { items.times { |_i| bf.insert(rand(items)) } }
22
+ x.report('include?:') { items.times { |_i| bf.include?(rand(items)) } }
21
23
  end
22
24
  puts
23
- end
25
+ end
@@ -1,11 +1,13 @@
1
- $:.push File.expand_path("../lib", __FILE__)
2
- require "redis-bloomfilter"
3
- require "benchmark"
4
- require "set"
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
4
+ require 'redis-bloomfilter'
5
+ require 'benchmark'
6
+ require 'set'
5
7
 
6
8
  def rand_word(length = 8)
7
9
  @charset ||= ('a'..'z').to_a
8
- @charset.shuffle.first(length).join
10
+ @charset.sample(length).join
9
11
  end
10
12
 
11
13
  items = ARGV[0].nil? ? 10_000 : ARGV[0].to_i
@@ -16,10 +18,10 @@ error_rate = 0.01
16
18
 
17
19
  bf = Redis::Bloomfilter.new(
18
20
  {
19
- :size => items,
20
- :error_rate => error_rate,
21
- :key_name => "bloom-filter-bench-#{driver}",
22
- :driver => driver
21
+ size: items,
22
+ error_rate: error_rate,
23
+ key_name: "bloom-filter-bench-#{driver}",
24
+ driver: driver
23
25
  }
24
26
  )
25
27
  bf.clear
@@ -27,7 +29,7 @@ error_rate = 0.01
27
29
  first_error_at = 0
28
30
  visited = Set.new
29
31
 
30
- Benchmark.bm(7) do |x|
32
+ Benchmark.bm(7) do |x|
31
33
  x.report do
32
34
  items.times do |i|
33
35
  item = rand_word
@@ -38,7 +40,7 @@ error_rate = 0.01
38
40
  end
39
41
  visited << item
40
42
  bf.insert item
41
- #print ".(#{"%.1f" % ((i.to_f/items.to_f) * 100)}%) " if i % 1000 == 0
43
+ # print ".(#{"%.1f" % ((i.to_f/items.to_f) * 100)}%) " if i % 1000 == 0
42
44
  end
43
45
  end
44
46
  end
@@ -50,4 +52,4 @@ error_rate = 0.01
50
52
  puts "Error found: #{error}"
51
53
  puts "Error rate: #{(error.to_f / items)}"
52
54
  puts
53
- end
55
+ end
@@ -1,4 +1,6 @@
1
- require "redis-bloomfilter"
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis-bloomfilter'
2
4
 
3
5
  # It creates a Bloom Filter using the default ruby driver
4
6
  # Number of elements expected : 10000
@@ -6,16 +8,16 @@ require "redis-bloomfilter"
6
8
  # Key name on Redis: my-bloom-filter
7
9
  # Redis: 127.0.0.1:6379 or an already existing connection
8
10
  @bf = Redis::Bloomfilter.new(
9
- :size => 10_000,
10
- :error_rate => 0.01,
11
- :key_name => 'my-bloom-filter'
11
+ size: 10_000,
12
+ error_rate: 0.01,
13
+ key_name: 'my-bloom-filter'
12
14
  )
13
15
 
14
16
  # Insert an element
15
- @bf.insert "foo"
17
+ @bf.insert 'foo'
16
18
  # Check if an element exists
17
- puts @bf.include?("foo") # => true
18
- puts @bf.include?("bar") # => false
19
+ puts @bf.include?('foo') # => true
20
+ puts @bf.include?('bar') # => false
19
21
 
20
22
  # Empty the BF and delete the key stored on redis
21
23
  @bf.clear
@@ -23,16 +25,16 @@ puts @bf.include?("bar") # => false
23
25
  # Using Lua's driver: only available on Redis >= 2.6.0
24
26
  # This driver should be prefered because is faster
25
27
  @bf = Redis::Bloomfilter.new(
26
- :size => 10_000,
27
- :error_rate => 0.01,
28
- :key_name => 'my-bloom-filter-lua',
29
- :driver => 'lua'
28
+ size: 10_000,
29
+ error_rate: 0.01,
30
+ key_name: 'my-bloom-filter-lua',
31
+ driver: 'lua'
30
32
  )
31
33
 
32
34
  # Specify a redis connection:
33
35
  # @bf = Redis::Bloomfilter.new(
34
- # :size => 10_000,
35
- # :error_rate => 0.01,
36
+ # :size => 10_000,
37
+ # :error_rate => 0.01,
36
38
  # :key_name => 'my-bloom-filter-lua',
37
39
  # :driver => 'lua',
38
40
  # :redis => Redis.new(:host => "10.0.1.1", :port => 6380)
@@ -1,7 +1,8 @@
1
- require "digest/sha1"
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/sha1'
2
4
  class Redis
3
5
  module BloomfilterDriver
4
-
5
6
  # It loads lua script into redis.
6
7
  # BF implementation is done by lua scripting
7
8
  # The alghoritm is executed directly on redis
@@ -20,97 +21,94 @@ class Redis
20
21
  set data, 1
21
22
  end
22
23
 
23
- def remove(data)
24
- set data, 0
25
- end
26
-
27
24
  def include?(key)
28
- r = @redis.evalsha(@check_fnc_sha, :keys => [@options[:key_name]], :argv => [@options[:size], @options[:error_rate], key])
29
- r == 1 ? true : false
25
+ r = @redis.evalsha(@check_fnc_sha, keys: [@options[:key_name]], argv: [@options[:size], @options[:error_rate], key])
26
+ r == 1
30
27
  end
31
28
 
32
29
  def clear
33
- @redis.keys("#{@options[:key_name]}:*").each {|k|@redis.del k}
30
+ @redis.keys("#{@options[:key_name]}:*").each { |k| @redis.del k }
34
31
  end
35
32
 
36
33
  protected
37
- # It loads the script inside Redis
38
- # Taken from https://github.com/ErikDubbelboer/redis-lua-scaling-bloom-filter
39
- # This is a scalable implementation of BF. It means the initial size can vary
40
- def lua_load
41
- add_fnc = %q(
42
- local entries = ARGV[1]
43
- local precision = ARGV[2]
44
- local set_value = ARGV[4]
45
- local index = math.ceil(redis.call('INCR', KEYS[1] .. ':count') / entries)
46
- local key = KEYS[1] .. ':' .. index
47
- local bits = math.floor(-(entries * math.log(precision * math.pow(0.5, index))) / 0.480453013)
34
+
35
+ # It loads the script inside Redis
36
+ # Taken from https://github.com/ErikDubbelboer/redis-lua-scaling-bloom-filter
37
+ # This is a scalable implementation of BF. It means the initial size can vary
38
+ def lua_load
39
+ add_fnc = "
40
+ local entries = ARGV[1]
41
+ local precision = ARGV[2]
42
+ local set_value = ARGV[4]
43
+ local index = math.ceil(redis.call('INCR', KEYS[1] .. ':count') / entries)
44
+ local key = KEYS[1] .. ':' .. index
45
+ local bits = math.floor(-(entries * math.log(precision * math.pow(0.5, index))) / 0.480453013)
46
+ local k = math.floor(0.693147180 * bits / entries)
47
+ local hash = redis.sha1hex(ARGV[3])
48
+ local h = { }
49
+ h[0] = tonumber(string.sub(hash, 0 , 8 ), 16)
50
+ h[1] = tonumber(string.sub(hash, 8 , 16), 16)
51
+ h[2] = tonumber(string.sub(hash, 16, 24), 16)
52
+ h[3] = tonumber(string.sub(hash, 24, 32), 16)
53
+ for i=1, k do
54
+ redis.call('SETBIT', key, (h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)]) % bits, set_value)
55
+ end
56
+ "
57
+
58
+ check_fnc = "
59
+
60
+ local entries = ARGV[1]
61
+ local precision = ARGV[2]
62
+ local index = redis.call('GET', KEYS[1] .. ':count')
63
+ if not index then
64
+ return 0
65
+ end
66
+ index = math.ceil(redis.call('GET', KEYS[1] .. ':count') / entries)
67
+ local hash = redis.sha1hex(ARGV[3])
68
+ local h = { }
69
+ h[0] = tonumber(string.sub(hash, 0 , 8 ), 16)
70
+ h[1] = tonumber(string.sub(hash, 8 , 16), 16)
71
+ h[2] = tonumber(string.sub(hash, 16, 24), 16)
72
+ h[3] = tonumber(string.sub(hash, 24, 32), 16)
73
+ local maxk = math.floor(0.693147180 * math.floor((entries * math.log(precision * math.pow(0.5, index))) / -0.480453013) / entries)
74
+ local b = { }
75
+ for i=1, maxk do
76
+ table.insert(b, h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)])
77
+ end
78
+ for n=1, index do
79
+ local key = KEYS[1] .. ':' .. n
80
+ local found = true
81
+ local bits = math.floor((entries * math.log(precision * math.pow(0.5, n))) / -0.480453013)
48
82
  local k = math.floor(0.693147180 * bits / entries)
49
- local hash = redis.sha1hex(ARGV[3])
50
- local h = { }
51
- h[0] = tonumber(string.sub(hash, 0 , 8 ), 16)
52
- h[1] = tonumber(string.sub(hash, 8 , 16), 16)
53
- h[2] = tonumber(string.sub(hash, 16, 24), 16)
54
- h[3] = tonumber(string.sub(hash, 24, 32), 16)
83
+
55
84
  for i=1, k do
56
- redis.call('SETBIT', key, (h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)]) % bits, set_value)
85
+ if redis.call('GETBIT', key, b[i] % bits) == 0 then
86
+ found = false
87
+ break
88
+ end
57
89
  end
58
- )
59
-
60
- check_fnc = %q(
61
90
 
62
- local entries = ARGV[1]
63
- local precision = ARGV[2]
64
- local index = redis.call('GET', KEYS[1] .. ':count')
65
- if not index then
66
- return 0
67
- end
68
- index = math.ceil(redis.call('GET', KEYS[1] .. ':count') / entries)
69
- local hash = redis.sha1hex(ARGV[3])
70
- local h = { }
71
- h[0] = tonumber(string.sub(hash, 0 , 8 ), 16)
72
- h[1] = tonumber(string.sub(hash, 8 , 16), 16)
73
- h[2] = tonumber(string.sub(hash, 16, 24), 16)
74
- h[3] = tonumber(string.sub(hash, 24, 32), 16)
75
- local maxk = math.floor(0.693147180 * math.floor((entries * math.log(precision * math.pow(0.5, index))) / -0.480453013) / entries)
76
- local b = { }
77
- for i=1, maxk do
78
- table.insert(b, h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)])
91
+ if found then
92
+ return 1
79
93
  end
80
- for n=1, index do
81
- local key = KEYS[1] .. ':' .. n
82
- local found = true
83
- local bits = math.floor((entries * math.log(precision * math.pow(0.5, n))) / -0.480453013)
84
- local k = math.floor(0.693147180 * bits / entries)
85
-
86
- for i=1, k do
87
- if redis.call('GETBIT', key, b[i] % bits) == 0 then
88
- found = false
89
- break
90
- end
91
- end
94
+ end
92
95
 
93
- if found then
94
- return 1
95
- end
96
- end
96
+ return 0
97
+ "
97
98
 
98
- return 0
99
- )
100
-
101
- @add_fnc_sha = Digest::SHA1.hexdigest(add_fnc)
102
- @check_fnc_sha = Digest::SHA1.hexdigest(check_fnc)
99
+ @add_fnc_sha = Digest::SHA1.hexdigest(add_fnc)
100
+ @check_fnc_sha = Digest::SHA1.hexdigest(check_fnc)
103
101
 
104
- loaded = @redis.script(:exists, [@add_fnc_sha, @check_fnc_sha]).uniq
105
- if loaded.count != 1 || loaded.first != true
106
- @add_fnc_sha = @redis.script(:load, add_fnc)
107
- @check_fnc_sha = @redis.script(:load, check_fnc)
108
- end
102
+ loaded = @redis.script(:exists, [@add_fnc_sha, @check_fnc_sha]).uniq
103
+ if loaded.count != 1 || loaded.first != true
104
+ @add_fnc_sha = @redis.script(:load, add_fnc)
105
+ @check_fnc_sha = @redis.script(:load, check_fnc)
109
106
  end
107
+ end
110
108
 
111
- def set(data, val)
112
- @redis.evalsha(@add_fnc_sha, :keys => [@options[:key_name]], :argv => [@options[:size], @options[:error_rate], data, val])
113
- end
109
+ def set(data, val)
110
+ @redis.evalsha(@add_fnc_sha, keys: [@options[:key_name]], argv: [@options[:size], @options[:error_rate], data, val])
111
+ end
114
112
  end
115
113
  end
116
- end
114
+ end
@@ -1,8 +1,9 @@
1
- require "digest/sha1"
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/sha1'
2
4
  class Redis
3
5
  module BloomfilterDriver
4
6
  class Ruby
5
-
6
7
  # Faster Ruby version.
7
8
  # This driver should be used if Redis version < 2.6
8
9
  attr_accessor :redis
@@ -11,58 +12,53 @@ class Redis
11
12
  end
12
13
 
13
14
  # Insert a new element
14
- def insert(data)
15
+ def insert(data)
15
16
  set data, 1
16
17
  end
17
18
 
18
19
  # It checks if a key is part of the set
19
20
  def include?(key)
20
-
21
21
  indexes = []
22
22
  indexes_for(key).each { |idx| indexes << idx }
23
- return false if @redis.getbit(@options[:key_name], indexes.shift) == 0
23
+ return false if @redis.getbit(@options[:key_name], indexes.shift).zero?
24
24
 
25
25
  result = @redis.pipelined do
26
- indexes.each {|idx| @redis.getbit(@options[:key_name], idx)}
26
+ indexes.each { |idx| @redis.getbit(@options[:key_name], idx) }
27
27
  end
28
28
 
29
29
  !result.include?(0)
30
30
  end
31
31
 
32
- # It removes an element from the filter
33
- def remove(data)
34
- set data, 0
35
- end
36
-
37
32
  # It deletes a bloomfilter
38
33
  def clear
39
34
  @redis.del @options[:key_name]
40
35
  end
41
36
 
42
37
  protected
43
- # Hashing strategy:
44
- # http://www.eecs.harvard.edu/~kirsch/pubs/bbbf/esa06.pdf
45
- def indexes_for data
46
- sha = Digest::SHA1.hexdigest(data.to_s)
47
- h = []
48
- h[0] = sha[0...8].to_i(16)
49
- h[1] = sha[8...16].to_i(16)
50
- h[2] = sha[16...24].to_i(16)
51
- h[3] = sha[24...32].to_i(16)
52
- idxs = []
53
38
 
54
- (@options[:hashes]).times {|i|
55
- v = (h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)]) % @options[:bits]
56
- idxs << v
57
- }
58
- idxs
39
+ # Hashing strategy:
40
+ # https://www.eecs.harvard.edu/~michaelm/postscripts/tr-02-05.pdf
41
+ def indexes_for(data)
42
+ sha = Digest::SHA1.hexdigest(data.to_s)
43
+ h = []
44
+ h[0] = sha[0...8].to_i(16)
45
+ h[1] = sha[8...16].to_i(16)
46
+ h[2] = sha[16...24].to_i(16)
47
+ h[3] = sha[24...32].to_i(16)
48
+ idxs = []
49
+
50
+ (@options[:hashes]).times do |i|
51
+ v = (h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)]) % @options[:bits]
52
+ idxs << v
59
53
  end
54
+ idxs
55
+ end
60
56
 
61
- def set(key, val)
62
- @redis.pipelined do
63
- indexes_for(key).each {|i| @redis.setbit @options[:key_name], i, val}
64
- end
57
+ def set(key, val)
58
+ @redis.pipelined do
59
+ indexes_for(key).each { |i| @redis.setbit @options[:key_name], i, val }
65
60
  end
61
+ end
66
62
  end
67
63
  end
68
- end
64
+ end
@@ -1,6 +1,8 @@
1
- require "digest/md5"
2
- require "digest/sha1"
3
- require "zlib"
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/md5'
4
+ require 'digest/sha1'
5
+ require 'zlib'
4
6
  class Redis
5
7
  module BloomfilterDriver
6
8
  # It uses different hash strategy
@@ -13,24 +15,19 @@ class Redis
13
15
  end
14
16
 
15
17
  # Insert a new element
16
- def insert(data)
18
+ def insert(data)
17
19
  set data, 1
18
20
  end
19
21
 
20
- # Insert a new element
21
- def remove(data)
22
- set data, 0
23
- end
24
-
25
22
  # It checks if a key is part of the set
26
23
  def include?(key)
27
24
  indexes = []
28
25
  indexes_for(key) { |idx| indexes << idx }
29
26
 
30
- return false if @redis.getbit(@options[:key_name], indexes.shift) == 0
27
+ return false if @redis.getbit(@options[:key_name], indexes.shift).zero?
31
28
 
32
29
  result = @redis.pipelined do
33
- indexes.each {|idx| @redis.getbit(@options[:key_name], idx)}
30
+ indexes.each { |idx| @redis.getbit(@options[:key_name], idx) }
34
31
  end
35
32
 
36
33
  !result.include?(0)
@@ -42,31 +39,32 @@ class Redis
42
39
  end
43
40
 
44
41
  protected
45
- def indexes_for(key, engine = nil)
46
- engine ||= @options[:hash_engine]
47
- @options[:hashes].times do |i|
48
- yield self.send("engine_#{engine}", key.to_s, i)
49
- end
50
- end
51
42
 
52
- # A set of different hash functions
53
- def engine_crc32(data, i)
54
- Zlib.crc32("#{i}-#{data}").to_i(16) % @options[:bits]
43
+ def indexes_for(key, engine = nil)
44
+ engine ||= @options[:hash_engine]
45
+ @options[:hashes].times do |i|
46
+ yield send("engine_#{engine}", key.to_s, i)
55
47
  end
48
+ end
56
49
 
57
- def engine_md5(data, i)
58
- Digest::MD5.hexdigest("#{i}-#{data}").to_i(16) % @options[:bits]
59
- end
50
+ # A set of different hash functions
51
+ def engine_crc32(data, i)
52
+ Zlib.crc32("#{i}-#{data}").to_i(16) % @options[:bits]
53
+ end
60
54
 
61
- def engine_sha1(data, i)
62
- Digest::SHA1.hexdigest("#{i}-#{data}").to_i(16) % @options[:bits]
63
- end
55
+ def engine_md5(data, i)
56
+ Digest::MD5.hexdigest("#{i}-#{data}").to_i(16) % @options[:bits]
57
+ end
64
58
 
65
- def set(data, val)
66
- @redis.pipelined do
67
- indexes_for(data) { |i| @redis.setbit @options[:key_name], i, val }
68
- end
59
+ def engine_sha1(data, i)
60
+ Digest::SHA1.hexdigest("#{i}-#{data}").to_i(16) % @options[:bits]
61
+ end
62
+
63
+ def set(data, val)
64
+ @redis.pipelined do
65
+ indexes_for(data) { |i| @redis.setbit @options[:key_name], i, val }
69
66
  end
67
+ end
70
68
  end
71
69
  end
72
- end
70
+ end
@@ -1,7 +1,9 @@
1
- require "redis"
2
- require "redis/connection/hiredis"
3
- require "redis/bloomfilter"
4
- require "redis/bloomfilter/version"
5
- require "bloomfilter_driver/ruby"
6
- require "bloomfilter_driver/lua"
7
- require "bloomfilter_driver/ruby_test"
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis'
4
+ require 'redis/connection/hiredis'
5
+ require 'redis/bloomfilter'
6
+ require 'redis/bloomfilter/version'
7
+ require 'bloomfilter_driver/ruby'
8
+ require 'bloomfilter_driver/lua'
9
+ require 'bloomfilter_driver/ruby_test'
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Redis
2
4
  class Bloomfilter
3
-
4
5
  attr_reader :options
5
6
  attr_reader :driver
6
7
 
@@ -8,17 +9,17 @@ class Redis
8
9
  # It creates a bloomfilter with a capacity of 1000 items and an error rate of 1%
9
10
  def initialize(options = {})
10
11
  @options = {
11
- :size => 1000,
12
- :error_rate => 0.01,
13
- :key_name => 'redis-bloomfilter',
14
- :hash_engine => 'md5',
15
- :redis => Redis.current,
16
- :driver => nil
12
+ size: 1000,
13
+ error_rate: 0.01,
14
+ key_name: 'redis-bloomfilter',
15
+ hash_engine: 'md5',
16
+ redis: Redis.current,
17
+ driver: nil
17
18
  }.merge options
18
19
 
19
- raise ArgumentError, "options[:size] && options[:error_rate] cannot be nil" if options[:error_rate].nil? || options[:size].nil?
20
+ raise ArgumentError, 'options[:size] && options[:error_rate] cannot be nil' if options[:error_rate].nil? || options[:size].nil?
20
21
 
21
- #Size provided, compute hashes and bits
22
+ # Size provided, compute hashes and bits
22
23
 
23
24
  @options[:size] = options[:size]
24
25
  @options[:error_rate] = options[:error_rate] ? options[:error_rate] : @options[:error_rate]
@@ -31,27 +32,27 @@ class Redis
31
32
  if @options[:driver].nil?
32
33
  ver = @redis.info['redis_version']
33
34
 
34
- if Gem::Version.new(ver) >= Gem::Version.new('2.6.0')
35
- @options[:driver] = 'lua'
36
- else
37
- @options[:driver] = 'ruby'
38
- end
35
+ @options[:driver] = if Gem::Version.new(ver) >= Gem::Version.new('2.6.0')
36
+ 'lua'
37
+ else
38
+ 'ruby'
39
+ end
39
40
  end
40
41
 
41
42
  driver_class = Redis::BloomfilterDriver.const_get(driver_name)
42
43
  @driver = driver_class.new @options
43
- @driver.redis = @redis
44
+ @driver.redis = @redis
44
45
  end
45
46
 
46
47
  # Methods used to calculate M and K
47
48
  # Taken from http://en.wikipedia.org/wiki/Bloom_filter#Probability_of_false_positives
48
- def self.optimal_m num_of_elements, false_positive_rate = 0.01
49
- (-1 * (num_of_elements) * Math.log(false_positive_rate) / (Math.log(2) ** 2)).round
49
+ def self.optimal_m(num_of_elements, false_positive_rate = 0.01)
50
+ (-1 * num_of_elements * Math.log(false_positive_rate) / (Math.log(2)**2)).round
50
51
  end
51
52
 
52
- def self.optimal_k num_of_elements, bf_size
53
+ def self.optimal_k(num_of_elements, bf_size)
53
54
  h = (Math.log(2) * (bf_size / num_of_elements)).round
54
- h+=1 if h == 0
55
+ h += 1 if h.zero?
55
56
  h
56
57
  end
57
58
 
@@ -65,19 +66,15 @@ class Redis
65
66
  @driver.include?(key)
66
67
  end
67
68
 
68
- def remove(key)
69
- @driver.remove key if @driver.respond_to? :remove
70
- end
71
-
72
69
  # It deletes a bloomfilter
73
70
  def clear
74
71
  @driver.clear
75
72
  end
76
73
 
77
74
  protected
78
- def driver_name
79
- @options[:driver].downcase.split('-').collect{|t| t.gsub(/(\w+)/){|s|s.capitalize}}.join
80
- end
81
75
 
76
+ def driver_name
77
+ @options[:driver].downcase.split('-').collect { |t| t.gsub(/(\w+)/, &:capitalize) }.join
78
+ end
82
79
  end
83
- end
80
+ end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Redis
2
4
  class Bloomfilter
3
- VERSION = "0.0.3"
5
+ VERSION = '1.0.0'
4
6
  def self.version
5
7
  "redis-bloomfilter version #{VERSION}"
6
8
  end
7
9
  end
8
- end
10
+ end
@@ -1,31 +1,32 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "redis/bloomfilter/version"
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
5
+ require 'redis/bloomfilter/version'
4
6
 
5
7
  Gem::Specification.new do |s|
6
- s.name = "redis-bloomfilter"
8
+ s.name = 'redis-bloomfilter'
7
9
  s.version = Redis::Bloomfilter::VERSION
8
- s.authors = ["Francesco Laurita"]
9
- s.email = ["francesco.laurita@gmail.com"]
10
- s.homepage = "https://github.com/taganaka/redis-bloomfilter"
11
- s.summary = %q{Distributed Bloom Filter implementation on Redis}
12
- s.description = %q{
10
+ s.authors = ['Francesco Laurita']
11
+ s.email = ['francesco.laurita@gmail.com']
12
+ s.homepage = 'https://github.com/taganaka/redis-bloomfilter'
13
+ s.summary = 'Distributed Bloom Filter implementation on Redis'
14
+ s.description = '
13
15
  Adds Redis::Bloomfilter class which can be used as ditributed bloom filter implementation on Redis.
14
16
  A Bloom filter is a space-efficient probabilistic data structure that is used to test whether an element is a member of a set.
15
- }
17
+ '
16
18
  s.licenses = ['MIT']
17
- s.rubyforge_project = "redis-bloomfilter"
19
+ s.rubyforge_project = 'redis-bloomfilter'
18
20
 
19
21
  s.files = `git ls-files`.split("\n")
20
22
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
- s.require_paths = ["lib"]
23
-
24
- s.add_runtime_dependency 'hiredis', '~> 0.5', '>= 0.5.2'
25
- s.add_runtime_dependency 'redis', '~> 3.0', '>= 3.0.4'
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
24
+ s.require_paths = ['lib']
26
25
 
27
- s.add_development_dependency "rspec"
28
- s.add_development_dependency "flexmock"
29
- s.add_development_dependency "rake"
26
+ s.add_runtime_dependency 'hiredis', '~> 0.6.1'
27
+ s.add_runtime_dependency 'redis', '~> 4.0', '>= 4.0.1'
30
28
 
29
+ s.add_development_dependency 'flexmock'
30
+ s.add_development_dependency 'rake'
31
+ s.add_development_dependency 'rspec'
31
32
  end
@@ -1,11 +1,12 @@
1
- require "spec_helper"
2
- require "set"
1
+ # frozen_string_literal: true
3
2
 
3
+ require 'spec_helper'
4
+ require 'set'
4
5
 
5
- def test_error_rate(bf,elems)
6
+ def test_error_rate(bf, elems)
6
7
  visited = Set.new
7
8
  error = 0
8
- elems.times do |i|
9
+ elems.times do |_i|
9
10
  a = rand(elems)
10
11
  error += 1 if bf.include?(a) != visited.include?(a)
11
12
  visited << a
@@ -14,86 +15,78 @@ def test_error_rate(bf,elems)
14
15
  error.to_f / elems
15
16
  end
16
17
 
17
- def factory options, driver
18
+ def factory(options, driver)
18
19
  options[:driver] = driver
19
20
  Redis::Bloomfilter.new options
20
21
  end
21
22
 
22
23
  describe Redis::Bloomfilter do
23
-
24
24
  it 'should return the right version' do
25
- Redis::Bloomfilter.version.should eq "redis-bloomfilter version #{Redis::Bloomfilter::VERSION}"
25
+ expect(Redis::Bloomfilter.version).to eq "redis-bloomfilter version #{Redis::Bloomfilter::VERSION}"
26
26
  end
27
27
 
28
28
  it 'should check for the initialize options' do
29
29
  expect { Redis::Bloomfilter.new }.to raise_error(ArgumentError)
30
- expect { Redis::Bloomfilter.new :size => 123 }.to raise_error(ArgumentError)
31
- expect { Redis::Bloomfilter.new :error_rate => 0.01 }.to raise_error(ArgumentError)
32
- expect { Redis::Bloomfilter.new :size => 123,:error_rate => 0.01, :driver => 'bibu' }.to raise_error(NameError)
30
+ expect { Redis::Bloomfilter.new size: 123 }.to raise_error(ArgumentError)
31
+ expect { Redis::Bloomfilter.new error_rate: 0.01 }.to raise_error(ArgumentError)
32
+ expect { Redis::Bloomfilter.new size: 123, error_rate: 0.01, driver: 'bibu' }.to raise_error(NameError)
33
33
  end
34
34
 
35
35
  it 'should choose the right driver based on the Redis version' do
36
-
37
- redis_mock = flexmock("redis")
38
- redis_mock.should_receive(:info).and_return({'redis_version' => '2.6.0'})
36
+ redis_mock = flexmock('redis')
37
+ redis_mock.should_receive(:info).and_return({ 'redis_version' => '2.6.0' })
39
38
  redis_mock.should_receive(:script).and_return([true, true])
40
- redis_mock_2_5 = flexmock("redis_2_5")
41
- redis_mock_2_5.should_receive(:info).and_return({'redis_version' => '2.5.0'})
39
+ redis_mock_2_5 = flexmock('redis_2_5')
40
+ redis_mock_2_5.should_receive(:info).and_return({ 'redis_version' => '2.5.0' })
42
41
 
43
- bf = factory({:size => 1000, :error_rate => 0.01, :key_name => 'ossom', :redis => redis_mock}, nil)
44
- bf.driver.should be_kind_of(Redis::BloomfilterDriver::Lua)
42
+ bf = factory({ size: 1000, error_rate: 0.01, key_name: 'ossom', redis: redis_mock }, nil)
43
+ expect(bf.driver).to be_kind_of(Redis::BloomfilterDriver::Lua)
45
44
 
46
- bf = factory({:size => 1000, :error_rate => 0.01, :key_name => 'ossom', :redis => redis_mock_2_5}, nil)
47
- bf.driver.should be_kind_of(Redis::BloomfilterDriver::Ruby)
45
+ bf = factory({ size: 1000, error_rate: 0.01, key_name: 'ossom', redis: redis_mock_2_5 }, nil)
46
+ expect(bf.driver).to be_kind_of(Redis::BloomfilterDriver::Ruby)
48
47
  end
49
48
 
50
49
  it 'should create a Redis::Bloomfilter object' do
51
- bf = factory({:size => 1000, :error_rate => 0.01, :key_name => 'ossom'}, 'ruby')
52
- bf.should be
53
- bf.options[:size].should eq 1000
54
- bf.options[:bits].should eq 9585
55
- bf.options[:hashes].should eq 6
56
- bf.options[:key_name].should eq 'ossom'
50
+ bf = factory({ size: 1000, error_rate: 0.01, key_name: 'ossom' }, 'ruby')
51
+ expect(bf).to be
52
+ expect(bf.options[:size]).to eq 1000
53
+ expect(bf.options[:bits]).to eq 9585
54
+ expect(bf.options[:hashes]).to eq 6
55
+ expect(bf.options[:key_name]).to eq 'ossom'
57
56
  bf.clear
58
57
  end
59
58
 
60
- %w(ruby lua ruby-test).each do |driver|
59
+ %w[ruby lua ruby-test].each do |driver|
61
60
  it 'should work' do
62
- bf = factory({:size => 1000, :error_rate => 0.01, :key_name => '__test_bf'},driver)
61
+ bf = factory({ size: 1000, error_rate: 0.01, key_name: '__test_bf' }, driver)
63
62
  bf.clear
64
- bf.include?("asdlol").should be false
65
- bf.insert "asdlol"
66
- bf.include?("asdlol").should be true
63
+ expect(bf.include?('asdlol')).to be false
64
+ bf.insert 'asdlol'
65
+ expect(bf.include?('asdlol')).to be true
67
66
  bf.clear
68
- bf.include?("asdlol").should be false
67
+ expect(bf.include?('asdlol')).to be false
69
68
  end
70
69
 
71
70
  it 'should honor the error rate' do
72
- bf = factory({:size => 100, :error_rate => 0.01, :key_name => '__test_bf'},driver)
71
+ bf = factory({ size: 100, error_rate: 0.02, key_name: '__test_bf' }, driver)
73
72
  bf.clear
74
73
  e = test_error_rate bf, 180
75
- e.should be <= bf.options[:error_rate]
74
+ expect(e.round(2)).to be <= bf.options[:error_rate].round(2)
76
75
  bf.clear
77
76
  end
78
77
 
79
- it 'should remove an elemnt from the filter' do
80
-
81
- bf = factory({:size => 100, :error_rate => 0.01, :key_name => '__test_bf'},driver)
82
- bf.insert "asdlolol"
83
- bf.include?("asdlolol").should be true
84
- bf.remove "asdlolol"
85
- bf.include?("asdlolol").should be false
86
-
78
+ it 'should add an element to the filter' do
79
+ bf = factory({ size: 100, error_rate: 0.01, key_name: '__test_bf' }, driver)
80
+ bf.insert 'asdlolol'
81
+ expect(bf.include?('asdlolol')).to be true
87
82
  end
88
83
  end
89
84
 
90
85
  it 'should be a scalable bloom filter' do
91
- bf = factory({:size => 10, :error_rate => 0.01, :key_name => '__test_bf'},'lua')
86
+ bf = factory({ size: 100, error_rate: 0.02, key_name: '__test_bf' }, 'lua')
92
87
  bf.clear
93
- e = test_error_rate(bf, 100)
94
- e.should be <= bf.options[:error_rate]
88
+ e = test_error_rate(bf, 150)
89
+ expect(e).to be <= bf.options[:error_rate]
95
90
  bf.clear
96
-
97
91
  end
98
-
99
- end
92
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by the `rspec --init` command. Conventionally, all
2
4
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
5
  # Require this file using `require "spec_helper"` to ensure that it is only
@@ -5,7 +7,6 @@
5
7
  #
6
8
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
9
  RSpec.configure do |config|
8
- config.treat_symbols_as_metadata_keys_with_true_values = true
9
10
  config.run_all_when_everything_filtered = true
10
11
  config.filter_run :focus
11
12
  config.mock_with :flexmock
@@ -17,6 +18,5 @@ RSpec.configure do |config|
17
18
  config.order = 'random'
18
19
  end
19
20
 
20
- $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
21
- require "redis-bloomfilter"
22
-
21
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
22
+ require 'redis-bloomfilter'
metadata CHANGED
@@ -1,98 +1,92 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-bloomfilter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francesco Laurita
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-24 00:00:00.000000000 Z
11
+ date: 2018-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hiredis
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.5'
20
- - - ! '>='
21
- - !ruby/object:Gem::Version
22
- version: 0.5.2
19
+ version: 0.6.1
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- version: '0.5'
30
- - - ! '>='
24
+ - - "~>"
31
25
  - !ruby/object:Gem::Version
32
- version: 0.5.2
26
+ version: 0.6.1
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: redis
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - ~>
31
+ - - "~>"
38
32
  - !ruby/object:Gem::Version
39
- version: '3.0'
40
- - - ! '>='
33
+ version: '4.0'
34
+ - - ">="
41
35
  - !ruby/object:Gem::Version
42
- version: 3.0.4
36
+ version: 4.0.1
43
37
  type: :runtime
44
38
  prerelease: false
45
39
  version_requirements: !ruby/object:Gem::Requirement
46
40
  requirements:
47
- - - ~>
41
+ - - "~>"
48
42
  - !ruby/object:Gem::Version
49
- version: '3.0'
50
- - - ! '>='
43
+ version: '4.0'
44
+ - - ">="
51
45
  - !ruby/object:Gem::Version
52
- version: 3.0.4
46
+ version: 4.0.1
53
47
  - !ruby/object:Gem::Dependency
54
- name: rspec
48
+ name: flexmock
55
49
  requirement: !ruby/object:Gem::Requirement
56
50
  requirements:
57
- - - ! '>='
51
+ - - ">="
58
52
  - !ruby/object:Gem::Version
59
53
  version: '0'
60
54
  type: :development
61
55
  prerelease: false
62
56
  version_requirements: !ruby/object:Gem::Requirement
63
57
  requirements:
64
- - - ! '>='
58
+ - - ">="
65
59
  - !ruby/object:Gem::Version
66
60
  version: '0'
67
61
  - !ruby/object:Gem::Dependency
68
- name: flexmock
62
+ name: rake
69
63
  requirement: !ruby/object:Gem::Requirement
70
64
  requirements:
71
- - - ! '>='
65
+ - - ">="
72
66
  - !ruby/object:Gem::Version
73
67
  version: '0'
74
68
  type: :development
75
69
  prerelease: false
76
70
  version_requirements: !ruby/object:Gem::Requirement
77
71
  requirements:
78
- - - ! '>='
72
+ - - ">="
79
73
  - !ruby/object:Gem::Version
80
74
  version: '0'
81
75
  - !ruby/object:Gem::Dependency
82
- name: rake
76
+ name: rspec
83
77
  requirement: !ruby/object:Gem::Requirement
84
78
  requirements:
85
- - - ! '>='
79
+ - - ">="
86
80
  - !ruby/object:Gem::Version
87
81
  version: '0'
88
82
  type: :development
89
83
  prerelease: false
90
84
  version_requirements: !ruby/object:Gem::Requirement
91
85
  requirements:
92
- - - ! '>='
86
+ - - ">="
93
87
  - !ruby/object:Gem::Version
94
88
  version: '0'
95
- description: ! "\n Adds Redis::Bloomfilter class which can be used as ditributed
89
+ description: "\n Adds Redis::Bloomfilter class which can be used as ditributed
96
90
  bloom filter implementation on Redis.\n A Bloom filter is a space-efficient probabilistic
97
91
  data structure that is used to test whether an element is a member of a set.\n "
98
92
  email:
@@ -101,9 +95,9 @@ executables: []
101
95
  extensions: []
102
96
  extra_rdoc_files: []
103
97
  files:
104
- - .gitignore
105
- - .rspec
106
- - .travis.yml
98
+ - ".gitignore"
99
+ - ".rspec"
100
+ - ".travis.yml"
107
101
  - Gemfile
108
102
  - LICENSE.txt
109
103
  - README.md
@@ -130,17 +124,17 @@ require_paths:
130
124
  - lib
131
125
  required_ruby_version: !ruby/object:Gem::Requirement
132
126
  requirements:
133
- - - ! '>='
127
+ - - ">="
134
128
  - !ruby/object:Gem::Version
135
129
  version: '0'
136
130
  required_rubygems_version: !ruby/object:Gem::Requirement
137
131
  requirements:
138
- - - ! '>='
132
+ - - ">="
139
133
  - !ruby/object:Gem::Version
140
134
  version: '0'
141
135
  requirements: []
142
136
  rubyforge_project: redis-bloomfilter
143
- rubygems_version: 2.2.2
137
+ rubygems_version: 2.7.2
144
138
  signing_key:
145
139
  specification_version: 4
146
140
  summary: Distributed Bloom Filter implementation on Redis