thinking-sphinx 2.0.7 → 2.0.8

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.
@@ -1,3 +1,5 @@
1
+ require 'blankslate'
2
+
1
3
  module ThinkingSphinx
2
4
  class Index
3
5
  # The Builder class is the core for the index definition block processing.
@@ -11,36 +13,29 @@ module ThinkingSphinx
11
13
  # your indexes. #where provides a method to add manual SQL conditions, and
12
14
  # set_property allows you to set some settings on a per-index basis. Check
13
15
  # out each method's documentation for better ideas of usage.
14
- #
15
- class Builder
16
- instance_methods.grep(/^[^_]/).each { |method|
17
- next if method.to_s == "instance_eval"
18
- define_method(method) {
19
- caller.grep(/irb.completion/).empty? ? method_missing(method) : super
20
- }
21
- }
22
-
16
+ #
17
+ class Builder < BlankSlate
23
18
  def self.generate(model, name = nil, &block)
24
19
  index = ThinkingSphinx::Index.new(model)
25
20
  index.name = name unless name.nil?
26
-
21
+
27
22
  Builder.new(index, &block) if block_given?
28
-
23
+
29
24
  index.delta_object = ThinkingSphinx::Deltas.parse index
30
25
  index
31
26
  end
32
-
27
+
33
28
  def initialize(index, &block)
34
29
  @index = index
35
30
  @explicit_source = false
36
-
31
+
37
32
  self.instance_eval &block
38
-
33
+
39
34
  if no_fields?
40
35
  raise "At least one field is necessary for an index"
41
36
  end
42
37
  end
43
-
38
+
44
39
  def define_source(&block)
45
40
  if @explicit_source
46
41
  @source = ThinkingSphinx::Source.new(@index)
@@ -48,10 +43,10 @@ module ThinkingSphinx
48
43
  else
49
44
  @explicit_source = true
50
45
  end
51
-
46
+
52
47
  self.instance_eval &block
53
48
  end
54
-
49
+
55
50
  # This is how you add fields - the strings Sphinx looks at - to your
56
51
  # index. Technically, to use this method, you need to pass in some
57
52
  # columns and options - but there's some neat method_missing stuff
@@ -63,26 +58,26 @@ module ThinkingSphinx
63
58
  # field.
64
59
  #
65
60
  # Adding Single-Column Fields:
66
- #
61
+ #
67
62
  # You can use symbols or methods - and can chain methods together to
68
63
  # get access down the associations tree.
69
- #
64
+ #
70
65
  # indexes :id, :as => :my_id
71
66
  # indexes :name, :sortable => true
72
67
  # indexes first_name, last_name, :sortable => true
73
68
  # indexes users.posts.content, :as => :post_content
74
69
  # indexes users(:id), :as => :user_ids
75
70
  #
76
- # Keep in mind that if any keywords for Ruby methods - such as id or
71
+ # Keep in mind that if any keywords for Ruby methods - such as id or
77
72
  # name - clash with your column names, you need to use the symbol
78
73
  # version (see the first, second and last examples above).
79
74
  #
80
75
  # If you specify multiple columns (example #2), a field will be created
81
76
  # for each. Don't use the :as option in this case. If you want to merge
82
77
  # those columns together, continue reading.
83
- #
78
+ #
84
79
  # Adding Multi-Column Fields:
85
- #
80
+ #
86
81
  # indexes [first_name, last_name], :as => :name
87
82
  # indexes [location, parent.location], :as => :location
88
83
  #
@@ -90,7 +85,7 @@ module ThinkingSphinx
90
85
  # them in an Array, as shown by the above examples. There's no
91
86
  # limitations on whether they're symbols or methods or what level of
92
87
  # associations they come from.
93
- #
88
+ #
94
89
  # Adding SQL Fragment Fields
95
90
  #
96
91
  # You can also define a field using an SQL fragment, useful for when
@@ -102,37 +97,37 @@ module ThinkingSphinx
102
97
  options = args.extract_options!
103
98
  args.each do |columns|
104
99
  field = Field.new(source, FauxColumn.coerce(columns), options)
105
-
100
+
106
101
  add_sort_attribute field, options if field.sortable
107
102
  add_facet_attribute field, options if field.faceted
108
103
  end
109
104
  end
110
-
105
+
111
106
  # This is the method to add attributes to your index (hence why it is
112
107
  # aliased as 'attribute'). The syntax is the same as #indexes, so use
113
108
  # that as starting point, but keep in mind the following points.
114
- #
109
+ #
115
110
  # An attribute can have an alias (the :as option), but it is always
116
111
  # sortable - so you don't need to explicitly request that. You _can_
117
112
  # specify the data type of the attribute (the :type option), but the
118
113
  # code's pretty good at figuring that out itself from peering into the
119
114
  # database.
120
- #
115
+ #
121
116
  # Attributes are limited to the following types: integers, floats,
122
117
  # datetimes (converted to timestamps), booleans, strings and MVAs
123
118
  # (:multi). Don't forget that Sphinx converts string attributes to
124
119
  # integers, which are useful for sorting, but that's about it.
125
- #
120
+ #
126
121
  # Collection of integers are known as multi-value attributes (MVAs).
127
122
  # Generally these would be through a has_many relationship, like in this
128
123
  # example:
129
- #
124
+ #
130
125
  # has posts(:id), :as => :post_ids
131
- #
126
+ #
132
127
  # This allows you to filter on any of the values tied to a specific
133
128
  # record. Might be best to read through the Sphinx documentation to get
134
129
  # a better idea of that though.
135
- #
130
+ #
136
131
  # Adding SQL Fragment Attributes
137
132
  #
138
133
  # You can also define an attribute using an SQL fragment, useful for
@@ -140,68 +135,68 @@ module ThinkingSphinx
140
135
  # the type of the attribute though:
141
136
  #
142
137
  # has "age < 18", :as => :minor, :type => :boolean
143
- #
138
+ #
144
139
  # If you're creating attributes for latitude and longitude, don't
145
140
  # forget that Sphinx expects these values to be in radians.
146
- #
141
+ #
147
142
  def has(*args)
148
143
  options = args.extract_options!
149
144
  args.each do |columns|
150
145
  attribute = Attribute.new(source, FauxColumn.coerce(columns), options)
151
-
146
+
152
147
  add_facet_attribute attribute, options if attribute.faceted
153
148
  end
154
149
  end
155
-
150
+
156
151
  def facet(*args)
157
152
  options = args.extract_options!
158
153
  options[:facet] = true
159
-
154
+
160
155
  args.each do |columns|
161
156
  attribute = Attribute.new(source, FauxColumn.coerce(columns), options)
162
-
157
+
163
158
  add_facet_attribute attribute, options
164
159
  end
165
160
  end
166
-
161
+
167
162
  def join(*args)
168
163
  args.each do |association|
169
164
  Join.new(source, association)
170
165
  end
171
166
  end
172
-
167
+
173
168
  # Use this method to add some manual SQL conditions for your index
174
169
  # request. You can pass in as many strings as you like, they'll get
175
170
  # joined together with ANDs later on.
176
- #
171
+ #
177
172
  # where "user_id = 10"
178
173
  # where "parent_type = 'Article'", "created_at < NOW()"
179
- #
174
+ #
180
175
  def where(*args)
181
176
  source.conditions += args
182
177
  end
183
-
178
+
184
179
  # Use this method to add some manual SQL strings to the GROUP BY
185
180
  # clause. You can pass in as many strings as you'd like, they'll get
186
181
  # joined together with commas later on.
187
- #
182
+ #
188
183
  # group_by "lat", "lng"
189
- #
184
+ #
190
185
  def group_by(*args)
191
186
  source.groupings += args
192
187
  end
193
-
188
+
194
189
  # This is what to use to set properties on the index. Chief amongst
195
190
  # those is the delta property - to allow automatic updates to your
196
191
  # indexes as new models are added and edited - but also you can
197
192
  # define search-related properties which will be the defaults for all
198
193
  # searches on the model.
199
- #
194
+ #
200
195
  # set_property :delta => true
201
196
  # set_property :field_weights => {"name" => 100}
202
197
  # set_property :order => "name ASC"
203
198
  # set_property :select => 'name'
204
- #
199
+ #
205
200
  # Also, the following two properties are particularly relevant for
206
201
  # geo-location searching - latitude_attr and longitude_attr. If your
207
202
  # attributes for these two values are named something other than
@@ -210,59 +205,59 @@ module ThinkingSphinx
210
205
  # geo-related search.
211
206
  #
212
207
  # set_property :latitude_attr => "lt", :longitude_attr => "lg"
213
- #
208
+ #
214
209
  # Please don't forget to add a boolean field named 'delta' to your
215
210
  # model's database table if enabling the delta index for it.
216
211
  # Valid options for the delta property are:
217
- #
212
+ #
218
213
  # true
219
214
  # false
220
215
  # :default
221
216
  # :delayed
222
217
  # :datetime
223
- #
224
- # You can also extend ThinkingSphinx::Deltas::DefaultDelta to implement
218
+ #
219
+ # You can also extend ThinkingSphinx::Deltas::DefaultDelta to implement
225
220
  # your own handling for delta indexing.
226
- #
221
+ #
227
222
  def set_property(*args)
228
223
  options = args.extract_options!
229
224
  options.each do |key, value|
230
225
  set_single_property key, value
231
226
  end
232
-
227
+
233
228
  set_single_property args[0], args[1] if args.length == 2
234
229
  end
235
230
  alias_method :set_properties, :set_property
236
-
231
+
237
232
  # Handles the generation of new columns for the field and attribute
238
233
  # definitions.
239
- #
234
+ #
240
235
  def method_missing(method, *args)
241
236
  FauxColumn.new(method, *args)
242
237
  end
243
-
238
+
244
239
  # A method to allow adding fields from associations which have names
245
240
  # that clash with method names in the Builder class (ie: properties,
246
241
  # fields, attributes).
247
- #
242
+ #
248
243
  # Example: indexes assoc(:properties).column
249
- #
244
+ #
250
245
  def assoc(assoc, *args)
251
246
  FauxColumn.new(assoc, *args)
252
247
  end
253
-
248
+
254
249
  # Use this method to generate SQL for your attributes, conditions, etc.
255
250
  # You can pass in as whatever ActiveRecord::Base.sanitize_sql accepts.
256
- #
251
+ #
257
252
  # where sanitize_sql(["active = ?", true])
258
253
  # #=> WHERE active = 1
259
- #
254
+ #
260
255
  def sanitize_sql(*args)
261
256
  @index.model.send(:sanitize_sql, *args)
262
257
  end
263
-
258
+
264
259
  private
265
-
260
+
266
261
  def source
267
262
  @source ||= begin
268
263
  source = ThinkingSphinx::Source.new(@index)
@@ -270,7 +265,7 @@ module ThinkingSphinx
270
265
  source
271
266
  end
272
267
  end
273
-
268
+
274
269
  def set_single_property(key, value)
275
270
  source_options = ThinkingSphinx::Configuration::SourceOptions
276
271
  if source_options.include?(key.to_s)
@@ -279,19 +274,19 @@ module ThinkingSphinx
279
274
  @index.local_options.merge! key => value
280
275
  end
281
276
  end
282
-
277
+
283
278
  def add_sort_attribute(field, options)
284
279
  add_internal_attribute field, options, "_sort"
285
280
  end
286
-
281
+
287
282
  def add_facet_attribute(property, options)
288
283
  add_internal_attribute property, options, "_facet", true
289
284
  @index.model.sphinx_facets << property.to_facet
290
285
  end
291
-
286
+
292
287
  def add_internal_attribute(property, options, suffix, crc = false)
293
288
  return unless ThinkingSphinx::Facet.translate?(property)
294
-
289
+
295
290
  Attribute.new(source,
296
291
  property.columns.collect { |col| col.clone },
297
292
  options.merge(
@@ -301,7 +296,7 @@ module ThinkingSphinx
301
296
  ).except(:facet)
302
297
  )
303
298
  end
304
-
299
+
305
300
  def no_fields?
306
301
  @index.sources.empty? || @index.sources.any? { |source|
307
302
  source.fields.length == 0
@@ -3,7 +3,7 @@ require 'rails'
3
3
 
4
4
  module ThinkingSphinx
5
5
  class Railtie < Rails::Railtie
6
-
6
+
7
7
  initializer 'thinking_sphinx.sphinx' do
8
8
  ThinkingSphinx::AutoVersion.detect
9
9
  end
@@ -26,16 +26,13 @@ module ThinkingSphinx
26
26
  end
27
27
 
28
28
  config.to_prepare do
29
- I18n.backend.reload!
30
- I18n.backend.available_locales
31
-
32
29
  # ActiveRecord::Base.to_crc32s is dependant on the subclasses being loaded
33
30
  # consistently. When the environment is reset, subclasses/descendants will
34
31
  # be lost but our context will not reload them for us.
35
32
  #
36
33
  # We reset the context which causes the subclasses/descendants to be
37
34
  # reloaded next time the context is called.
38
- #
35
+ #
39
36
  ThinkingSphinx.reset_context!
40
37
  end
41
38
 
@@ -224,6 +224,10 @@ module ThinkingSphinx
224
224
  def next_page?
225
225
  !next_page.nil?
226
226
  end
227
+
228
+ def last_page?
229
+ next_page.nil?
230
+ end
227
231
 
228
232
  # The previous page number of the result set. If this is the first page,
229
233
  # then nil is returned.
@@ -763,8 +767,8 @@ module ThinkingSphinx
763
767
  filter_value(value.first).first..filter_value(value.last).first
764
768
  when Array
765
769
  value.collect { |v| filter_value(v) }.flatten
766
- when Time
767
- [value.to_i]
770
+ when Date, Time
771
+ [value.to_time.to_i]
768
772
  when NilClass
769
773
  0
770
774
  else
@@ -33,12 +33,22 @@ namespace :thinking_sphinx do
33
33
 
34
34
  Dir["#{config.searchd_file_path}/*.spl"].each { |file| File.delete(file) }
35
35
 
36
- config.controller.start
37
-
38
- if sphinx_running?
39
- puts "Started successfully (pid #{sphinx_pid})."
36
+ if ENV["NODETACH"] == "true"
37
+ unless pid = fork
38
+ config.controller.start(:nodetach => true)
39
+ end
40
+ Signal.trap('TERM') { Process.kill(:QUIT, pid) }
41
+ Signal.trap('INT') { Process.kill(:QUIT, pid) }
42
+ Process.wait(pid)
40
43
  else
41
- puts "Failed to start searchd daemon. Check #{config.searchd_log_file}"
44
+ config.controller.start
45
+
46
+ if sphinx_running?
47
+ puts "Started successfully (pid #{sphinx_pid})."
48
+ else
49
+ puts "Failed to start searchd daemon. Check #{config.searchd_log_file}"
50
+ puts "Be sure to run thinking_sphinx:index before thinking_sphinx:start"
51
+ end
42
52
  end
43
53
  end
44
54
 
@@ -1,3 +1,3 @@
1
1
  module ThinkingSphinx
2
- Version = '2.0.7'
2
+ Version = '2.0.8'
3
3
  end
@@ -1,13 +1,6 @@
1
1
  require 'active_record'
2
- prefix = defined?(JRUBY_VERSION) ? "jdbc" : ""
3
- require "active_record/connection_adapters/#{prefix}mysql_adapter"
4
- require "active_record/connection_adapters/mysql2_adapter"
5
-
6
- begin
7
- require "active_record/connection_adapters/#{prefix}postgresql_adapter"
8
- rescue LoadError
9
- # No postgres? no prob...
10
- end
2
+ require 'active_record/connection_adapters/mysql2_adapter'
3
+ require 'active_record/connection_adapters/postgresql_adapter'
11
4
  require 'yaml'
12
5
 
13
6
  class SphinxHelper