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.
- data/HISTORY +23 -0
- data/README.textile +11 -3
- data/features/attribute_transformation.feature +4 -4
- data/features/step_definitions/common_steps.rb +6 -2
- data/features/thinking_sphinx/db/fixtures/robots.rb +7 -13
- data/lib/cucumber/thinking_sphinx/internal_world.rb +5 -0
- data/lib/cucumber/thinking_sphinx/sql_logger.rb +3 -7
- data/lib/thinking_sphinx/active_record.rb +1 -1
- data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +1 -1
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +10 -2
- data/lib/thinking_sphinx/association.rb +1 -0
- data/lib/thinking_sphinx/attribute.rb +6 -8
- data/lib/thinking_sphinx/configuration.rb +52 -23
- data/lib/thinking_sphinx/context.rb +1 -5
- data/lib/thinking_sphinx/index/builder.rb +64 -69
- data/lib/thinking_sphinx/railtie.rb +2 -5
- data/lib/thinking_sphinx/search.rb +6 -2
- data/lib/thinking_sphinx/tasks.rb +15 -5
- data/lib/thinking_sphinx/version.rb +1 -1
- data/spec/sphinx_helper.rb +2 -9
- data/spec/support/rails.rb +9 -2
- data/spec/thinking_sphinx/configuration_spec.rb +72 -34
- data/spec/thinking_sphinx/context_spec.rb +7 -5
- data/spec/thinking_sphinx_spec.rb +3 -3
- metadata +103 -98
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+)
|
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
|
-
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
Robot.
|
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
|
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
|
-
|
12
|
+
super sql, name, binds
|
17
13
|
end
|
18
14
|
end
|
19
15
|
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?(
|
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
|
-
|
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) &&
|
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 =
|
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
|
-
|
241
|
-
|
238
|
+
join.join_type = Arel::OuterJoin
|
239
|
+
relation = relation.joins(join)
|
242
240
|
end
|
243
241
|
|
244
|
-
relation = relation.
|
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 =
|
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
|
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
|
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"), ''
|