picky 2.7.0 → 3.0.0.pre1
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.
- 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
|