outoftime-sunspot 0.8.9 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/README.rdoc +13 -21
  2. data/Rakefile +0 -2
  3. data/TODO +2 -15
  4. data/VERSION.yml +2 -2
  5. data/bin/sunspot-configure-solr +46 -0
  6. data/bin/sunspot-solr +15 -7
  7. data/lib/sunspot/adapters.rb +5 -1
  8. data/lib/sunspot/composite_setup.rb +186 -0
  9. data/lib/sunspot/configuration.rb +7 -1
  10. data/lib/sunspot/data_extractor.rb +10 -0
  11. data/lib/sunspot/date_facet.rb +36 -0
  12. data/lib/sunspot/date_facet_row.rb +17 -0
  13. data/lib/sunspot/dsl/field_query.rb +72 -0
  14. data/lib/sunspot/dsl/fields.rb +30 -3
  15. data/lib/sunspot/dsl/query.rb +16 -35
  16. data/lib/sunspot/dsl/query_facet.rb +31 -0
  17. data/lib/sunspot/dsl/scope.rb +76 -20
  18. data/lib/sunspot/dsl/search.rb +30 -0
  19. data/lib/sunspot/dsl.rb +1 -1
  20. data/lib/sunspot/facet.rb +17 -3
  21. data/lib/sunspot/facet_row.rb +4 -4
  22. data/lib/sunspot/field.rb +130 -207
  23. data/lib/sunspot/field_factory.rb +126 -0
  24. data/lib/sunspot/indexer.rb +61 -14
  25. data/lib/sunspot/instantiated_facet.rb +38 -0
  26. data/lib/sunspot/instantiated_facet_row.rb +12 -0
  27. data/lib/sunspot/query/base_query.rb +90 -0
  28. data/lib/sunspot/query/connective.rb +77 -0
  29. data/lib/sunspot/query/dynamic_query.rb +39 -56
  30. data/lib/sunspot/query/field_facet.rb +132 -4
  31. data/lib/sunspot/query/field_query.rb +57 -0
  32. data/lib/sunspot/query/pagination.rb +1 -1
  33. data/lib/sunspot/query/query_facet.rb +72 -0
  34. data/lib/sunspot/query/query_facet_row.rb +19 -0
  35. data/lib/sunspot/query/restriction.rb +9 -7
  36. data/lib/sunspot/query/scope.rb +165 -0
  37. data/lib/sunspot/query/sort.rb +17 -14
  38. data/lib/sunspot/query/sort_composite.rb +33 -0
  39. data/lib/sunspot/query.rb +162 -351
  40. data/lib/sunspot/query_facet.rb +33 -0
  41. data/lib/sunspot/query_facet_row.rb +21 -0
  42. data/lib/sunspot/schema.rb +165 -0
  43. data/lib/sunspot/search/hit.rb +62 -0
  44. data/lib/sunspot/search.rb +104 -41
  45. data/lib/sunspot/session.rb +64 -32
  46. data/lib/sunspot/setup.rb +119 -48
  47. data/lib/sunspot/type.rb +48 -2
  48. data/lib/sunspot.rb +74 -8
  49. data/solr/solr/conf/schema.xml +44 -225
  50. data/spec/api/build_search_spec.rb +557 -63
  51. data/spec/api/indexer_spec.rb +156 -74
  52. data/spec/api/query_spec.rb +55 -31
  53. data/spec/api/search_retrieval_spec.rb +210 -33
  54. data/spec/api/session_spec.rb +81 -26
  55. data/spec/api/sunspot_spec.rb +5 -7
  56. data/spec/integration/faceting_spec.rb +130 -0
  57. data/spec/integration/keyword_search_spec.rb +72 -31
  58. data/spec/integration/scoped_search_spec.rb +13 -0
  59. data/spec/integration/stored_fields_spec.rb +10 -0
  60. data/spec/mocks/blog.rb +3 -0
  61. data/spec/mocks/comment.rb +12 -23
  62. data/spec/mocks/connection.rb +84 -0
  63. data/spec/mocks/mock_adapter.rb +11 -3
  64. data/spec/mocks/mock_record.rb +41 -0
  65. data/spec/mocks/photo.rb +8 -0
  66. data/spec/mocks/post.rb +18 -23
  67. data/spec/spec_helper.rb +29 -14
  68. data/tasks/gemspec.rake +4 -3
  69. data/tasks/rdoc.rake +2 -2
  70. data/tasks/schema.rake +19 -0
  71. data/templates/schema.xml.haml +24 -0
  72. metadata +48 -7
  73. data/spec/mocks/base_class.rb +0 -2
@@ -0,0 +1,165 @@
1
+ using_rubygems = false
2
+ begin
3
+ require 'haml'
4
+ rescue LoadError => e
5
+ if using_rubygems
6
+ raise(e)
7
+ else
8
+ using_rubygems = true
9
+ require 'rubygems'
10
+ retry
11
+ end
12
+ end
13
+
14
+ module Sunspot
15
+ #
16
+ # Object that encapsulates schema information for building a Solr schema.xml
17
+ # file. This class is used by the schema:compile task as well as the
18
+ # sunspot-configure-solr executable.
19
+ #
20
+ class Schema #:nodoc:all
21
+ FieldType = Struct.new(:name, :class_name, :suffix)
22
+ FieldVariant = Struct.new(:attribute, :suffix)
23
+
24
+ DEFAULT_TOKENIZER = 'solr.StandardTokenizerFactory'
25
+ DEFAULT_FILTERS = %w(solr.StandardFilterFactory solr.LowerCaseFilterFactory)
26
+
27
+ FIELD_TYPES = [
28
+ FieldType.new('boolean', 'Bool', 'b'),
29
+ FieldType.new('sfloat', 'SortableFloat', 'f'),
30
+ FieldType.new('date', 'Date', 'd'),
31
+ FieldType.new('sint', 'SortableInt', 'i'),
32
+ FieldType.new('string', 'Str', 's')
33
+ ]
34
+
35
+ FIELD_VARIANTS = [
36
+ FieldVariant.new('multiValued', 'm'),
37
+ FieldVariant.new('stored', 's')
38
+ ]
39
+
40
+ attr_reader :tokenizer, :filters
41
+
42
+ def initialize
43
+ @tokenizer = DEFAULT_TOKENIZER
44
+ @filters = DEFAULT_FILTERS.dup
45
+ end
46
+
47
+ #
48
+ # Attribute field types defined in the schema
49
+ #
50
+ def types
51
+ FIELD_TYPES
52
+ end
53
+
54
+ #
55
+ # DynamicField instances representing all the available types and variants
56
+ #
57
+ def dynamic_fields
58
+ fields = []
59
+ for field_variants in variant_combinations
60
+ for type in FIELD_TYPES
61
+ fields << DynamicField.new(type, field_variants)
62
+ end
63
+ end
64
+ fields
65
+ end
66
+
67
+ #
68
+ # Which tokenizer to use for text fields
69
+ #
70
+ def tokenizer=(tokenizer)
71
+ @tokenizer =
72
+ if tokenizer =~ /\./
73
+ tokenizer
74
+ else
75
+ "solr.#{tokenizer}TokenizerFactory"
76
+ end
77
+ end
78
+
79
+ #
80
+ # Add a filter for text field tokenization
81
+ #
82
+ def add_filter(filter)
83
+ @filters <<
84
+ if filter =~ /\./
85
+ filter
86
+ else
87
+ "solr.#{filter}FilterFactory"
88
+ end
89
+ end
90
+
91
+ #
92
+ # Return an XML representation of this schema using the Haml template
93
+ #
94
+ def to_xml
95
+ template = File.read(
96
+ File.join(
97
+ File.dirname(__FILE__),
98
+ '..',
99
+ '..',
100
+ 'templates',
101
+ 'schema.xml.haml'
102
+ )
103
+ )
104
+ engine = Haml::Engine.new(template)
105
+ engine.render(Object.new, :schema => self)
106
+ end
107
+
108
+ private
109
+
110
+ #
111
+ # All of the possible combinations of variants
112
+ #
113
+ def variant_combinations
114
+ combinations = []
115
+ 0.upto(2 ** FIELD_VARIANTS.length - 1) do |b|
116
+ combinations << combination = []
117
+ FIELD_VARIANTS.each_with_index do |variant, i|
118
+ combination << variant if b & 1<<i > 0
119
+ end
120
+ end
121
+ combinations
122
+ end
123
+
124
+ #
125
+ # Represents a dynamic field (in the Solr schema sense, not the Sunspot
126
+ # sense).
127
+ #
128
+ class DynamicField
129
+ def initialize(type, field_variants)
130
+ @type, @field_variants = type, field_variants
131
+ end
132
+
133
+ #
134
+ # Name of the field in the schema
135
+ #
136
+ def name
137
+ variant_suffixes = @field_variants.map { |variant| variant.suffix }.join
138
+ "*_#{@type.suffix}#{variant_suffixes}"
139
+ end
140
+
141
+ #
142
+ # Name of the type as defined in the schema
143
+ #
144
+ def type
145
+ @type.name
146
+ end
147
+
148
+ #
149
+ # Implement magic methods to ask if a field is of a particular variant.
150
+ # Returns "true" if the field is of that variant and "false" otherwise.
151
+ #
152
+ def method_missing(name, *args, &block)
153
+ if name.to_s =~ /\?$/ && args.empty?
154
+ if @field_variants.any? { |variant| "#{variant.attribute}?" == name.to_s }
155
+ 'true'
156
+ else
157
+ 'false'
158
+ end
159
+ else
160
+ super(name.to_sym, *args, &block)
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,62 @@
1
+ module Sunspot
2
+ class Search
3
+ class Hit
4
+ SPECIAL_KEYS = Set.new(%w(id type score)) #:nodoc:
5
+
6
+ #
7
+ # Primary key of object associated with this hit, as string.
8
+ #
9
+ attr_reader :primary_key
10
+ #
11
+ # Class name of object associated with this hit, as string.
12
+ #
13
+ attr_reader :class_name
14
+ #
15
+ # Keyword relevance score associated with this result. Nil if this hit
16
+ # is not from a keyword search.
17
+ #
18
+ attr_reader :score
19
+
20
+ attr_writer :instance #:nodoc:
21
+
22
+ def initialize(raw_hit, search) #:nodoc:
23
+ @class_name, @primary_key = *raw_hit['id'].match(/([^ ]+) (.+)/)[1..2]
24
+ @score = raw_hit['score']
25
+ @search = search
26
+ @stored_values = raw_hit
27
+ @stored_cache = {}
28
+ end
29
+
30
+ #
31
+ # Retrieve stored field value. For any attribute field configured with
32
+ # :stored => true, the Hit object will contain the stored value for
33
+ # that field. The value of this field will be typecast according to the
34
+ # type of the field.
35
+ #
36
+ # ==== Parameters
37
+ #
38
+ # field_name<Symbol>::
39
+ # The name of the field for which to retrieve the stored value.
40
+ #
41
+ def stored(field_name)
42
+ @stored_cache[field_name.to_sym] ||=
43
+ begin
44
+ field = Sunspot::Setup.for(@class_name).field(field_name)
45
+ field.cast(@stored_values[field.indexed_name])
46
+ end
47
+ end
48
+
49
+ #
50
+ # Retrieve the instance associated with this hit. This is lazy-loaded, but
51
+ # the first time it is called on any hit, all the hits for the search will
52
+ # load their instances using the adapter's #load_all method.
53
+ #
54
+ def instance
55
+ if @instance.nil?
56
+ @search.populate_hits!
57
+ end
58
+ @instance
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,22 +1,21 @@
1
+ require File.join(File.dirname(__FILE__), 'search', 'hit')
2
+
1
3
  module Sunspot
2
4
  #
3
5
  # This class encapsulates the results of a Solr search. It provides access
4
6
  # to search results, total result count, facets, and pagination information.
5
- # Instances of Search are returned by the Sunspot.search method.
7
+ # Instances of Search are returned by the Sunspot.search and
8
+ # Sunspot.new_search methods.
6
9
  #
7
10
  class Search
8
- # Objects of this class are returned by the #raw_results method.
9
- RawResult = Struct.new(:class_name, :primary_key)
10
-
11
11
  # Query information for this search. If you wish to build the query without
12
12
  # using the search DSL, this method allows you to access the query API
13
13
  # directly. See Sunspot#new_search for how to construct the search object
14
14
  # in this case.
15
15
  attr_reader :query
16
16
 
17
- def initialize(connection, query) #:nodoc:
18
- @connection = connection
19
- @query = query
17
+ def initialize(connection, setup, query) #:nodoc:
18
+ @connection, @setup, @query = connection, setup, query
20
19
  end
21
20
 
22
21
  #
@@ -28,7 +27,7 @@ module Sunspot
28
27
  #
29
28
  def execute!
30
29
  params = @query.to_params
31
- @solr_result = @connection.query(params.delete(:q), params)
30
+ @solr_result = @connection.select(params)
32
31
  self
33
32
  end
34
33
 
@@ -43,27 +42,27 @@ module Sunspot
43
42
  #
44
43
  def results
45
44
  @results ||= if @query.page && defined?(WillPaginate::Collection)
46
- WillPaginate::Collection.create(@query.page, @query.per_page, @solr_result.total_hits) do |pager|
47
- pager.replace(result_objects)
45
+ WillPaginate::Collection.create(@query.page, @query.per_page, total) do |pager|
46
+ pager.replace(hits.map { |hit| hit.instance })
48
47
  end
49
48
  else
50
- result_objects
49
+ hits.map { |hit| hit.instance }
51
50
  end
52
51
  end
53
52
 
54
53
  #
55
- # Access raw results without instantiating objects from persistent storage.
56
- # This may be useful if you are using search as an intermediate step in data
57
- # retrieval. Returns an ordered collection of objects that respond to
58
- # #class_name and #primary_key
54
+ # Access raw Solr result information. Returns a collection of Hit objects
55
+ # that contain the class name, primary key, keyword relevance score (if
56
+ # applicable), and any stored fields.
59
57
  #
60
58
  # ==== Returns
61
59
  #
62
- # Array:: Ordered collection of raw results
60
+ # Array:: Ordered collection of Hit objects
63
61
  #
64
- def raw_results
65
- @raw_results ||= hit_ids.map { |hit_id| RawResult.new(*hit_id.match(/([^ ]+) (.+)/)[1..2]) }
62
+ def hits
63
+ @hits ||= solr_response['docs'].map { |doc| Hit.new(doc, self) }
66
64
  end
65
+ alias_method :raw_results, :hits
67
66
 
68
67
  #
69
68
  # The total number of documents matching the query parameters
@@ -73,7 +72,7 @@ module Sunspot
73
72
  # Integer:: Total matching documents
74
73
  #
75
74
  def total
76
- @total ||= @solr_result.total_hits
75
+ @total ||= solr_response['numFound']
77
76
  end
78
77
 
79
78
  #
@@ -91,8 +90,15 @@ module Sunspot
91
90
  def facet(field_name)
92
91
  (@facets_cache ||= {})[field_name.to_sym] ||=
93
92
  begin
94
- field = @query.field(field_name)
95
- Facet.new(@solr_result.field_facets(field.indexed_name), field)
93
+ query_facet(field_name) ||
94
+ begin
95
+ field = field(field_name)
96
+ date_facet(field) ||
97
+ begin
98
+ facet_class = field.reference ? InstantiatedFacet : Facet
99
+ facet_class.new(@solr_result['facet_counts']['facet_fields'][field.indexed_name], field)
100
+ end
101
+ end
96
102
  end
97
103
  end
98
104
 
@@ -125,35 +131,92 @@ module Sunspot
125
131
  def dynamic_facet(base_name, dynamic_name)
126
132
  (@dynamic_facets_cache ||= {})[[base_name.to_sym, dynamic_name.to_sym]] ||=
127
133
  begin
128
- field = @query.dynamic_field(base_name).build(dynamic_name)
129
- Facet.new(@solr_result.field_facets(field.indexed_name), field)
134
+ field = @setup.dynamic_field_factory(base_name).build(dynamic_name)
135
+ Facet.new(@solr_result['facet_counts']['facet_fields'][field.indexed_name], field)
130
136
  end
131
137
  end
132
138
 
133
- private
139
+ #
140
+ # Get the data accessor that will be used to load a particular class out of
141
+ # persistent storage. Data accessors can implement any methods that may be
142
+ # useful for refining how data is loaded out of storage. When building a
143
+ # search manually (e.g., using the Sunspot#new_search method), this should
144
+ # be used before calling #execute(). Use the
145
+ # Sunspot::DSL::Search#data_accessor_for method when building searches using
146
+ # the block DSL.
147
+ #
148
+ def data_accessor_for(clazz)
149
+ (@data_accessors ||= {})[clazz.name.to_sym] ||=
150
+ Adapters::DataAccessor.create(clazz)
151
+ end
134
152
 
135
153
  #
136
- # Collection of instantiated result objects corresponding to the results
137
- # returned by Solr.
138
- #
139
- # ==== Returns
154
+ # Build this search using a DSL block.
140
155
  #
141
- # Array:: Collection of instantiated result objects
142
- #
143
- def result_objects
144
- raw_results.inject({}) do |type_id_hash, raw_result|
145
- (type_id_hash[raw_result.class_name] ||= []) << raw_result.primary_key
146
- type_id_hash
147
- end.inject([]) do |results, pair|
148
- type_name, ids = pair
149
- results.concat(Adapters::DataAccessor.create(Util.full_const_get(type_name)).load_all(ids))
150
- end.sort_by do |result|
151
- hit_ids.index(Adapters::InstanceAdapter.adapt(result).index_id)
156
+ def build(&block) #:nodoc:
157
+ Util.instance_eval_or_call(dsl, &block)
158
+ self
159
+ end
160
+
161
+ def populate_hits! #:nodoc:
162
+ type_hit_hash = Hash.new { |h, k| h[k] = [] }
163
+ id_hit_hash = {}
164
+ for hit in hits
165
+ type_hit_hash[hit.class_name] << hit
166
+ id_hit_hash[hit.primary_key] = hit
167
+ end
168
+ type_hit_hash.each_pair do |class_name, hits|
169
+ ids = hits.map { |hit| hit.primary_key }
170
+ for instance in data_accessor_for(Util.full_const_get(class_name)).load_all(ids)
171
+ hit = id_hit_hash[Adapters::InstanceAdapter.adapt(instance).id.to_s]
172
+ hit.instance = instance
173
+ end
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def solr_response
180
+ @solr_response ||= @solr_result['response']
181
+ end
182
+
183
+ def doc_ids
184
+ @doc_ids ||= solr_response['docs'].map { |doc| doc['id'] }
185
+ end
186
+
187
+ def dsl
188
+ DSL::Search.new(self)
189
+ end
190
+
191
+ def raw_facet(field)
192
+ if field.type == Type::TimeType
193
+ @solr_result['facet_counts']['facet_dates'][field.indexed_name]
194
+ end || @solr_result['facet_counts']['facet_fields'][field.indexed_name]
195
+ end
196
+
197
+ def date_facet(field)
198
+ if field.type == Type::TimeType
199
+ if @solr_result['facet_counts'].has_key?('facet_dates')
200
+ if facet_result = @solr_result['facet_counts']['facet_dates'][field.indexed_name]
201
+ DateFacet.new(facet_result, field)
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ def query_facet(name)
208
+ if query_facet = @query.query_facet(name.to_sym)
209
+ if @solr_result['facet_counts'].has_key?('facet_queries')
210
+ QueryFacet.new(
211
+ query_facet,
212
+ @solr_result['facet_counts']['facet_queries']
213
+ )
214
+ end
152
215
  end
153
216
  end
154
217
 
155
- def hit_ids
156
- @hit_ids ||= @solr_result.hits.map { |hit| hit['id'] }
218
+ def field(name)
219
+ @setup.field(name)
157
220
  end
158
221
  end
159
222
  end
@@ -8,6 +8,20 @@ module Sunspot
8
8
  # again here.
9
9
  #
10
10
  class Session
11
+ class <<self
12
+ attr_writer :connection_class #:nodoc:
13
+
14
+ #
15
+ # For testing purposes
16
+ #
17
+ def connection_class #:nodoc:
18
+ @connection_class ||= RSolr::Connection
19
+ end
20
+ end
21
+
22
+ #
23
+ # Sunspot::Configuration object for this session
24
+ #
11
25
  attr_reader :config
12
26
 
13
27
  #
@@ -27,7 +41,13 @@ module Sunspot
27
41
  #
28
42
  def new_search(*types)
29
43
  types.flatten!
30
- Search.new(connection, Query.new(types, @config))
44
+ setup =
45
+ if types.length == 1
46
+ Setup.for(types.first)
47
+ else
48
+ CompositeSetup.for(types)
49
+ end
50
+ Search.new(connection, setup, Query::Query.new(setup, @config))
31
51
  end
32
52
 
33
53
  #
@@ -36,7 +56,7 @@ module Sunspot
36
56
  def search(*types, &block)
37
57
  options = types.last.is_a?(Hash) ? types.pop : {}
38
58
  search = new_search(*types)
39
- search.query.build(&block) if block
59
+ search.build(&block) if block
40
60
  search.query.options = options
41
61
  search.execute!
42
62
  end
@@ -44,20 +64,10 @@ module Sunspot
44
64
  #
45
65
  # See Sunspot.index
46
66
  #
47
- #--
48
- # FIXME The fact that we have to break this out by class and index each
49
- # class separately is artificial, imposed by the fact that indexers
50
- # are initialized with a particular setup, and are responsible for
51
- # sending add messages to Solr. It might be worth considering a
52
- # singleton indexer (per session) and have the indexer itself find
53
- # the appropriate setup to use for each object.
54
- #
55
67
  def index(*objects)
56
68
  objects.flatten!
57
69
  @updates += objects.length
58
- objects.group_by { |object| object.class }.to_hash.each_pair do |clazz, objs|
59
- indexer_for(objs.first).add(objs)
60
- end
70
+ indexer.add(objects)
61
71
  end
62
72
 
63
73
  #
@@ -83,7 +93,7 @@ module Sunspot
83
93
  objects.flatten!
84
94
  @updates += objects.length
85
95
  for object in objects
86
- indexer_for(object).remove(object)
96
+ indexer.remove(object)
87
97
  end
88
98
  end
89
99
 
@@ -95,6 +105,27 @@ module Sunspot
95
105
  commit
96
106
  end
97
107
 
108
+ #
109
+ # See Sunspot.remove_by_id
110
+ #
111
+ def remove_by_id(clazz, id)
112
+ class_name =
113
+ if clazz.is_a?(Class)
114
+ clazz.name
115
+ else
116
+ clazz.to_s
117
+ end
118
+ indexer.remove_by_id(class_name, id)
119
+ end
120
+
121
+ #
122
+ # See Sunspot.remove_by_id!
123
+ #
124
+ def remove_by_id!(clazz, id)
125
+ remove_by_id(clazz, id)
126
+ commit
127
+ end
128
+
98
129
  #
99
130
  # See Sunspot.remove_all
100
131
  #
@@ -106,7 +137,7 @@ module Sunspot
106
137
  else
107
138
  @updates += classes.length
108
139
  for clazz in classes
109
- Setup.for(clazz).indexer(connection).remove_all
140
+ indexer.remove_all(clazz)
110
141
  end
111
142
  end
112
143
  end
@@ -133,26 +164,16 @@ module Sunspot
133
164
  commit if dirty?
134
165
  end
135
166
 
136
- private
137
-
138
167
  #
139
- # Get the Setup object for the given object's class.
140
- #
141
- # ==== Parameters
142
- #
143
- # object<Object>:: The object whose setup is to be retrieved
144
- #
145
- # ==== Returns
146
- #
147
- # Sunspot::Setup:: The setup for the object's class
168
+ # See Sunspot.batch
148
169
  #
149
- def setup_for(object)
150
- Setup.for(object.class) || raise(NoSetupError, "Sunspot is not configured for #{object.class.inspect}")
170
+ def batch
171
+ indexer.start_batch
172
+ yield
173
+ indexer.flush_batch
151
174
  end
152
175
 
153
- def indexer_for(object)
154
- setup_for(object).indexer(connection)
155
- end
176
+ private
156
177
 
157
178
  #
158
179
  # Retrieve the Solr connection for this session, creating one if it does not
@@ -163,7 +184,18 @@ module Sunspot
163
184
  # Solr::Connection:: The connection for this session
164
185
  #
165
186
  def connection
166
- @connection ||= Solr::Connection.new(config.solr.url)
187
+ @connection ||=
188
+ begin
189
+ connection = self.class.connection_class.new(
190
+ RSolr::Adapter::HTTP.new(:url => config.solr.url)
191
+ )
192
+ connection.adapter.connector.adapter_name = config.http_client
193
+ connection
194
+ end
195
+ end
196
+
197
+ def indexer
198
+ @indexer ||= Indexer.new(connection)
167
199
  end
168
200
  end
169
201
  end