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.
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