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 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 IN (2, 3)'` (2 and 1+2)
17
- - builds not-index-using sql with `bitfield ... ,:query_mode => :bit_operator` and `User.bitfield_sql(:insane => true, :stupid => false) == '(users.my_bits & 3) = 1'`, always slower than IN() sql, since it will not use an existing index (tested for up to 64 values)
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: ` script/plugin install git://github.com/grosser/bitfields.git `
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.5
1
+ 0.2.0
@@ -1,5 +1,5 @@
1
- bit_counts = [2,3,4,5,6,7,8,9,10,11,12,13,14]
2
- record_counts = (1..20).to_a.map{|i| i * 50_000 }
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
- data = record_counts.map do |record_count|
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
- colors = {:bit => '00xx00', :in => 'xx0000'}
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() -- #{use_index ? 'with' : 'without'} index"
118
+ title = "bit-operator vs IN -- #{use_index ? 'with' : 'without'} index"
128
119
  url = GoogleChart::LineChart.new('600x500', title, false) do |line|
129
- max = 0
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
- max = [data.max, max].max
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', "%.3f" % [max*100]]
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.1.5"
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/1.rb",
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] || :in_list)
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
@@ -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: 17
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 5
10
- version: 0.1.5
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/1.rb
34
+ - benchmark/bit_operator_vs_in.rb
35
35
  - bitfields.gemspec
36
36
  - lib/bitfields.rb
37
37
  - spec/bitfields_spec.rb