sunspot 0.9.8 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/History.txt +32 -0
  2. data/README.rdoc +40 -3
  3. data/TODO +10 -8
  4. data/VERSION.yml +2 -2
  5. data/bin/sunspot-configure-solr +22 -28
  6. data/bin/sunspot-solr +50 -29
  7. data/lib/sunspot/adapters.rb +1 -1
  8. data/lib/sunspot/composite_setup.rb +13 -15
  9. data/lib/sunspot/configuration.rb +14 -0
  10. data/lib/sunspot/data_extractor.rb +3 -0
  11. data/lib/sunspot/dsl/field_query.rb +33 -6
  12. data/lib/sunspot/dsl/fields.rb +14 -1
  13. data/lib/sunspot/dsl/fulltext.rb +168 -0
  14. data/lib/sunspot/dsl/query.rb +82 -5
  15. data/lib/sunspot/dsl/query_facet.rb +3 -3
  16. data/lib/sunspot/dsl/restriction.rb +7 -7
  17. data/lib/sunspot/dsl/scope.rb +17 -10
  18. data/lib/sunspot/dsl/search.rb +2 -2
  19. data/lib/sunspot/dsl.rb +2 -1
  20. data/lib/sunspot/facet.rb +9 -1
  21. data/lib/sunspot/facet_data.rb +56 -7
  22. data/lib/sunspot/facet_row.rb +2 -0
  23. data/lib/sunspot/field.rb +50 -26
  24. data/lib/sunspot/field_factory.rb +15 -0
  25. data/lib/sunspot/indexer.rb +6 -0
  26. data/lib/sunspot/instantiated_facet.rb +6 -9
  27. data/lib/sunspot/instantiated_facet_row.rb +7 -2
  28. data/lib/sunspot/query/boost_query.rb +20 -0
  29. data/lib/sunspot/query/connective.rb +98 -35
  30. data/lib/sunspot/query/dismax.rb +69 -0
  31. data/lib/sunspot/query/field_facet.rb +1 -22
  32. data/lib/sunspot/query/fulltext_base_query.rb +47 -0
  33. data/lib/sunspot/query/highlighting.rb +43 -0
  34. data/lib/sunspot/query/local.rb +24 -0
  35. data/lib/sunspot/query/pagination.rb +3 -4
  36. data/lib/sunspot/query/query.rb +93 -0
  37. data/lib/sunspot/query/query_facet.rb +14 -9
  38. data/lib/sunspot/query/query_facet_row.rb +3 -3
  39. data/lib/sunspot/query/query_field_facet.rb +10 -3
  40. data/lib/sunspot/query/restriction.rb +36 -15
  41. data/lib/sunspot/query/scope.rb +3 -159
  42. data/lib/sunspot/query/sort.rb +84 -15
  43. data/lib/sunspot/query/text_field_boost.rb +15 -0
  44. data/lib/sunspot/query.rb +2 -188
  45. data/lib/sunspot/schema.rb +7 -25
  46. data/lib/sunspot/search/highlight.rb +38 -0
  47. data/lib/sunspot/search/hit.rb +50 -3
  48. data/lib/sunspot/search.rb +51 -32
  49. data/lib/sunspot/session.rb +32 -12
  50. data/lib/sunspot/setup.rb +47 -10
  51. data/lib/sunspot/text_field_setup.rb +29 -0
  52. data/lib/sunspot/type.rb +4 -4
  53. data/lib/sunspot/util.rb +27 -1
  54. data/lib/sunspot.rb +8 -17
  55. data/solr/solr/conf/schema.xml +54 -40
  56. data/solr/solr/conf/solrconfig.xml +30 -0
  57. data/solr/solr/lib/geoapi-nogenerics-2.1-M2.jar +0 -0
  58. data/solr/solr/lib/gt2-referencing-2.3.1.jar +0 -0
  59. data/solr/solr/lib/jsr108-0.01.jar +0 -0
  60. data/solr/solr/lib/locallucene.jar +0 -0
  61. data/solr/solr/lib/localsolr.jar +0 -0
  62. data/spec/api/indexer/attributes_spec.rb +100 -0
  63. data/spec/api/indexer/batch_spec.rb +46 -0
  64. data/spec/api/indexer/dynamic_fields_spec.rb +33 -0
  65. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  66. data/spec/api/indexer/fulltext_spec.rb +43 -0
  67. data/spec/api/indexer/removal_spec.rb +46 -0
  68. data/spec/api/indexer/spec_helper.rb +1 -0
  69. data/spec/api/indexer_spec.rb +1 -308
  70. data/spec/api/query/connectives_spec.rb +162 -0
  71. data/spec/api/query/dsl_spec.rb +12 -0
  72. data/spec/api/query/dynamic_fields_spec.rb +149 -0
  73. data/spec/api/query/faceting_spec.rb +272 -0
  74. data/spec/api/query/fulltext_spec.rb +193 -0
  75. data/spec/api/query/highlighting_spec.rb +138 -0
  76. data/spec/api/query/local_spec.rb +54 -0
  77. data/spec/api/query/ordering_pagination_spec.rb +95 -0
  78. data/spec/api/query/scope_spec.rb +266 -0
  79. data/spec/api/query/spec_helper.rb +1 -0
  80. data/spec/api/query/text_field_scoping_spec.rb +30 -0
  81. data/spec/api/query/types_spec.rb +20 -0
  82. data/spec/api/search/dynamic_fields_spec.rb +27 -0
  83. data/spec/api/search/faceting_spec.rb +206 -0
  84. data/spec/api/search/highlighting_spec.rb +65 -0
  85. data/spec/api/search/hits_spec.rb +62 -0
  86. data/spec/api/search/results_spec.rb +52 -0
  87. data/spec/api/search/search_spec.rb +23 -0
  88. data/spec/api/search/spec_helper.rb +1 -0
  89. data/spec/api/spec_helper.rb +1 -1
  90. data/spec/helpers/indexer_helper.rb +29 -0
  91. data/spec/helpers/query_helper.rb +13 -0
  92. data/spec/helpers/search_helper.rb +78 -0
  93. data/spec/integration/faceting_spec.rb +1 -1
  94. data/spec/integration/highlighting_spec.rb +22 -0
  95. data/spec/integration/keyword_search_spec.rb +65 -0
  96. data/spec/integration/local_search_spec.rb +56 -0
  97. data/spec/integration/scoped_search_spec.rb +15 -1
  98. data/spec/integration/spec_helper.rb +3 -3
  99. data/spec/mocks/connection.rb +14 -1
  100. data/spec/mocks/photo.rb +1 -1
  101. data/spec/mocks/post.rb +5 -3
  102. data/spec/mocks/super_class.rb +2 -0
  103. data/spec/spec_helper.rb +13 -0
  104. data/tasks/gemspec.rake +18 -7
  105. data/tasks/schema.rake +1 -1
  106. data/tasks/spec.rake +1 -1
  107. data/templates/schema.xml.erb +36 -0
  108. metadata +117 -48
  109. data/lib/sunspot/query/base_query.rb +0 -90
  110. data/lib/sunspot/query/dynamic_query.rb +0 -69
  111. data/lib/sunspot/query/field_query.rb +0 -63
  112. data/spec/api/build_search_spec.rb +0 -1017
  113. data/spec/api/query_spec.rb +0 -153
  114. data/spec/api/search_retrieval_spec.rb +0 -362
  115. data/templates/schema.xml.haml +0 -24
data/lib/sunspot/query.rb CHANGED
@@ -1,191 +1,5 @@
1
- %w(base_query scope field_query connective dynamic_query field_facet query_facet
2
- query_facet_row query_field_facet pagination restriction sort
3
- sort_composite).each do |file|
4
- require File.join(File.dirname(__FILE__), 'query', file)
5
- end
6
-
1
+ %w(connective boost_query dismax field_facet highlighting local pagination restriction query query_facet query_field_facet query_facet_row scope sort sort_composite text_field_boost).each { |file| require(File.join(File.dirname(__FILE__), 'query', file)) }
7
2
  module Sunspot
8
- module Query #:nodoc:
9
- #
10
- # This class encapsulates a query that is to be sent to Solr. The query is
11
- # constructed in the block passed to the Sunspot.search method, using the
12
- # Sunspot::DSL::Query interface. It can also be accessed directly by calling
13
- # #query on a Search object (presumably a not-yet-run one created using
14
- # Sunspot#new_search), which might be more suitable than the DSL when an
15
- # intermediate object has responsibility for building the query dynamically.
16
- #--
17
- # Instances of Query, as well as all of the components it contains, respond to
18
- # the #to_params method, which returns a hash of parameters in the format
19
- # recognized by the solr-ruby API.
20
- #
21
- class Query < FieldQuery
22
- attr_reader :query_facets #:nodoc:
23
-
24
- def initialize(types, setup, configuration) #:nodoc:
25
- @setup = setup
26
- @components = []
27
- @query_facets = {}
28
- @components << @base_query = BaseQuery.new(types, setup)
29
- @components << @pagination = Pagination.new(configuration)
30
- @components << @sort = SortComposite.new
31
- end
32
-
33
- #
34
- # Set the keywords for this query. Keywords are parsed with Solr's dismax
35
- # handler.
36
- #
37
- def keywords=(keywords)
38
- set_keywords(keywords)
39
- end
40
-
41
- #
42
- # Add a component to the query. Used by objects that proxy to the query
43
- # object.
44
- #
45
- # ==== Parameters
46
- #
47
- # component<~to_params>:: Query component to add.
48
- #
49
- def add_component(component) #:nodoc:
50
- @components << component
51
- end
52
-
53
- #
54
- # Sets @start and @rows instance variables using pagination semantics
55
- #
56
- # ==== Parameters
57
- #
58
- # page<Integer>:: Page on which to start
59
- # per_page<Integer>::
60
- # How many rows to display per page. Default taken from
61
- # Sunspot.config.pagination.default_per_page
62
- #
63
- def paginate(page, per_page = nil)
64
- @pagination.page, @pagination.per_page = page, per_page
65
- end
66
-
67
- #
68
- # Add random ordering to the search. This can be added after other
69
- # field-based sorts if desired.
70
- #
71
- def order_by_random
72
- add_sort(Sort.new(RandomField.new))
73
- end
74
-
75
- #
76
- # Representation of this query as solr-ruby parameters. Constructs the hash
77
- # by deep-merging scope and facet parameters, adding in various other
78
- # parameters from instance data.
79
- #
80
- # Note that solr-ruby takes the :q parameter as a separate argument; for
81
- # the sake of consistency, the Query object ignores this fact (the Search
82
- # object extracts it back out).
83
- #
84
- # ==== Returns
85
- #
86
- # Hash:: Representation of query in solr-ruby form
87
- #
88
- def to_params #:nodoc:
89
- params = {}
90
- query_components = []
91
- for component in @components
92
- Util.deep_merge!(params, component.to_params)
93
- end
94
- params
95
- end
96
-
97
- #
98
- # Page that this query will return (used by Sunspot::Search to expose
99
- # pagination)
100
- #
101
- # ==== Returns
102
- #
103
- # Integer:: Page number
104
- #
105
- def page #:nodoc:
106
- @pagination.page
107
- end
108
-
109
- #
110
- # Number of rows per page that this query will return (used by
111
- # Sunspot::Search to expose pagination)
112
- #
113
- # ==== Returns
114
- #
115
- # Integer:: Rows per page
116
- #
117
- def per_page #:nodoc:
118
- @pagination.per_page
119
- end
120
-
121
- #
122
- # Get the query facet with the given name. Used by the Search object to
123
- # match query facet results with the requested query facets.
124
- #
125
- def query_facet(name) #:nodoc:
126
- @query_facets[name.to_sym]
127
- end
128
-
129
- #
130
- # Add a Sort object into this query's sort composite.
131
- #
132
- def add_sort(sort) #:nodoc:
133
- @sort << sort
134
- end
135
-
136
- #
137
- # Set the keywords for this query, along with keyword options. See
138
- # Query::BaseQuery for information on what the options do.
139
- #
140
- def set_keywords(keywords, options = {}) #:nodoc:
141
- @base_query.keywords = keywords
142
- @base_query.keyword_options = options
143
- end
144
-
145
- #
146
- # Pass in search options as a hash. This is not the preferred way of
147
- # building a Sunspot search, but it is made available as experience shows
148
- # Ruby developers like to pass in hashes. Probably nice for quick one-offs
149
- # on the console, anyway.
150
- #
151
- # ==== Options (+options+)
152
- #
153
- # :keywords:: Keyword string for fulltext search
154
- # :conditions::
155
- # Hash of key-value pairs, where keys are field names, and values are one
156
- # of scalar, Array, or Range. Scalars are evaluated as EqualTo
157
- # restrictions; Arrays are AnyOf restrictions, and Ranges are Between
158
- # restrictions.
159
- # :order::
160
- # Order the search results. Either a string or array of strings of the
161
- # form "field_name direction"
162
- # :page::
163
- # Page to use for pagination
164
- # :per_page::
165
- # Number of results to show per page
166
- #
167
- def options=(options) #:nodoc:
168
- if options.has_key?(:keywords)
169
- self.keywords = options[:keywords]
170
- end
171
- if options.has_key?(:conditions)
172
- options[:conditions].each_pair do |field_name, value|
173
- begin
174
- add_shorthand_restriction(field_name, value)
175
- rescue UnrecognizedFieldError
176
- # ignore fields we don't recognize
177
- end
178
- end
179
- end
180
- if options.has_key?(:order)
181
- for order in Array(options[:order])
182
- order_by(*order.split(' '))
183
- end
184
- end
185
- if options.has_key?(:page)
186
- paginate(options[:page], options[:per_page])
187
- end
188
- end
189
- end
3
+ module Query #:nodoc:all
190
4
  end
191
5
  end
@@ -1,15 +1,4 @@
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
1
+ require 'erb'
13
2
 
14
3
  module Sunspot
15
4
  #
@@ -29,7 +18,9 @@ module Sunspot
29
18
  FieldType.new('sfloat', 'SortableFloat', 'f'),
30
19
  FieldType.new('date', 'Date', 'd'),
31
20
  FieldType.new('sint', 'SortableInt', 'i'),
32
- FieldType.new('string', 'Str', 's')
21
+ FieldType.new('string', 'Str', 's'),
22
+ FieldType.new('sdouble', 'SortableDouble', 'e'),
23
+ FieldType.new('slong', 'SortableLong', 'l')
33
24
  ]
34
25
 
35
26
  FIELD_VARIANTS = [
@@ -89,20 +80,11 @@ module Sunspot
89
80
  end
90
81
 
91
82
  #
92
- # Return an XML representation of this schema using the Haml template
83
+ # Return an XML representation of this schema using the ERB template
93
84
  #
94
85
  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)
86
+ template = File.join(File.dirname(__FILE__), '..', '..', 'templates', 'schema.xml.erb')
87
+ ERB.new(File.read(template), nil, '-').result(binding)
106
88
  end
107
89
 
108
90
  private
@@ -0,0 +1,38 @@
1
+ module Sunspot
2
+ class Search
3
+ #
4
+ # A Highlight represents a single highlighted fragment of text from a
5
+ # document. Depending on the highlighting parameters used for search, there
6
+ # may be more than one Highlight object for a given field in a given result.
7
+ #
8
+ class Highlight
9
+ HIGHLIGHT_MATCHER = /@@@hl@@@(.*?)@@@endhl@@@/ #:nodoc:
10
+
11
+ #
12
+ # The name of the field in which the highlight appeared.
13
+ #
14
+ attr_reader :field_name
15
+
16
+ def initialize(field_name, highlight) #:nodoc:
17
+ @field_name = field_name.to_sym
18
+ @highlight = highlight.to_s.strip
19
+ end
20
+
21
+ #
22
+ # Returns the highlighted text with formatting according to the template given in &block.
23
+ # When no block is given, &lt;em&gt; and &lt;/em&gt; are used to surround the highlight.
24
+ #
25
+ # ==== Example
26
+ #
27
+ # search.highlights(:body).first.format { |word| "<strong>#{word}</strong>" }
28
+ #
29
+ def format(&block)
30
+ block ||= proc { |word| "<em>#{word}</em>" }
31
+ @highlight.gsub(HIGHLIGHT_MATCHER) do
32
+ block.call(Regexp.last_match[1])
33
+ end
34
+ end
35
+ alias_method :formatted, :format
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,11 @@
1
1
  module Sunspot
2
2
  class Search
3
+ #
4
+ # Hit objects represent the raw information returned by Solr for a single
5
+ # document. As well as the primary key and class name, hit objects give
6
+ # access to stored field values, keyword relevance score, and geographical
7
+ # distance (for geographical search).
8
+ #
3
9
  class Hit
4
10
  SPECIAL_KEYS = Set.new(%w(id type score)) #:nodoc:
5
11
 
@@ -16,15 +22,34 @@ module Sunspot
16
22
  # is not from a keyword search.
17
23
  #
18
24
  attr_reader :score
25
+ #
26
+ # For geographical searches, this is the distance between the search
27
+ # centerpoint and the document's location. Otherwise, it's nil.
28
+ #
29
+ attr_reader :distance
19
30
 
20
31
  attr_writer :instance #:nodoc:
21
32
 
22
- def initialize(raw_hit, search) #:nodoc:
33
+ def initialize(raw_hit, highlights, search) #:nodoc:
23
34
  @class_name, @primary_key = *raw_hit['id'].match(/([^ ]+) (.+)/)[1..2]
24
35
  @score = raw_hit['score']
36
+ @distance = raw_hit['geo_distance'].to_f if raw_hit['geo_distance']
25
37
  @search = search
26
38
  @stored_values = raw_hit
27
39
  @stored_cache = {}
40
+ @highlights = highlights
41
+ end
42
+
43
+ #
44
+ # Returns all highlights for this hit when called without parameters.
45
+ # When a field_name is provided, returns only the highlight for this field.
46
+ #
47
+ def highlights(field_name = nil)
48
+ if field_name.nil?
49
+ highlights_cache.values.flatten
50
+ else
51
+ highlights_cache[field_name.to_sym]
52
+ end
28
53
  end
29
54
 
30
55
  #
@@ -41,7 +66,7 @@ module Sunspot
41
66
  def stored(field_name)
42
67
  @stored_cache[field_name.to_sym] ||=
43
68
  begin
44
- field = Sunspot::Setup.for(@class_name).field(field_name)
69
+ field = setup.field(field_name)
45
70
  field.cast(@stored_values[field.indexed_name])
46
71
  end
47
72
  end
@@ -58,9 +83,31 @@ module Sunspot
58
83
  @instance
59
84
  end
60
85
 
61
- def inspect
86
+ def inspect #:nodoc:
62
87
  "#<Sunspot::Search::Hit:#{@class_name} #{@primary_key}>"
63
88
  end
89
+
90
+ private
91
+
92
+ def setup
93
+ @setup ||= Sunspot::Setup.for(@class_name)
94
+ end
95
+
96
+ def highlights_cache
97
+ @highlights_cache ||=
98
+ begin
99
+ cache = {}
100
+ if @highlights
101
+ @highlights.each_pair do |indexed_field_name, highlight_strings|
102
+ field_name = indexed_field_name.sub(/_[a-z]+$/, '').to_sym
103
+ cache[field_name] = highlight_strings.map do |highlight_string|
104
+ Highlight.new(field_name, highlight_string)
105
+ end
106
+ end
107
+ end
108
+ cache
109
+ end
110
+ end
64
111
  end
65
112
  end
66
113
  end
@@ -1,4 +1,6 @@
1
- require File.join(File.dirname(__FILE__), 'search', 'hit')
1
+ %w(hit highlight).each do |file|
2
+ require File.join(File.dirname(__FILE__), 'search', file)
3
+ end
2
4
 
3
5
  module Sunspot
4
6
  #
@@ -14,8 +16,9 @@ module Sunspot
14
16
  # in this case.
15
17
  attr_reader :query
16
18
 
17
- def initialize(connection, setup, query) #:nodoc:
19
+ def initialize(connection, setup, query, configuration) #:nodoc:
18
20
  @connection, @setup, @query = connection, setup, query
21
+ @query.paginate(1, configuration.pagination.default_per_page)
19
22
  end
20
23
 
21
24
  #
@@ -25,11 +28,13 @@ module Sunspot
25
28
  # Sunspot#new_search(), you will need to call this method after building the
26
29
  # query.
27
30
  #
28
- def execute!
31
+ def execute
32
+ reset
29
33
  params = @query.to_params
30
34
  @solr_result = @connection.select(params)
31
35
  self
32
36
  end
37
+ alias_method :execute!, :execute #:nodoc: deprecated
33
38
 
34
39
  #
35
40
  # Get the collection of results as instantiated objects. If WillPaginate is
@@ -60,7 +65,7 @@ module Sunspot
60
65
  # Array:: Ordered collection of Hit objects
61
66
  #
62
67
  def hits
63
- @hits ||= solr_response['docs'].map { |doc| Hit.new(doc, self) }
68
+ @hits ||= solr_response['docs'].map { |doc| Hit.new(doc, highlights_for(doc), self) }
64
69
  end
65
70
  alias_method :raw_results, :hits
66
71
 
@@ -76,23 +81,16 @@ module Sunspot
76
81
  end
77
82
 
78
83
  #
79
- # Get the facet object for the given field. This field will need to have
80
- # been requested as a field facet inside the search block.
81
- #
82
- # ==== Parameters
83
- #
84
- # field_name<Symbol>:: field name for which to get the facet
85
- #
86
- # ==== Returns
84
+ # Get the facet object for the given name. `name` can either be the name
85
+ # given to a query facet, or the field name of a field facet. Returns a
86
+ # Sunspot::Facet object.
87
87
  #
88
- # Sunspot::Facet:: Facet object for the given field
89
- #
90
- def facet(field_name)
91
- (@facets_cache ||= {})[field_name.to_sym] ||=
88
+ def facet(name)
89
+ (@facets_cache ||= {})[name.to_sym] ||=
92
90
  begin
93
- facet_data = query_facet_data(field_name) ||
91
+ facet_data = query_facet_data(name) ||
94
92
  begin
95
- field = field(field_name)
93
+ field = field(name)
96
94
  date_facet_data(field) ||
97
95
  FacetData::FieldFacetData.new(@solr_result['facet_counts']['facet_fields'][field.indexed_name], field)
98
96
  end
@@ -144,19 +142,34 @@ module Sunspot
144
142
  # Sunspot::DSL::Search#data_accessor_for method when building searches using
145
143
  # the block DSL.
146
144
  #
147
- def data_accessor_for(clazz)
145
+ def data_accessor_for(clazz) #:nodoc:
148
146
  (@data_accessors ||= {})[clazz.name.to_sym] ||=
149
147
  Adapters::DataAccessor.create(clazz)
150
148
  end
151
149
 
152
150
  #
153
- # Build this search using a DSL block.
151
+ # Build this search using a DSL block. This method can be called more than
152
+ # once on an unexecuted search (e.g., Sunspot.new_search) in order to build
153
+ # a search incrementally.
154
+ #
155
+ # === Example
156
+ #
157
+ # search = Sunspot.new_search(Post)
158
+ # search.build do
159
+ # with(:published_at).less_than Time.now
160
+ # end
161
+ # search.execute!
154
162
  #
155
- def build(&block) #:nodoc:
163
+ def build(&block)
156
164
  Util.instance_eval_or_call(dsl, &block)
157
165
  self
158
166
  end
159
167
 
168
+ #
169
+ # Populate the Hit objects with their instances. This is invoked the first
170
+ # time any hit has its instance requested, and all hits are loaded as a
171
+ # batch.
172
+ #
160
173
  def populate_hits! #:nodoc:
161
174
  id_hit_hash = Hash.new { |h, k| h[k] = {} }
162
175
  hits.each do |hit|
@@ -171,24 +184,18 @@ module Sunspot
171
184
  end
172
185
  end
173
186
 
187
+ def inspect #:nodoc:
188
+ "<Sunspot::Search:#{query.to_params.inspect}>"
189
+ end
190
+
174
191
  private
175
192
 
176
193
  def solr_response
177
194
  @solr_response ||= @solr_result['response']
178
195
  end
179
196
 
180
- def doc_ids
181
- @doc_ids ||= solr_response['docs'].map { |doc| doc['id'] }
182
- end
183
-
184
197
  def dsl
185
- DSL::Search.new(self)
186
- end
187
-
188
- def raw_facet(field)
189
- if field.type == Type::TimeType
190
- @solr_result['facet_counts']['facet_dates'][field.indexed_name]
191
- end || @solr_result['facet_counts']['facet_fields'][field.indexed_name]
198
+ DSL::Search.new(self, @setup)
192
199
  end
193
200
 
194
201
  def date_facet_data(field)
@@ -212,8 +219,20 @@ module Sunspot
212
219
  end
213
220
  end
214
221
 
222
+ def highlights_for(doc)
223
+ if @solr_result['highlighting']
224
+ @solr_result['highlighting'][doc['id']]
225
+ end
226
+ end
227
+
215
228
  def field(name)
216
229
  @setup.field(name)
217
230
  end
231
+
232
+ # Clear out all the cached ivars so the search can be called again.
233
+ def reset
234
+ @results = @hits = @total = @facets_cache =
235
+ @dynamic_facets_cache = @solr_response = @doc_ids = nil
236
+ end
218
237
  end
219
238
  end
@@ -29,10 +29,11 @@ module Sunspot
29
29
  # connection. Usually you will want to stick with the default arguments
30
30
  # when instantiating your own sessions.
31
31
  #
32
- def initialize(config = Configuration.build, connection = nil)
32
+ def initialize(config = Configuration.build, connection = nil, master_connection = nil)
33
33
  @config = config
34
34
  yield(@config) if block_given?
35
35
  @connection = connection
36
+ @master_connection = master_connection
36
37
  @updates = 0
37
38
  end
38
39
 
@@ -41,23 +42,24 @@ module Sunspot
41
42
  #
42
43
  def new_search(*types)
43
44
  types.flatten!
45
+ if types.empty?
46
+ raise(ArgumentError, "You must specify at least one type to search")
47
+ end
44
48
  setup =
45
49
  if types.length == 1
46
50
  Setup.for(types.first)
47
51
  else
48
52
  CompositeSetup.for(types)
49
53
  end
50
- Search.new(connection, setup, Query::Query.new(types, setup, @config))
54
+ Search.new(connection, setup, Query::Query.new(types), @config)
51
55
  end
52
56
 
53
57
  #
54
58
  # See Sunspot.search
55
59
  #
56
60
  def search(*types, &block)
57
- options = types.last.is_a?(Hash) ? types.pop : {}
58
61
  search = new_search(*types)
59
62
  search.build(&block) if block
60
- search.query.options = options
61
63
  search.execute!
62
64
  end
63
65
 
@@ -83,7 +85,7 @@ module Sunspot
83
85
  #
84
86
  def commit
85
87
  @updates = 0
86
- connection.commit
88
+ master_connection.commit
87
89
  end
88
90
 
89
91
  #
@@ -133,7 +135,7 @@ module Sunspot
133
135
  classes.flatten!
134
136
  if classes.empty?
135
137
  @updates += 1
136
- Indexer.remove_all(connection)
138
+ Indexer.remove_all(master_connection)
137
139
  else
138
140
  @updates += classes.length
139
141
  for clazz in classes
@@ -187,16 +189,34 @@ module Sunspot
187
189
  @connection ||=
188
190
  begin
189
191
  self.class.connection_class.connect(:url => config.solr.url, :adapter => config.http_client)
190
- # connection = self.class.connection_class.new(
191
- # RSolr::Adapter::HTTP.new(:url => config.solr.url)
192
- # )
193
- # connection.adapter.connector.adapter_name = config.http_client
194
- # connection
192
+ end
193
+ end
194
+
195
+ #
196
+ # Retrieve the Solr connection to the master for this session, creating one
197
+ # if it does not already exist.
198
+ #
199
+ # ==== Returns
200
+ #
201
+ # Solr::Connection:: The connection for this session
202
+ #
203
+ def master_connection
204
+ @master_connection ||=
205
+ begin
206
+ if config.master_solr.url && config.master_solr.url != config.solr.url
207
+ master_connection = self.class.connection_class.new(
208
+ RSolr::Adapter::HTTP.new(:url => config.master_solr.url)
209
+ )
210
+ master_connection.adapter.connector.adapter_name = config.http_client
211
+ master_connection
212
+ else
213
+ connection
214
+ end
195
215
  end
196
216
  end
197
217
 
198
218
  def indexer
199
- @indexer ||= Indexer.new(connection)
219
+ @indexer ||= Indexer.new(master_connection)
200
220
  end
201
221
  end
202
222
  end