picky 3.6.11 → 3.6.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,7 +8,6 @@ module Picky
8
8
  class Allocation # :nodoc:all
9
9
 
10
10
  attr_reader :count,
11
- :ids,
12
11
  :score,
13
12
  :combinations,
14
13
  :result_identifier
@@ -42,6 +41,12 @@ module Picky
42
41
  @backend.ids combinations, amount, offset
43
42
  end
44
43
 
44
+ # Ids return by default [].
45
+ #
46
+ def ids
47
+ @ids ||= []
48
+ end
49
+
45
50
  # This starts the searching process.
46
51
  #
47
52
  def process! amount, offset
@@ -6,8 +6,6 @@ module Picky
6
6
  #
7
7
  class Allocations # :nodoc:all
8
8
 
9
- attr_reader :total
10
-
11
9
  delegate :each,
12
10
  :empty?,
13
11
  :first,
@@ -63,6 +61,7 @@ module Picky
63
61
  # Parameters:
64
62
  # * amount: the amount of ids to calculate
65
63
  # * offset: the offset from where in the result set to take the ids
64
+ # * terminate_early: Whether to calculate all allocations.
66
65
  #
67
66
  # Note: With an amount of 0, an offset > 0 doesn't make much
68
67
  # sense, as seen in the live search.
@@ -72,18 +71,28 @@ module Picky
72
71
  #
73
72
  # Note: It's possible that no ids are returned by an allocation, but a count. (In case of an offset)
74
73
  #
75
- def process! amount, offset = 0
76
- @total = 0
74
+ def process! amount, offset = 0, terminate_early = nil
77
75
  current_offset = 0
78
- @allocations.each do |allocation|
76
+ each do |allocation|
79
77
  ids = allocation.process! amount, offset
80
- @total = @total + allocation.count # the total mixed in
81
78
  if ids.empty?
82
79
  offset = offset - allocation.count unless offset.zero?
83
80
  else
84
81
  amount = amount - ids.size # we need less results from the following allocation
85
82
  offset = 0 # we have already passed the offset
86
83
  end
84
+ if terminate_early
85
+ break if terminate_early < 0 && offset <= 0
86
+ terminate_early -= 1
87
+ end
88
+ end
89
+ end
90
+
91
+ # The total is simply the sum of the counts of all allocations.
92
+ #
93
+ def total
94
+ @total ||= inject(0) do |total, allocation|
95
+ total + (allocation.count || break)
87
96
  end
88
97
  end
89
98
 
data/lib/picky/results.rb CHANGED
@@ -24,9 +24,9 @@ module Picky
24
24
 
25
25
  # Create new results and calculate the ids.
26
26
  #
27
- def self.from query, amount, offset, allocations
27
+ def self.from query, amount, offset, allocations, extra_allocations = nil
28
28
  results = new query, amount, offset, allocations
29
- results.prepare!
29
+ results.prepare! extra_allocations
30
30
  results
31
31
  end
32
32
 
@@ -53,8 +53,8 @@ module Picky
53
53
  # Without this, the allocations are not processed,
54
54
  # and no ids are calculated.
55
55
  #
56
- def prepare!
57
- allocations.process! amount, offset
56
+ def prepare! extra_allocations = nil
57
+ allocations.process! amount, offset, extra_allocations
58
58
  end
59
59
 
60
60
  # Returns a hash with the allocations, offset, duration and total.
data/lib/picky/search.rb CHANGED
@@ -80,6 +80,37 @@ module Picky
80
80
  amount ? @max_allocations = amount : @max_allocations
81
81
  end
82
82
 
83
+ # Tells Picky to terminate calculating ids if it has enough ids.
84
+ # (So, early)
85
+ #
86
+ # Important note: Do not use this for the live search!
87
+ # (As Picky needs to calculate the total)
88
+ #
89
+ # Note: When using the Picky interface, do not terminate too
90
+ # early as this will kill off the allocation selections.
91
+ # A value of
92
+ # early_terminate 5
93
+ # is probably a good idea to show the user 5 extra
94
+ # beyond the needed ones.
95
+ #
96
+ # Examples:
97
+ # # Terminate if you have enough ids.
98
+ # #
99
+ # search = Search.new(index1, index2, index3) do
100
+ # terminate_early
101
+ # end
102
+ #
103
+ # # After calculating enough ids,
104
+ # # calculate 5 extra allocations for the interface.
105
+ # #
106
+ # search = Search.new(index1, index2, index3) do
107
+ # terminate_early 5
108
+ # end
109
+ #
110
+ def terminate_early extra_allocations = 0
111
+ @extra_allocations = extra_allocations.respond_to?(:to_hash) ? extra_allocations[:with_extra_allocations] : extra_allocations
112
+ end
113
+
83
114
  # Examples:
84
115
  # search = Search.new(books_index, dvd_index, mp3_index) do
85
116
  # boost [:author, :title] => +3,
@@ -174,7 +205,7 @@ module Picky
174
205
  # Note: Internal method, use #search to search.
175
206
  #
176
207
  def execute tokens, ids, offset, original_text = nil
177
- Results.from original_text, ids, offset, sorted_allocations(tokens, @max_allocations)
208
+ Results.from original_text, ids, offset, sorted_allocations(tokens, @max_allocations), @extra_allocations
178
209
  end
179
210
 
180
211
  # Delegates the tokenizing to the query tokenizer.
@@ -25,6 +25,8 @@ module Picky
25
25
  :dump,
26
26
  :load,
27
27
 
28
+ :empty,
29
+
28
30
  :bundle_for,
29
31
  :build_realtime_mapping,
30
32
 
@@ -38,7 +38,7 @@ describe 'Search#max_allocations' do
38
38
  category :text4
39
39
  end
40
40
 
41
- thing = Struct.new(:id, :text1, :text2, :text3, :text4)
41
+ thing = Struct.new :id, :text1, :text2, :text3, :text4
42
42
  index.add thing.new(1, 'hello world', 'hello world', 'hello world', 'hello world')
43
43
  index.add thing.new(2, 'hello world', 'hello world', 'hello world', 'hello world')
44
44
  index.add thing.new(3, 'hello world', 'hello world', 'hello world', 'hello world')
@@ -49,7 +49,7 @@ describe 'Search#max_allocations' do
49
49
  try = Picky::Search.new index
50
50
 
51
51
  threshold = performance_of do
52
- try.search('hello world')
52
+ try.search 'hello world'
53
53
  end
54
54
 
55
55
  try_again = Picky::Search.new index do
@@ -57,8 +57,8 @@ describe 'Search#max_allocations' do
57
57
  end
58
58
 
59
59
  performance_of do
60
- try_again.search('hello world')
61
- end.should < (threshold*7/9)
60
+ try_again.search 'hello world'
61
+ end.should < (threshold*2/3)
62
62
  end
63
63
 
64
64
  end
@@ -4,6 +4,34 @@ require 'spec_helper'
4
4
 
5
5
  describe "Regression" do
6
6
 
7
+ it 'does not get confused' do
8
+ index = Picky::Index.new :dynamic_weights do
9
+ category :text1
10
+ category :text2
11
+ category :text3
12
+ category :text4
13
+ end
14
+ try = Picky::Search.new index
15
+
16
+ try.search('hello hello hello').allocations.size.should == 0
17
+
18
+ thing = Struct.new(:id, :text1, :text2, :text3, :text4)
19
+ index.add thing.new(1, 'hello', 'hello', 'hello', 'world')
20
+
21
+ try.search('hello hello hello').allocations.size.should == 27
22
+
23
+ index.add thing.new(2, 'hello', 'hello', 'hello', 'world')
24
+ index.add thing.new(3, 'hello', 'hello', 'hello', 'world')
25
+ index.add thing.new(4, 'hello', 'hello', 'hello', 'world')
26
+ index.add thing.new(5, 'hello', 'hello', 'hello', 'world')
27
+
28
+ try.search('hello hello hello').allocations.size.should == 27
29
+
30
+ index.add thing.new(6, 'world', 'world', 'world', 'hello')
31
+
32
+ try.search('hello hello world').allocations.size.should == 64
33
+ end
34
+
7
35
  # # This was described by Niko
8
36
  # # and references a case where
9
37
  # # an attribute and the id referenced
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe 'Search#terminate_early' do
6
+
7
+ it 'terminates early' do
8
+ index = Picky::Index.new :terminate_early do
9
+ category :text1
10
+ category :text2
11
+ category :text3
12
+ category :text4
13
+ end
14
+
15
+ thing = Struct.new :id, :text1, :text2, :text3, :text4
16
+ index.add thing.new(1, 'hello', 'hello', 'hello', 'hello')
17
+ index.add thing.new(2, 'hello', 'hello', 'hello', 'hello')
18
+ index.add thing.new(3, 'hello', 'hello', 'hello', 'hello')
19
+ index.add thing.new(4, 'hello', 'hello', 'hello', 'hello')
20
+ index.add thing.new(5, 'hello', 'hello', 'hello', 'hello')
21
+ index.add thing.new(6, 'hello', 'hello', 'hello', 'hello')
22
+
23
+ try = Picky::Search.new index
24
+ try.search('hello').ids.should == [6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5]
25
+
26
+ try = Picky::Search.new index do
27
+ terminate_early
28
+ end
29
+ try.search('hello', 3).ids.should == [6, 5, 4]
30
+
31
+ try = Picky::Search.new index do
32
+ terminate_early
33
+ end
34
+ try.search('hello', 9).ids.should == [6, 5, 4, 3, 2, 1, 6, 5, 4]
35
+
36
+ try = Picky::Search.new index do
37
+ terminate_early with_extra_allocations: 0
38
+ end
39
+ try.search('hello', 9).ids.should == [6, 5, 4, 3, 2, 1, 6, 5, 4]
40
+
41
+ try = Picky::Search.new index do
42
+ terminate_early 0
43
+ end
44
+ try.search('hello').ids.should == [6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1]
45
+
46
+ try = Picky::Search.new index do
47
+ terminate_early with_extra_allocations: 0
48
+ end
49
+ try.search('hello').ids.should == [6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1]
50
+
51
+ try = Picky::Search.new index do
52
+ terminate_early 2
53
+ end
54
+ try.search('hello', 13).ids.should == [6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6]
55
+
56
+ try = Picky::Search.new index do
57
+ terminate_early with_extra_allocations: 2
58
+ end
59
+ try.search('hello').ids.should == [6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5]
60
+
61
+ try = Picky::Search.new index do
62
+ terminate_early with_extra_allocations: 1234
63
+ end
64
+ try.search('hello').ids.should == [6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5]
65
+
66
+ slow = performance_of do
67
+ try.search 'hello'
68
+ end
69
+
70
+ try = Picky::Search.new index do
71
+ terminate_early
72
+ end
73
+ fast = performance_of do
74
+ try.search 'hello'
75
+ end
76
+
77
+ (slow/fast).should
78
+ end
79
+
80
+ end
@@ -86,6 +86,34 @@ describe Picky::Query::Allocations do
86
86
  @allocation3 = stub :allocation3, :process! => [], :count => 2 #, ids: [8, 9]
87
87
  @allocations = described_class.new [@allocation1, @allocation2, @allocation3]
88
88
  end
89
+ describe 'lazy evaluation' do
90
+ context 'small amount' do
91
+ before(:each) do
92
+ @amount = 3
93
+ @offset = 1
94
+ end
95
+ it 'should call the process! method right' do
96
+ @allocation1.should_receive(:process!).once.with(3,1).and_return [1, 2, 3]
97
+ @allocation2.should_receive(:process!).once.with(0,0).and_return [] # TODO Actually ok?
98
+ @allocation3.should_receive(:process!).never
99
+
100
+ @allocations.process! @amount, @offset, 0
101
+ end
102
+ end
103
+ context 'larger amount' do
104
+ before(:each) do
105
+ @amount = 1
106
+ @offset = 0
107
+ end
108
+ it 'should call the process! method right' do
109
+ @allocation1.should_receive(:process!).once.with(1,0).and_return [1]
110
+ @allocation2.should_receive(:process!).once.with(0,0).and_return [] # TODO Actually ok?
111
+ @allocation3.should_receive(:process!).never
112
+
113
+ @allocations.process! @amount, @offset, 0
114
+ end
115
+ end
116
+ end
89
117
  describe 'amount spanning 3 allocations' do
90
118
  before(:each) do
91
119
  @amount = 6
@@ -27,6 +27,18 @@ describe Picky::Query::Combination do
27
27
  it 'should hash the token and the bundle' do
28
28
  @combination.hash.should == [@token, @category].hash
29
29
  end
30
+ it 'should distinguish two combinations sufficiently' do
31
+ category1 = stub :category,
32
+ :identifier => 'some_category_identifier1'
33
+
34
+ category2 = stub :category,
35
+ :identifier => 'some_category_identifier2'
36
+
37
+ combination1 = described_class.new @token, category1
38
+ combination2 = described_class.new @token, category2
39
+
40
+ combination1.hash.should_not == combination2.hash
41
+ end
30
42
  end
31
43
 
32
44
  describe 'to_result' do
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: 3.6.11
4
+ version: 3.6.12
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-30 00:00:00.000000000 Z
12
+ date: 2011-12-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70356380283200 !ruby/object:Gem::Requirement
16
+ requirement: &70344058312240 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,21 +21,21 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70356380283200
24
+ version_requirements: *70344058312240
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: picky-client
27
- requirement: &70356380282520 !ruby/object:Gem::Requirement
27
+ requirement: &70344058311560 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
31
31
  - !ruby/object:Gem::Version
32
- version: 3.6.11
32
+ version: 3.6.12
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70356380282520
35
+ version_requirements: *70344058311560
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rack
38
- requirement: &70356380282100 !ruby/object:Gem::Requirement
38
+ requirement: &70344058310640 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70356380282100
46
+ version_requirements: *70344058310640
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rack_fast_escape
49
- requirement: &70356380281640 !ruby/object:Gem::Requirement
49
+ requirement: &70344058309960 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70356380281640
57
+ version_requirements: *70344058309960
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: text
60
- requirement: &70356380281200 !ruby/object:Gem::Requirement
60
+ requirement: &70344058309020 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *70356380281200
68
+ version_requirements: *70344058309020
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: yajl-ruby
71
- requirement: &70356380280720 !ruby/object:Gem::Requirement
71
+ requirement: &70344058308480 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *70356380280720
79
+ version_requirements: *70344058308480
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: activesupport
82
- requirement: &70356380280220 !ruby/object:Gem::Requirement
82
+ requirement: &70344058307980 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '3.0'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *70356380280220
90
+ version_requirements: *70344058307980
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: unicorn
93
- requirement: &70356380279800 !ruby/object:Gem::Requirement
93
+ requirement: &70344058323920 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :runtime
100
100
  prerelease: false
101
- version_requirements: *70356380279800
101
+ version_requirements: *70344058323920
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: sinatra
104
- requirement: &70356380279340 !ruby/object:Gem::Requirement
104
+ requirement: &70344058323380 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,7 +109,7 @@ dependencies:
109
109
  version: '0'
110
110
  type: :runtime
111
111
  prerelease: false
112
- version_requirements: *70356380279340
112
+ version_requirements: *70344058323380
113
113
  description: Fast Ruby semantic text search engine with comfortable single field interface.
114
114
  email: florian.hanke+picky@gmail.com
115
115
  executables:
@@ -276,6 +276,7 @@ files:
276
276
  - spec/functional/realtime_spec.rb
277
277
  - spec/functional/regression_spec.rb
278
278
  - spec/functional/speed_spec.rb
279
+ - spec/functional/terminate_early_spec.rb
279
280
  - spec/lib/adapters/rack/base_spec.rb
280
281
  - spec/lib/adapters/rack/live_parameters_spec.rb
281
282
  - spec/lib/adapters/rack/query_spec.rb
@@ -419,6 +420,7 @@ test_files:
419
420
  - spec/functional/realtime_spec.rb
420
421
  - spec/functional/regression_spec.rb
421
422
  - spec/functional/speed_spec.rb
423
+ - spec/functional/terminate_early_spec.rb
422
424
  - spec/lib/adapters/rack/base_spec.rb
423
425
  - spec/lib/adapters/rack/live_parameters_spec.rb
424
426
  - spec/lib/adapters/rack/query_spec.rb