thinking-sphinx 2.0.6 → 2.0.7
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 +157 -0
- data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
- data/lib/cucumber/thinking_sphinx/internal_world.rb +127 -0
- data/lib/cucumber/thinking_sphinx/sql_logger.rb +20 -0
- data/lib/thinking-sphinx.rb +1 -0
- data/lib/thinking_sphinx/action_controller.rb +31 -0
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +53 -0
- data/lib/thinking_sphinx/active_record/collection_proxy.rb +40 -0
- data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +27 -0
- data/lib/thinking_sphinx/active_record/delta.rb +65 -0
- data/lib/thinking_sphinx/active_record/has_many_association.rb +37 -0
- data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
- data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
- data/lib/thinking_sphinx/active_record/scopes.rb +110 -0
- data/lib/thinking_sphinx/active_record.rb +383 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +62 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +171 -0
- data/lib/thinking_sphinx/association.rb +229 -0
- data/lib/thinking_sphinx/attribute.rb +407 -0
- data/lib/thinking_sphinx/auto_version.rb +38 -0
- data/lib/thinking_sphinx/bundled_search.rb +44 -0
- data/lib/thinking_sphinx/class_facet.rb +20 -0
- data/lib/thinking_sphinx/configuration.rb +335 -0
- data/lib/thinking_sphinx/context.rb +77 -0
- data/lib/thinking_sphinx/core/string.rb +15 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
- data/lib/thinking_sphinx/deltas.rb +28 -0
- data/lib/thinking_sphinx/deploy/capistrano.rb +99 -0
- data/lib/thinking_sphinx/excerpter.rb +23 -0
- data/lib/thinking_sphinx/facet.rb +128 -0
- data/lib/thinking_sphinx/facet_search.rb +170 -0
- data/lib/thinking_sphinx/field.rb +98 -0
- data/lib/thinking_sphinx/index/builder.rb +312 -0
- data/lib/thinking_sphinx/index/faux_column.rb +118 -0
- data/lib/thinking_sphinx/index.rb +157 -0
- data/lib/thinking_sphinx/join.rb +37 -0
- data/lib/thinking_sphinx/property.rb +185 -0
- data/lib/thinking_sphinx/railtie.rb +46 -0
- data/lib/thinking_sphinx/search.rb +995 -0
- data/lib/thinking_sphinx/search_methods.rb +439 -0
- data/lib/thinking_sphinx/sinatra.rb +7 -0
- data/lib/thinking_sphinx/source/internal_properties.rb +51 -0
- data/lib/thinking_sphinx/source/sql.rb +157 -0
- data/lib/thinking_sphinx/source.rb +194 -0
- data/lib/thinking_sphinx/tasks.rb +132 -0
- data/lib/thinking_sphinx/test.rb +55 -0
- data/lib/thinking_sphinx/version.rb +3 -0
- data/lib/thinking_sphinx.rb +296 -0
- metadata +53 -4
data/HISTORY
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
2.0.6 - August 28th 2011
|
2
|
+
* Don't change superclass index definitions (otherwise sibling subclasses can end up with each others' index definitions).
|
3
|
+
* Improved scope support (Andrew White).
|
4
|
+
* Fixed association-filtered searches for Rails 3.1 (Andrew White).
|
5
|
+
* Fixed polymorphic association support for Rails 3.1 (Jonathan Viney).
|
6
|
+
* 1.4.7 changes.
|
7
|
+
|
8
|
+
2.0.5 - May 25th 2011
|
9
|
+
* Railtie equivalent for Sinatra.
|
10
|
+
* Rails 3.1 support (Florent Piteau).
|
11
|
+
* 1.4.6 changes.
|
12
|
+
|
13
|
+
2.0.4 - May 12th 2011
|
14
|
+
* Only determine Sphinx version when initializing Thinking Sphinx, instead of upon require.
|
15
|
+
* Still execute logged blocks for searching even when logging isn't enabled.
|
16
|
+
* 1.4.5 changes.
|
17
|
+
|
18
|
+
2.0.3 - April 3rd 2011
|
19
|
+
* 1.4.4 changes.
|
20
|
+
|
21
|
+
2.0.2 - January 13th 2011
|
22
|
+
* Improvement to loading the environment via Rake (Brenton Fletcher).
|
23
|
+
* 1.4.2 changes.
|
24
|
+
* 1.4.1 changes.
|
25
|
+
|
26
|
+
2.0.1 - November 19th 2011
|
27
|
+
* Intermittent as_json bug fix.
|
28
|
+
* Using Rails 3 logging system (Stephen Celis).
|
29
|
+
* Fixes for Rails 3.0.2 (Ivan Ukhov).
|
30
|
+
|
31
|
+
2.0.0 - November 14th 2011
|
32
|
+
* Handle has_many conditions defined as a hash (Artem Orlov).
|
33
|
+
* Deprecation fixes for logging (Anton Sozontov).
|
34
|
+
* Only load Thinking Sphinx if ActiveRecord is loaded (Matthew Higgins).
|
35
|
+
* Rails 3 support.
|
36
|
+
* 1.4.0 changes.
|
37
|
+
|
38
|
+
1.4.7 - August 28th 2011
|
39
|
+
* 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
|
+
* Extra flexibility for association attribute references (Andrew White).
|
41
|
+
* Setting up test suite for Travis, and fixing tests across many rubies.
|
42
|
+
* Removing test for filtering on wordcount attribute - seems Sphinx doesn't like that.
|
43
|
+
* If many indices are searched on, only use the first for excerpts.
|
44
|
+
* Allow MVA facets from SQL strings that are all integers to skip object translation.
|
45
|
+
* Adding total_count method for better Kaminari support (Dan Pickett).
|
46
|
+
* Switching from Jeweler to Bundler for gem management.
|
47
|
+
* Cleaning up Context - re-using camelized class names.
|
48
|
+
* Simplified auto-star regex for 1.9 - no need to set the unicode flag.
|
49
|
+
* Don't update sphinx_internal_id attribute when updating a record's attributes.
|
50
|
+
* Ignore empty arrays for :without_ids (Jason Rust).
|
51
|
+
* Now catching Riddle::ResponseError when we aren't fussed about the responses from Sphinx.
|
52
|
+
* Distinguishing between Ruby 1.8 and 1.9 for auto-star regex (Andrew White).
|
53
|
+
* Don't re-index at the end of suspended_delta calls if requested.
|
54
|
+
* Adding stop_timeout setting to configure how long ts:stop should wait while checking whether Sphinx has stopped (Justin Tanner).
|
55
|
+
* Speed improvement for types_to_crcs (Josh Goebel).
|
56
|
+
* Adding a touched_reindex_file option, for a file that will be touched after indices are rotated (Rémi Prévost).
|
57
|
+
* Better support for the Mysql2 adapter (Andrew White).
|
58
|
+
* More pagination methods - next_page? and first_page?.
|
59
|
+
* Adding use_64_bit option to ensure all timestamp attributes are treated as 64 bit integers (Andrey Deryabin).
|
60
|
+
* Simpler, less brittle support for all Sphinx settings via Riddle (Clemens Kofler).
|
61
|
+
* Optimised start/stop behaviour in Capistrano recipe (Lars Weiler).
|
62
|
+
* Catch and ignore timeout errors for low priority requests.
|
63
|
+
* Don't double up on de-polymorphised associations for generated Sphinx source SQL queries.
|
64
|
+
|
65
|
+
1.4.6 - May 25th 2011
|
66
|
+
* Require Riddle 1.3.3 or better.
|
67
|
+
* Cast Sphinx document ids to 32bit integers to keep PostgreSQL happy when Sphinx is compiled for id64 support (Bruno Santschi).
|
68
|
+
* Making sure suspended_delta always exists on indexed models, whether indices have been defined or not.
|
69
|
+
* Workaround for ActsAsTaggableOn to ensure association joins work.
|
70
|
+
* Use Sphinx string attributes instead of class_crc integers to determine which class each search result is from (Sphinx 2.x or newer).
|
71
|
+
* String attribute support - and so, using this instead of str2ordinal for string sorting.
|
72
|
+
* Support for fields with paired string attributes and wordcount attributes (Sphinx 1.10-beta or newer).
|
73
|
+
* Support for file fields (Sphinx 1.10-beta or newer).
|
74
|
+
* Wordcount attribute support (Sphinx 1.10-beta or newer).
|
75
|
+
* Sinatra support is only used when Rails isn't loaded.
|
76
|
+
* Sphinx 2.0.x (including 2.0.2-dev) support.
|
77
|
+
* :without_any filtering option (the reverse of :with_all).
|
78
|
+
|
79
|
+
1.4.5 - May 12th 2011
|
80
|
+
* Require Riddle 1.3.2 or better.
|
81
|
+
* Improved 1.9.2 support
|
82
|
+
* Don't hack Array - make ThinkingSphinx::Search a subclass of Array instead.
|
83
|
+
* Distinguishing between suspended deltas and disabled deltas.
|
84
|
+
* Removing most Thread references, opting for Mutexes for better multi-threading support.
|
85
|
+
* Added support for client_key to allow for authenticated Sphinx communication.
|
86
|
+
* Sphinx 2.0.1 support.
|
87
|
+
* Don't try to translate nil values (Alex Chee).
|
88
|
+
* Performance fix for facets (Clemens Kofler).
|
89
|
+
* Use Sphinx 0.9.9 in Capistrano recipe (Konstantin Shabanov).
|
90
|
+
* Sinatra support (Patrick Tulskie).
|
91
|
+
* Offer sanitize_sql method within define_index blocks (Matt Todd).
|
92
|
+
* Performance fix: caching primary_key_for_sphinx value.
|
93
|
+
|
94
|
+
1.4.4 - April 3 2011
|
95
|
+
* Being consistent with === comparisons for Array monkeypatch.
|
96
|
+
* Kaminari pagination support.
|
97
|
+
* Ensure deltas are supported across multiple indices for a given model.
|
98
|
+
* Use custom index names for excerpts calls if that's what exists (Javier Ramírez).
|
99
|
+
* Allow for Sphinx versions compiled from source (Greg Weber).
|
100
|
+
* Ensuring thread is being required to allow Mutexes to work.
|
101
|
+
* Ensure primary_key_for_sphinx is inherited to subclasses (Robert Stern).
|
102
|
+
* Don't complain if there's an error when checking for documents in a given index.
|
103
|
+
|
104
|
+
1.4.3 - January 29th 2011
|
105
|
+
* Don't memoize the database adapter.
|
106
|
+
* Allow queries to just return specific attributes via the :only option, instead of ActiveRecord objects (Hans Hasselberg).
|
107
|
+
* Performance fix: Only add sphinx scopes to has_many associations if there are scopes in play (Kirill Maximov).
|
108
|
+
* Fixing CRC32 function for PostgreSQL to handle UTF characters that use more than one byte.
|
109
|
+
|
110
|
+
1.4.2 - January 13th 2011
|
111
|
+
* Ignore Sphinx errors when updating attributes (it's a low priority request).
|
112
|
+
* Don't use the CRC32 function within the generated SQL queries for class types.
|
113
|
+
* Direct facets to use a method to translate values using :value.
|
114
|
+
* Ensure that if one sphinx_internal_id attribute is a bigint, they all are.
|
115
|
+
* Ignore Sphinx errors when marking documents as deleted (it's a low priority request).
|
116
|
+
* Don't select columns from polymorphic joins if they're not available.
|
117
|
+
* Don't query Sphinx for a facet request if zero facets are requested.
|
118
|
+
* Ensure we're only using local_options when it's available (Paul Schyska).
|
119
|
+
* Don't presume the environment for Capistrano is production (Robert Glaser).
|
120
|
+
* Allow for custom database adapters.
|
121
|
+
* Fix for custom delta columns (Marcin Stecki).
|
122
|
+
|
123
|
+
1.4.1 - December 21st 2010
|
124
|
+
* No longer looking for attributes in :conditions option when searching.
|
125
|
+
* Copy and then modify sort arguments.
|
126
|
+
* Allow the generation of Sphinx configuration objects without necessarily writing to the configuration file.
|
127
|
+
* Allow Sphinx 1.10-beta versions that have been compiled with id64 support.
|
128
|
+
* Raise Sphinx errors when searching - which can be ignored using the :ignore_errors option (Matt Todd).
|
129
|
+
* Handle complex :include arguments, including hashes within arrays (Paco Guzmán).
|
130
|
+
* Handle hashes passed through to :include when searching.
|
131
|
+
* Can now require either 'thinking_sphinx' or 'thinking-sphinx'.
|
132
|
+
* Excerpts are less fussy about inputs.
|
133
|
+
* No longer hard-coding RSpec colour setting - that's up to the developer.
|
134
|
+
* Require Riddle 1.2.0 or better.
|
135
|
+
* Use searchd to stop Sphinx (via --stop or --stopwait arguments) (Matt Todd).
|
136
|
+
|
137
|
+
1.4.0 - November 14th 2010
|
138
|
+
* No longer supporting attributes in :conditions option.
|
139
|
+
* Case insensitive field sorting if :sortable is set to :insensitive.
|
140
|
+
* Using Bundler for development
|
141
|
+
* Can use make facet requests on existing searches and Sphinx scopes.
|
142
|
+
* Don't star field markers in search queries.
|
143
|
+
* Can use search_for_ids on Sphinx scopes.
|
144
|
+
* Ensure the CRC32 function for PostgreSQL handles empty strings and NULLs.
|
145
|
+
* Allow custom determination for database adapters.
|
146
|
+
* :include search option now limits itself to the relevant classes when searching across more than one class.
|
147
|
+
* Don't memoize primary_key_for_sphinx, which was causing trouble when objects are deleted or frozen.
|
148
|
+
* Allow for excerpts options when searching (using :excerpt_options) (Lee Capps).
|
149
|
+
* Wrap UTC in quotes when setting the timezone in PostgreSQL (Keith Pitt).
|
150
|
+
* Don't colourize logs unless ActiveRecord allows for it.
|
151
|
+
* Consistent contribution documentation (Sam Goldstein).
|
152
|
+
* Allow the setting of Riddle's timeout value via sphinx.yml (Sam Goldstein).
|
153
|
+
* 1.10-beta support.
|
154
|
+
* Can set the riddle client for the search request using :client.
|
155
|
+
* Fixing default sphinx scope support.
|
156
|
+
* Make a reasonable attempt to check that Sphinx has stopped (Matt Todd).
|
157
|
+
* Be more verbose if a model can't be loaded by Thinking Sphinx.
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'cucumber/thinking_sphinx/sql_logger'
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
module ThinkingSphinx
|
5
|
+
class InternalWorld
|
6
|
+
attr_accessor :temporary_directory, :migrations_directory,
|
7
|
+
:models_directory, :fixtures_directory, :database_file
|
8
|
+
attr_accessor :adapter, :database, :username,
|
9
|
+
:password, :host
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
pwd = Dir.pwd
|
13
|
+
@temporary_directory = "#{pwd}/tmp"
|
14
|
+
@migrations_directory = "#{pwd}/features/thinking_sphinx/db/migrations"
|
15
|
+
@models_directory = "#{pwd}/features/thinking_sphinx/models"
|
16
|
+
@fixtures_directory = "#{pwd}/features/thinking_sphinx/db/fixtures"
|
17
|
+
@database_file = "#{pwd}/features/thinking_sphinx/database.yml"
|
18
|
+
|
19
|
+
@adapter = (ENV['DATABASE'] || 'mysql').gsub /^mysql$/, 'mysql2'
|
20
|
+
@database = 'thinking_sphinx'
|
21
|
+
@username = @adapter[/mysql/] ? 'root' : 'postgres'
|
22
|
+
# @password = 'thinking_sphinx'
|
23
|
+
@host = 'localhost'
|
24
|
+
end
|
25
|
+
|
26
|
+
def setup
|
27
|
+
make_temporary_directory
|
28
|
+
|
29
|
+
configure_cleanup
|
30
|
+
configure_thinking_sphinx
|
31
|
+
configure_active_record
|
32
|
+
|
33
|
+
prepare_data
|
34
|
+
setup_sphinx
|
35
|
+
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def configure_database
|
40
|
+
ActiveRecord::Base.establish_connection database_settings
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def config
|
47
|
+
@config ||= ::ThinkingSphinx::Configuration.instance
|
48
|
+
end
|
49
|
+
|
50
|
+
def make_temporary_directory
|
51
|
+
FileUtils.mkdir_p temporary_directory
|
52
|
+
Dir["#{temporary_directory}/*"].each do |file|
|
53
|
+
FileUtils.rm_rf file
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def configure_thinking_sphinx
|
58
|
+
config.config_file = "#{temporary_directory}/sphinx.conf"
|
59
|
+
config.searchd_log_file = "#{temporary_directory}/searchd.log"
|
60
|
+
config.query_log_file = "#{temporary_directory}/searchd.query.log"
|
61
|
+
config.pid_file = "#{temporary_directory}/searchd.pid"
|
62
|
+
config.searchd_file_path = "#{temporary_directory}/indexes/"
|
63
|
+
|
64
|
+
::ThinkingSphinx.suppress_delta_output = true
|
65
|
+
end
|
66
|
+
|
67
|
+
def configure_cleanup
|
68
|
+
Kernel.at_exit do
|
69
|
+
::ThinkingSphinx::Configuration.instance.controller.stop
|
70
|
+
sleep(0.5) # Ensure Sphinx has shut down completely
|
71
|
+
::ThinkingSphinx::ActiveRecord::LogSubscriber.logger.close
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def yaml_database_settings
|
76
|
+
return {} unless File.exist?(@database_file)
|
77
|
+
|
78
|
+
YAML.load open(@database_file)
|
79
|
+
end
|
80
|
+
|
81
|
+
def database_settings
|
82
|
+
{
|
83
|
+
'adapter' => @adapter,
|
84
|
+
'database' => @database,
|
85
|
+
'username' => @username,
|
86
|
+
'password' => @password,
|
87
|
+
'host' => @host
|
88
|
+
}.merge yaml_database_settings
|
89
|
+
end
|
90
|
+
|
91
|
+
def configure_active_record
|
92
|
+
::ThinkingSphinx::ActiveRecord::LogSubscriber.logger = Logger.new(
|
93
|
+
open("#{temporary_directory}/active_record.log", "a")
|
94
|
+
)
|
95
|
+
|
96
|
+
ActiveRecord::Base.connection.class.send(
|
97
|
+
:include, Cucumber::ThinkingSphinx::SqlLogger
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
def prepare_data
|
102
|
+
::ThinkingSphinx.deltas_enabled = false
|
103
|
+
|
104
|
+
load_files migrations_directory
|
105
|
+
load_files models_directory
|
106
|
+
load_files fixtures_directory
|
107
|
+
|
108
|
+
::ThinkingSphinx.deltas_enabled = true
|
109
|
+
end
|
110
|
+
|
111
|
+
def load_files(path)
|
112
|
+
files = Dir["#{path}/*.rb"].sort!
|
113
|
+
files.each do |file|
|
114
|
+
require file.gsub(/\.rb$/, '')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def setup_sphinx
|
119
|
+
FileUtils.mkdir_p config.searchd_file_path
|
120
|
+
|
121
|
+
config.build
|
122
|
+
config.controller.index
|
123
|
+
config.controller.start
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module ThinkingSphinx
|
3
|
+
module SqlLogger
|
4
|
+
def self.included(base)
|
5
|
+
base.send :alias_method_chain, :execute, :query_record
|
6
|
+
end
|
7
|
+
|
8
|
+
IGNORED_SQL = [
|
9
|
+
/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/,
|
10
|
+
/^SELECT @@ROWCOUNT/, /^SHOW FIELDS/
|
11
|
+
]
|
12
|
+
|
13
|
+
def execute_with_query_record(sql, name = nil, &block)
|
14
|
+
$queries_executed ||= []
|
15
|
+
$queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
|
16
|
+
execute_without_query_record(sql, name, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'thinking_sphinx'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module ActionController
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
attr_internal :query_runtime
|
8
|
+
|
9
|
+
def cleanup_view_runtime
|
10
|
+
log_subscriber = ThinkingSphinx::ActiveRecord::LogSubscriber
|
11
|
+
query_runtime_pre_render = log_subscriber.reset_runtime
|
12
|
+
runtime = super
|
13
|
+
query_runtime_post_render = log_subscriber.reset_runtime
|
14
|
+
self.query_runtime = query_runtime_pre_render + query_runtime_post_render
|
15
|
+
runtime - query_runtime_post_render
|
16
|
+
end
|
17
|
+
|
18
|
+
def append_info_to_payload(payload)
|
19
|
+
super
|
20
|
+
payload[:query_runtime] = query_runtime
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def log_process_action(payload)
|
25
|
+
messages, query_runtime = super, payload[:query_runtime]
|
26
|
+
messages << ("Sphinx: %.1fms" % query_runtime.to_f) if query_runtime
|
27
|
+
messages
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module ActiveRecord
|
3
|
+
module AttributeUpdates
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
after_save :update_attribute_values
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def update_attribute_values
|
13
|
+
return true unless ThinkingSphinx.updates_enabled? &&
|
14
|
+
ThinkingSphinx.sphinx_running?
|
15
|
+
|
16
|
+
self.class.sphinx_indexes.each do |index|
|
17
|
+
attribute_pairs = attribute_values_for_index(index)
|
18
|
+
attribute_names = attribute_pairs.keys
|
19
|
+
attribute_values = attribute_names.collect { |key|
|
20
|
+
attribute_pairs[key]
|
21
|
+
}
|
22
|
+
|
23
|
+
update_index index.core_name, attribute_names, attribute_values
|
24
|
+
next unless index.delta?
|
25
|
+
update_index index.delta_name, attribute_names, attribute_values
|
26
|
+
end
|
27
|
+
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def updatable_attributes(index)
|
32
|
+
index.attributes.select { |attrib| attrib.updatable? }
|
33
|
+
end
|
34
|
+
|
35
|
+
def attribute_values_for_index(index)
|
36
|
+
updatable_attributes(index).inject({}) { |hash, attrib|
|
37
|
+
hash[attrib.unique_name.to_s] = attrib.live_value self
|
38
|
+
hash
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def update_index(index_name, attribute_names, attribute_values)
|
43
|
+
config = ThinkingSphinx::Configuration.instance
|
44
|
+
config.client.update index_name, attribute_names, {
|
45
|
+
sphinx_document_id => attribute_values
|
46
|
+
}
|
47
|
+
rescue Riddle::ConnectionError, Riddle::ResponseError,
|
48
|
+
ThinkingSphinx::SphinxError, Errno::ETIMEDOUT
|
49
|
+
# Not the end of the world if Sphinx isn't running.
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module ActiveRecord
|
3
|
+
module CollectionProxy
|
4
|
+
def search(*args)
|
5
|
+
options = args.extract_options!
|
6
|
+
options[:with] ||= {}
|
7
|
+
options[:with].merge! default_filter
|
8
|
+
|
9
|
+
args << options
|
10
|
+
proxy_association.klass.search(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def attribute_for_foreign_key
|
16
|
+
if proxy_association.reflection.through_reflection
|
17
|
+
foreign_key = proxy_association.reflection.through_reflection.foreign_key
|
18
|
+
else
|
19
|
+
foreign_key = proxy_association.reflection.foreign_key
|
20
|
+
end
|
21
|
+
|
22
|
+
proxy_association.klass.define_indexes
|
23
|
+
(proxy_association.klass.sphinx_indexes || []).each do |index|
|
24
|
+
attribute = index.attributes.detect { |attrib|
|
25
|
+
attrib.columns.length == 1 &&
|
26
|
+
attrib.columns.first.__name == foreign_key.to_sym ||
|
27
|
+
attrib.alias == foreign_key.to_sym
|
28
|
+
}
|
29
|
+
return attribute unless attribute.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
raise "Missing Attribute for Foreign Key #{foreign_key}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_filter
|
36
|
+
{attribute_for_foreign_key.unique_name => proxy_association.owner.id}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module ActiveRecord
|
3
|
+
module CollectionProxyWithScopes
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
alias_method_chain :method_missing, :sphinx_scopes
|
7
|
+
alias_method_chain :respond_to?, :sphinx_scopes
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing_with_sphinx_scopes(method, *args, &block)
|
12
|
+
klass = proxy_association.klass
|
13
|
+
if klass.respond_to?(:sphinx_scopes) && klass.sphinx_scopes.include?(method)
|
14
|
+
klass.search(:with => default_filter).send(method, *args, &block)
|
15
|
+
else
|
16
|
+
method_missing_without_sphinx_scopes(method, *args, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_to_with_sphinx_scopes?(method)
|
21
|
+
proxy_association.klass.respond_to?(:sphinx_scopes) &&
|
22
|
+
proxy_association.klass.sphinx_scopes.include?(scope) ||
|
23
|
+
respond_to_without_sphinx_scopes?(method)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module ActiveRecord
|
3
|
+
# This module contains all the delta-related code for models. There isn't
|
4
|
+
# really anything you need to call manually in here - except perhaps
|
5
|
+
# index_delta, but not sure what reason why.
|
6
|
+
#
|
7
|
+
module Delta
|
8
|
+
# Code for after_commit callback is written by Eli Miller:
|
9
|
+
# http://elimiller.blogspot.com/2007/06/proper-cache-expiry-with-aftercommit.html
|
10
|
+
# with slight modification from Joost Hietbrink.
|
11
|
+
#
|
12
|
+
def self.included(base)
|
13
|
+
base.class_eval do
|
14
|
+
class << self
|
15
|
+
# Build the delta index for the related model. This won't be called
|
16
|
+
# if running in the test environment.
|
17
|
+
#
|
18
|
+
def index_delta(instance = nil)
|
19
|
+
delta_objects.each { |obj| obj.index(self, instance) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def delta_objects
|
23
|
+
self.sphinx_indexes.collect(&:delta_object).compact
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def toggled_delta?
|
28
|
+
self.class.delta_objects.any? { |obj| obj.toggled(self) }
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Set the delta value for the model to be true.
|
34
|
+
def toggle_delta
|
35
|
+
self.class.delta_objects.each { |obj|
|
36
|
+
obj.toggle(self)
|
37
|
+
} if should_toggle_delta?
|
38
|
+
end
|
39
|
+
|
40
|
+
# Build the delta index for the related model. This won't be called
|
41
|
+
# if running in the test environment.
|
42
|
+
#
|
43
|
+
def index_delta
|
44
|
+
self.class.index_delta(self) if self.class.delta_objects.any? { |obj|
|
45
|
+
obj.toggled(self)
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def should_toggle_delta?
|
50
|
+
self.new_record? || indexed_data_changed?
|
51
|
+
end
|
52
|
+
|
53
|
+
def indexed_data_changed?
|
54
|
+
sphinx_indexes.any? { |index|
|
55
|
+
index.fields.any? { |field| field.changed?(self) } ||
|
56
|
+
index.attributes.any? { |attrib|
|
57
|
+
attrib.public? && attrib.changed?(self) && !attrib.updatable?
|
58
|
+
}
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module ActiveRecord
|
3
|
+
module HasManyAssociation
|
4
|
+
def search(*args)
|
5
|
+
options = args.extract_options!
|
6
|
+
options[:with] ||= {}
|
7
|
+
options[:with].merge! default_filter
|
8
|
+
|
9
|
+
args << options
|
10
|
+
@reflection.klass.search(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def attribute_for_foreign_key
|
16
|
+
foreign_key = @reflection.primary_key_name
|
17
|
+
stack = [@reflection.options[:through]].compact
|
18
|
+
|
19
|
+
@reflection.klass.define_indexes
|
20
|
+
(@reflection.klass.sphinx_indexes || []).each do |index|
|
21
|
+
attribute = index.attributes.detect { |attrib|
|
22
|
+
attrib.columns.length == 1 &&
|
23
|
+
attrib.columns.first.__name == foreign_key.to_sym ||
|
24
|
+
attrib.alias == foreign_key.to_sym
|
25
|
+
}
|
26
|
+
return attribute unless attribute.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
raise "Missing Attribute for Foreign Key #{foreign_key}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_filter
|
33
|
+
{attribute_for_foreign_key.unique_name => @owner.id}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module ActiveRecord
|
3
|
+
module HasManyAssociationWithScopes
|
4
|
+
def method_missing(method, *args, &block)
|
5
|
+
if responds_to_scope(method)
|
6
|
+
@reflection.klass.
|
7
|
+
search(:with => default_filter).
|
8
|
+
send(method, *args, &block)
|
9
|
+
else
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def responds_to_scope(scope)
|
16
|
+
@reflection.klass.respond_to?(:sphinx_scopes) &&
|
17
|
+
@reflection.klass.sphinx_scopes.include?(scope)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'active_support/log_subscriber'
|
2
|
+
|
3
|
+
module ThinkingSphinx
|
4
|
+
module ActiveRecord
|
5
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
6
|
+
def self.runtime=(value)
|
7
|
+
Thread.current['thinking_sphinx_query_runtime'] = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.runtime
|
11
|
+
Thread.current['thinking_sphinx_query_runtime'] ||= 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.reset_runtime
|
15
|
+
rt, self.runtime = runtime, 0
|
16
|
+
rt
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
super
|
21
|
+
@odd_or_even = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def query(event)
|
25
|
+
self.class.runtime += event.duration
|
26
|
+
return unless logger.debug?
|
27
|
+
|
28
|
+
identifier = color('Sphinx Query (%.1fms)' % event.duration, GREEN, true)
|
29
|
+
query = event.payload[:query]
|
30
|
+
query = color query, nil, true if odd?
|
31
|
+
|
32
|
+
debug " #{identifier} #{query}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def message(event)
|
36
|
+
return unless logger.debug?
|
37
|
+
|
38
|
+
identifier = color 'Sphinx', GREEN, true
|
39
|
+
message = event.payload[:message]
|
40
|
+
message = color message, nil, true if odd?
|
41
|
+
|
42
|
+
debug " #{identifier} #{message}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def odd?
|
46
|
+
@odd_or_even = !@odd_or_even
|
47
|
+
end
|
48
|
+
|
49
|
+
def logger
|
50
|
+
return @logger if defined? @logger
|
51
|
+
self.logger = ::ActiveRecord::Base.logger
|
52
|
+
end
|
53
|
+
|
54
|
+
def logger=(logger)
|
55
|
+
@logger = logger
|
56
|
+
end
|
57
|
+
|
58
|
+
attach_to :thinking_sphinx
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|