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.
Files changed (213) hide show
  1. data/lib/picky/adapters/rack/base.rb +20 -16
  2. data/lib/picky/adapters/rack/live_parameters.rb +28 -24
  3. data/lib/picky/adapters/rack/search.rb +67 -0
  4. data/lib/picky/adapters/rack.rb +27 -23
  5. data/lib/picky/application.rb +246 -236
  6. data/lib/picky/backend/base.rb +115 -119
  7. data/lib/picky/backend/file/basic.rb +102 -98
  8. data/lib/picky/backend/file/json.rb +27 -23
  9. data/lib/picky/backend/file/marshal.rb +32 -28
  10. data/lib/picky/backend/file/text.rb +45 -41
  11. data/lib/picky/backend/files.rb +19 -15
  12. data/lib/picky/backend/redis/basic.rb +76 -72
  13. data/lib/picky/backend/redis/list_hash.rb +40 -36
  14. data/lib/picky/backend/redis/string_hash.rb +30 -26
  15. data/lib/picky/backend/redis.rb +32 -28
  16. data/lib/picky/bundle.rb +82 -57
  17. data/lib/{bundling.rb → picky/bundling.rb} +0 -0
  18. data/lib/picky/calculations/location.rb +51 -47
  19. data/lib/picky/categories.rb +60 -56
  20. data/lib/picky/categories_indexed.rb +73 -82
  21. data/lib/picky/categories_indexing.rb +12 -8
  22. data/lib/picky/category.rb +109 -120
  23. data/lib/picky/category_indexed.rb +39 -41
  24. data/lib/picky/category_indexing.rb +123 -125
  25. data/lib/picky/character_substituters/west_european.rb +32 -26
  26. data/lib/{constants.rb → picky/constants.rb} +0 -0
  27. data/lib/picky/cores.rb +96 -92
  28. data/lib/{deployment.rb → picky/deployment.rb} +0 -0
  29. data/lib/picky/frontend_adapters/rack.rb +133 -118
  30. data/lib/picky/generators/aliases.rb +5 -3
  31. data/lib/picky/generators/base.rb +11 -7
  32. data/lib/picky/generators/partial/default.rb +7 -3
  33. data/lib/picky/generators/partial/none.rb +24 -20
  34. data/lib/picky/generators/partial/strategy.rb +20 -16
  35. data/lib/picky/generators/partial/substring.rb +94 -90
  36. data/lib/picky/generators/partial_generator.rb +11 -7
  37. data/lib/picky/generators/similarity/default.rb +9 -5
  38. data/lib/picky/generators/similarity/double_metaphone.rb +20 -16
  39. data/lib/picky/generators/similarity/metaphone.rb +20 -16
  40. data/lib/picky/generators/similarity/none.rb +23 -19
  41. data/lib/picky/generators/similarity/phonetic.rb +49 -45
  42. data/lib/picky/generators/similarity/soundex.rb +20 -16
  43. data/lib/picky/generators/similarity/strategy.rb +10 -6
  44. data/lib/picky/generators/similarity_generator.rb +11 -7
  45. data/lib/picky/generators/strategy.rb +14 -10
  46. data/lib/picky/generators/weights/default.rb +9 -5
  47. data/lib/picky/generators/weights/logarithmic.rb +30 -26
  48. data/lib/picky/generators/weights/strategy.rb +10 -6
  49. data/lib/picky/generators/weights_generator.rb +11 -7
  50. data/lib/picky/helpers/measuring.rb +20 -16
  51. data/lib/picky/indexed/bundle/base.rb +39 -37
  52. data/lib/picky/indexed/bundle/memory.rb +68 -64
  53. data/lib/picky/indexed/bundle/redis.rb +73 -69
  54. data/lib/picky/indexed/wrappers/bundle/calculation.rb +26 -22
  55. data/lib/picky/indexed/wrappers/bundle/location.rb +30 -26
  56. data/lib/picky/indexed/wrappers/bundle/wrapper.rb +36 -32
  57. data/lib/picky/indexed/wrappers/category/location.rb +17 -13
  58. data/lib/picky/indexed/wrappers/exact_first.rb +46 -42
  59. data/lib/picky/indexers/base.rb +26 -22
  60. data/lib/picky/indexers/parallel.rb +62 -58
  61. data/lib/picky/indexers/serial.rb +41 -37
  62. data/lib/picky/indexes/index.rb +400 -0
  63. data/lib/picky/indexes/index_indexed.rb +24 -0
  64. data/lib/picky/indexes/index_indexing.rb +138 -0
  65. data/lib/picky/indexes/memory.rb +20 -0
  66. data/lib/picky/indexes/redis.rb +20 -0
  67. data/lib/picky/indexes.rb +68 -61
  68. data/lib/picky/indexes_indexed.rb +16 -12
  69. data/lib/picky/indexes_indexing.rb +41 -37
  70. data/lib/picky/indexing/bundle/base.rb +216 -205
  71. data/lib/picky/indexing/bundle/memory.rb +16 -11
  72. data/lib/picky/indexing/bundle/redis.rb +14 -12
  73. data/lib/picky/indexing/wrappers/category/location.rb +17 -13
  74. data/lib/picky/interfaces/live_parameters.rb +159 -154
  75. data/lib/picky/loader.rb +267 -304
  76. data/lib/picky/loggers/search.rb +20 -13
  77. data/lib/picky/no_source_specified_exception.rb +7 -3
  78. data/lib/picky/performant.rb +6 -2
  79. data/lib/picky/query/allocation.rb +71 -67
  80. data/lib/picky/query/allocations.rb +99 -94
  81. data/lib/picky/query/combination.rb +70 -66
  82. data/lib/picky/query/combinations/base.rb +56 -52
  83. data/lib/picky/query/combinations/memory.rb +36 -32
  84. data/lib/picky/query/combinations/redis.rb +66 -62
  85. data/lib/picky/query/indexes.rb +175 -160
  86. data/lib/picky/query/qualifier_category_mapper.rb +43 -0
  87. data/lib/picky/query/token.rb +165 -172
  88. data/lib/picky/query/tokens.rb +86 -82
  89. data/lib/picky/query/weights.rb +44 -48
  90. data/lib/picky/query.rb +5 -1
  91. data/lib/picky/rack/harakiri.rb +51 -47
  92. data/lib/picky/results.rb +81 -77
  93. data/lib/picky/search.rb +169 -158
  94. data/lib/picky/sinatra.rb +34 -0
  95. data/lib/picky/sources/base.rb +73 -70
  96. data/lib/picky/sources/couch.rb +61 -57
  97. data/lib/picky/sources/csv.rb +68 -64
  98. data/lib/picky/sources/db.rb +139 -135
  99. data/lib/picky/sources/delicious.rb +52 -48
  100. data/lib/picky/sources/mongo.rb +68 -63
  101. data/lib/picky/sources/wrappers/base.rb +20 -16
  102. data/lib/picky/sources/wrappers/location.rb +37 -33
  103. data/lib/picky/statistics.rb +46 -43
  104. data/lib/picky/tasks.rb +3 -0
  105. data/lib/picky/tokenizers/base.rb +192 -187
  106. data/lib/picky/tokenizers/index.rb +25 -21
  107. data/lib/picky/tokenizers/location.rb +33 -29
  108. data/lib/picky/tokenizers/query.rb +49 -43
  109. data/lib/picky.rb +21 -13
  110. data/lib/tasks/application.rake +1 -1
  111. data/lib/tasks/index.rake +3 -3
  112. data/lib/tasks/routes.rake +1 -1
  113. data/lib/tasks/server.rake +1 -1
  114. data/spec/lib/adapters/rack/base_spec.rb +1 -1
  115. data/spec/lib/adapters/rack/live_parameters_spec.rb +1 -1
  116. data/spec/lib/adapters/rack/query_spec.rb +1 -1
  117. data/spec/lib/application_spec.rb +39 -32
  118. data/spec/lib/backend/file/basic_spec.rb +2 -2
  119. data/spec/lib/backend/file/json_spec.rb +2 -2
  120. data/spec/lib/backend/file/marshal_spec.rb +2 -2
  121. data/spec/lib/backend/file/text_spec.rb +1 -1
  122. data/spec/lib/backend/files_spec.rb +14 -24
  123. data/spec/lib/backend/redis/basic_spec.rb +2 -2
  124. data/spec/lib/backend/redis/list_hash_spec.rb +3 -3
  125. data/spec/lib/backend/redis/string_hash_spec.rb +3 -3
  126. data/spec/lib/backend/redis_spec.rb +20 -13
  127. data/spec/lib/calculations/location_spec.rb +1 -1
  128. data/spec/lib/categories_indexed_spec.rb +16 -34
  129. data/spec/lib/category_indexed_spec.rb +9 -27
  130. data/spec/lib/category_indexing_spec.rb +2 -3
  131. data/spec/lib/category_spec.rb +10 -10
  132. data/spec/lib/character_substituters/west_european_spec.rb +6 -5
  133. data/spec/lib/cores_spec.rb +17 -17
  134. data/spec/lib/extensions/symbol_spec.rb +15 -1
  135. data/spec/lib/frontend_adapters/rack_spec.rb +20 -20
  136. data/spec/lib/generators/aliases_spec.rb +3 -3
  137. data/spec/lib/generators/cacher_strategy_spec.rb +1 -1
  138. data/spec/lib/generators/partial/default_spec.rb +3 -3
  139. data/spec/lib/generators/partial/none_spec.rb +2 -2
  140. data/spec/lib/generators/partial/substring_spec.rb +1 -1
  141. data/spec/lib/generators/partial_generator_spec.rb +3 -3
  142. data/spec/lib/generators/similarity/double_metaphone_spec.rb +1 -1
  143. data/spec/lib/generators/similarity/metaphone_spec.rb +1 -1
  144. data/spec/lib/generators/similarity/none_spec.rb +1 -1
  145. data/spec/lib/generators/similarity/phonetic_spec.rb +1 -1
  146. data/spec/lib/generators/similarity/soundex_spec.rb +1 -1
  147. data/spec/lib/generators/similarity_generator_spec.rb +2 -2
  148. data/spec/lib/generators/weights/logarithmic_spec.rb +1 -1
  149. data/spec/lib/generators/weights_generator_spec.rb +1 -1
  150. data/spec/lib/helpers/measuring_spec.rb +2 -2
  151. data/spec/lib/indexed/bundle/memory_spec.rb +6 -6
  152. data/spec/lib/indexed/bundle/redis_spec.rb +4 -4
  153. data/spec/lib/indexed/wrappers/bundle/calculation_spec.rb +2 -3
  154. data/spec/lib/indexed/wrappers/bundle/wrapper_spec.rb +2 -2
  155. data/spec/lib/indexed/wrappers/exact_first_spec.rb +5 -5
  156. data/spec/lib/indexers/base_spec.rb +1 -1
  157. data/spec/lib/indexers/parallel_spec.rb +1 -1
  158. data/spec/lib/indexers/serial_spec.rb +1 -1
  159. data/spec/lib/{index/base_indexed_spec.rb → indexes/index_indexed_spec.rb} +3 -3
  160. data/spec/lib/{index/base_indexing_spec.rb → indexes/index_indexing_spec.rb} +19 -2
  161. data/spec/lib/{index/base_spec.rb → indexes/index_spec.rb} +6 -25
  162. data/spec/lib/{index → indexes}/redis_spec.rb +1 -1
  163. data/spec/lib/indexes_class_spec.rb +2 -2
  164. data/spec/lib/indexes_indexed_spec.rb +1 -1
  165. data/spec/lib/indexes_indexing_spec.rb +1 -1
  166. data/spec/lib/indexes_spec.rb +1 -1
  167. data/spec/lib/indexing/bundle/base_spec.rb +7 -5
  168. data/spec/lib/indexing/bundle/memory_partial_generation_speed_spec.rb +4 -4
  169. data/spec/lib/indexing/bundle/memory_spec.rb +15 -15
  170. data/spec/lib/indexing/bundle/redis_spec.rb +9 -9
  171. data/spec/lib/interfaces/live_parameters_spec.rb +5 -5
  172. data/spec/lib/loader_spec.rb +17 -19
  173. data/spec/lib/loggers/search_spec.rb +2 -2
  174. data/spec/lib/query/allocation_spec.rb +1 -1
  175. data/spec/lib/query/allocations_spec.rb +1 -1
  176. data/spec/lib/query/combination_spec.rb +4 -4
  177. data/spec/lib/query/combinations/base_spec.rb +1 -1
  178. data/spec/lib/query/combinations/memory_spec.rb +1 -1
  179. data/spec/lib/query/combinations/redis_spec.rb +1 -1
  180. data/spec/lib/query/indexes_spec.rb +7 -2
  181. data/spec/lib/query/qualifier_category_mapper_spec.rb +34 -0
  182. data/spec/lib/query/token_spec.rb +32 -53
  183. data/spec/lib/query/tokens_spec.rb +30 -35
  184. data/spec/lib/query/weights_spec.rb +16 -16
  185. data/spec/lib/rack/harakiri_spec.rb +5 -5
  186. data/spec/lib/results_spec.rb +1 -1
  187. data/spec/lib/search_spec.rb +24 -22
  188. data/spec/lib/sinatra_spec.rb +36 -0
  189. data/spec/lib/sources/base_spec.rb +1 -1
  190. data/spec/lib/sources/couch_spec.rb +9 -9
  191. data/spec/lib/sources/csv_spec.rb +7 -7
  192. data/spec/lib/sources/db_spec.rb +2 -2
  193. data/spec/lib/sources/delicious_spec.rb +5 -5
  194. data/spec/lib/sources/mongo_spec.rb +7 -7
  195. data/spec/lib/sources/wrappers/base_spec.rb +2 -2
  196. data/spec/lib/sources/wrappers/location_spec.rb +1 -1
  197. data/spec/lib/statistics_spec.rb +1 -1
  198. data/spec/lib/tokenizers/base_spec.rb +2 -2
  199. data/spec/lib/tokenizers/index_spec.rb +1 -1
  200. data/spec/lib/tokenizers/query_spec.rb +1 -1
  201. metadata +30 -30
  202. data/lib/picky/adapters/rack/query.rb +0 -65
  203. data/lib/picky/index/base.rb +0 -409
  204. data/lib/picky/index/base_indexed.rb +0 -29
  205. data/lib/picky/index/base_indexing.rb +0 -127
  206. data/lib/picky/index/memory.rb +0 -16
  207. data/lib/picky/index/redis.rb +0 -16
  208. data/lib/picky/query/qualifiers.rb +0 -76
  209. data/lib/picky/query/solr.rb +0 -60
  210. data/lib/picky/signals.rb +0 -8
  211. data/lib/picky-tasks.rb +0 -6
  212. data/lib/tasks/spec.rake +0 -11
  213. data/spec/lib/query/qualifiers_spec.rb +0 -31
@@ -1,55 +1,59 @@
1
- module Rack # :nodoc:
2
-
3
- # Simple Rack Middleware to kill Unicorns after X requests.
4
- #
5
- # Use as follows in e.g. your rackup File:
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
- cattr_accessor :after
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
- def call env
33
- harakiri
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
- # If yes, kills itself (Unicorn will answer the request, honorably).
12
+ # Then the Unicorn will commit suicide after 100 requests (50 is the default).
40
13
  #
41
- # Note: Sends its process a QUIT signal if it is time.
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
- def harakiri?
51
- @requests >= @quit_after_requests
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
- # This is the internal results object. Usually, to_marshal, or to_json
2
- # is called on it to get a string for the answer.
3
- #
4
- class Results
1
+ module Picky
5
2
 
6
- # Duration is set externally by the query.
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
- attr_writer :duration
9
- attr_reader :allocations, :offset, :amount
6
+ class Results
10
7
 
11
- # Takes instances of Query::Allocations as param.
12
- #
13
- def initialize amount = 0, offset = 0, allocations = Query::Allocations.new
14
- @offset = offset
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
- # Returns a hash with the allocations, offset, duration and total.
27
- #
28
- def serialize
29
- { allocations: allocations.to_result,
30
- offset: offset,
31
- duration: duration,
32
- total: total }
33
- end
34
- # The default format is json.
35
- #
36
- def to_response options = {}
37
- to_json options
38
- end
39
- # Convert to json format.
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
- # 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
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
- # 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
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
- # Convenience methods.
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
- # Delegates to allocations.
71
- #
72
- def ids amount = 20
73
- allocations.ids amount
74
- end
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
- # = Picky Queries
2
- #
3
- # A Picky Search 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
- class Search
13
-
14
- include Helpers::Measuring
15
-
16
- attr_reader :indexes
17
- attr_writer :tokenizer
18
- attr_accessor :weights
19
-
20
- # Takes:
21
- # * A number of indexes
22
- # * Options hash (optional) with:
23
- # * tokenizer: Tokenizers::Query.default by default.
24
- # * weights: A hash of weights, or a Query::Weights object.
25
- #
26
- # TODO Add identifiers_to_remove (rename) and reduce_allocations_to_amount (rename).
27
- # TODO categories_to_remove ?
28
- #
29
- # It is also possible to define the tokenizer and weights like so.
30
- # Example:
31
- # Search.new(index1, index2, index3) do
32
- # searching removes_characters: /[^a-z]/, etc.
33
- # weights [:author, :title] => +3, [:title, :isbn] => +1
34
- # end
35
- #
36
- def initialize *index_definitions
37
- options = Hash === index_definitions.last ? index_definitions.pop : {}
38
-
39
- @indexes = Query::Indexes.new *index_definitions, combinations_type_for(index_definitions)
40
- searching options[:tokenizer]
41
- boost options[:weights]
42
-
43
- instance_eval(&Proc.new) if block_given?
44
- end
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
- # TODO Doc. Spec.
47
- #
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)
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
- # Returns the tokenizer if set or if not, the query tokenizer.
63
- #
64
- def tokenizer
65
- @tokenizer || Tokenizers::Query.default
66
- end
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
- # TODO Doc. Spec.
69
- #
70
- # Example:
71
- # Search.new(index1, index2, index3) do
72
- # searching removes_characters: /[^a-z]/, etc.
73
- # boost [:author, :title] => +3, [:title, :isbn] => +1
74
- # end
75
- #
76
- def boost options
77
- weights = options || Query::Weights.new
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
- # Returns the right combinations strategy for
82
- # a number of query indexes.
83
- #
84
- # Currently it isn't possible using Memory and Redis etc.
85
- # indexes in the same query index group.
86
- #
87
- # Picky will raise a Query::Indexes::DifferentTypesError.
88
- #
89
- @@mapping = {
90
- Index::Memory => Query::Combinations::Memory,
91
- Index::Redis => Query::Combinations::Redis
92
- }
93
- def combinations_type_for index_definitions_ary
94
- index_types = index_definitions_ary.map(&:class)
95
- index_types.uniq!
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 to_s
107
- "Currently it isn't possible to mix #{@types.join(" and ")} Indexes in the same Search instance."
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
- # Runs the actual search using Query::Tokens.
129
- #
130
- # Note: Internal method, use #search_with_text.
131
- #
132
- def search tokens, ids = 20, offset = 0
133
- results = nil
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
- duration = timed do
136
- results = execute tokens, ids, offset
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
- results
141
- end
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
- # Execute a search using Query::Tokens.
144
- #
145
- # Note: Internal method, use #search_with_text.
146
- #
147
- def execute tokens, ids, offset
148
- Results.from ids, offset, sorted_allocations(tokens)
149
- end
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
- # Delegates the tokenizing to the query tokenizer.
152
- #
153
- # Parameters:
154
- # * text: The text to tokenize.
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
- # Gets sorted allocations for the tokens.
161
- #
162
- def sorted_allocations tokens # :nodoc:
163
- @indexes.prepared_allocations_for tokens, weights
164
- end
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