sunspot 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/README.rdoc +3 -0
  2. data/TODO +6 -5
  3. data/bin/sunspot-solr +4 -0
  4. data/installer/config/schema.yml +24 -0
  5. data/lib/sunspot/composite_setup.rb +14 -0
  6. data/lib/sunspot/dsl/adjustable.rb +47 -0
  7. data/lib/sunspot/dsl/fulltext.rb +23 -8
  8. data/lib/sunspot/dsl/function.rb +14 -0
  9. data/lib/sunspot/dsl/functional.rb +41 -0
  10. data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  11. data/lib/sunspot/dsl/paginatable.rb +28 -0
  12. data/lib/sunspot/dsl/search.rb +1 -1
  13. data/lib/sunspot/dsl/{query.rb → standard_query.rb} +4 -49
  14. data/lib/sunspot/dsl.rb +3 -2
  15. data/lib/sunspot/field.rb +16 -2
  16. data/lib/sunspot/indexer.rb +1 -1
  17. data/lib/sunspot/installer/schema_builder.rb +1 -1
  18. data/lib/sunspot/installer/solrconfig_updater.rb +13 -0
  19. data/lib/sunspot/installer/task_helper.rb +1 -1
  20. data/lib/sunspot/query/abstract_field_facet.rb +5 -0
  21. data/lib/sunspot/query/boost_query.rb +5 -1
  22. data/lib/sunspot/query/{query.rb → common_query.rb} +26 -20
  23. data/lib/sunspot/query/composite_fulltext.rb +31 -0
  24. data/lib/sunspot/query/dismax.rb +45 -6
  25. data/lib/sunspot/query/function_query.rb +52 -0
  26. data/lib/sunspot/query/more_like_this.rb +60 -0
  27. data/lib/sunspot/query/more_like_this_query.rb +12 -0
  28. data/lib/sunspot/query/standard_query.rb +20 -0
  29. data/lib/sunspot/query/text_field_boost.rb +2 -0
  30. data/lib/sunspot/query.rb +3 -2
  31. data/lib/sunspot/search/abstract_search.rb +302 -0
  32. data/lib/sunspot/search/date_facet.rb +1 -1
  33. data/lib/sunspot/search/facet_row.rb +1 -1
  34. data/lib/sunspot/search/field_facet.rb +1 -1
  35. data/lib/sunspot/search/highlight.rb +1 -1
  36. data/lib/sunspot/search/hit.rb +1 -1
  37. data/lib/sunspot/search/more_like_this_search.rb +31 -0
  38. data/lib/sunspot/search/query_facet.rb +1 -1
  39. data/lib/sunspot/search/standard_search.rb +21 -0
  40. data/lib/sunspot/search.rb +3 -288
  41. data/lib/sunspot/server.rb +8 -4
  42. data/lib/sunspot/session.rb +30 -2
  43. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +1 -1
  44. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +9 -0
  45. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +9 -2
  46. data/lib/sunspot/setup.rb +32 -3
  47. data/lib/sunspot/type.rb +74 -0
  48. data/lib/sunspot/util.rb +3 -2
  49. data/lib/sunspot/version.rb +1 -1
  50. data/lib/sunspot.rb +9 -1
  51. data/solr/solr/conf/schema.xml +12 -0
  52. data/solr/solr/conf/solrconfig.xml +6 -0
  53. data/spec/api/indexer/attributes_spec.rb +9 -3
  54. data/spec/api/indexer/fulltext_spec.rb +2 -2
  55. data/spec/api/indexer/removal_spec.rb +1 -1
  56. data/spec/api/query/advanced_manipulation_examples.rb +35 -0
  57. data/spec/api/query/{connectives_spec.rb → connectives_examples.rb} +19 -19
  58. data/spec/api/query/{dynamic_fields_spec.rb → dynamic_fields_examples.rb} +33 -17
  59. data/spec/api/query/{faceting_spec.rb → faceting_examples.rb} +146 -43
  60. data/spec/api/query/{fulltext_spec.rb → fulltext_examples.rb} +81 -47
  61. data/spec/api/query/function_spec.rb +70 -0
  62. data/spec/api/query/{highlighting_spec.rb → highlighting_examples.rb} +27 -27
  63. data/spec/api/query/{local_spec.rb → local_examples.rb} +5 -5
  64. data/spec/api/query/more_like_this_spec.rb +140 -0
  65. data/spec/api/query/{ordering_pagination_spec.rb → ordering_pagination_examples.rb} +16 -16
  66. data/spec/api/query/{scope_spec.rb → scope_examples.rb} +44 -61
  67. data/spec/api/query/standard_spec.rb +28 -0
  68. data/spec/api/query/{text_field_scoping_spec.rb → text_field_scoping_examples.rb} +5 -5
  69. data/spec/api/search/dynamic_fields_spec.rb +6 -0
  70. data/spec/api/search/faceting_spec.rb +10 -10
  71. data/spec/api/search/hits_spec.rb +1 -1
  72. data/spec/api/search/results_spec.rb +10 -0
  73. data/spec/api/server_spec.rb +6 -0
  74. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +2 -2
  75. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +17 -0
  76. data/spec/api/spec_helper.rb +2 -0
  77. data/spec/helpers/query_helper.rb +25 -0
  78. data/spec/helpers/search_helper.rb +4 -0
  79. data/spec/integration/faceting_spec.rb +8 -0
  80. data/spec/integration/keyword_search_spec.rb +75 -3
  81. data/spec/integration/local_search_spec.rb +1 -1
  82. data/spec/integration/more_like_this_spec.rb +43 -0
  83. data/spec/mocks/comment.rb +1 -1
  84. data/spec/mocks/connection.rb +27 -12
  85. data/spec/mocks/post.rb +5 -4
  86. data/spec/spec_helper.rb +4 -21
  87. data/tasks/gemspec.rake +1 -1
  88. metadata +39 -27
  89. data/spec/api/query/adjust_params_spec.rb +0 -37
  90. data/spec/api/query/facet_local_params_spec.rb +0 -103
@@ -1,5 +1,5 @@
1
1
  module Sunspot
2
- class Search
2
+ module Search
3
3
  class FacetRow
4
4
  attr_reader :value, :count
5
5
  attr_writer :instance #:nodoc:
@@ -1,5 +1,5 @@
1
1
  module Sunspot
2
- class Search
2
+ module Search
3
3
  #
4
4
  # A FieldFacet is a facet whose rows are all values for a certain field, in
5
5
  # contrast to a QueryFacet, whose rows represent arbitrary queries.
@@ -1,5 +1,5 @@
1
1
  module Sunspot
2
- class Search
2
+ module Search
3
3
  #
4
4
  # A Highlight represents a single highlighted fragment of text from a
5
5
  # document. Depending on the highlighting parameters used for search, there
@@ -1,5 +1,5 @@
1
1
  module Sunspot
2
- class Search
2
+ module Search
3
3
  #
4
4
  # Hit objects represent the raw information returned by Solr for a single
5
5
  # document. As well as the primary key and class name, hit objects give
@@ -0,0 +1,31 @@
1
+ module Sunspot
2
+ #
3
+ # This class encapsulates the results of a Solr MoreLikeThis search. It provides access
4
+ # to search results, total result count, and pagination information.
5
+ # Instances of MoreLikeThis are returned by the Sunspot.more_like_this and
6
+ # Sunspot.new_more_like_this methods.
7
+ #
8
+ module Search
9
+ class MoreLikeThisSearch < AbstractSearch
10
+ def execute
11
+ if @query.more_like_this.fields.empty?
12
+ @setup.all_more_like_this_fields.each do |field|
13
+ @query.more_like_this.add_field(field)
14
+ end
15
+ end
16
+ super
17
+ end
18
+
19
+ def request_handler
20
+ super || :mlt
21
+ end
22
+
23
+ private
24
+
25
+ # override
26
+ def dsl
27
+ DSL::MoreLikeThisQuery.new(self, @query, @setup)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,5 @@
1
1
  module Sunspot
2
- class Search
2
+ module Search
3
3
  class QueryFacet
4
4
  RequestedFacet = Struct.new(:label, :boolean_phrase) #:nodoc:
5
5
 
@@ -0,0 +1,21 @@
1
+ module Sunspot
2
+ module Search
3
+ #
4
+ # This class encapsulates the results of a Solr search. It provides access
5
+ # to search results, total result count, facets, and pagination information.
6
+ # Instances of Search are returned by the Sunspot.search and
7
+ # Sunspot.new_search methods.
8
+ #
9
+ class StandardSearch < AbstractSearch
10
+ def request_handler
11
+ super || :select
12
+ end
13
+
14
+ private
15
+
16
+ def dsl
17
+ DSL::Search.new(self, @setup)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,294 +1,9 @@
1
- %w(query_facet field_facet date_facet facet_row hit
2
- highlight).each do |file|
1
+ %w(abstract_search standard_search more_like_this_search query_facet field_facet
2
+ date_facet facet_row hit highlight).each do |file|
3
3
  require File.join(File.dirname(__FILE__), 'search', file)
4
4
  end
5
5
 
6
6
  module Sunspot
7
- #
8
- # This class encapsulates the results of a Solr search. It provides access
9
- # to search results, total result count, facets, and pagination information.
10
- # Instances of Search are returned by the Sunspot.search and
11
- # Sunspot.new_search methods.
12
- #
13
- class Search
14
- attr_reader :query #:nodoc:
15
- #
16
- # Retrieve all facet objects defined for this search, in order they were
17
- # defined. To retrieve an individual facet by name, use #facet()
18
- #
19
- attr_reader :facets
20
-
21
- def initialize(connection, setup, query, configuration) #:nodoc:
22
- @connection, @setup, @query = connection, setup, query
23
- @query.paginate(1, configuration.pagination.default_per_page)
24
- @facets = []
25
- @facets_by_name = {}
26
- end
27
-
28
- #
29
- # Execute the search on the Solr instance and store the results. If you
30
- # use Sunspot#search() to construct your searches, there is no need to call
31
- # this method as it has already been called. If you use
32
- # Sunspot#new_search(), you will need to call this method after building the
33
- # query.
34
- #
35
- def execute
36
- reset
37
- params = @query.to_params
38
- @solr_result = @connection.select(params)
39
- self
40
- end
41
- alias_method :execute!, :execute #:nodoc: deprecated
42
-
43
- #
44
- # Get the collection of results as instantiated objects. If WillPaginate is
45
- # available, the results will be a WillPaginate::Collection instance; if
46
- # not, it will be a vanilla Array.
47
- #
48
- # If not all of the results referenced by the Solr hits actually exist in
49
- # the data store, Sunspot will only return the results that do exist.
50
- #
51
- # ==== Returns
52
- #
53
- # WillPaginate::Collection or Array:: Instantiated result objects
54
- #
55
- def results
56
- @results ||= maybe_will_paginate(verified_hits.map { |hit| hit.instance })
57
- end
58
-
59
- #
60
- # Access raw Solr result information. Returns a collection of Hit objects
61
- # that contain the class name, primary key, keyword relevance score (if
62
- # applicable), and any stored fields.
63
- #
64
- # ==== Options (options)
65
- #
66
- # :verify::
67
- # Only return hits that reference objects that actually exist in the data
68
- # store. This causes results to be eager-loaded from the data store,
69
- # unlike the normal behavior of this method, which only loads the
70
- # referenced results when Hit#result is first called.
71
- #
72
- # ==== Returns
73
- #
74
- # Array:: Ordered collection of Hit objects
75
- #
76
- def hits(options = {})
77
- if options[:verify]
78
- verified_hits
79
- else
80
- @hits ||=
81
- maybe_will_paginate(
82
- solr_response['docs'].map do |doc|
83
- Hit.new(doc, highlights_for(doc), distance_for(doc), self)
84
- end
85
- )
86
- end
87
- end
88
- alias_method :raw_results, :hits
89
-
90
- #
91
- # Convenience method to iterate over hit and result objects. Block is
92
- # yielded a Sunspot::Server::Hit instance and a Sunspot::Server::Result
93
- # instance.
94
- #
95
- # Note that this method iterates over verified hits (see #hits method
96
- # for more information).
97
- #
98
- def each_hit_with_result
99
- verified_hits.each do |hit|
100
- yield(hit, hit.result)
101
- end
102
- end
103
-
104
- #
105
- # The total number of documents matching the query parameters
106
- #
107
- # ==== Returns
108
- #
109
- # Integer:: Total matching documents
110
- #
111
- def total
112
- @total ||= solr_response['numFound']
113
- end
114
-
115
- #
116
- # Get the facet object for the given name. `name` can either be the name
117
- # given to a query facet, or the field name of a field facet. Returns a
118
- # Sunspot::Facet object.
119
- #
120
- # ==== Parameters
121
- #
122
- # name<Symbol>::
123
- # Name of the field to return the facet for, or the name given to the
124
- # query facet when the search was constructed.
125
- # dynamic_name<Symbol>::
126
- # If faceting on a dynamic field, this is the dynamic portion of the field
127
- # name.
128
- #
129
- # ==== Example:
130
- #
131
- # search = Sunspot.search(Post) do
132
- # facet :category_ids
133
- # dynamic :custom do
134
- # facet :cuisine
135
- # end
136
- # facet :age do
137
- # row 'Less than a month' do
138
- # with(:published_at).greater_than(1.month.ago)
139
- # end
140
- # row 'Less than a year' do
141
- # with(:published_at, 1.year.ago..1.month.ago)
142
- # end
143
- # row 'More than a year' do
144
- # with(:published_at).less_than(1.year.ago)
145
- # end
146
- # end
147
- # end
148
- # search.facet(:category_ids)
149
- # #=> Facet for :category_ids field
150
- # search.facet(:custom, :cuisine)
151
- # #=> Facet for the dynamic field :cuisine in the :custom field definition
152
- # search.facet(:age)
153
- # #=> Facet for the query facet named :age
154
- #
155
- def facet(name, dynamic_name = nil)
156
- if name
157
- if dynamic_name
158
- @facets_by_name[:"#{name}:#{dynamic_name}"]
159
- else
160
- @facets_by_name[name.to_sym]
161
- end
162
- end
163
- end
164
-
165
- #
166
- # Deprecated in favor of optional second argument to #facet
167
- #
168
- def dynamic_facet(base_name, dynamic_name) #:nodoc:
169
- facet(base_name, dynamic_name)
170
- end
171
-
172
- #
173
- # Get the data accessor that will be used to load a particular class out of
174
- # persistent storage. Data accessors can implement any methods that may be
175
- # useful for refining how data is loaded out of storage. When building a
176
- # search manually (e.g., using the Sunspot#new_search method), this should
177
- # be used before calling #execute(). Use the
178
- # Sunspot::DSL::Search#data_accessor_for method when building searches using
179
- # the block DSL.
180
- #
181
- def data_accessor_for(clazz) #:nodoc:
182
- (@data_accessors ||= {})[clazz.name.to_sym] ||=
183
- Adapters::DataAccessor.create(clazz)
184
- end
185
-
186
- #
187
- # Build this search using a DSL block. This method can be called more than
188
- # once on an unexecuted search (e.g., Sunspot.new_search) in order to build
189
- # a search incrementally.
190
- #
191
- # === Example
192
- #
193
- # search = Sunspot.new_search(Post)
194
- # search.build do
195
- # with(:published_at).less_than Time.now
196
- # end
197
- # search.execute!
198
- #
199
- def build(&block)
200
- Util.instance_eval_or_call(dsl, &block)
201
- self
202
- end
203
-
204
- #
205
- # Populate the Hit objects with their instances. This is invoked the first
206
- # time any hit has its instance requested, and all hits are loaded as a
207
- # batch.
208
- #
209
- def populate_hits #:nodoc:
210
- id_hit_hash = Hash.new { |h, k| h[k] = {} }
211
- hits.each do |hit|
212
- id_hit_hash[hit.class_name][hit.primary_key] = hit
213
- end
214
- id_hit_hash.each_pair do |class_name, hits|
215
- ids = hits.map { |id, hit| hit.primary_key }
216
- data_accessor = data_accessor_for(Util.full_const_get(class_name))
217
- hits_for_class = id_hit_hash[class_name]
218
- data_accessor.load_all(ids).each do |result|
219
- hit = hits_for_class.delete(Adapters::InstanceAdapter.adapt(result).id.to_s)
220
- hit.result = result
221
- end
222
- hits_for_class.values.each { |hit| hit.result = nil }
223
- end
224
- end
225
-
226
- def inspect #:nodoc:
227
- "<Sunspot::Search:#{query.to_params.inspect}>"
228
- end
229
-
230
- def add_field_facet(field, options = {}) #:nodoc:
231
- name = (options[:name] || field.name)
232
- add_facet(name, FieldFacet.new(field, self, options))
233
- end
234
-
235
- def add_date_facet(field, options) #:nodoc:
236
- name = (options[:name] || field.name)
237
- add_facet(name, DateFacet.new(field, self, options))
238
- end
239
-
240
- def add_query_facet(name, options) #:nodoc:
241
- add_facet(name, QueryFacet.new(name, self, options))
242
- end
243
-
244
- def facet_response #:nodoc:
245
- @solr_result['facet_counts']
246
- end
247
-
248
- private
249
-
250
- def solr_response
251
- @solr_response ||= @solr_result['response']
252
- end
253
-
254
- def dsl
255
- DSL::Search.new(self, @setup)
256
- end
257
-
258
- def highlights_for(doc)
259
- if @solr_result['highlighting']
260
- @solr_result['highlighting'][doc['id']]
261
- end
262
- end
263
-
264
- def distance_for(doc)
265
- if @solr_result['distances']
266
- @solr_result['distances'][doc['id']]
267
- end
268
- end
269
-
270
- def verified_hits
271
- @verified_hits ||= maybe_will_paginate(hits.select { |hit| hit.instance })
272
- end
273
-
274
- def maybe_will_paginate(collection)
275
- if defined?(WillPaginate::Collection)
276
- WillPaginate::Collection.create(@query.page, @query.per_page, total) do |pager|
277
- pager.replace(collection)
278
- end
279
- else
280
- collection
281
- end
282
- end
283
-
284
- # Clear out all the cached ivars so the search can be called again.
285
- def reset
286
- @results = @hits = @verified_hits = @total = @solr_response = @doc_ids = nil
287
- end
288
-
289
- def add_facet(name, facet)
290
- @facets << facet
291
- @facets_by_name[name.to_sym] = facet
292
- end
7
+ module Search
293
8
  end
294
9
  end
@@ -17,7 +17,7 @@ module Sunspot
17
17
  LOG_LEVELS = Set['SEVERE', 'WARNING', 'INFO', 'CONFIG', 'FINE', 'FINER', 'FINEST']
18
18
 
19
19
  attr_accessor :min_memory, :max_memory, :port, :solr_data_dir, :solr_home, :log_file
20
- attr_writer :pid_dir, :pid_file, :log_level, :solr_data_dir, :solr_home
20
+ attr_writer :pid_dir, :pid_file, :log_level, :solr_data_dir, :solr_home, :solr_jar
21
21
 
22
22
  #
23
23
  # Start the sunspot-solr server. Bootstrap solr_home first,
@@ -69,8 +69,8 @@ module Sunspot
69
69
  command << "-Dsolr.data.dir=#{solr_data_dir}" if solr_data_dir
70
70
  command << "-Dsolr.solr.home=#{solr_home}" if solr_home
71
71
  command << "-Djava.util.logging.config.file=#{logging_config_path}" if logging_config_path
72
- command << '-jar' << File.basename(SOLR_START_JAR)
73
- FileUtils.cd(File.dirname(SOLR_START_JAR)) do
72
+ command << '-jar' << File.basename(solr_jar)
73
+ FileUtils.cd(File.dirname(solr_jar)) do
74
74
  exec(Escape.shell_command(command))
75
75
  end
76
76
  end
@@ -125,7 +125,11 @@ module Sunspot
125
125
  end
126
126
 
127
127
  def solr_home
128
- File.expand_path(@solr_home || File.join(File.dirname(SOLR_START_JAR), 'solr'))
128
+ File.expand_path(@solr_home || File.join(File.dirname(solr_jar), 'solr'))
129
+ end
130
+
131
+ def solr_jar
132
+ @solr_jar || SOLR_START_JAR
129
133
  end
130
134
 
131
135
  private
@@ -41,7 +41,12 @@ module Sunspot
41
41
  #
42
42
  def new_search(*types, &block)
43
43
  types.flatten!
44
- search = Search.new(connection, setup_for_types(types), Query::Query.new(types), @config)
44
+ search = Search::StandardSearch.new(
45
+ connection,
46
+ setup_for_types(types),
47
+ Query::StandardQuery.new(types),
48
+ @config
49
+ )
45
50
  search.build(&block) if block
46
51
  search
47
52
  end
@@ -51,7 +56,30 @@ module Sunspot
51
56
  #
52
57
  def search(*types, &block)
53
58
  search = new_search(*types, &block)
54
- search.execute!
59
+ search.execute
60
+ end
61
+
62
+ #
63
+ # See Sunspot.new_more_like_this
64
+ #
65
+ def new_more_like_this(object, *types, &block)
66
+ types[0] ||= object.class
67
+ mlt = Search::MoreLikeThisSearch.new(
68
+ connection,
69
+ setup_for_types(types),
70
+ Query::MoreLikeThisQuery.new(object, types),
71
+ @config
72
+ )
73
+ mlt.build(&block) if block
74
+ mlt
75
+ end
76
+
77
+ #
78
+ # See Sunspot.more_like_this
79
+ #
80
+ def more_like_this(object, *types, &block)
81
+ mlt = new_more_like_this(object, *types, &block)
82
+ mlt.execute
55
83
  end
56
84
 
57
85
  #
@@ -21,7 +21,7 @@ module Sunspot
21
21
  :config, :delete_dirty?, :dirty?, :index, :index!, :remove,
22
22
  :remove!, :remove_all, :remove_all!, :remove_by_id,
23
23
  :remove_by_id!, :to => :master_session
24
- delegate :new_search, :search, :to => :slave_session
24
+ delegate :new_search, :search, :new_more_like_this, :more_like_this, :to => :slave_session
25
25
 
26
26
  def initialize(master_session, slave_session)
27
27
  @master_session, @slave_session = master_session, slave_session
@@ -167,6 +167,15 @@ module Sunspot
167
167
  new_search(*types).execute
168
168
  end
169
169
 
170
+ def more_like_this(object, &block)
171
+ #FIXME should use shards
172
+ new_more_like_this(object, &block).execute
173
+ end
174
+
175
+ def new_more_like_this(object, &block)
176
+ @search_session.new_more_like_this(object, &block)
177
+ end
178
+
170
179
  #
171
180
  # True if any shard session is dirty. Note that directly using the
172
181
  # #commit_if_dirty method is more efficient if that's what you're
@@ -1,3 +1,4 @@
1
+ require 'monitor'
1
2
  require File.join(File.dirname(__FILE__), 'abstract_session_proxy')
2
3
 
3
4
  module Sunspot
@@ -8,10 +9,15 @@ module Sunspot
8
9
  # proxy.
9
10
  #
10
11
  class ThreadLocalSessionProxy < AbstractSessionProxy
12
+ FINALIZER = Proc.new do |object_id|
13
+ Thread.current[:"sunspot_session_#{object_id}"] = nil
14
+ end
15
+
11
16
  # The configuration with which the thread-local sessions are initialized.
12
17
  attr_reader :config
18
+ @@next_id = 0
13
19
 
14
- delegate :batch, :commit, :commit_if_delete_dirty, :commit_if_dirty, :delete_dirty?, :dirty?, :index, :index!, :new_search, :remove, :remove!, :remove_all, :remove_all!, :remove_by_id, :remove_by_id!, :search, :to => :session
20
+ delegate :batch, :commit, :commit_if_delete_dirty, :commit_if_dirty, :delete_dirty?, :dirty?, :index, :index!, :new_search, :remove, :remove!, :remove_all, :remove_all!, :remove_by_id, :remove_by_id!, :search, :more_like_this, :new_more_like_this, :to => :session
15
21
 
16
22
  #
17
23
  # Optionally pass an existing Sunspot::Configuration object. If none is
@@ -20,10 +26,11 @@ module Sunspot
20
26
  #
21
27
  def initialize(config = Sunspot::Configuration.new)
22
28
  @config = config
29
+ ObjectSpace.define_finalizer(self, FINALIZER)
23
30
  end
24
31
 
25
32
  def session #:nodoc:
26
- Thread.current[:sunspot_session] ||= Session.new(config)
33
+ Thread.current[:"sunspot_session_#{object_id}"] ||= Session.new(config)
27
34
  end
28
35
  end
29
36
  end
data/lib/sunspot/setup.rb CHANGED
@@ -12,6 +12,7 @@ module Sunspot
12
12
  @field_factories_cache, @text_field_factories_cache,
13
13
  @dynamic_field_factories_cache = *Array.new(6) { Hash.new }
14
14
  @stored_field_factories_cache = Hash.new { |h, k| h[k] = [] }
15
+ @more_like_this_field_factories_cache = Hash.new { |h, k| h[k] = [] }
15
16
  @dsl = DSL::Fields.new(self)
16
17
  add_field_factory(:class, Type::ClassType.instance)
17
18
  end
@@ -24,13 +25,16 @@ module Sunspot
24
25
  # Add field factory for scope/ordering
25
26
  #
26
27
  def add_field_factory(name, type, options = {}, &block)
27
- stored = options[:stored]
28
+ stored, more_like_this = options[:stored], options[:more_like_this]
28
29
  field_factory = FieldFactory::Static.new(name, type, options, &block)
29
30
  @field_factories[field_factory.signature] = field_factory
30
31
  @field_factories_cache[field_factory.name] = field_factory
31
32
  if stored
32
33
  @stored_field_factories_cache[field_factory.name] << field_factory
33
34
  end
35
+ if more_like_this
36
+ @more_like_this_field_factories_cache[field_factory.name] << field_factory
37
+ end
34
38
  end
35
39
 
36
40
  #
@@ -41,13 +45,16 @@ module Sunspot
41
45
  # field_factories<Array>:: Array of Sunspot::Field objects
42
46
  #
43
47
  def add_text_field_factory(name, options = {}, &block)
44
- stored = options[:stored]
48
+ stored, more_like_this = options[:stored], options[:more_like_this]
45
49
  field_factory = FieldFactory::Static.new(name, Type::TextType.instance, options, &block)
46
50
  @text_field_factories[name] = field_factory
47
51
  @text_field_factories_cache[field_factory.name] = field_factory
48
52
  if stored
49
53
  @stored_field_factories_cache[field_factory.name] << field_factory
50
54
  end
55
+ if more_like_this
56
+ @more_like_this_field_factories_cache[field_factory.name] << field_factory
57
+ end
51
58
  end
52
59
 
53
60
  #
@@ -58,13 +65,16 @@ module Sunspot
58
65
  # field_factories<Array>:: Array of dynamic field objects
59
66
  #
60
67
  def add_dynamic_field_factory(name, type, options = {}, &block)
61
- stored = options[:stored]
68
+ stored, more_like_this = options[:stored], options[:more_like_this]
62
69
  field_factory = FieldFactory::Dynamic.new(name, type, options, &block)
63
70
  @dynamic_field_factories[field_factory.signature] = field_factory
64
71
  @dynamic_field_factories_cache[field_factory.name] = field_factory
65
72
  if stored
66
73
  @stored_field_factories_cache[field_factory.name] << field_factory
67
74
  end
75
+ if more_like_this
76
+ @more_like_this_field_factories_cache[field_factory.name] << field_factory
77
+ end
68
78
  end
69
79
 
70
80
  #
@@ -147,6 +157,16 @@ module Sunspot
147
157
  end
148
158
  end
149
159
 
160
+ #
161
+ # Return one or more more_like_this fields (can be either attribute or text fields)
162
+ # for the given name.
163
+ #
164
+ def more_like_this_fields(field_name)
165
+ @more_like_this_field_factories_cache[field_name.to_sym].map do |field_factory|
166
+ field_factory.build
167
+ end
168
+ end
169
+
150
170
  #
151
171
  # Return the DynamicFieldFactory with the given base name
152
172
  #
@@ -171,6 +191,15 @@ module Sunspot
171
191
  text_field_factories.map { |text_field_factory| text_field_factory.build }
172
192
  end
173
193
 
194
+ #
195
+ # Return all more_like_this fields
196
+ #
197
+ def all_more_like_this_fields
198
+ @more_like_this_field_factories_cache.values.map do |field_factories|
199
+ field_factories.map { |field_factory| field_factory.build }
200
+ end.flatten
201
+ end
202
+
174
203
  #
175
204
  # Get the field_factories associated with this setup as well as all inherited field_factories
176
205
  #