picky 1.5.4 → 2.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,190 +0,0 @@
1
- # = Picky Queries
2
- #
3
- # A Picky Query is an object which:
4
- # * holds one or more indexes
5
- # * offers an interface to query these indexes.
6
- #
7
- # You connect URL paths to indexes via a Query.
8
- #
9
- # We recommend not to use this directly, but connect it to an URL and query through one of these
10
- # (Protip: Use "curl 'localhost:8080/query/path?query=exampletext')" in a Terminal.
11
- #
12
- # There are two flavors of queries:
13
- # * Query::Full (Full results with all infos)
14
- # * Query::Live (Same as the Full results without result ids. Useful for query result counters.)
15
- #
16
- module Query
17
-
18
- # The base query class.
19
- #
20
- # Not directly instantiated. However, its methods are used by its subclasses, Full and Live.
21
- #
22
- class Base
23
-
24
- include Helpers::Measuring
25
-
26
- attr_reader :indexes
27
- attr_writer :tokenizer, :identifiers_to_remove
28
- attr_accessor :reduce_to_amount, :weights
29
-
30
- # Takes:
31
- # * A number of indexes
32
- # * Options hash (optional) with:
33
- # * tokenizer: Tokenizers::Query.default by default.
34
- # * weights: A hash of weights, or a Query::Weights object.
35
- #
36
- def initialize *index_definitions
37
- options = Hash === index_definitions.last ? index_definitions.pop : {}
38
-
39
- @indexes = Internals::Query::Indexes.new *index_definitions, combinations_type_for(index_definitions)
40
- @tokenizer = options[:tokenizer] || Internals::Tokenizers::Query.default
41
- weights = options[:weights] || Weights.new
42
- @weights = Hash === weights ? Weights.new(weights) : weights
43
- end
44
-
45
- # Returns the right combinations strategy for
46
- # a number of query indexes.
47
- #
48
- # Currently it isn't possible using Memory and Redis etc.
49
- # indexes in the same query index group.
50
- #
51
- # Picky will raise a Query::Indexes::DifferentTypesError.
52
- #
53
- @@mapping = {
54
- Index::Memory => Internals::Query::Combinations::Memory,
55
- Index::Redis => Internals::Query::Combinations::Redis
56
- }
57
- def combinations_type_for index_definitions_ary
58
- index_types = index_definitions_ary.map(&:class)
59
- index_types.uniq!
60
- raise_different(index_types) if index_types.size > 1
61
- !index_types.empty? && @@mapping[*index_types] || Internals::Query::Combinations::Memory
62
- end
63
- # Currently it isn't possible using Memory and Redis etc.
64
- # indexes in the same query index group.
65
- #
66
- class DifferentTypesError < StandardError
67
- def initialize types
68
- @types = types
69
- end
70
- def to_s
71
- "Currently it isn't possible to mix #{@types.join(" and ")} Indexes in the same Query."
72
- end
73
- end
74
- def raise_different index_types
75
- raise DifferentTypesError.new(index_types)
76
- end
77
-
78
- # This is the main entry point for a query.
79
- # Use this in specs and also for running queries.
80
- #
81
- # Parameters:
82
- # * text: The search text.
83
- # * offset = 0: _optional_ The offset from which position to return the ids. Useful for pagination.
84
- #
85
- # Note: The Rack adapter calls this method after unravelling the HTTP request.
86
- #
87
- def search_with_text text, offset = 0
88
- search tokenized(text), offset
89
- end
90
-
91
- # Runs the actual search using Query::Tokens.
92
- #
93
- # Note: Internal method, use #search_with_text.
94
- #
95
- def search tokens, offset = 0
96
- results = nil
97
-
98
- duration = timed do
99
- results = execute tokens, offset
100
- end
101
- results.duration = duration.round 6
102
-
103
- results
104
- end
105
-
106
- # Execute a search using Query::Tokens.
107
- #
108
- # Note: Internal method, use #search_with_text.
109
- #
110
- def execute tokens, offset
111
- result_type.from offset, sorted_allocations(tokens)
112
- end
113
-
114
- # Delegates the tokenizing to the query tokenizer.
115
- #
116
- # Parameters:
117
- # * text: The text to tokenize.
118
- #
119
- def tokenized text
120
- @tokenizer.tokenize text
121
- end
122
-
123
- # Gets sorted allocations for the tokens.
124
- #
125
- # This generates the possible allocations, sorted.
126
- #
127
- # TODO Smallify.
128
- #
129
- # TODO Rename: allocations
130
- #
131
- def sorted_allocations tokens # :nodoc:
132
- # Get the allocations.
133
- #
134
- # TODO Pass in reduce_to_amount (aka max_allocations)
135
- #
136
- # TODO uniq, score, sort in there
137
- #
138
- allocations = @indexes.allocations_for tokens
139
-
140
- # Callbacks.
141
- #
142
- # TODO Reduce before sort?
143
- #
144
- reduce allocations
145
- remove_from allocations
146
-
147
- # Remove double allocations.
148
- #
149
- allocations.uniq
150
-
151
- # Score the allocations using weights as bias.
152
- #
153
- allocations.calculate_score weights
154
-
155
- # Sort the allocations.
156
- # (allocations are sorted according to score, highest to lowest)
157
- #
158
- allocations.sort
159
-
160
- # Return the allocations.
161
- #
162
- allocations
163
- end
164
- def reduce allocations # :nodoc:
165
- allocations.reduce_to reduce_to_amount if reduce_to_amount
166
- end
167
-
168
- #
169
- #
170
- def remove_from allocations # :nodoc:
171
- allocations.remove identifiers_to_remove
172
- end
173
- #
174
- #
175
- def identifiers_to_remove # :nodoc:
176
- @identifiers_to_remove ||= []
177
- end
178
-
179
- # Display some nice information for the user.
180
- #
181
- def to_s
182
- s = "#{self.class}("
183
- s << @indexes.indexes.map(&:name).join(', ')
184
- s << ", weights: #{@weights}" unless @weights.empty?
185
- s << ")"
186
- s
187
- end
188
-
189
- end
190
- end
@@ -1,20 +0,0 @@
1
- module Query
2
-
3
- # This Query class performs full queries.
4
- #
5
- # It includes in its results:
6
- # * A count of results.
7
- # * All possible combinations with its weights.
8
- # * The top X result ids.
9
- #
10
- class Full < Base
11
-
12
- # Returns Results::Full as its result type.
13
- #
14
- def result_type
15
- Internals::Results::Full
16
- end
17
-
18
- end
19
-
20
- end
@@ -1,24 +0,0 @@
1
- module Query
2
-
3
- # This Query class performs live queries.
4
- #
5
- # It is useful for updating counters, or any job where you don't need the result ids.
6
- #
7
- # It includes in its results:
8
- # * A count of results.
9
- # * All possible combinations with its weights.
10
- #
11
- # But not:
12
- # * The top X result ids.
13
- #
14
- class Live < Base
15
-
16
- # Returns Results::Live as its result type.
17
- #
18
- def result_type
19
- Internals::Results::Live
20
- end
21
-
22
- end
23
-
24
- end
@@ -1,78 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Internals::Results::Full do
4
-
5
- describe "class accessors" do
6
- it "should have accessors for max_results" do
7
- max_results = stub :max_results
8
- old_results = described_class.max_results
9
- described_class.max_results = max_results
10
-
11
- described_class.max_results.should == max_results
12
-
13
- described_class.max_results = old_results
14
- end
15
- end
16
-
17
- describe 'initialize' do
18
- context 'without allocations' do
19
- it 'should not fail' do
20
- lambda {
21
- described_class.new
22
- }.should_not raise_error
23
- end
24
- it 'should set the allocations to allocations that are empty' do
25
- described_class.new.instance_variable_get(:@allocations).should be_empty
26
- end
27
- end
28
- context 'with allocations' do
29
- it 'should not fail' do
30
- lambda {
31
- described_class.new :some_allocations
32
- }.should_not raise_error
33
- end
34
- it 'should set the allocations to an empty array' do
35
- described_class.new(:unimportant, :some_allocations).instance_variable_get(:@allocations).should == :some_allocations
36
- end
37
- end
38
- end
39
-
40
- describe 'duration' do
41
- before(:each) do
42
- @results = described_class.new
43
- end
44
- it 'should return the set duration' do
45
- @results.duration = :some_duration
46
-
47
- @results.duration.should == :some_duration
48
- end
49
- it 'should return 0 as a default' do
50
- @results.duration.should == 0
51
- end
52
- end
53
-
54
- describe 'total' do
55
- it 'should delegate to allocations.total' do
56
- allocations = stub :allocations
57
- results = described_class.new nil, allocations
58
-
59
- allocations.should_receive(:total).once
60
-
61
- results.total
62
- end
63
- end
64
-
65
- describe 'prepare!' do
66
- before(:each) do
67
- @results = described_class.new
68
- @allocations = stub :allocations
69
- @results.stub! :allocations => @allocations
70
- end
71
- it 'should process' do
72
- @allocations.should_receive(:process!).once.with(20, 0).ordered
73
-
74
- @results.prepare!
75
- end
76
- end
77
-
78
- end
@@ -1,88 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Internals::Results::Live do
4
-
5
- it "is the right subclass" do
6
- described_class.should < Internals::Results::Base
7
- end
8
-
9
- it "logs correctly" do
10
- Time.stub! :now => Time.parse('2010-10-25 01:25:07')
11
-
12
- described_class.new.to_log('some query').should == '.|2010-10-25 01:25:07|0.000000|some query | 0| 0| 0|'
13
- end
14
-
15
- describe "class accessors" do
16
- it "should have accessors for max_results" do
17
- max_results = stub :max_results
18
- old_results = described_class.max_results
19
- described_class.max_results = max_results
20
-
21
- described_class.max_results.should == max_results
22
-
23
- described_class.max_results = old_results
24
- end
25
- end
26
-
27
- describe 'initialize' do
28
- context 'without allocations' do
29
- it 'should not fail' do
30
- lambda {
31
- described_class.new
32
- }.should_not raise_error
33
- end
34
- it 'should set the allocations to allocations that are empty' do
35
- described_class.new.instance_variable_get(:@allocations).should be_empty
36
- end
37
- end
38
- context 'with allocations' do
39
- it 'should not fail' do
40
- lambda {
41
- described_class.new :some_allocations
42
- }.should_not raise_error
43
- end
44
- it 'should set the allocations to an empty array' do
45
- described_class.new(:unimportant, :some_allocations).instance_variable_get(:@allocations).should == :some_allocations
46
- end
47
- end
48
- end
49
-
50
- describe 'duration' do
51
- before(:each) do
52
- @results = described_class.new
53
- end
54
- it 'should return the set duration' do
55
- @results.duration = :some_duration
56
-
57
- @results.duration.should == :some_duration
58
- end
59
- it 'should return 0 as a default' do
60
- @results.duration.should == 0
61
- end
62
- end
63
-
64
- describe 'total' do
65
- it 'should delegate to allocations.total' do
66
- allocations = stub :allocations
67
- results = described_class.new nil, allocations
68
-
69
- allocations.should_receive(:total).once
70
-
71
- results.total
72
- end
73
- end
74
-
75
- describe 'prepare!' do
76
- before(:each) do
77
- @results = described_class.new
78
- @allocations = stub :allocations
79
- @results.stub! :allocations => @allocations
80
- end
81
- it 'should generate truncate the ids fully' do
82
- @allocations.should_receive(:process!).once.with(0, 0).ordered
83
-
84
- @results.prepare!
85
- end
86
- end
87
-
88
- end
@@ -1,19 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Query::Full do
4
-
5
- before(:each) do
6
- @type = stub :type
7
- @index = stub :index, :indexed => @type
8
- end
9
-
10
- describe 'result_type' do
11
- before(:each) do
12
- @query = described_class.new @index
13
- end
14
- it "should return a specific type" do
15
- @query.result_type.should == Internals::Results::Full
16
- end
17
- end
18
-
19
- end
@@ -1,31 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Query::Live do
4
-
5
- before(:each) do
6
- @indexed = stub :indexed
7
- @index = stub :index, :indexed => @indexed
8
- end
9
-
10
- describe 'result_type' do
11
- before(:each) do
12
- @query = described_class.new @index
13
- end
14
- it "should return a specific type" do
15
- @query.result_type.should == Internals::Results::Live
16
- end
17
- end
18
-
19
- describe "execute" do
20
- before(:each) do
21
- @query = described_class.new @index
22
- end
23
- it "should get allocations" do
24
- @query.result_type.should_receive :from
25
- @query.should_receive(:sorted_allocations).once
26
-
27
- @query.execute 'some_query', 0
28
- end
29
- end
30
-
31
- end