bitfields 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +11 -3
- data/VERSION +1 -1
- data/benchmark/{1.rb → bit_operator_vs_in.rb} +11 -20
- data/bitfields.gemspec +2 -2
- data/lib/bitfields.rb +1 -1
- data/spec/bitfields_spec.rb +6 -6
- metadata +5 -5
data/README.markdown
CHANGED
@@ -13,8 +13,8 @@ e.g. true-false-false = 1, false-true-false = 2, true-false-true = 5 (1,2,4,8,.
|
|
13
13
|
|
14
14
|
- records changes `user.chamges == {:seller => [false, true]}`
|
15
15
|
- adds scopes `User.seller.stupid.first` (deactivate with `bitfield ..., :scopes => false`)
|
16
|
-
- builds sql `User.bitfield_sql(:insane => true, :stupid => false) == 'users.my_bits
|
17
|
-
- builds
|
16
|
+
- builds sql `User.bitfield_sql(:insane => true, :stupid => false) == '(users.my_bits & 3) = 1'`
|
17
|
+
- builds index-using sql with `bitfield ... ,:query_mode => :in_list` and `User.bitfield_sql(:insane => true, :stupid => false) == 'users.my_bits IN (2, 3)'` (2 and 1+2), often slower than :bit_operator sql especially for high number of bits
|
18
18
|
- builds update sql `User.set_bitfield_sql(:insane => true, :stupid => false) == 'my_bits = (my_bits | 6) - 4'`
|
19
19
|
- **faster sql than any other bitfield lib** through combination of multiple bits into a single sql statement
|
20
20
|
- gives access to bits `User.bitfields[:my_bits][:stupid] == 4`
|
@@ -22,7 +22,7 @@ e.g. true-false-false = 1, false-true-false = 2, true-false-true = 5 (1,2,4,8,.
|
|
22
22
|
Install
|
23
23
|
=======
|
24
24
|
As Gem: ` sudo gem install bitfields `
|
25
|
-
Or as Rails plugin: `
|
25
|
+
Or as Rails plugin: ` rails plugin install git://github.com/grosser/bitfields.git `
|
26
26
|
|
27
27
|
### Migration
|
28
28
|
ALWAYS set a default, bitfield queries will not work for NULL
|
@@ -38,6 +38,14 @@ Update all users
|
|
38
38
|
Delete the shop when a user is no longer a seller
|
39
39
|
before_save :delete_shop, :if => lambda{|u| u.changes['seller'] == [true, false]}
|
40
40
|
|
41
|
+
TIPS
|
42
|
+
====
|
43
|
+
- Never do: "#{bitfield_sql(...)} AND #{bitfield_sql(...)}", merge both into one hash
|
44
|
+
- bit_operator is faster in most cases, use :query_mode => :in_list sparingly
|
45
|
+
- standard mysql integer is 4 byte -> 32 bitfields
|
46
|
+
|
47
|
+
![performance](http://chart.apis.google.com/chart?chtt=bit-operator+vs+IN+--+with+index&chd=s:CEGIKNPRUW,DEHJLOQSVX,CFHKMPSYXZ,DHJMPSVYbe,DHLPRVZbfi,FKOUZeinsx,FLQWbglqw2,HNTZfkqw19,BDEGHJLMOP,BDEGIKLNOQ,BDFGIKLNPQ,BDFGILMNPR,BDFHJKMOQR,BDFHJLMOQS,BDFHJLNPRT,BDFHJLNPRT&chxt=x,y&chxl=0:|100K|200K|300K|400K|500K|600K|700K|800K|900K|1000K|1:|0|1441.671ms&cht=lc&chs=600x500&chdl=2bits+%28in%29|3bits+%28in%29|4bits+%28in%29|6bits+%28in%29|8bits+%28in%29|10bits+%28in%29|12bits+%28in%29|14bits+%28in%29|2bits+%28bit%29|3bits+%28bit%29|4bits+%28bit%29|6bits+%28bit%29|8bits+%28bit%29|10bits+%28bit%29|12bits+%28bit%29|14bits+%28bit%29&chco=0000ff,0000ee,0000dd,0000cc,0000bb,0000aa,000099,000088,ff0000,ee0000,dd0000,cc0000,bb0000,aa0000,990000,880000)
|
48
|
+
|
41
49
|
TODO
|
42
50
|
====
|
43
51
|
- convenient named scope `User.with_bitfields(:xxx=>true, :yy=>false)`
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -1,5 +1,5 @@
|
|
1
|
-
bit_counts = [2,3,4,
|
2
|
-
record_counts = (1..
|
1
|
+
bit_counts = [2,3,4,6,8,10,12,14]
|
2
|
+
record_counts = (1..10).to_a.map{|i| i * 100_000 }
|
3
3
|
use_index = true
|
4
4
|
database = ARGV[0]
|
5
5
|
|
@@ -90,15 +90,6 @@ def test_speed(bit_counts, query_mode)
|
|
90
90
|
Hash[result]
|
91
91
|
end
|
92
92
|
|
93
|
-
#determines a unique color based on a name
|
94
|
-
def color_for(name)
|
95
|
-
name = name.inspect + "some randomness for fun"
|
96
|
-
hash = (name.hash % 1_000_000_000).to_s #get a hash of a constant size
|
97
|
-
colors = [hash[0..1], hash[2..3], hash[4..5]].map{|c| c.to_f / 100.0 * 16} #use 3 parts of the hash to get numbers from 0 to 15.99
|
98
|
-
palette = ('0'..'9').to_a + ('a'..'f').to_a #hex values 0..f
|
99
|
-
colors.map{|c| palette[c.floor].to_s * 2} * '' #each color is duplicated and the joined
|
100
|
-
end
|
101
|
-
|
102
93
|
connect(database)
|
103
94
|
create_model_table(bit_counts, use_index)
|
104
95
|
class User < ActiveRecord::Base
|
@@ -108,10 +99,10 @@ create_model_fields(bit_counts)
|
|
108
99
|
puts "creating test data"
|
109
100
|
last = 0
|
110
101
|
|
102
|
+
# collect graph data
|
111
103
|
graphs = {:bit => {}, :in => {}}
|
112
|
-
|
104
|
+
record_counts.each do |record_count|
|
113
105
|
create(bit_counts, record_count-last)
|
114
|
-
puts User.count
|
115
106
|
last = record_count
|
116
107
|
|
117
108
|
puts "testing with #{record_count} records -- bit_operator"
|
@@ -119,26 +110,26 @@ data = record_counts.map do |record_count|
|
|
119
110
|
|
120
111
|
puts "testing with #{record_count} records -- in_list"
|
121
112
|
graphs[:in][record_count] = test_speed(bit_counts, :in_list)
|
122
|
-
|
123
113
|
end
|
124
114
|
|
125
|
-
|
115
|
+
# print them
|
116
|
+
colors = {:bit => 'xx0000', :in => '0000xx'}
|
126
117
|
alpha_num = (('0'..'9').to_a + ('a'..'f').to_a).reverse
|
127
|
-
title = "bit-operator vs IN
|
118
|
+
title = "bit-operator vs IN -- #{use_index ? 'with' : 'without'} index"
|
128
119
|
url = GoogleChart::LineChart.new('600x500', title, false) do |line|
|
129
|
-
|
120
|
+
max_y = 0
|
130
121
|
graphs.each do |type, line_data|
|
131
122
|
bit_counts.each do |bit_count|
|
132
123
|
data = record_counts.map{|rc| line_data[rc][bit_count] }
|
133
124
|
name = "#{bit_count}bits (#{type})"
|
134
|
-
color = colors[type].sub('xx', alpha_num[bit_count]*2)
|
125
|
+
color = colors[type].sub('xx', alpha_num[bit_counts.index(bit_count)]*2)
|
135
126
|
line.data(name, data, color)
|
136
|
-
|
127
|
+
max_y = [data.max, max_y].max
|
137
128
|
end
|
138
129
|
end
|
139
130
|
|
140
131
|
line.axis :x, :labels => record_counts.map{|c|"#{c/1000}K"}
|
141
|
-
line.axis :y, :labels => ['0', "%.
|
132
|
+
line.axis :y, :labels => ['0', "%.3fms" % [max_y*1000]]
|
142
133
|
end.to_url
|
143
134
|
|
144
135
|
puts url
|
data/bitfields.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{bitfields}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Michael Grosser"]
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
"README.markdown",
|
19
19
|
"Rakefile",
|
20
20
|
"VERSION",
|
21
|
-
"benchmark/
|
21
|
+
"benchmark/bit_operator_vs_in.rb",
|
22
22
|
"bitfields.gemspec",
|
23
23
|
"lib/bitfields.rb",
|
24
24
|
"spec/bitfields_spec.rb",
|
data/lib/bitfields.rb
CHANGED
@@ -71,7 +71,7 @@ module Bitfields
|
|
71
71
|
private
|
72
72
|
|
73
73
|
def bitfield_sql_by_column(column, bit_values, options={})
|
74
|
-
mode = options[:query_mode] || (bitfield_options[column][:query_mode] || :
|
74
|
+
mode = options[:query_mode] || (bitfield_options[column][:query_mode] || :bit_operator)
|
75
75
|
case mode
|
76
76
|
when :in_list then
|
77
77
|
max = (bitfields[column].values.max * 2) - 1
|
data/spec/bitfields_spec.rb
CHANGED
@@ -150,23 +150,23 @@ describe Bitfields do
|
|
150
150
|
|
151
151
|
describe :bitfield_sql do
|
152
152
|
it "includes true states" do
|
153
|
-
User.bitfield_sql(:insane => true).should == 'users.bits IN (2,3,6,7)' # 2, 1+2, 2+4, 1+2+4
|
153
|
+
User.bitfield_sql({:insane => true}, :query_mode => :in_list).should == 'users.bits IN (2,3,6,7)' # 2, 1+2, 2+4, 1+2+4
|
154
154
|
end
|
155
155
|
|
156
156
|
it "includes invalid states" do
|
157
|
-
User.bitfield_sql(:insane => false).should == 'users.bits IN (0,1,4,5)' # 0, 1, 4, 4+1
|
157
|
+
User.bitfield_sql({:insane => false}, :query_mode => :in_list).should == 'users.bits IN (0,1,4,5)' # 0, 1, 4, 4+1
|
158
158
|
end
|
159
159
|
|
160
160
|
it "can combine multiple fields" do
|
161
|
-
User.bitfield_sql(:seller => true, :insane => true).should == 'users.bits IN (3,7)' # 1+2, 1+2+4
|
161
|
+
User.bitfield_sql({:seller => true, :insane => true}, :query_mode => :in_list).should == 'users.bits IN (3,7)' # 1+2, 1+2+4
|
162
162
|
end
|
163
163
|
|
164
164
|
it "can combine multiple fields with different values" do
|
165
|
-
User.bitfield_sql(:seller => true, :insane => false).should == 'users.bits IN (1,5)' # 1, 1+4
|
165
|
+
User.bitfield_sql({:seller => true, :insane => false}, :query_mode => :in_list).should == 'users.bits IN (1,5)' # 1, 1+4
|
166
166
|
end
|
167
167
|
|
168
168
|
it "combines multiple columns into one sql" do
|
169
|
-
sql = MultiBitUser.bitfield_sql(:seller => true, :insane => false, :one => true, :four => true)
|
169
|
+
sql = MultiBitUser.bitfield_sql({:seller => true, :insane => false, :one => true, :four => true}, :query_mode => :in_list)
|
170
170
|
sql.should == 'users.bits IN (1,5) AND users.more_bits IN (5,7)' # 1, 1+4 AND 1+4, 1+2+4
|
171
171
|
end
|
172
172
|
|
@@ -174,7 +174,7 @@ describe Bitfields do
|
|
174
174
|
u1 = MultiBitUser.create!(:seller => true, :one => true)
|
175
175
|
u2 = MultiBitUser.create!(:seller => true, :one => false)
|
176
176
|
u3 = MultiBitUser.create!(:seller => false, :one => false)
|
177
|
-
MultiBitUser.all(:conditions => MultiBitUser.bitfield_sql(:seller => true, :one => false)).should == [u2]
|
177
|
+
MultiBitUser.all(:conditions => MultiBitUser.bitfield_sql({:seller => true, :one => false}, :query_mode => :in_list)).should == [u2]
|
178
178
|
end
|
179
179
|
|
180
180
|
describe 'with bit operator mode' do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitfields
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Michael Grosser
|
@@ -31,7 +31,7 @@ files:
|
|
31
31
|
- README.markdown
|
32
32
|
- Rakefile
|
33
33
|
- VERSION
|
34
|
-
- benchmark/
|
34
|
+
- benchmark/bit_operator_vs_in.rb
|
35
35
|
- bitfields.gemspec
|
36
36
|
- lib/bitfields.rb
|
37
37
|
- spec/bitfields_spec.rb
|