thinking-sphinx 2.0.7 → 2.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,3 +1,15 @@
1
+ 2.0.8 - October 10th 2011
2
+ * Actually looking at Rails engines paths for models (Eduardo Casanova).
3
+ * Ensuring associations for acts-as-taggable-on are tweaked for Rails 3.1 (Anthony).
4
+ * Bug fix for association collection proxies in Rails 3.1 (Pavel Forkert).
5
+ * References all known Rails model paths - better for Rails 3 Engines.
6
+ * Don't reload I18n anymore - the load order isn't an issue in Rails 3 or 3.1.
7
+ * Fixing attribute source queries for Rails 3.1.
8
+ * 1.4.8 changes
9
+
10
+ 2.0.7 - August 29th 2011
11
+ * Making sure lib directory is actually a part of the gem. Oops.
12
+
1
13
  2.0.6 - August 28th 2011
2
14
  * Don't change superclass index definitions (otherwise sibling subclasses can end up with each others' index definitions).
3
15
  * Improved scope support (Andrew White).
@@ -35,6 +47,17 @@
35
47
  * Rails 3 support.
36
48
  * 1.4.0 changes.
37
49
 
50
+ 1.4.8 - October 10th 2011
51
+ * Adding smart default (id) for primary key column name.
52
+ * Making error message when daemon fails to start a little more helpful (Tony Pitale).
53
+ * Adding ThinkingSphinx::Search#last_page? for Kaminari (Pete Deffendol).
54
+ * If ENV['NODETACH'] is set when calling ts:start, pass that flag through to Riddle to start Sphinx in the foreground (Aaron Gibralter).
55
+ * Catch and report all errors when models are loaded (Martin Gordon).
56
+ * Bringing in builder gem's BlankSlate for our Builder - better at keeping global Rake methods out of the way.
57
+ * Use PostgreSQL's array_agg function if it exists, instead of array_accum (PG v8.4 or newer).
58
+ * Shuffle multiple Sphinx addresses by default, but allow that to be turned off (Ngan Pham).
59
+ * Fix string attributes when using Sphinx 2.0.1 and bin_path.
60
+
38
61
  1.4.7 - August 28th 2011
39
62
  * Don't search for objects when updating attributes on every save - if the object isn't there, then the error will be caught anyway.
40
63
  * Extra flexibility for association attribute references (Andrew White).
data/README.textile CHANGED
@@ -19,11 +19,11 @@ To quickly see if your system is ready to run the thinking sphinx specs, run the
19
19
  To get the spec suite running, you will need to install the ginger gem:
20
20
 
21
21
  <pre><code>sudo gem install ginger --source http://gemcutter.org</code></pre>
22
-
22
+
23
23
  Then install the cucumber, yard, jeweler and rspec gems. Make sure you have a git install version 1.6.0.0 or higher, otherwise the jeweler gem won't install. Bluecloth is required for some of the yard documentation.
24
24
 
25
25
  <pre>
26
- sudo gem install bluecloth cucumber yard jeweler rspec
26
+ sudo gem install bluecloth cucumber yard jeweler rspec
27
27
  </pre>
28
28
 
29
29
  Then set up your database:
@@ -32,7 +32,7 @@ Then set up your database:
32
32
  cp spec/fixtures/database.yml.default spec/fixtures/database.yml &&
33
33
  mysqladmin -u root create thinking_sphinx
34
34
  </pre>
35
-
35
+
36
36
  This last step can be done automatically by the contribute.rb script if all dependencies are met.
37
37
 
38
38
  Make sure you don't have another Sphinx daemon (searchd) running. If you do, quit it with "rake ts:stop"
@@ -216,3 +216,11 @@ Since I first released this library, there's been quite a few people who have su
216
216
  * Justin Tanner
217
217
  * Josh Goebel
218
218
  * Jonathan Viney
219
+ * Ngan Pham
220
+ * Martin Gordon
221
+ * Aaron Gibralter
222
+ * Pavel Forkert
223
+ * Anthony Byram
224
+ * Pete Deffendol
225
+ * Eduardo Casanova
226
+ * Tony Pitale
@@ -2,19 +2,19 @@ Feature: Handle not-quite-supported column types as attributes
2
2
  In order for Thinking Sphinx to be more understanding with model structures
3
3
  The plugin
4
4
  Should be able to use translatable columns as attributes
5
-
5
+
6
6
  Scenario: Decimals as floats
7
7
  Given Sphinx is running
8
8
  And I am searching on alphas
9
9
  When I filter between 1.0 and 3.0 on cost
10
10
  Then I should get 2 results
11
-
11
+
12
12
  Scenario: Dates as Datetimes
13
13
  Given Sphinx is running
14
14
  And I am searching on alphas
15
- When I filter between 1 and 3 days ago on created_on
15
+ When I filter between 1 and 3 days ago on created_on by date
16
16
  Then I should get 2 results
17
-
17
+
18
18
  Scenario: Timestamps as Datetimes
19
19
  Given Sphinx is running
20
20
  And I am searching on alphas
@@ -95,9 +95,13 @@ When /^I filter between ([\d\.]+) and ([\d\.]+) on (\w+)$/ do |first, last, attr
95
95
  end
96
96
  end
97
97
 
98
- When /^I filter between (\d+) and (\d+) days ago on (\w+)$/ do |last, first, attribute|
98
+ When /^I filter between (\d+) and (\d+) days ago on (\w+)(?: by (date))?$/ do |last, first, attribute, by_date|
99
99
  @results = nil
100
- @with[attribute.to_sym] = first.to_i.days.ago..last.to_i.days.ago
100
+
101
+ first, last = first.to_i.days.ago, last.to_i.days.ago
102
+ first, last = first.to_date, last.to_date if by_date
103
+
104
+ @with[attribute.to_sym] = first..last
101
105
  end
102
106
 
103
107
  When /^I filter by (\w+) between (\d+) and (\d+)$/ do |attribute, first, last|
@@ -1,14 +1,8 @@
1
- # Reset the primary key to allow us to create robots with specific internal_ids
2
- class Robot < ActiveRecord::Base
3
- set_primary_key :alternate_primary_key
4
- end
5
-
6
- Robot.create :name => 'Fritz', :internal_id => 'F0001'
7
- Robot.create :name => 'Sizzle', :internal_id => 'S0001'
8
- Robot.create :name => 'Sizzle Jr.', :internal_id => 'S0002'
9
- Robot.create :name => 'Expendable', :internal_id => 'E0001'
10
-
11
- # Annnnnnnnnnd we're back
12
- class Robot < ActiveRecord::Base
13
- set_primary_key :internal_id
1
+ {
2
+ 'F0001' => 'Fritz',
3
+ 'S0001' => 'Sizzle',
4
+ 'S0002' => 'Sizzle Jr.',
5
+ 'E0001' => 'Expendable'
6
+ }.each do |internal_id, name|
7
+ Robot.connection.execute "INSERT INTO robots (name, internal_id) VALUES ('#{name}', '#{internal_id}')"
14
8
  end
@@ -69,6 +69,7 @@ module Cucumber
69
69
  ::ThinkingSphinx::Configuration.instance.controller.stop
70
70
  sleep(0.5) # Ensure Sphinx has shut down completely
71
71
  ::ThinkingSphinx::ActiveRecord::LogSubscriber.logger.close
72
+ ::ActiveRecord::Base.logger.close
72
73
  end
73
74
  end
74
75
 
@@ -89,6 +90,10 @@ module Cucumber
89
90
  end
90
91
 
91
92
  def configure_active_record
93
+ ::ActiveRecord::Base.logger = Logger.new(
94
+ open("#{temporary_directory}/active_record.log", "a")
95
+ )
96
+
92
97
  ::ThinkingSphinx::ActiveRecord::LogSubscriber.logger = Logger.new(
93
98
  open("#{temporary_directory}/active_record.log", "a")
94
99
  )
@@ -1,19 +1,15 @@
1
1
  module Cucumber
2
2
  module ThinkingSphinx
3
3
  module SqlLogger
4
- def self.included(base)
5
- base.send :alias_method_chain, :execute, :query_record
6
- end
7
-
8
4
  IGNORED_SQL = [
9
5
  /^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/,
10
6
  /^SELECT @@ROWCOUNT/, /^SHOW FIELDS/
11
7
  ]
12
-
13
- def execute_with_query_record(sql, name = nil, &block)
8
+
9
+ def log(sql, name = 'SQL', binds = [])
14
10
  $queries_executed ||= []
15
11
  $queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
16
- execute_without_query_record(sql, name, &block)
12
+ super sql, name, binds
17
13
  end
18
14
  end
19
15
  end
@@ -36,7 +36,7 @@ module ThinkingSphinx
36
36
  @sphinx_primary_key_attribute ||
37
37
  superclass.primary_key_for_sphinx
38
38
  else
39
- primary_key
39
+ primary_key || 'id'
40
40
  end
41
41
  end
42
42
  end
@@ -19,7 +19,7 @@ module ThinkingSphinx
19
19
 
20
20
  def respond_to_with_sphinx_scopes?(method)
21
21
  proxy_association.klass.respond_to?(:sphinx_scopes) &&
22
- proxy_association.klass.sphinx_scopes.include?(scope) ||
22
+ proxy_association.klass.sphinx_scopes.include?(method) ||
23
23
  respond_to_without_sphinx_scopes?(method)
24
24
  end
25
25
  end
@@ -20,7 +20,11 @@ module ThinkingSphinx
20
20
  end
21
21
 
22
22
  def group_concatenate(clause, separator = ' ')
23
- "array_to_string(array_accum(COALESCE(#{clause}, '0')), '#{separator}')"
23
+ if connection.raw_connection.server_version >= 80400
24
+ "array_to_string(array_agg(COALESCE(#{clause}, '0')), '#{separator}')"
25
+ else
26
+ "array_to_string(array_accum(COALESCE(#{clause}, '0')), '#{separator}')"
27
+ end
24
28
  end
25
29
 
26
30
  def cast_to_string(clause)
@@ -105,7 +109,11 @@ module ThinkingSphinx
105
109
  end
106
110
 
107
111
  def create_array_accum_function
108
- if connection.raw_connection.respond_to?(:server_version) && connection.raw_connection.server_version > 80200
112
+ if connection.raw_connection.respond_to?(:server_version) &&
113
+ connection.raw_connection.server_version >= 80400
114
+ return
115
+ elsif connection.raw_connection.respond_to?(:server_version) &&
116
+ connection.raw_connection.server_version > 80200
109
117
  execute <<-SQL
110
118
  CREATE AGGREGATE array_accum (anyelement)
111
119
  (
@@ -214,6 +214,7 @@ module ThinkingSphinx
214
214
  if defined?(ActsAsTaggableOn) &&
215
215
  @reflection.klass == ActsAsTaggableOn::Tagging &&
216
216
  @reflection.name.to_s[/_taggings$/]
217
+ condition.gsub! /taggings\.tag_id = tags\.id AND/, "" if ThinkingSphinx.rails_3_1?
217
218
  condition = condition.gsub /taggings\./, "#{quoted_alias @join}."
218
219
  end
219
220
 
@@ -22,10 +22,6 @@ module ThinkingSphinx
22
22
  :wordcount => :sql_attr_str2wordcount
23
23
  }
24
24
 
25
- if Riddle.loaded_version.to_i > 1
26
- SphinxTypeMappings[:string] = :sql_attr_string
27
- end
28
-
29
25
  # To create a new attribute, you'll need to pass in either a single Column
30
26
  # or an array of them, and some (optional) options.
31
27
  #
@@ -234,14 +230,16 @@ module ThinkingSphinx
234
230
  end_assoc = end_association_for_mva
235
231
  raise "Could not determine SQL for MVA" if base_assoc.nil?
236
232
 
237
- relation = Arel::Table.new(base_assoc.table)
233
+ relation = ::ActiveRecord::Relation.new(
234
+ base_assoc.reflection.klass, Arel::Table.new(base_assoc.table)
235
+ )
238
236
 
239
237
  association_joins.each do |join|
240
- relation = relation.join(join.relation, Arel::OuterJoin).
241
- on(*join.association_join)
238
+ join.join_type = Arel::OuterJoin
239
+ relation = relation.joins(join)
242
240
  end
243
241
 
244
- relation = relation.project "#{foreign_key_for_mva base_assoc} #{ThinkingSphinx.unique_id_expression(adapter, offset)} AS #{quote_column('id')}, #{primary_key_for_mva(end_assoc)} AS #{quote_column(unique_name)}"
242
+ relation = relation.select "#{foreign_key_for_mva base_assoc} #{ThinkingSphinx.unique_id_expression(adapter, offset)} AS #{quote_column('id')}, #{primary_key_for_mva(end_assoc)} AS #{quote_column(unique_name)}"
245
243
 
246
244
  relation.to_sql
247
245
  end
@@ -49,7 +49,7 @@ module ThinkingSphinx
49
49
  #
50
50
  class Configuration
51
51
  include Singleton
52
-
52
+
53
53
  SourceOptions = Riddle::Configuration::SQLSource.settings.map { |setting|
54
54
  setting.to_s
55
55
  } - %w( type sql_query_pre sql_query sql_joined_field sql_file_field
@@ -61,17 +61,17 @@ module ThinkingSphinx
61
61
  setting.to_s
62
62
  } - %w( source prefix_fields infix_fields )
63
63
  CustomOptions = %w( disable_range use_64_bit )
64
-
64
+
65
65
  attr_accessor :searchd_file_path, :allow_star, :app_root,
66
66
  :model_directories, :delayed_job_priority, :indexed_models, :use_64_bit,
67
- :touched_reindex_file, :stop_timeout, :version
68
-
67
+ :touched_reindex_file, :stop_timeout, :version, :shuffle
68
+
69
69
  attr_accessor :source_options, :index_options
70
-
70
+
71
71
  attr_reader :configuration, :controller
72
-
72
+
73
73
  @@environment = nil
74
-
74
+
75
75
  # Load in the configuration settings - this will look for config/sphinx.yml
76
76
  # and parse it according to the current environment.
77
77
  #
@@ -107,11 +107,11 @@ module ThinkingSphinx
107
107
  self.searchd_file_path = "#{self.app_root}/db/sphinx/#{environment}"
108
108
  self.allow_star = false
109
109
  self.stop_timeout = 5
110
- self.model_directories = ["#{app_root}/app/models/"] +
111
- Dir.glob("#{app_root}/vendor/plugins/*/app/models/")
110
+ self.model_directories = initial_model_directories
112
111
  self.delayed_job_priority = 0
113
112
  self.indexed_models = []
114
-
113
+ self.shuffle = true
114
+
115
115
  self.source_options = {}
116
116
  self.index_options = {
117
117
  :charset_type => "utf-8"
@@ -121,6 +121,10 @@ module ThinkingSphinx
121
121
  parse_config
122
122
  self.version ||= @controller.sphinx_version
123
123
 
124
+ ThinkingSphinx::Attribute::SphinxTypeMappings.merge!(
125
+ :string => :sql_attr_string
126
+ ) if Riddle.loaded_version.to_i > 1
127
+
124
128
  self
125
129
  end
126
130
 
@@ -135,7 +139,7 @@ module ThinkingSphinx
135
139
  ENV['RAILS_ENV'] || 'development'
136
140
  end
137
141
  end
138
-
142
+
139
143
  def self.reset_environment
140
144
  ThinkingSphinx.mutex.synchronize do
141
145
  @@environment = nil
@@ -145,7 +149,7 @@ module ThinkingSphinx
145
149
  def environment
146
150
  self.class.environment
147
151
  end
148
-
152
+
149
153
  def generate
150
154
  @configuration.indexes.clear
151
155
 
@@ -153,20 +157,20 @@ module ThinkingSphinx
153
157
  model = model.constantize
154
158
  model.define_indexes
155
159
  @configuration.indexes.concat model.to_riddle
156
-
160
+
157
161
  enforce_common_attribute_types
158
162
  end
159
163
  end
160
-
164
+
161
165
  # Generate the config file for Sphinx by using all the settings defined and
162
166
  # looping through all the models with indexes to build the relevant
163
167
  # indexer and searchd configuration, and sources and indexes details.
164
168
  #
165
169
  def build(file_path=nil)
166
170
  file_path ||= "#{self.config_file}"
167
-
171
+
168
172
  generate
169
-
173
+
170
174
  open(file_path, "w") do |file|
171
175
  file.write @configuration.render
172
176
  end
@@ -245,11 +249,11 @@ module ThinkingSphinx
245
249
  def indexer_binary_name=(name)
246
250
  @controller.indexer_binary_name = name
247
251
  end
248
-
252
+
249
253
  attr_accessor :timeout
250
-
254
+
251
255
  def client
252
- client = Riddle::Client.new address, port,
256
+ client = Riddle::Client.new shuffled_addresses, port,
253
257
  configuration.searchd.client_key
254
258
  client.max_matches = configuration.searchd.max_matches || 1000
255
259
  client.timeout = timeout || 0
@@ -310,26 +314,51 @@ module ThinkingSphinx
310
314
  send("#{key}=", value) if self.respond_to?("#{key}")
311
315
  end
312
316
  end
313
-
317
+
314
318
  def enforce_common_attribute_types
315
319
  sql_indexes = configuration.indexes.reject { |index|
316
320
  index.is_a? Riddle::Configuration::DistributedIndex
317
321
  }
318
-
322
+
319
323
  return unless sql_indexes.any? { |index|
320
324
  index.sources.any? { |source|
321
325
  source.sql_attr_bigint.include? :sphinx_internal_id
322
326
  }
323
327
  }
324
-
328
+
325
329
  sql_indexes.each { |index|
326
330
  index.sources.each { |source|
327
331
  next if source.sql_attr_bigint.include? :sphinx_internal_id
328
-
332
+
329
333
  source.sql_attr_bigint << :sphinx_internal_id
330
334
  source.sql_attr_uint.delete :sphinx_internal_id
331
335
  }
332
336
  }
333
337
  end
338
+
339
+ def shuffled_addresses
340
+ return address unless shuffle
341
+
342
+ addresses = Array(address)
343
+ if addresses.respond_to?(:shuffle)
344
+ addresses.shuffle
345
+ else
346
+ address.sort_by { rand }
347
+ end
348
+ end
349
+
350
+ def initial_model_directories
351
+ directories = ["#{app_root}/app/models/"] +
352
+ Dir.glob("#{app_root}/vendor/plugins/*/app/models/")
353
+
354
+ if defined?(Rails)
355
+ directories += Rails.application.paths['app/models'].to_a
356
+ directories += Rails.application.railties.engines.collect { |engine|
357
+ engine.paths['app/models'].to_a
358
+ }.flatten
359
+ end
360
+
361
+ directories
362
+ end
334
363
  end
335
364
  end
@@ -62,11 +62,7 @@ class ThinkingSphinx::Context
62
62
 
63
63
  begin
64
64
  camelized_model.constantize
65
- rescue LoadError
66
- model_name.gsub!(/.*[\/\\]/, '').nil? ? next : retry
67
- rescue NameError
68
- next
69
- rescue StandardError => err
65
+ rescue Exception => err
70
66
  STDERR.puts "Warning: Error loading #{file}:"
71
67
  STDERR.puts err.message
72
68
  STDERR.puts err.backtrace.join("\n"), ''