gojee-sunspot 2.0.3 → 2.0.4

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 (178) hide show
  1. data/.gitignore +12 -0
  2. data/Gemfile +5 -0
  3. data/History.txt +252 -0
  4. data/LICENSE +18 -0
  5. data/Rakefile +13 -0
  6. data/TODO +13 -0
  7. data/lib/light_config.rb +40 -0
  8. data/lib/sunspot/adapters.rb +265 -0
  9. data/lib/sunspot/batcher.rb +62 -0
  10. data/lib/sunspot/class_set.rb +23 -0
  11. data/lib/sunspot/composite_setup.rb +202 -0
  12. data/lib/sunspot/configuration.rb +53 -0
  13. data/lib/sunspot/data_extractor.rb +50 -0
  14. data/lib/sunspot/dsl/adjustable.rb +47 -0
  15. data/lib/sunspot/dsl/field_group.rb +57 -0
  16. data/lib/sunspot/dsl/field_query.rb +327 -0
  17. data/lib/sunspot/dsl/fields.rb +103 -0
  18. data/lib/sunspot/dsl/fulltext.rb +243 -0
  19. data/lib/sunspot/dsl/function.rb +27 -0
  20. data/lib/sunspot/dsl/functional.rb +44 -0
  21. data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  22. data/lib/sunspot/dsl/paginatable.rb +32 -0
  23. data/lib/sunspot/dsl/query_facet.rb +36 -0
  24. data/lib/sunspot/dsl/restriction.rb +25 -0
  25. data/lib/sunspot/dsl/restriction_with_near.rb +160 -0
  26. data/lib/sunspot/dsl/scope.rb +217 -0
  27. data/lib/sunspot/dsl/search.rb +30 -0
  28. data/lib/sunspot/dsl/standard_query.rb +123 -0
  29. data/lib/sunspot/dsl.rb +5 -0
  30. data/lib/sunspot/field.rb +193 -0
  31. data/lib/sunspot/field_factory.rb +129 -0
  32. data/lib/sunspot/indexer.rb +136 -0
  33. data/lib/sunspot/query/abstract_field_facet.rb +52 -0
  34. data/lib/sunspot/query/bbox.rb +15 -0
  35. data/lib/sunspot/query/boost_query.rb +24 -0
  36. data/lib/sunspot/query/common_query.rb +96 -0
  37. data/lib/sunspot/query/composite_fulltext.rb +36 -0
  38. data/lib/sunspot/query/connective.rb +206 -0
  39. data/lib/sunspot/query/date_field_facet.rb +14 -0
  40. data/lib/sunspot/query/dismax.rb +132 -0
  41. data/lib/sunspot/query/field_facet.rb +41 -0
  42. data/lib/sunspot/query/field_group.rb +36 -0
  43. data/lib/sunspot/query/filter.rb +38 -0
  44. data/lib/sunspot/query/function_query.rb +52 -0
  45. data/lib/sunspot/query/geo.rb +53 -0
  46. data/lib/sunspot/query/geofilt.rb +16 -0
  47. data/lib/sunspot/query/highlighting.rb +62 -0
  48. data/lib/sunspot/query/more_like_this.rb +61 -0
  49. data/lib/sunspot/query/more_like_this_query.rb +12 -0
  50. data/lib/sunspot/query/pagination.rb +42 -0
  51. data/lib/sunspot/query/query_facet.rb +16 -0
  52. data/lib/sunspot/query/restriction.rb +262 -0
  53. data/lib/sunspot/query/scope.rb +9 -0
  54. data/lib/sunspot/query/sort.rb +109 -0
  55. data/lib/sunspot/query/sort_composite.rb +34 -0
  56. data/lib/sunspot/query/standard_query.rb +16 -0
  57. data/lib/sunspot/query/text_field_boost.rb +17 -0
  58. data/lib/sunspot/query.rb +11 -0
  59. data/lib/sunspot/schema.rb +151 -0
  60. data/lib/sunspot/search/abstract_search.rb +281 -0
  61. data/lib/sunspot/search/date_facet.rb +35 -0
  62. data/lib/sunspot/search/facet_row.rb +27 -0
  63. data/lib/sunspot/search/field_facet.rb +88 -0
  64. data/lib/sunspot/search/field_group.rb +32 -0
  65. data/lib/sunspot/search/group.rb +50 -0
  66. data/lib/sunspot/search/highlight.rb +38 -0
  67. data/lib/sunspot/search/hit.rb +150 -0
  68. data/lib/sunspot/search/hit_enumerable.rb +72 -0
  69. data/lib/sunspot/search/more_like_this_search.rb +31 -0
  70. data/lib/sunspot/search/paginated_collection.rb +57 -0
  71. data/lib/sunspot/search/query_facet.rb +67 -0
  72. data/lib/sunspot/search/standard_search.rb +21 -0
  73. data/lib/sunspot/search.rb +9 -0
  74. data/lib/sunspot/session.rb +262 -0
  75. data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
  76. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
  77. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
  78. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
  79. data/lib/sunspot/session_proxy/multicore_session_proxy.rb +67 -0
  80. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
  81. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  82. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
  83. data/lib/sunspot/session_proxy.rb +95 -0
  84. data/lib/sunspot/setup.rb +350 -0
  85. data/lib/sunspot/text_field_setup.rb +29 -0
  86. data/lib/sunspot/type.rb +393 -0
  87. data/lib/sunspot/util.rb +252 -0
  88. data/lib/sunspot/version.rb +3 -0
  89. data/lib/sunspot.rb +579 -0
  90. data/log/.gitignore +1 -0
  91. data/pkg/.gitignore +1 -0
  92. data/script/console +10 -0
  93. data/spec/api/adapters_spec.rb +33 -0
  94. data/spec/api/batcher_spec.rb +112 -0
  95. data/spec/api/binding_spec.rb +50 -0
  96. data/spec/api/class_set_spec.rb +24 -0
  97. data/spec/api/hit_enumerable_spec.rb +47 -0
  98. data/spec/api/indexer/attributes_spec.rb +149 -0
  99. data/spec/api/indexer/batch_spec.rb +72 -0
  100. data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
  101. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  102. data/spec/api/indexer/fulltext_spec.rb +43 -0
  103. data/spec/api/indexer/removal_spec.rb +53 -0
  104. data/spec/api/indexer/spec_helper.rb +1 -0
  105. data/spec/api/indexer_spec.rb +14 -0
  106. data/spec/api/query/advanced_manipulation_examples.rb +35 -0
  107. data/spec/api/query/connectives_examples.rb +189 -0
  108. data/spec/api/query/dsl_spec.rb +18 -0
  109. data/spec/api/query/dynamic_fields_examples.rb +165 -0
  110. data/spec/api/query/faceting_examples.rb +397 -0
  111. data/spec/api/query/fulltext_examples.rb +313 -0
  112. data/spec/api/query/function_spec.rb +79 -0
  113. data/spec/api/query/geo_examples.rb +68 -0
  114. data/spec/api/query/group_spec.rb +32 -0
  115. data/spec/api/query/highlighting_examples.rb +245 -0
  116. data/spec/api/query/more_like_this_spec.rb +140 -0
  117. data/spec/api/query/ordering_pagination_examples.rb +116 -0
  118. data/spec/api/query/scope_examples.rb +275 -0
  119. data/spec/api/query/spatial_examples.rb +27 -0
  120. data/spec/api/query/spec_helper.rb +1 -0
  121. data/spec/api/query/standard_spec.rb +29 -0
  122. data/spec/api/query/text_field_scoping_examples.rb +30 -0
  123. data/spec/api/query/types_spec.rb +20 -0
  124. data/spec/api/search/dynamic_fields_spec.rb +33 -0
  125. data/spec/api/search/faceting_spec.rb +360 -0
  126. data/spec/api/search/highlighting_spec.rb +69 -0
  127. data/spec/api/search/hits_spec.rb +131 -0
  128. data/spec/api/search/paginated_collection_spec.rb +36 -0
  129. data/spec/api/search/results_spec.rb +72 -0
  130. data/spec/api/search/search_spec.rb +23 -0
  131. data/spec/api/search/spec_helper.rb +1 -0
  132. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
  133. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
  134. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
  135. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
  136. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  137. data/spec/api/session_proxy/spec_helper.rb +9 -0
  138. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +39 -0
  139. data/spec/api/session_spec.rb +232 -0
  140. data/spec/api/spec_helper.rb +3 -0
  141. data/spec/api/sunspot_spec.rb +29 -0
  142. data/spec/ext.rb +11 -0
  143. data/spec/helpers/indexer_helper.rb +17 -0
  144. data/spec/helpers/integration_helper.rb +8 -0
  145. data/spec/helpers/mock_session_helper.rb +13 -0
  146. data/spec/helpers/query_helper.rb +26 -0
  147. data/spec/helpers/search_helper.rb +68 -0
  148. data/spec/integration/dynamic_fields_spec.rb +57 -0
  149. data/spec/integration/faceting_spec.rb +251 -0
  150. data/spec/integration/field_grouping_spec.rb +66 -0
  151. data/spec/integration/geospatial_spec.rb +85 -0
  152. data/spec/integration/highlighting_spec.rb +44 -0
  153. data/spec/integration/indexing_spec.rb +55 -0
  154. data/spec/integration/keyword_search_spec.rb +317 -0
  155. data/spec/integration/local_search_spec.rb +64 -0
  156. data/spec/integration/more_like_this_spec.rb +43 -0
  157. data/spec/integration/scoped_search_spec.rb +354 -0
  158. data/spec/integration/stored_fields_spec.rb +12 -0
  159. data/spec/integration/test_pagination.rb +43 -0
  160. data/spec/integration/unicode_spec.rb +15 -0
  161. data/spec/mocks/adapters.rb +32 -0
  162. data/spec/mocks/blog.rb +3 -0
  163. data/spec/mocks/comment.rb +21 -0
  164. data/spec/mocks/connection.rb +126 -0
  165. data/spec/mocks/mock_adapter.rb +30 -0
  166. data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
  167. data/spec/mocks/mock_record.rb +52 -0
  168. data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
  169. data/spec/mocks/photo.rb +11 -0
  170. data/spec/mocks/post.rb +86 -0
  171. data/spec/mocks/super_class.rb +2 -0
  172. data/spec/mocks/user.rb +13 -0
  173. data/spec/spec_helper.rb +40 -0
  174. data/sunspot.gemspec +42 -0
  175. data/tasks/rdoc.rake +27 -0
  176. data/tasks/schema.rake +19 -0
  177. data/tasks/todo.rake +4 -0
  178. metadata +261 -3
@@ -0,0 +1,350 @@
1
+ module Sunspot
2
+ #
3
+ # This class encapsulates the search/indexing setup for a given class. Its
4
+ # contents are built using the Sunspot.setup method.
5
+ #
6
+ class Setup #:nodoc:
7
+ attr_reader :class_object_id
8
+ def initialize(clazz)
9
+ @class_object_id = clazz.object_id
10
+ @class_name = clazz.name
11
+ @field_factories, @text_field_factories, @dynamic_field_factories,
12
+ @field_factories_cache, @text_field_factories_cache,
13
+ @dynamic_field_factories_cache = *Array.new(6) { Hash.new }
14
+ @stored_field_factories_cache = Hash.new { |h, k| h[k] = [] }
15
+ @more_like_this_field_factories_cache = Hash.new { |h, k| h[k] = [] }
16
+ @dsl = DSL::Fields.new(self)
17
+ add_field_factory(:class, Type::ClassType.instance)
18
+ end
19
+
20
+ def type_names
21
+ [@class_name]
22
+ end
23
+
24
+ #
25
+ # Add field factory for scope/ordering
26
+ #
27
+ def add_field_factory(name, type, options = {}, &block)
28
+ stored, more_like_this = options[:stored], options[:more_like_this]
29
+ field_factory = FieldFactory::Static.new(name, type, options, &block)
30
+ @field_factories[field_factory.signature] = field_factory
31
+ @field_factories_cache[field_factory.name] = field_factory
32
+ if stored
33
+ @stored_field_factories_cache[field_factory.name] << field_factory
34
+ end
35
+ if more_like_this
36
+ @more_like_this_field_factories_cache[field_factory.name] << field_factory
37
+ end
38
+ end
39
+
40
+ #
41
+ # Add field_factories for fulltext search
42
+ #
43
+ # ==== Parameters
44
+ #
45
+ # field_factories<Array>:: Array of Sunspot::Field objects
46
+ #
47
+ def add_text_field_factory(name, options = {}, &block)
48
+ stored, more_like_this = options[:stored], options[:more_like_this]
49
+ field_factory = FieldFactory::Static.new(name, Type::TextType.instance, options, &block)
50
+ @text_field_factories[name] = field_factory
51
+ @text_field_factories_cache[field_factory.name] = field_factory
52
+ if stored
53
+ @stored_field_factories_cache[field_factory.name] << field_factory
54
+ end
55
+ if more_like_this
56
+ @more_like_this_field_factories_cache[field_factory.name] << field_factory
57
+ end
58
+ end
59
+
60
+ #
61
+ # Add dynamic field_factories
62
+ #
63
+ # ==== Parameters
64
+ #
65
+ # field_factories<Array>:: Array of dynamic field objects
66
+ #
67
+ def add_dynamic_field_factory(name, type, options = {}, &block)
68
+ stored, more_like_this = options[:stored], options[:more_like_this]
69
+ field_factory = FieldFactory::Dynamic.new(name, type, options, &block)
70
+ @dynamic_field_factories[field_factory.signature] = field_factory
71
+ @dynamic_field_factories_cache[field_factory.name] = field_factory
72
+ if stored
73
+ @stored_field_factories_cache[field_factory.name] << field_factory
74
+ end
75
+ if more_like_this
76
+ @more_like_this_field_factories_cache[field_factory.name] << field_factory
77
+ end
78
+ end
79
+
80
+ #
81
+ # Add a document boost to documents at index time. Document boost can be
82
+ # static (the same for all documents of this class), or extracted on a per-
83
+ # document basis using either attribute or block extraction as per usual.
84
+ #
85
+ def add_document_boost(attr_name, &block)
86
+ @document_boost_extractor =
87
+ if attr_name
88
+ if attr_name.respond_to?(:to_f)
89
+ DataExtractor::Constant.new(attr_name)
90
+ else
91
+ DataExtractor::AttributeExtractor.new(attr_name)
92
+ end
93
+ else
94
+ DataExtractor::BlockExtractor.new(&block)
95
+ end
96
+ end
97
+
98
+ #
99
+ # Builder method for evaluating the setup DSL
100
+ #
101
+ def setup(&block)
102
+ Util.instance_eval_or_call(@dsl, &block)
103
+ end
104
+
105
+ #
106
+ # Return the Field with the given (public-facing) name
107
+ #
108
+ def field(field_name)
109
+ if field_factory = @field_factories_cache[field_name.to_sym]
110
+ field_factory.build
111
+ else
112
+ raise(
113
+ UnrecognizedFieldError,
114
+ "No field configured for #{@class_name} with name '#{field_name}'"
115
+ )
116
+ end
117
+ end
118
+
119
+ #
120
+ # Return one or more text fields with the given public-facing name. This
121
+ # implementation will always return a single field (in an array), but
122
+ # CompositeSetup objects might return more than one.
123
+ #
124
+ def text_fields(field_name)
125
+ text_field =
126
+ if field_factory = @text_field_factories_cache[field_name.to_sym]
127
+ field_factory.build
128
+ else
129
+ raise(
130
+ UnrecognizedFieldError,
131
+ "No text field configured for #{@class_name} with name '#{field_name}'"
132
+ )
133
+ end
134
+ [text_field]
135
+ end
136
+
137
+ #
138
+ # Return one or more stored fields (can be either attribute or text fields)
139
+ # for the given name.
140
+ #
141
+ def stored_fields(field_name, dynamic_field_name = nil)
142
+ @stored_field_factories_cache[field_name.to_sym].map do |field_factory|
143
+ if dynamic_field_name
144
+ field_factory.build(dynamic_field_name)
145
+ else
146
+ field_factory.build
147
+ end
148
+ end
149
+ end
150
+
151
+ #
152
+ # Return one or more more_like_this fields (can be either attribute or text fields)
153
+ # for the given name.
154
+ #
155
+ def more_like_this_fields(field_name)
156
+ @more_like_this_field_factories_cache[field_name.to_sym].map do |field_factory|
157
+ field_factory.build
158
+ end
159
+ end
160
+
161
+ #
162
+ # Return the DynamicFieldFactory with the given base name
163
+ #
164
+ def dynamic_field_factory(field_name)
165
+ @dynamic_field_factories_cache[field_name.to_sym] || raise(
166
+ UnrecognizedFieldError,
167
+ "No dynamic field configured for #{@class_name} with name '#{field_name}'"
168
+ )
169
+ end
170
+
171
+ #
172
+ # Return all attribute fields
173
+ #
174
+ def fields
175
+ field_factories.map { |field_factory| field_factory.build }
176
+ end
177
+
178
+ #
179
+ # Return all text fields
180
+ #
181
+ def all_text_fields
182
+ text_field_factories.map { |text_field_factory| text_field_factory.build }
183
+ end
184
+
185
+ #
186
+ # Return all more_like_this fields
187
+ #
188
+ def all_more_like_this_fields
189
+ @more_like_this_field_factories_cache.values.map do |field_factories|
190
+ field_factories.map { |field_factory| field_factory.build }
191
+ end.flatten
192
+ end
193
+
194
+ #
195
+ # Get the field_factories associated with this setup as well as all inherited field_factories
196
+ #
197
+ # ==== Returns
198
+ #
199
+ # Array:: Collection of all field_factories associated with this setup
200
+ #
201
+ def field_factories
202
+ collection_from_inheritable_hash(:field_factories)
203
+ end
204
+
205
+ #
206
+ # Get the text field_factories associated with this setup as well as all inherited
207
+ # text field_factories
208
+ #
209
+ # ==== Returns
210
+ #
211
+ # Array:: Collection of all text field_factories associated with this setup
212
+ #
213
+ def text_field_factories
214
+ collection_from_inheritable_hash(:text_field_factories)
215
+ end
216
+
217
+ #
218
+ # Get all static, dynamic, and text field_factories associated with this setup as
219
+ # well as all inherited field_factories
220
+ #
221
+ # ==== Returns
222
+ #
223
+ # Array:: Collection of all text and scope field_factories associated with this setup
224
+ #
225
+ def all_field_factories
226
+ all_field_factories = []
227
+ all_field_factories.concat(field_factories).concat(text_field_factories).concat(dynamic_field_factories)
228
+ all_field_factories
229
+ end
230
+
231
+ #
232
+ # Get all dynamic field_factories for this and parent setups
233
+ #
234
+ # ==== Returns
235
+ #
236
+ # Array:: Dynamic field_factories
237
+ #
238
+ def dynamic_field_factories
239
+ collection_from_inheritable_hash(:dynamic_field_factories)
240
+ end
241
+
242
+ #
243
+ # Return the class associated with this setup.
244
+ #
245
+ # ==== Returns
246
+ #
247
+ # clazz<Class>:: Class setup is configured for
248
+ #
249
+ def clazz
250
+ Util.full_const_get(@class_name)
251
+ end
252
+
253
+ #
254
+ # Get the document boost for a given model
255
+ #
256
+ def document_boost_for(model)
257
+ if @document_boost_extractor
258
+ @document_boost_extractor.value_for(model)
259
+ end
260
+ end
261
+
262
+ protected
263
+
264
+ #
265
+ # Get the nearest inherited setup, if any
266
+ #
267
+ # ==== Returns
268
+ #
269
+ # Sunspot::Setup:: Setup for the nearest ancestor of this setup's class
270
+ #
271
+ def parent
272
+ Setup.for(clazz.superclass)
273
+ end
274
+
275
+ def get_inheritable_hash(name)
276
+ hash = instance_variable_get(:"@#{name}")
277
+ parent.get_inheritable_hash(name).each_pair do |key, value|
278
+ hash[key] = value unless hash.has_key?(key)
279
+ end if parent
280
+ hash
281
+ end
282
+
283
+ private
284
+
285
+ def collection_from_inheritable_hash(name)
286
+ get_inheritable_hash(name).values
287
+ end
288
+
289
+ class <<self
290
+ #
291
+ # Retrieve or create the Setup instance for the given class, evaluating
292
+ # the given block to add to the setup's configuration
293
+ #
294
+ def setup(clazz, &block) #:nodoc:
295
+ self.for!(clazz).setup(&block)
296
+ end
297
+
298
+ #
299
+ # Retrieve the setup instance for the given class, or for the nearest
300
+ # ancestor that has a setup, if any.
301
+ #
302
+ # ==== Parameters
303
+ #
304
+ # clazz<Class>:: Class for which to retrieve a setup
305
+ #
306
+ # ==== Returns
307
+ #
308
+ # Sunspot::Setup::
309
+ # Setup instance associated with the given class or its nearest ancestor
310
+ #
311
+ def for(clazz) #:nodoc:
312
+ setups[clazz.name.to_sym] || self.for(clazz.superclass) if clazz
313
+ end
314
+
315
+ protected
316
+
317
+ #
318
+ # Retrieve or create a Setup instance for this class
319
+ #
320
+ # ==== Parameters
321
+ #
322
+ # clazz<Class>:: Class for which to retrieve a setup
323
+ #
324
+ # ==== Returns
325
+ #
326
+ # Sunspot::Setup:: New or existing setup for this class
327
+ #
328
+ def for!(clazz) #:nodoc:
329
+ setup = setups[clazz.name.to_sym]
330
+ if setup && setup.class_object_id == clazz.object_id
331
+ setup
332
+ else
333
+ setups[clazz.name.to_sym] = new(clazz)
334
+ end
335
+ end
336
+
337
+ private
338
+
339
+ # Singleton hash of class names to Setup instances
340
+ #
341
+ # ==== Returns
342
+ #
343
+ # Hash:: Class names keyed to Setup instances
344
+ #
345
+ def setups
346
+ @setups ||= {}
347
+ end
348
+ end
349
+ end
350
+ end
@@ -0,0 +1,29 @@
1
+ module Sunspot
2
+ #
3
+ # A TextFieldSetup encapsulates a regular (or composite) setup, and exposes
4
+ # the #field() method returning text fields instead of attribute fields.
5
+ #
6
+ class TextFieldSetup #:nodoc:
7
+ def initialize(setup)
8
+ @setup = setup
9
+ end
10
+
11
+ #
12
+ # Return a text field with the given name. Duck-type compatible with
13
+ # Setup and CompositeSetup, but return text fields instead.
14
+ #
15
+ def field(name)
16
+ fields = @setup.text_fields(name)
17
+ if fields
18
+ if fields.length == 1
19
+ fields.first
20
+ else
21
+ raise(
22
+ Sunspot::UnrecognizedFieldError,
23
+ "The text field with name #{name} has incompatible configurations for the classes #{@setup.type_names.join(', ')}"
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end