bitfields 0.1.5 → 0.2.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/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