picky 4.11.1 → 4.11.2

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.
@@ -16,13 +16,40 @@ module Picky
16
16
  # Gets the weight for this token's text.
17
17
  #
18
18
  def weight token
19
- bundle_for(token).weight token.text
19
+ bundle = bundle_for token
20
+ if range = token.range
21
+ # The math is not perfectly correct, but you
22
+ # get my idea. Also, we could return early.
23
+ #
24
+ # TODO Possible to speed up more?
25
+ #
26
+ range.inject(nil) do |sum, text|
27
+ weight = bundle.weight(text)
28
+ weight && (weight + (sum || 0)) || sum
29
+ end
30
+ else
31
+ bundle.weight token.text
32
+ end
20
33
  end
21
34
 
22
35
  # Gets the ids for this token's text.
23
36
  #
24
37
  def ids token
25
- bundle_for(token).ids token.text
38
+ bundle = bundle_for token
39
+ if range = token.range
40
+ # Adding all to an array, then flattening
41
+ # is faster than using ary + ary.
42
+ #
43
+ range.inject([]) do |result, text|
44
+ # It is 30% faster using the empty check
45
+ # than just << [].
46
+ #
47
+ ids = bundle.ids text
48
+ ids.empty? ? result : result << ids
49
+ end.flatten
50
+ else
51
+ bundle.ids token.text
52
+ end
26
53
  end
27
54
 
28
55
  # Returns the right index bundle for this token.
@@ -34,6 +61,8 @@ module Picky
34
61
  # Returns a combination for the token,
35
62
  # or nil, if there is none.
36
63
  #
64
+ # TODO Don't throw away the weight, instead store it in the combination?
65
+ #
37
66
  def combination_for token
38
67
  weight(token) && Query::Combination.new(token, self)
39
68
  end
@@ -43,6 +43,7 @@ module Picky
43
43
  qualify
44
44
  partialize
45
45
  similarize
46
+ rangify
46
47
  remove_illegals
47
48
  self
48
49
  end
@@ -186,6 +187,27 @@ module Picky
186
187
  redefine_illegals
187
188
  end
188
189
 
190
+ # Define a character which makes a token a range token.
191
+ #
192
+ # Default is '-'.
193
+ #
194
+ # Example:
195
+ # Picky::Query::Token.range_character = "…"
196
+ # try.search("year:2000…2008") # Will find results in a range.
197
+ #
198
+ @@range_character = '-'
199
+ def self.range_character= character
200
+ @@range_character = character
201
+ end
202
+ def rangify
203
+ if @text.include? @@range_character
204
+ @range = Range.new *@text.split(@@range_character, 2)
205
+ end
206
+ end
207
+ def range
208
+ @range
209
+ end
210
+
189
211
  # Is this a "similar" character?
190
212
  #
191
213
  def similar?
@@ -4,11 +4,14 @@ require 'spec_helper'
4
4
 
5
5
  describe 'custom delimiters' do
6
6
 
7
+ # TODO Add range query delimiter.
8
+ #
7
9
  after(:each) do
8
10
  Picky::Query::Token.partial_character = '\*'
9
11
  Picky::Query::Token.no_partial_character = '"'
10
12
  Picky::Query::Token.similar_character = '~'
11
13
  Picky::Query::Token.no_similar_character = '"'
14
+ Picky::Query::Token.range_character = '-'
12
15
  Picky::Query::Token.qualifier_text_delimiter = ':'
13
16
  Picky::Query::Token.qualifiers_delimiter = ','
14
17
  end
@@ -76,4 +79,25 @@ describe 'custom delimiters' do
76
79
  Picky::Query::Token.qualifiers_delimiter = '|'
77
80
  try.search("text1|text2?hello text2?world").ids.should == [1]
78
81
  end
82
+
83
+ it 'offers custom range characters to be set' do
84
+ index = Picky::Index.new :custom_range_character do
85
+ category :year
86
+ end
87
+
88
+ rangy = Struct.new :id, :year
89
+
90
+ index.add rangy.new(1, 1977)
91
+ index.add rangy.new(2, 1989)
92
+ index.add rangy.new(3, 2001)
93
+ index.add rangy.new(4, 2012)
94
+ index.add rangy.new(4, 3000)
95
+
96
+ try = Picky::Search.new index
97
+ try.search("1980-2015").ids.should == [2,3,4]
98
+
99
+ try.search("1980…2015").ids.should == []
100
+ Picky::Query::Token.range_character = "…"
101
+ try.search("1980…2015").ids.should == [2,3,4]
102
+ end
79
103
  end
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe 'range queries' do
6
+
7
+ let(:index) do
8
+ index = Picky::Index.new :range_queries do
9
+ category :year
10
+ category :alphabet
11
+ end
12
+
13
+ rangy = Struct.new :id, :year, :alphabet
14
+
15
+ index.add rangy.new(1, 2000, 'g')
16
+ index.add rangy.new(2, 1977, 'f')
17
+ index.add rangy.new(3, 1989, 'a')
18
+ index.add rangy.new(4, 2011, 'u')
19
+ index.add rangy.new(5, 3000, 'v')
20
+ index.add rangy.new(6, 1291, 'z')
21
+ index.add rangy.new(7, 881, 'm')
22
+ index.add rangy.new(8, 1984, 'l')
23
+
24
+ index
25
+ end
26
+ let(:try) { Picky::Search.new index }
27
+
28
+ it 'still works with exact queries' do
29
+ try.search('1980').ids.should == []
30
+ try.search('1989').ids.should == [3]
31
+ end
32
+
33
+ it 'works with misses' do
34
+ try.search('900-1200').ids.should == []
35
+ end
36
+
37
+ it 'survives huge ranges' do
38
+ try.search('0-10000 a').ids.should == [3]
39
+
40
+ # Quote to make it non-partial.
41
+ #
42
+ try.search('0-3000"').ids.should == [7,6,2,8,3,1,4,5]
43
+ end
44
+ it 'is semi-reasonably fast with huge ranges' do
45
+ # Quote to make it non-partial.
46
+ #
47
+ performance_of { try.search('0-3000"') }.should < 0.21
48
+
49
+ # Note it is much much faster with an additional token.
50
+ #
51
+ performance_of { try.search('0-3000 a') }.should < 0.0085
52
+ end
53
+
54
+ it 'handles basic range queries' do
55
+ try.search('1980-2001').ids.should == [8,3,1]
56
+ try.search('f-u').ids.should == [2,1,8,7,4]
57
+ end
58
+
59
+ it 'can handle qualifiers' do
60
+ try.search('year:1980-2001').ids.should == [8,3,1]
61
+ try.search('alphabet:f-u').ids.should == [2,1,8,7,4]
62
+ end
63
+
64
+ it 'can be combined with other search words' do
65
+ try.search('1980-2001 a').ids.should == [3]
66
+ try.search('f-u 881').ids.should == [7]
67
+ end
68
+
69
+ it 'can handle multiple range queries' do
70
+ try.search('1980-2001 a-h').ids.should == [3,1]
71
+ try.search('f-u 881-1977').ids.should == [2,7]
72
+ end
73
+
74
+ it 'can be combined with partial queries' do
75
+ try.search('198* a-h').ids.should == [3]
76
+ try.search('a-h 198').ids.should == [3]
77
+ end
78
+
79
+ it 'works with nonsensical ranges' do
80
+ try.search('h-a').ids.should == []
81
+ end
82
+
83
+ # it 'handles combined range/partial queries' do
84
+ # # TODO This still needs to be refined. It is madness.
85
+ # #
86
+ # try.search('198-200*').ids.should == [8,3,1,4,5,7,6,2]
87
+ # end
88
+
89
+ end
@@ -64,53 +64,120 @@ describe Picky::Category do
64
64
  end
65
65
 
66
66
  describe 'weight' do
67
- before(:each) do
68
- @token = stub :token, :text => :some_text
69
- end
70
- context 'partial bundle' do
71
- before(:each) do
72
- @category.stub! :bundle_for => @partial
67
+ let(:token) { stub :token, :text => :some_text }
68
+ context 'without range' do
69
+ before :each do
70
+ token.stub! :range => nil
71
+ end
72
+ context 'partial bundle' do
73
+ before(:each) do
74
+ @category.stub! :bundle_for => @partial
75
+ end
76
+ it 'should receive weight with the token text' do
77
+ @partial.should_receive(:weight).once.with :some_text
78
+
79
+ @category.weight token
80
+ end
73
81
  end
74
- it 'should receive weight with the token text' do
75
- @partial.should_receive(:weight).once.with :some_text
82
+ context 'exact bundle' do
83
+ before(:each) do
84
+ @category.stub! :bundle_for => @exact
85
+ end
86
+ it 'should receive weight with the token text' do
87
+ @exact.should_receive(:weight).once.with :some_text
76
88
 
77
- @category.weight @token
89
+ @category.weight token
90
+ end
78
91
  end
79
92
  end
80
- context 'exact bundle' do
81
- before(:each) do
82
- @category.stub! :bundle_for => @exact
93
+ context 'with range' do
94
+ before :each do
95
+ token.stub! :range => (1..3)
96
+ end
97
+ context 'partial bundle' do
98
+ before(:each) do
99
+ @category.stub! :bundle_for => @partial
100
+ end
101
+ it 'should receive weight with the token text' do
102
+ @partial.should_receive(:weight).once.times.with(1).and_return(1)
103
+ @partial.should_receive(:weight).once.times.with(2).and_return(2)
104
+ @partial.should_receive(:weight).once.times.with(3).and_return(3)
105
+
106
+ @category.weight(token).should == 6
107
+ end
108
+ it 'returns nil if none hit' do
109
+ @partial.should_receive(:weight).once.times.with(1).and_return(nil)
110
+ @partial.should_receive(:weight).once.times.with(2).and_return(nil)
111
+ @partial.should_receive(:weight).once.times.with(3).and_return(nil)
112
+
113
+ @category.weight(token).should == nil
114
+ end
83
115
  end
84
- it 'should receive weight with the token text' do
85
- @exact.should_receive(:weight).once.with :some_text
116
+ context 'exact bundle' do
117
+ before(:each) do
118
+ @category.stub! :bundle_for => @exact
119
+ end
120
+ it 'should receive weight with the token text' do
121
+ @exact.should_receive(:weight).once.times.with(1).and_return(1)
122
+ @exact.should_receive(:weight).once.times.with(2).and_return(2)
123
+ @exact.should_receive(:weight).once.times.with(3).and_return(3)
86
124
 
87
- @category.weight @token
125
+ @category.weight(token).should == 6
126
+ end
88
127
  end
89
128
  end
90
129
  end
91
130
 
92
131
  describe 'ids' do
93
- before(:each) do
94
- @token = stub :token, :text => :some_text
95
- end
96
- context 'partial bundle' do
97
- before(:each) do
98
- @category.stub! :bundle_for => @partial
132
+ let(:token) { stub :token, :text => :some_text }
133
+ context 'without range' do
134
+ before(:each) { token.stub! :range => nil }
135
+ context 'partial bundle' do
136
+ before(:each) do
137
+ @category.stub! :bundle_for => @partial
138
+ end
139
+ it 'should receive ids with the token text' do
140
+ @partial.should_receive(:ids).once.with :some_text
141
+
142
+ @category.ids token
143
+ end
99
144
  end
100
- it 'should receive ids with the token text' do
101
- @partial.should_receive(:ids).once.with :some_text
145
+ context 'exact bundle' do
146
+ before(:each) do
147
+ @category.stub! :bundle_for => @exact
148
+ end
149
+ it 'should receive ids with the token text' do
150
+ @exact.should_receive(:ids).once.with :some_text
102
151
 
103
- @category.ids @token
152
+ @category.ids token
153
+ end
104
154
  end
105
155
  end
106
- context 'exact bundle' do
107
- before(:each) do
108
- @category.stub! :bundle_for => @exact
156
+ context 'with range' do
157
+ before(:each) { token.stub! :range => (1..3) }
158
+ context 'partial bundle' do
159
+ before(:each) do
160
+ @category.stub! :bundle_for => @partial
161
+ end
162
+ it 'should receive ids with the token text' do
163
+ @partial.should_receive(:ids).once.with(1).and_return [1]
164
+ @partial.should_receive(:ids).once.with(2).and_return [2]
165
+ @partial.should_receive(:ids).once.with(3).and_return [3]
166
+
167
+ @category.ids(token).should == [1,2,3]
168
+ end
109
169
  end
110
- it 'should receive ids with the token text' do
111
- @exact.should_receive(:ids).once.with :some_text
170
+ context 'exact bundle' do
171
+ before(:each) do
172
+ @category.stub! :bundle_for => @exact
173
+ end
174
+ it 'should receive ids with the token text' do
175
+ @exact.should_receive(:ids).once.with(1).and_return [1]
176
+ @exact.should_receive(:ids).once.with(2).and_return [2]
177
+ @exact.should_receive(:ids).once.with(3).and_return [3]
112
178
 
113
- @category.ids @token
179
+ @category.ids(token).should == [1,2,3]
180
+ end
114
181
  end
115
182
  end
116
183
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: picky
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.11.1
4
+ version: 4.11.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-03 00:00:00.000000000 Z
12
+ date: 2012-11-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -34,7 +34,7 @@ dependencies:
34
34
  requirements:
35
35
  - - ~>
36
36
  - !ruby/object:Gem::Version
37
- version: 4.11.1
37
+ version: 4.11.2
38
38
  type: :development
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  requirements:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
- version: 4.11.1
45
+ version: 4.11.2
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: text
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -282,6 +282,7 @@ files:
282
282
  - spec/functional/non_specific_ids_larger_than_20_spec.rb
283
283
  - spec/functional/only_spec.rb
284
284
  - spec/functional/pool_spec.rb
285
+ - spec/functional/range_queries_spec.rb
285
286
  - spec/functional/realtime_spec.rb
286
287
  - spec/functional/regression_spec.rb
287
288
  - spec/functional/reloading_spec.rb
@@ -445,6 +446,7 @@ test_files:
445
446
  - spec/functional/non_specific_ids_larger_than_20_spec.rb
446
447
  - spec/functional/only_spec.rb
447
448
  - spec/functional/pool_spec.rb
449
+ - spec/functional/range_queries_spec.rb
448
450
  - spec/functional/realtime_spec.rb
449
451
  - spec/functional/regression_spec.rb
450
452
  - spec/functional/reloading_spec.rb