picky 3.6.11 → 3.6.12
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/picky/query/allocation.rb +6 -1
- data/lib/picky/query/allocations.rb +15 -6
- data/lib/picky/results.rb +4 -4
- data/lib/picky/search.rb +32 -1
- data/lib/picky/wrappers/category/exact_first.rb +2 -0
- data/spec/functional/max_allocations_spec.rb +4 -4
- data/spec/functional/regression_spec.rb +28 -0
- data/spec/functional/terminate_early_spec.rb +80 -0
- data/spec/lib/query/allocations_spec.rb +28 -0
- data/spec/lib/query/combination_spec.rb +12 -0
- metadata +23 -21
@@ -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
|
-
|
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.
|
@@ -38,7 +38,7 @@ describe 'Search#max_allocations' do
|
|
38
38
|
category :text4
|
39
39
|
end
|
40
40
|
|
41
|
-
thing = Struct.new
|
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
|
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
|
61
|
-
end.should < (threshold*
|
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.
|
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-
|
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: &
|
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: *
|
24
|
+
version_requirements: *70344058312240
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: picky-client
|
27
|
-
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.
|
32
|
+
version: 3.6.12
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70344058311560
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rack
|
38
|
-
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: *
|
46
|
+
version_requirements: *70344058310640
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rack_fast_escape
|
49
|
-
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: *
|
57
|
+
version_requirements: *70344058309960
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: text
|
60
|
-
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: *
|
68
|
+
version_requirements: *70344058309020
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: yajl-ruby
|
71
|
-
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: *
|
79
|
+
version_requirements: *70344058308480
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: activesupport
|
82
|
-
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: *
|
90
|
+
version_requirements: *70344058307980
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: unicorn
|
93
|
-
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: *
|
101
|
+
version_requirements: *70344058323920
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: sinatra
|
104
|
-
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: *
|
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
|