picky 2.7.0 → 3.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/picky/adapters/rack/base.rb +20 -16
- data/lib/picky/adapters/rack/live_parameters.rb +28 -24
- data/lib/picky/adapters/rack/search.rb +67 -0
- data/lib/picky/adapters/rack.rb +27 -23
- data/lib/picky/application.rb +246 -236
- data/lib/picky/backend/base.rb +115 -119
- data/lib/picky/backend/file/basic.rb +102 -98
- data/lib/picky/backend/file/json.rb +27 -23
- data/lib/picky/backend/file/marshal.rb +32 -28
- data/lib/picky/backend/file/text.rb +45 -41
- data/lib/picky/backend/files.rb +19 -15
- data/lib/picky/backend/redis/basic.rb +76 -72
- data/lib/picky/backend/redis/list_hash.rb +40 -36
- data/lib/picky/backend/redis/string_hash.rb +30 -26
- data/lib/picky/backend/redis.rb +32 -28
- data/lib/picky/bundle.rb +82 -57
- data/lib/{bundling.rb → picky/bundling.rb} +0 -0
- data/lib/picky/calculations/location.rb +51 -47
- data/lib/picky/categories.rb +60 -56
- data/lib/picky/categories_indexed.rb +73 -82
- data/lib/picky/categories_indexing.rb +12 -8
- data/lib/picky/category.rb +109 -120
- data/lib/picky/category_indexed.rb +39 -41
- data/lib/picky/category_indexing.rb +123 -125
- data/lib/picky/character_substituters/west_european.rb +32 -26
- data/lib/{constants.rb → picky/constants.rb} +0 -0
- data/lib/picky/cores.rb +96 -92
- data/lib/{deployment.rb → picky/deployment.rb} +0 -0
- data/lib/picky/frontend_adapters/rack.rb +133 -118
- data/lib/picky/generators/aliases.rb +5 -3
- data/lib/picky/generators/base.rb +11 -7
- data/lib/picky/generators/partial/default.rb +7 -3
- data/lib/picky/generators/partial/none.rb +24 -20
- data/lib/picky/generators/partial/strategy.rb +20 -16
- data/lib/picky/generators/partial/substring.rb +94 -90
- data/lib/picky/generators/partial_generator.rb +11 -7
- data/lib/picky/generators/similarity/default.rb +9 -5
- data/lib/picky/generators/similarity/double_metaphone.rb +20 -16
- data/lib/picky/generators/similarity/metaphone.rb +20 -16
- data/lib/picky/generators/similarity/none.rb +23 -19
- data/lib/picky/generators/similarity/phonetic.rb +49 -45
- data/lib/picky/generators/similarity/soundex.rb +20 -16
- data/lib/picky/generators/similarity/strategy.rb +10 -6
- data/lib/picky/generators/similarity_generator.rb +11 -7
- data/lib/picky/generators/strategy.rb +14 -10
- data/lib/picky/generators/weights/default.rb +9 -5
- data/lib/picky/generators/weights/logarithmic.rb +30 -26
- data/lib/picky/generators/weights/strategy.rb +10 -6
- data/lib/picky/generators/weights_generator.rb +11 -7
- data/lib/picky/helpers/measuring.rb +20 -16
- data/lib/picky/indexed/bundle/base.rb +39 -37
- data/lib/picky/indexed/bundle/memory.rb +68 -64
- data/lib/picky/indexed/bundle/redis.rb +73 -69
- data/lib/picky/indexed/wrappers/bundle/calculation.rb +26 -22
- data/lib/picky/indexed/wrappers/bundle/location.rb +30 -26
- data/lib/picky/indexed/wrappers/bundle/wrapper.rb +36 -32
- data/lib/picky/indexed/wrappers/category/location.rb +17 -13
- data/lib/picky/indexed/wrappers/exact_first.rb +46 -42
- data/lib/picky/indexers/base.rb +26 -22
- data/lib/picky/indexers/parallel.rb +62 -58
- data/lib/picky/indexers/serial.rb +41 -37
- data/lib/picky/indexes/index.rb +400 -0
- data/lib/picky/indexes/index_indexed.rb +24 -0
- data/lib/picky/indexes/index_indexing.rb +138 -0
- data/lib/picky/indexes/memory.rb +20 -0
- data/lib/picky/indexes/redis.rb +20 -0
- data/lib/picky/indexes.rb +68 -61
- data/lib/picky/indexes_indexed.rb +16 -12
- data/lib/picky/indexes_indexing.rb +41 -37
- data/lib/picky/indexing/bundle/base.rb +216 -205
- data/lib/picky/indexing/bundle/memory.rb +16 -11
- data/lib/picky/indexing/bundle/redis.rb +14 -12
- data/lib/picky/indexing/wrappers/category/location.rb +17 -13
- data/lib/picky/interfaces/live_parameters.rb +159 -154
- data/lib/picky/loader.rb +267 -304
- data/lib/picky/loggers/search.rb +20 -13
- data/lib/picky/no_source_specified_exception.rb +7 -3
- data/lib/picky/performant.rb +6 -2
- data/lib/picky/query/allocation.rb +71 -67
- data/lib/picky/query/allocations.rb +99 -94
- data/lib/picky/query/combination.rb +70 -66
- data/lib/picky/query/combinations/base.rb +56 -52
- data/lib/picky/query/combinations/memory.rb +36 -32
- data/lib/picky/query/combinations/redis.rb +66 -62
- data/lib/picky/query/indexes.rb +175 -160
- data/lib/picky/query/qualifier_category_mapper.rb +43 -0
- data/lib/picky/query/token.rb +165 -172
- data/lib/picky/query/tokens.rb +86 -82
- data/lib/picky/query/weights.rb +44 -48
- data/lib/picky/query.rb +5 -1
- data/lib/picky/rack/harakiri.rb +51 -47
- data/lib/picky/results.rb +81 -77
- data/lib/picky/search.rb +169 -158
- data/lib/picky/sinatra.rb +34 -0
- data/lib/picky/sources/base.rb +73 -70
- data/lib/picky/sources/couch.rb +61 -57
- data/lib/picky/sources/csv.rb +68 -64
- data/lib/picky/sources/db.rb +139 -135
- data/lib/picky/sources/delicious.rb +52 -48
- data/lib/picky/sources/mongo.rb +68 -63
- data/lib/picky/sources/wrappers/base.rb +20 -16
- data/lib/picky/sources/wrappers/location.rb +37 -33
- data/lib/picky/statistics.rb +46 -43
- data/lib/picky/tasks.rb +3 -0
- data/lib/picky/tokenizers/base.rb +192 -187
- data/lib/picky/tokenizers/index.rb +25 -21
- data/lib/picky/tokenizers/location.rb +33 -29
- data/lib/picky/tokenizers/query.rb +49 -43
- data/lib/picky.rb +21 -13
- data/lib/tasks/application.rake +1 -1
- data/lib/tasks/index.rake +3 -3
- data/lib/tasks/routes.rake +1 -1
- data/lib/tasks/server.rake +1 -1
- data/spec/lib/adapters/rack/base_spec.rb +1 -1
- data/spec/lib/adapters/rack/live_parameters_spec.rb +1 -1
- data/spec/lib/adapters/rack/query_spec.rb +1 -1
- data/spec/lib/application_spec.rb +39 -32
- data/spec/lib/backend/file/basic_spec.rb +2 -2
- data/spec/lib/backend/file/json_spec.rb +2 -2
- data/spec/lib/backend/file/marshal_spec.rb +2 -2
- data/spec/lib/backend/file/text_spec.rb +1 -1
- data/spec/lib/backend/files_spec.rb +14 -24
- data/spec/lib/backend/redis/basic_spec.rb +2 -2
- data/spec/lib/backend/redis/list_hash_spec.rb +3 -3
- data/spec/lib/backend/redis/string_hash_spec.rb +3 -3
- data/spec/lib/backend/redis_spec.rb +20 -13
- data/spec/lib/calculations/location_spec.rb +1 -1
- data/spec/lib/categories_indexed_spec.rb +16 -34
- data/spec/lib/category_indexed_spec.rb +9 -27
- data/spec/lib/category_indexing_spec.rb +2 -3
- data/spec/lib/category_spec.rb +10 -10
- data/spec/lib/character_substituters/west_european_spec.rb +6 -5
- data/spec/lib/cores_spec.rb +17 -17
- data/spec/lib/extensions/symbol_spec.rb +15 -1
- data/spec/lib/frontend_adapters/rack_spec.rb +20 -20
- data/spec/lib/generators/aliases_spec.rb +3 -3
- data/spec/lib/generators/cacher_strategy_spec.rb +1 -1
- data/spec/lib/generators/partial/default_spec.rb +3 -3
- data/spec/lib/generators/partial/none_spec.rb +2 -2
- data/spec/lib/generators/partial/substring_spec.rb +1 -1
- data/spec/lib/generators/partial_generator_spec.rb +3 -3
- data/spec/lib/generators/similarity/double_metaphone_spec.rb +1 -1
- data/spec/lib/generators/similarity/metaphone_spec.rb +1 -1
- data/spec/lib/generators/similarity/none_spec.rb +1 -1
- data/spec/lib/generators/similarity/phonetic_spec.rb +1 -1
- data/spec/lib/generators/similarity/soundex_spec.rb +1 -1
- data/spec/lib/generators/similarity_generator_spec.rb +2 -2
- data/spec/lib/generators/weights/logarithmic_spec.rb +1 -1
- data/spec/lib/generators/weights_generator_spec.rb +1 -1
- data/spec/lib/helpers/measuring_spec.rb +2 -2
- data/spec/lib/indexed/bundle/memory_spec.rb +6 -6
- data/spec/lib/indexed/bundle/redis_spec.rb +4 -4
- data/spec/lib/indexed/wrappers/bundle/calculation_spec.rb +2 -3
- data/spec/lib/indexed/wrappers/bundle/wrapper_spec.rb +2 -2
- data/spec/lib/indexed/wrappers/exact_first_spec.rb +5 -5
- data/spec/lib/indexers/base_spec.rb +1 -1
- data/spec/lib/indexers/parallel_spec.rb +1 -1
- data/spec/lib/indexers/serial_spec.rb +1 -1
- data/spec/lib/{index/base_indexed_spec.rb → indexes/index_indexed_spec.rb} +3 -3
- data/spec/lib/{index/base_indexing_spec.rb → indexes/index_indexing_spec.rb} +19 -2
- data/spec/lib/{index/base_spec.rb → indexes/index_spec.rb} +6 -25
- data/spec/lib/{index → indexes}/redis_spec.rb +1 -1
- data/spec/lib/indexes_class_spec.rb +2 -2
- data/spec/lib/indexes_indexed_spec.rb +1 -1
- data/spec/lib/indexes_indexing_spec.rb +1 -1
- data/spec/lib/indexes_spec.rb +1 -1
- data/spec/lib/indexing/bundle/base_spec.rb +7 -5
- data/spec/lib/indexing/bundle/memory_partial_generation_speed_spec.rb +4 -4
- data/spec/lib/indexing/bundle/memory_spec.rb +15 -15
- data/spec/lib/indexing/bundle/redis_spec.rb +9 -9
- data/spec/lib/interfaces/live_parameters_spec.rb +5 -5
- data/spec/lib/loader_spec.rb +17 -19
- data/spec/lib/loggers/search_spec.rb +2 -2
- data/spec/lib/query/allocation_spec.rb +1 -1
- data/spec/lib/query/allocations_spec.rb +1 -1
- data/spec/lib/query/combination_spec.rb +4 -4
- data/spec/lib/query/combinations/base_spec.rb +1 -1
- data/spec/lib/query/combinations/memory_spec.rb +1 -1
- data/spec/lib/query/combinations/redis_spec.rb +1 -1
- data/spec/lib/query/indexes_spec.rb +7 -2
- data/spec/lib/query/qualifier_category_mapper_spec.rb +34 -0
- data/spec/lib/query/token_spec.rb +32 -53
- data/spec/lib/query/tokens_spec.rb +30 -35
- data/spec/lib/query/weights_spec.rb +16 -16
- data/spec/lib/rack/harakiri_spec.rb +5 -5
- data/spec/lib/results_spec.rb +1 -1
- data/spec/lib/search_spec.rb +24 -22
- data/spec/lib/sinatra_spec.rb +36 -0
- data/spec/lib/sources/base_spec.rb +1 -1
- data/spec/lib/sources/couch_spec.rb +9 -9
- data/spec/lib/sources/csv_spec.rb +7 -7
- data/spec/lib/sources/db_spec.rb +2 -2
- data/spec/lib/sources/delicious_spec.rb +5 -5
- data/spec/lib/sources/mongo_spec.rb +7 -7
- data/spec/lib/sources/wrappers/base_spec.rb +2 -2
- data/spec/lib/sources/wrappers/location_spec.rb +1 -1
- data/spec/lib/statistics_spec.rb +1 -1
- data/spec/lib/tokenizers/base_spec.rb +2 -2
- data/spec/lib/tokenizers/index_spec.rb +1 -1
- data/spec/lib/tokenizers/query_spec.rb +1 -1
- metadata +30 -30
- data/lib/picky/adapters/rack/query.rb +0 -65
- data/lib/picky/index/base.rb +0 -409
- data/lib/picky/index/base_indexed.rb +0 -29
- data/lib/picky/index/base_indexing.rb +0 -127
- data/lib/picky/index/memory.rb +0 -16
- data/lib/picky/index/redis.rb +0 -16
- data/lib/picky/query/qualifiers.rb +0 -76
- data/lib/picky/query/solr.rb +0 -60
- data/lib/picky/signals.rb +0 -8
- data/lib/picky-tasks.rb +0 -6
- data/lib/tasks/spec.rake +0 -11
- data/spec/lib/query/qualifiers_spec.rb +0 -31
data/lib/picky/rack/harakiri.rb
CHANGED
@@ -1,55 +1,59 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
# Rack::Harakiri.after = 100
|
8
|
-
# use Rack::Harakiri
|
9
|
-
#
|
10
|
-
# Then the Unicorn will commit suicide after 100 requests (50 is the default).
|
11
|
-
#
|
12
|
-
# The Master Unicorn process forks a new child Unicorn to replace the old one.
|
13
|
-
#
|
14
|
-
class Harakiri
|
15
|
-
|
16
|
-
# Set the amount of requests before the Unicorn commits Harakiri.
|
1
|
+
module Picky
|
2
|
+
|
3
|
+
module Rack # :nodoc:
|
4
|
+
|
5
|
+
# Simple Rack Middleware to kill Unicorns after X requests.
|
17
6
|
#
|
18
|
-
|
19
|
-
|
20
|
-
def initialize app
|
21
|
-
@app = app
|
22
|
-
|
23
|
-
@requests = 0
|
24
|
-
@quit_after_requests = self.class.after || 50
|
25
|
-
end
|
26
|
-
|
27
|
-
# #call interface method.
|
28
|
-
#
|
29
|
-
# Harakiri is a middleware, so it delegates the the app or
|
30
|
-
# the next middleware after checking if it is time to honorably retire.
|
7
|
+
# Use as follows in e.g. your rackup File:
|
31
8
|
#
|
32
|
-
|
33
|
-
|
34
|
-
@app.call env
|
35
|
-
end
|
36
|
-
|
37
|
-
# Checks to see if it is time to honorably retire.
|
9
|
+
# Rack::Harakiri.after = 100
|
10
|
+
# use Rack::Harakiri
|
38
11
|
#
|
39
|
-
#
|
12
|
+
# Then the Unicorn will commit suicide after 100 requests (50 is the default).
|
40
13
|
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
def harakiri
|
44
|
-
@requests = @requests + 1
|
45
|
-
Process.kill(:QUIT, Process.pid) if harakiri?
|
46
|
-
end
|
47
|
-
|
48
|
-
# Is it time to honorably retire?
|
14
|
+
# The Master Unicorn process forks a new child Unicorn to replace the old one.
|
49
15
|
#
|
50
|
-
|
51
|
-
|
16
|
+
class Harakiri
|
17
|
+
|
18
|
+
# Set the amount of requests before the Unicorn commits Harakiri.
|
19
|
+
#
|
20
|
+
cattr_accessor :after
|
21
|
+
|
22
|
+
def initialize app
|
23
|
+
@app = app
|
24
|
+
|
25
|
+
@requests = 0
|
26
|
+
@quit_after_requests = self.class.after || 50
|
27
|
+
end
|
28
|
+
|
29
|
+
# #call interface method.
|
30
|
+
#
|
31
|
+
# Harakiri is a middleware, so it delegates the the app or
|
32
|
+
# the next middleware after checking if it is time to honorably retire.
|
33
|
+
#
|
34
|
+
def call env
|
35
|
+
harakiri
|
36
|
+
@app.call env
|
37
|
+
end
|
38
|
+
|
39
|
+
# Checks to see if it is time to honorably retire.
|
40
|
+
#
|
41
|
+
# If yes, kills itself (Unicorn will answer the request, honorably).
|
42
|
+
#
|
43
|
+
# Note: Sends its process a QUIT signal if it is time.
|
44
|
+
#
|
45
|
+
def harakiri
|
46
|
+
@requests = @requests + 1
|
47
|
+
Process.kill(:QUIT, Process.pid) if harakiri?
|
48
|
+
end
|
49
|
+
|
50
|
+
# Is it time to honorably retire?
|
51
|
+
#
|
52
|
+
def harakiri?
|
53
|
+
@requests >= @quit_after_requests
|
54
|
+
end
|
55
|
+
|
52
56
|
end
|
53
|
-
|
54
57
|
end
|
58
|
+
|
55
59
|
end
|
data/lib/picky/results.rb
CHANGED
@@ -1,89 +1,93 @@
|
|
1
|
-
|
2
|
-
# is called on it to get a string for the answer.
|
3
|
-
#
|
4
|
-
class Results
|
1
|
+
module Picky
|
5
2
|
|
6
|
-
#
|
3
|
+
# This is the internal results object. Usually, to_marshal, or to_json
|
4
|
+
# is called on it to get a string for the answer.
|
7
5
|
#
|
8
|
-
|
9
|
-
attr_reader :allocations, :offset, :amount
|
6
|
+
class Results
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@amount = amount
|
16
|
-
@allocations = allocations
|
17
|
-
end
|
18
|
-
# Create new results and calculate the ids.
|
19
|
-
#
|
20
|
-
def self.from amount, offset, allocations
|
21
|
-
results = new amount, offset, allocations
|
22
|
-
results.prepare!
|
23
|
-
results
|
24
|
-
end
|
8
|
+
# Duration is set externally by the query.
|
9
|
+
#
|
10
|
+
attr_writer :duration
|
11
|
+
attr_reader :allocations, :offset, :amount
|
25
12
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
#
|
41
|
-
def to_json options = {}
|
42
|
-
serialize.to_json options
|
43
|
-
end
|
13
|
+
# Takes instances of Query::Allocations as param.
|
14
|
+
#
|
15
|
+
def initialize amount = 0, offset = 0, allocations = Query::Allocations.new
|
16
|
+
@offset = offset
|
17
|
+
@amount = amount
|
18
|
+
@allocations = allocations
|
19
|
+
end
|
20
|
+
# Create new results and calculate the ids.
|
21
|
+
#
|
22
|
+
def self.from amount, offset, allocations
|
23
|
+
results = new amount, offset, allocations
|
24
|
+
results.prepare!
|
25
|
+
results
|
26
|
+
end
|
44
27
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
28
|
+
# Returns a hash with the allocations, offset, duration and total.
|
29
|
+
#
|
30
|
+
def serialize
|
31
|
+
{ allocations: allocations.to_result,
|
32
|
+
offset: offset,
|
33
|
+
duration: duration,
|
34
|
+
total: total }
|
35
|
+
end
|
36
|
+
# Convert to json format.
|
37
|
+
#
|
38
|
+
def to_json options = {}
|
39
|
+
serialize.to_json options
|
40
|
+
end
|
41
|
+
# The default format is json.
|
42
|
+
#
|
43
|
+
alias to_response to_json
|
53
44
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
#
|
63
|
-
def total
|
64
|
-
@total || @total = allocations.total || 0
|
65
|
-
end
|
45
|
+
# This starts the actual processing.
|
46
|
+
#
|
47
|
+
# Without this, the allocations are not processed,
|
48
|
+
# and no ids are calculated.
|
49
|
+
#
|
50
|
+
def prepare!
|
51
|
+
allocations.process! amount, offset
|
52
|
+
end
|
66
53
|
|
67
|
-
|
68
|
-
|
54
|
+
# Duration default is 0.
|
55
|
+
#
|
56
|
+
def duration
|
57
|
+
@duration || 0
|
58
|
+
end
|
59
|
+
# The total results. Delegates to the allocations.
|
60
|
+
#
|
61
|
+
# Caches.
|
62
|
+
#
|
63
|
+
def total
|
64
|
+
@total || @total = allocations.total || 0
|
65
|
+
end
|
69
66
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
allocations.
|
74
|
-
|
67
|
+
# Convenience methods.
|
68
|
+
#
|
69
|
+
|
70
|
+
# Delegates to allocations.
|
71
|
+
#
|
72
|
+
def ids amount = 20
|
73
|
+
allocations.ids amount
|
74
|
+
end
|
75
|
+
|
76
|
+
# Human readable log.
|
77
|
+
#
|
78
|
+
# TODO Should this be to_s? (And it should also hold the original query?)
|
79
|
+
#
|
80
|
+
def to_log query
|
81
|
+
"#{log_type}|#{Time.now.to_s(:db)}|#{'%8f' % duration}|#{'%-50s' % query}|#{'%8d' % total}|#{'%4d' % offset}|#{'%2d' % allocations.size}|"
|
82
|
+
end
|
83
|
+
# The first character in the blog designates what type of query it is.
|
84
|
+
#
|
85
|
+
# No calculated ids means: No results.
|
86
|
+
#
|
87
|
+
def log_type
|
88
|
+
amount.zero?? :'.' : :'>'
|
89
|
+
end
|
75
90
|
|
76
|
-
# Human readable log.
|
77
|
-
#
|
78
|
-
def to_log query
|
79
|
-
"#{log_type}|#{Time.now.to_s(:db)}|#{'%8f' % duration}|#{'%-50s' % query}|#{'%8d' % total}|#{'%4d' % offset}|#{'%2d' % allocations.size}|"
|
80
|
-
end
|
81
|
-
# The first character in the blog designates what type of query it is.
|
82
|
-
#
|
83
|
-
# No calculated ids means: No results.
|
84
|
-
#
|
85
|
-
def log_type
|
86
|
-
amount.zero?? :'.' : :'>'
|
87
91
|
end
|
88
92
|
|
89
93
|
end
|
data/lib/picky/search.rb
CHANGED
@@ -1,176 +1,187 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
1
|
+
module Picky
|
2
|
+
|
3
|
+
# = Picky Queries
|
4
|
+
#
|
5
|
+
# A Picky Search is an object which:
|
6
|
+
# * holds one or more indexes
|
7
|
+
# * offers an interface to query these indexes.
|
8
|
+
#
|
9
|
+
# You connect URL paths to indexes via a Query.
|
10
|
+
#
|
11
|
+
# We recommend not to use this directly, but connect it to an URL and query through one of these
|
12
|
+
# (Protip: Use "curl 'localhost:8080/query/path?query=exampletext')" in a Terminal.
|
13
|
+
#
|
14
|
+
class Search
|
15
|
+
|
16
|
+
include Helpers::Measuring
|
17
|
+
|
18
|
+
attr_reader :indexes
|
19
|
+
attr_writer :tokenizer
|
20
|
+
attr_accessor :weights
|
21
|
+
|
22
|
+
# Takes:
|
23
|
+
# * A number of indexes
|
24
|
+
# * Options hash (optional) with:
|
25
|
+
# * tokenizer: Tokenizers::Query.default by default.
|
26
|
+
# * weights: A hash of weights, or a Query::Weights object.
|
27
|
+
#
|
28
|
+
# TODO Add identifiers_to_remove (rename) and reduce_allocations_to_amount (rename).
|
29
|
+
# TODO categories_to_remove ?
|
30
|
+
#
|
31
|
+
# It is also possible to define the tokenizer and weights like so.
|
32
|
+
# Example:
|
33
|
+
# Search.new(index1, index2, index3) do
|
34
|
+
# searching removes_characters: /[^a-z]/, etc.
|
35
|
+
# weights [:author, :title] => +3, [:title, :isbn] => +1
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
def initialize *index_definitions
|
39
|
+
options = Hash === index_definitions.last ? index_definitions.pop : {}
|
40
|
+
|
41
|
+
@indexes = Query::Indexes.new *index_definitions, combinations_type_for(index_definitions)
|
42
|
+
searching options[:tokenizer]
|
43
|
+
boost options[:weights]
|
44
|
+
|
45
|
+
instance_eval(&Proc.new) if block_given?
|
46
|
+
end
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
options && Tokenizers::Query.new(options)
|
48
|
+
# Example:
|
49
|
+
# Search.new(index1, index2, index3) do
|
50
|
+
# searching removes_characters: /[^a-z]/, etc.
|
51
|
+
# weights [:author, :title] => +3, [:title, :isbn] => +1
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
def searching options
|
55
|
+
@tokenizer = if options.respond_to?(:tokenize)
|
56
|
+
options
|
57
|
+
else
|
58
|
+
options && Tokenizers::Query.new(options)
|
59
|
+
end
|
59
60
|
end
|
60
|
-
end
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
# Returns the tokenizer if set or if not, the query tokenizer.
|
63
|
+
#
|
64
|
+
def tokenizer
|
65
|
+
@tokenizer || Tokenizers::Query.default
|
66
|
+
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
@weights = Hash === weights ? Query::Weights.new(weights) : weights
|
79
|
-
end
|
68
|
+
# Example:
|
69
|
+
# Search.new(index1, index2, index3) do
|
70
|
+
# searching removes_characters: /[^a-z]/, etc.
|
71
|
+
# boost [:author, :title] => +3, [:title, :isbn] => +1
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
def boost options
|
75
|
+
weights = options || Query::Weights.new
|
76
|
+
@weights = Hash === weights ? Query::Weights.new(weights) : weights
|
77
|
+
end
|
80
78
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
raise_different(index_types) if index_types.size > 1
|
97
|
-
!index_types.empty? && @@mapping[*index_types] || Query::Combinations::Memory
|
98
|
-
end
|
99
|
-
# Currently it isn't possible using Memory and Redis etc.
|
100
|
-
# indexes in the same query index group.
|
101
|
-
#
|
102
|
-
class DifferentTypesError < StandardError
|
103
|
-
def initialize types
|
104
|
-
@types = types
|
79
|
+
# Returns the right combinations strategy for
|
80
|
+
# a number of query indexes.
|
81
|
+
#
|
82
|
+
# Currently it isn't possible using Memory and Redis etc.
|
83
|
+
# indexes in the same query index group.
|
84
|
+
#
|
85
|
+
# Picky will raise a Query::Indexes::DifferentTypesError.
|
86
|
+
#
|
87
|
+
@@mapping = {
|
88
|
+
Indexes::Memory => Query::Combinations::Memory,
|
89
|
+
Indexes::Redis => Query::Combinations::Redis
|
90
|
+
}
|
91
|
+
def combinations_type_for index_definitions_ary
|
92
|
+
index_types = extract_index_types index_definitions_ary
|
93
|
+
!index_types.empty? && @@mapping[*index_types] || Query::Combinations::Memory
|
105
94
|
end
|
106
|
-
def
|
107
|
-
|
95
|
+
def extract_index_types index_definitions_ary
|
96
|
+
index_types = index_definitions_ary.map(&:class)
|
97
|
+
index_types.uniq!
|
98
|
+
check_index_types index_types
|
99
|
+
index_types
|
100
|
+
end
|
101
|
+
def check_index_types index_types
|
102
|
+
raise_different index_types if index_types.size > 1
|
103
|
+
end
|
104
|
+
# Currently it isn't possible using Memory and Redis etc.
|
105
|
+
# indexes in the same query index group.
|
106
|
+
#
|
107
|
+
class DifferentTypesError < StandardError
|
108
|
+
def initialize types
|
109
|
+
@types = types
|
110
|
+
end
|
111
|
+
def to_s
|
112
|
+
"Currently it isn't possible to mix #{@types.join(" and ")} Indexes in the same Search instance."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
def raise_different index_types
|
116
|
+
raise DifferentTypesError.new(index_types)
|
108
117
|
end
|
109
|
-
end
|
110
|
-
def raise_different index_types
|
111
|
-
raise DifferentTypesError.new(index_types)
|
112
|
-
end
|
113
|
-
|
114
|
-
# This is the main entry point for a query.
|
115
|
-
# Use this in specs and also for running queries.
|
116
|
-
#
|
117
|
-
# Parameters:
|
118
|
-
# * text: The search text.
|
119
|
-
# * ids = 20: _optional_ The amount of ids to calculate (with offset).
|
120
|
-
# * offset = 0: _optional_ The offset from which position to return the ids. Useful for pagination.
|
121
|
-
#
|
122
|
-
# Note: The Rack adapter calls this method after unravelling the HTTP request.
|
123
|
-
#
|
124
|
-
def search_with_text text, ids = 20, offset = 0
|
125
|
-
search tokenized(text), ids, offset
|
126
|
-
end
|
127
118
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
119
|
+
# This is the main entry point for a query.
|
120
|
+
# Use this in specs and also for running queries.
|
121
|
+
#
|
122
|
+
# Parameters:
|
123
|
+
# * text: The search text.
|
124
|
+
# * ids = 20: _optional_ The amount of ids to calculate (with offset).
|
125
|
+
# * offset = 0: _optional_ The offset from which position to return the ids. Useful for pagination.
|
126
|
+
#
|
127
|
+
# Note: The Rack adapter calls this method after unravelling the HTTP request.
|
128
|
+
#
|
129
|
+
# TODO Rename to search.
|
130
|
+
#
|
131
|
+
def search_with_text text, ids = 20, offset = 0
|
132
|
+
search tokenized(text), ids.to_i, offset.to_i
|
133
|
+
end
|
134
134
|
|
135
|
-
|
136
|
-
|
135
|
+
# Runs the actual search using Query::Tokens.
|
136
|
+
#
|
137
|
+
# Note: Internal method, use #search_with_text.
|
138
|
+
#
|
139
|
+
# TODO Rename to search_with.
|
140
|
+
#
|
141
|
+
def search tokens, ids = 20, offset = 0
|
142
|
+
results = nil
|
143
|
+
|
144
|
+
duration = timed do
|
145
|
+
results = execute tokens, ids, offset
|
146
|
+
end
|
147
|
+
results.duration = duration.round 6
|
148
|
+
|
149
|
+
results
|
137
150
|
end
|
138
|
-
results.duration = duration.round 6
|
139
151
|
|
140
|
-
|
141
|
-
|
152
|
+
# Execute a search using Query::Tokens.
|
153
|
+
#
|
154
|
+
# Note: Internal method, use #search_with_text.
|
155
|
+
#
|
156
|
+
def execute tokens, ids, offset
|
157
|
+
Results.from ids, offset, sorted_allocations(tokens)
|
158
|
+
end
|
142
159
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
160
|
+
# Delegates the tokenizing to the query tokenizer.
|
161
|
+
#
|
162
|
+
# Parameters:
|
163
|
+
# * text: The text to tokenize.
|
164
|
+
#
|
165
|
+
def tokenized text
|
166
|
+
tokenizer.tokenize text
|
167
|
+
end
|
150
168
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
def tokenized text
|
157
|
-
tokenizer.tokenize text
|
158
|
-
end
|
169
|
+
# Gets sorted allocations for the tokens.
|
170
|
+
#
|
171
|
+
def sorted_allocations tokens # :nodoc:
|
172
|
+
indexes.prepared_allocations_for tokens, weights
|
173
|
+
end
|
159
174
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
175
|
+
# Display some nice information for the user.
|
176
|
+
#
|
177
|
+
def to_s
|
178
|
+
s = "#{self.class}("
|
179
|
+
s << @indexes.indexes.map(&:name).join(', ')
|
180
|
+
s << ", weights: #{@weights}" unless @weights.empty?
|
181
|
+
s << ")"
|
182
|
+
s
|
183
|
+
end
|
165
184
|
|
166
|
-
# Display some nice information for the user.
|
167
|
-
#
|
168
|
-
def to_s
|
169
|
-
s = "#{self.class}("
|
170
|
-
s << @indexes.indexes.map(&:name).join(', ')
|
171
|
-
s << ", weights: #{@weights}" unless @weights.empty?
|
172
|
-
s << ")"
|
173
|
-
s
|
174
185
|
end
|
175
186
|
|
176
187
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Picky
|
2
|
+
|
3
|
+
# This Module is used to install delegator methods
|
4
|
+
# into the class for use with Sinatra.
|
5
|
+
#
|
6
|
+
module Sinatra
|
7
|
+
|
8
|
+
# Privatizes app file methods.
|
9
|
+
#
|
10
|
+
def self.extended into
|
11
|
+
private :indexing, :searching
|
12
|
+
end
|
13
|
+
|
14
|
+
# Delegates to Application.
|
15
|
+
#
|
16
|
+
def indexing options
|
17
|
+
Application.indexing options
|
18
|
+
end
|
19
|
+
|
20
|
+
# Delegates to Application.
|
21
|
+
#
|
22
|
+
def searching options
|
23
|
+
Application.searching options
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check if toplevel Sinatra picky methods need to be installed.
|
31
|
+
#
|
32
|
+
if private_methods.include? :get
|
33
|
+
extend Picky::Sinatra
|
34
|
+
end
|